路漫漫其修远兮
吾将上下而求索

ansible学习:playbook,template,role

1 playbook基础组件

1.1 yaml语言介绍

YAML:   #将所有的需要执行的命令放置在一个文件中,在每台主机都将这个文件执行一遍,所有任务都可以执行完成。就像脚本来完成多个任务一样

YAML is a data serialization format designed for human readability and  interaction with scripting languages.

安装:[root@localhost ~]#yum info PyYAML

数据结构:
    key:value
    
    - item1
    - item2
    - item3
    
    {name:jerry, age:21}

1.2 Hosts和Users

playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,其可以是一个或多个由冒号分隔主机组;remote_user则用于指定远程主机上的执行任务的用户。如上面示例中的        

   -hosts: webnodes
     remote_user: root

不过,remote_user也可用于各task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户。

 - hosts: webnodes
      remote_user: andy
      tasks:
        - name: test connection
          ping:
          remote_user: andy
          sudo: yes

1.3 任务列表和action

play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自下而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。

task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致。

每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出。

定义task的可以使用“action: module options”或“module: options”的格式,推荐使用后者以实现向后兼容。如果action一行的内容过多,也中使用在行首使用几个空白字符进行换行。

tasks:
  - name: make sure apache is running
    service: name=httpd state=running

在众多模块中,只有command和shell模块仅需要给定一个列表而无需使用“key=value”格式,例如:
    tasks:
      - name: disable selinux
        command: /sbin/setenforce 0

如果命令或脚本的退出码不为零,可以使用如下方式替代:
    tasks:
      - name: run this command and ignore the result
        shell: /usr/bin/somecommand || /bin/true        

或者使用ignore_errors来忽略错误信息:
    tasks:
      - name: run this command and ignore the result
        shell: /usr/bin/somecommand
        ignore_errors: True

1.4 handlers

用于当关注的资源发生变化时采取一定的操作。

“notify”这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作。
在notify中列出的操作称为handler,即notify中调用handler中定义的操作。
    - name: template configuration file
      template: src=template.j2 dest=/etc/foo.conf
      notify:
         - restart memcached
         - restart apache   

handler是task列表,这些task与前述的task并没有本质上的不同。
    handlers:
        - name: restart memcached
          service:  name=memcached state=restarted
        - name: restart apache
          service: name=apache state=restarted

案例:当 /etc/httpd/conf/httpd.conf 文件改变的时候,即源文件和目标文件不同,重启 httpd 服务

httpd.yaml
- hosts: hbhosts
  remote_user: root
  tasks:
    - name: ensure httpd latest version
      yum: name=httpd state=present
    - name: authkeys configure file
      copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf force=yes
      notify:
        - restart httpd
    - name: ha.cf configure file
      copy: src=/root/hb_conf/ha.cf dest=/etc/ha.d/ha.cf
      notify: 
        - restart abc
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted
    - name: restart abc
      service: name=abc state=restarted

PlayBook的核心元素:

Tasks:任务,由模块定义的操作的列表;
Variables:变量
Templates:模板,即使用了模板语法的文本文件;
Handlers:由特定条件触发的Tasks;
Roles:角色;

playbook的基础组件:
    Hosts:运行指定任务的目标主机;
    remote_user:在远程主机以哪个用户身份执行;
        sudo_user:非管理员需要拥有sudo权限;
    tasks:任务列表
        模块,模块参数:
            格式:
                (1) action: module arguments
                (2) module: arguments
                
示例1:
    - hosts: all
    remote_user: root   #以哪个用户身份运行
    tasks:
    - name: install a group #每个任务要起一个名字
      group: name=mygrp system=true     #模块名:参数
    - name: install a user
      user: name=user1 group=mygrp system=true  #模块名:参数

    - hosts: websrvs
    remote_user: root
    tasks:
    - name: install httpd package
      yum: name=httpd
    - name: start httpd service 
      service: name=httpd state=started

运行playbook,使用ansible-playbook命令

(1) 检测语法
    ansible-playbook  --syntax-check  /path/to/playbook.yaml
(2) 测试运行,干跑
    ansible-playbook -C /path/to/playbook.yaml  #干跑的时候有的服务因为前面没有实际安装会执行不成功,实际运行的时候可以运行
        --list-hosts    #影响的主机
        --list-tasks
        --list-tags
(3) 运行
    ansible-playbook  /path/to/playbook.yaml
        -t TAGS, --tags=TAGS    #只执行某个标签
        --skip-tags=SKIP_TAGS   #跳过指定的标签任务
        --start-at-task=START_AT    #从指定的某个任务开始运行
                        
主机:192.168.170.21
[root@localhost ~]#cat test.yaml    #注意缩进,好像很严格
- hosts: all
  remote_user: root
  tasks:
  - name: install a group
    group: name=mygrp system=true
  - name: install a user
    user: name=user1 group=mygrp system=true

- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name=httpd
  - name: start httpd service
    service: name=httpd state=started
                        
[root@localhost ~]#ansible-playbook  ./test.yaml    #执行操作


主机:192.168.170.21
[root@localhost ~]#cat test.yaml    #在root目录下面有一个httpd.conf文件,里面监听的端口是8080,
#让远程的主机安装好httpd服务,并将ansible主机的配置文件拷贝过去,启动服务
- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name=httpd
  - name: cp a conf file
    copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf
  - name: start httpd service
    service: name=httpd state=started

在其他主机看到,监听端口是8080

handlers:由特定条件触发的Tasks;

调用及定义方式:

tasks:
- name: TASK_NAME   #当确实执行这个命令的时候,会执行notify,只有当文件改变的时候才会复制文件,
才会执行notify。当两边的文件相同,文件不变,不会执行复制命令,那么notify就不会执行,还要再看
  module: arguments
  notify: HANDLER_NAME
handlers:
- name: HANDLER_NAME
  module: arguments
  
示例:
[root@localhost ~]#cat test.yaml 
- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name=httpd
  - name: cp a conf file
    copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf
    notify: reload httpd service 
  - name: start httpd service
    service: name=httpd state=started
  handlers: 
  - name: reload httpd service 
    shell: systemctl reload httpd.service

tags:给指定的任务定义一个调用标识;

格式 
    - name: NAME
      module: arguments
      tags: TAG_ID
                  
[root@localhost ~]#cat test.yaml 
- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name=httpd
  - name: cp a conf file
    copy: src=/root/httpd.conf dest=/etc/httpd/conf/httpd.conf
    notify: reload httpd service 
    tags: abc   #调用标签前要先指定标签名字
  - name: start httpd service
    service: name=httpd state=started
                  
[root@localhost ~]#ansible-playbook -t abc ./test.yaml  #只执行abc标签的命令
[root@localhost ~]#ansible-playbook -skip-tags=abc ./test.yaml  #跳过abc标签的命令

变量Variables:

类型:
    内建:
        (1) facts
    自定义:
        (1) 命令行传递;
            -e VAR=VALUE
        (2) 在playbook中定义
            vars:
            - var_name: value
            - var_name: value   
        (3) 在hosts Inventory中为每个主机定义专用变量值;在hosts文件中添加
            (a) 向不同的主机传递不同的变量 ;
                IP/HOSTNAME variable_name=value
            (b) 向组内的所有主机传递相同的变量 ;
                [groupname:vars] 
                variable_name=value
        (4) Inventory还可以使用参数:
            用于定义ansible远程连接目标主机时使用的属性,而非传递给playbook的变量;连接ssh的时候的属性
                ansible_ssh_host
                ansible_ssh_port
                ansible_ssh_user
                ansible_ssh_pass
                ansible_sudo_pass
                ...
                
        (5) 在角色调用时传递
            roles:
            - { role: ROLE_NAME, var: value, ...}

变量调用:
    {{ var_name }}

主机:192.168.170.21                   
[root@localhost ~]#cat test.yaml 
- hosts: websrvs
  remote_user: root
  tasks:
  - name: install httpd package
    yum: name={{ pkgname }}

[root@localhost ~]#ansible-playbook -e pkgname=vsftpd ./test.yaml   #在命令行中指定变量的值就可以执行

或者这样配置,将变量定义在内部也可以
主机:192.168.170.21                   
[root@localhost ~]#cat test.yaml 
- hosts: websrvs
  remote_user: root
  vars:
  - pkgname: memcached
  tasks:
  - name: install httpd package
    yum: name={{ pkgname }}

[root@localhost ~]#ansible-playbook ./test.yaml #在命令行中指定变量的值就可以执行

Templates:模板,配置每个主机的时候的配置文件不一定相同,具体的参数跟主机的某些属性有关系

文本文件,内部嵌套有模板语言脚本(使用模板语言编写),这是一个配置文件,里面的内容就像php嵌套在html中,只有变量才会被替换,然后将配置文件传递给ansible去配置主机,

Jinja2 is a template engine written in pure Python.  It provides a  Django inspired non-XML syntax but supports inline expressions and an optional sandboxed environment.

语法:
    字面量:
        字符串:使用单引号或双引号; 
        数字:整数、浮点数;
        列表:[item1, item2, ...]
        元组:(item1, item2, ...)
        字典:{key1:value1, key2:value2, ...}
        布尔型:true/false
        
    算术运算:
        +, -, *, /, //, %, **
        
    比较操作:
        ==, !=, >, <, >=, <=
        
    逻辑运算:and, or, not 
    
执行模板文件中的脚本,并生成结果数据流,需要使用template模块;
    template:
        -a ”“
            src=
            dest=
            mode=
            onwer=
            group=
            
    注意:此模板不能在命令行使用,而只能用于playbook;
                
示例:根据模板,不同核心数开启不同数量的进程

主机:192.168.170.21
[root@localhost ~]#ansible all -m setup | grep cpu  #为了测试,将其中一个虚拟机的核心数调整为4
        "ansible_processor_vcpus": 4, 
        "ansible_processor_vcpus": 1, 
        "ansible_processor_vcpus": 1, 

将一个nginx的配置文件放在root目录下面,修改
[root@localhost ~]#mv nginx.conf nginx.conf.j2 #为了便于区别,将配置文件后缀名改为:j2

[root@localhost ~]#cat nginx.conf.j2 
user nginx;
worker_processes {{ ansible_processor_vcpus }}; #cpu的数量修改为变量,这个变量就是上面setup输出的变量名称
...

[root@localhost ~]#cat test.yaml 
- hosts: websrvs
  remote_user: root
  tasks:
  - name: install nginx package
    yum: name=nginx state=latest
  - name: cp a conf file
    template: src=/root/nginx.conf.j2 dest=/etc/nginx/nginx.conf    #使用template复制过去的配置文件,里面的变量会被自动其值替换。用copy命令复制过去不能替换变量
  - name: start nginx
    service: name=nginx state=started enabled=true

主机:12.18.1.21
[root@localhost ~]#cat /etc/nginx/nginx.conf
user nginx;
worker_processes 4; #自动替换为其变量的值

并且也开启了四个进程,测试成功
root       4106  0.0  0.2 122220  2216 ?        Ss   20:36   0:00 nginx: master process /usr/sbin/nginx
nginx      4107  0.0  0.4 124732  3492 ?        S    20:36   0:00 nginx: worker process
nginx      4108  0.0  0.4 124732  3492 ?        S    20:36   0:00 nginx: worker process
nginx      4109  0.0  0.4 124732  3492 ?        S    20:36   0:00 nginx: worker process
nginx      4110  0.0  0.4 124732  3492 ?        S    20:36   0:00 nginx: worker process

条件判断,循环:

when语句:在tasks中使用,Jinja2的语法格式;在条件满足的情况下会执行

    -   hosts: all
    remote_user: root
    tasks:
    - name: install nginx package
    yum: name=nginx state=latest
    - name: start nginx service on CentOS6
    shell: service nginx start
    when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "6"    #变量为centos6执行
    - name: start nginx service
    shell: systemctl start nginx.service
    when: ansible_distribution == "CentOS" and ansible_distribution_major_version == "7"    #变量为centos7执行
    
循环:迭代,需要重复执行的任务;
对迭代项的引用,固定变量名为"item”,只能使用item;使用with_item属性给定要迭代的元素;
    元素:列表
        字符串
        字典
    
基于字符串列表给出元素示例:用循环来安装多个程序包
主机:192.168.170.21               
[root@localhost ~]#cat test.yaml    #这样,所有的主机都会安装上下面的的rpm包,提高效率
- hosts: websrvs
  remote_user: root
  tasks:
  - name: install package
    yum: name={{ item }} state=latest
    with_items:
    - httpd
    - php
    - php-mysql
    - php-gd
    

基于字典列表给元素示例:
主机:192.168.170.21
[root@localhost ~]#cat test.yaml 
- hosts: websrvs
  remote_user: root
  tasks:
  - name: create groups
    group: name={{ item }} state=present
    with_items:
    - groupx1
    - groupx2
    - groupx3
  - name: create users
    user: name={{ item.name }} group={{ item.group }} state=present 
    #这里的这个item和上面创建组的item不一样,是两个变量名,没有关系。这里有三个item,每个item有两个元素,
    #创建用户的时候看需要item中的哪个元素,放上去,就可以,这里的元素多个应该都可以
    with_items:
    - {name: 'userx1', group: 'groupx1'}
    - {name: 'userx2', group: 'groupx2'}
    - {name: 'userx3', group: 'groupx3'}

ansible以任务为中心,所有主机跑完第一个任务,然后所有的任务才跑第二个主机

角色:roles

以特定的层级目录结构进行组织的tasks、variables、handlers、templates、files等;
	role_name/
		files/:存储由copy或script等模块调用的文件; 
		tasks/:此目录中至少应该有一个名为main.yml的文件,用于定义各task;其它的文件需要由main.yml进行“包含”调用;
		handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler;其它的文件需要由main.yml进行“包含”调用;
		vars/:此目录中至少应该有一个名为main.yml的文件,用于定义各variable;其它的文件需要由main.yml进行“包含”调用;
		templates/:存储由template模块调用的模板文本;
		meta/:此目录中至少应该有一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要由main.yml进行“包含”调用;
		default/:此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;
		
在playbook中调用角色的方法:
	- hosts: HOSTS
	  remote_user: USERNAME
	  roles:
	  - ROLE1
	  - ROLE2
	  - { role: ROLE3, VARIABLE: VALUE, ...}
	  - { role: ROLE4, when: CONDITION }
                  
                  
[root@localhost /etc/ansible/roles]#mkdir ./{nginx,memcached,httpd,mysql}/{files,templates,vars,handlers,meta,default,tasks} -pv

[root@localhost /etc/ansible/roles]#tree nginx
nginx
├── default
├── files
│   └── nginx-1.10.0-1.el7.ngx.x86_64.rpm   #这个rpm包是从ftp上面下载的,以这个rpm包和对应的配置文件为例子。用yum上面的rpm包也可以,
├── handlers
│   └── main.yml
├── meta
├── tasks
│   └── main.yml
├── templates
│   ├── default.conf.j2
│   └── nginx.conf.j2
└── vars
    └── main.yml

7 directories, 6 files
                  
[root@localhost /etc/ansible/roles/nginx/tasks]#cat main.yml 
- name: copy nginx package to remote file
  copy: src=nginx-1.10.0-1.el7.ngx.x86_64.rpm dest=/tmp/nginx-1.10.0-1.el7.ngx.x86_64.rpm   #文件默认去files目录下面去找
  tags: cprpm
- name: install nginx packages
  yum: name=/tmp/nginx-1.10.0-1.el7.ngx.x86_64.rpm state=present
- name: install conf file
  template: src=nginx.conf.j2  dest=/etc/nginx/nginx.conf   #这里的template文件不用指明目录位置,以.j2结尾的文件会自动去template目录中查找,只用写文件名就可以,修改了cpu数量
  tags: ngxconf
  notify: reload nginx service
- name: install default file    #将default.conf配置文件修改后也传递过去,修改了监听的端口,由自己定义变量来赋值
  template: src=default.conf.j2  dest=/etc/nginx/conf.d/default.conf
  tags: ngxconf
  notify: reload nginx service
- name: start nginx service
  service: name=nginx enabled=true state=started
  
  
[root@localhost /etc/ansible/roles/nginx/templates]#cat nginx.conf.j2 

user  nginx;
worker_processes  {{ ansible_processor_vcpus }};
  
[root@localhost /etc/ansible/roles/nginx/templates]#cat default.conf.j2 
server {
    listen       {{ ngxport }};     #这个变量自己定义
  
  
                  
[root@localhost /etc/ansible/roles/nginx/handlers]#cat main.yml 
- name: reload nginx service 
  service: name=nginx state=restarted
                  

[root@localhost /etc/ansible/roles/nginx/vars]#cat main.yml 
ngxport: 8090
                  
                  
-----------------------------------------------------------               
[root@localhost /etc/ansible]#vim ansible.cfg   #查看ansible默认去哪里找roles文件,将文件放置在定义的路径中                  
roles_path    = /etc/ansible/roles      


[root@localhost ~]#cat nginx.yml    #这里是剧本,来调用上面的所有文件,放到下面定义的路径中
- hosts: ngxsrvs
  remote_user: root
  roles:
  - nginx

[root@localhost ~]#ansible-playbook --syntax-check nginx.yml    #显示没有语法错误

playbook: nginx.yml
  
[root@localhost ~]#ansible-playbook -t cprpm ./nginx.yml    #先执行复制rpm命令,成功            
[root@localhost ~]#ansible-playbook ./nginx.yml     #执行成功,监听的端口,进程的数量都正确

这里有很多示例值得参考

https://github.com/ansible/ansible-examples

https://my.oschina.net/jastme/blog/510707  

未经允许不得转载:江哥架构师笔记 » ansible学习:playbook,template,role

分享到:更多 ()

评论 抢沙发

评论前必须登录!