经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 数据库/运维 » Linux/Shell » 查看文章
devops工具-Ansible进阶playbook&roles - W-D
来源:cnblogs  作者:W-D  时间:2019/3/26 8:36:50  对本文有异议

一、playbook介绍

    playbook 是 Ansible 管理配置、部署应用的核心所在,一个playbook由有多“play组成”,而一个play实际就是一个task,每个task是由多个ansible基本模块构成,这样我们可以用 playbook 来描述想在远程主机执行的一些列操作,包括安装部署、配置管理、任务处理等等。
    playbook是通过yaml格式来定义的,支持同步和异步方式来运行,运行顺序是从上到下运行每个我们定义的task,从而实现各种复杂任务。关于yaml语法可以参考这里。 

二、核心元素

一个playbook中比较核心的组成由以下几部分组成:
  • Hosts和User:主机列表和用户,定义一个playbook操作的远程主机以及登录的用户
  • Tasks:tasks代表任务集,定义远程主机运行的任务列表,
  • Variables:变量,可以在playbook中传递变量
  • Templates:模版,复用配置文件,jinja2模版引擎
  • Handlers和Notify:触发器,用于在task任务结束后,可以触发其他操作 

hosts和users

  hosts代表是playbook中的主机列表,可以是主机组、单独主机、多个主机(中间以冒号分隔开)、还可以使用通配符,users代表登录目标主机的用户,使用remote_user指定,当需要使用sudo进行操作时候,需要制定become参数。
  1. ---
  2. - hosts: test #指定主机组
  3. remote_user: root #指定ssh登录的用户
  4. tasks:
  5. - name: test server
  6. ping:

使用sudo切换

  1. ---
  2. - hosts: test
  3. remote_user: root
  4. become: yes #become参数在2.6版本用于指定sudo
  5. become_user: admin #sudo的用户
  6. tasks:
  7. - name: test server
  8. ping:

tasks

  Tasks是playbook中核心组成成分部分,用于在定义远程主机执行的一系列操作,这里成为任务集。每个task运行顺序按照playbook文件中从上到下依次运行,当某个task运行出错时停止(可以使用ignore_erros参数忽略错误改变)。每个task须有个name用于标记此次的任务名称,同时也能友好方便我们阅读结果。
定义语法: 
  1. tasks:
  2. - name: task_name #任务名称
  3. module: module_args #使用的模块,以及模块参数,列如yum: name=nginx state=present

示例:
安装一个httpd服务并启动: 
  1. ---
  2. - hosts: 10.1.210.51
  3. remote_user: root
  4. tasks:
  5. - name: install package #使用yum安装包
  6. yum: name=httpd state=present
  7. - name: start httpd #启动该服务
  8. service: name=httpd state=started

执行:

variables

  playbook可使用变量来代替某些重复性的字符串,定义变量方式有多种,而引用变量只需要通过双括号引用(Jinji2模版语法),例如:{{ var_name }}。 变量命名仅能由字母、数字和下划线组成,且只能以字母开头。
变量定义:
1.直接使用facts
在上一篇文章红介绍了setup模块收集facts信息,而这些信息可以用来作为我们playbook的变量使用,例如ansible_all_ipv4_addresses这个facts变量保存的是主机的IP地址。
示例:
  1. ---
  2. - hosts: 10.1.210.53
  3. remote_user: root
  4. tasks:
  5. - name: test facts var
  6. shell: echo "{{ ansible_all_ipv4_addresses }}" > host_ip.txt

2.主机清单中定义变量
格式: 
  1. #单独主机变量,优先级高于公共变量
  2. host varname=value
  3. #主机组变量
  4. [groupname:vars] #为指定主机组自定义变量,vars为关键字
  5. varname=value

列如:

  1. vi /etc/ansible/hosts
  2. 10.1.210.51 business=card #变量名为business值为card
  3. 10.1.210.53
  4. [dev]
  5. 10.1.210.33
  6. 10.1.210.32
  7. [dev:vars] #组变量定义
  8. myname=wd

示例:

  1. [root@app52 ~]# cat test.yaml
  2. ---
  3. - hosts: 10.1.210.51
  4. remote_user: root
  5. tasks:
  6. - name: test facts from hosts
  7. debug: var=business #使用debug打印变量的值,var参数测试变量不需要用{{ }}

运行playbook:

3.playbook中定义变量

格式:

  1. vars:
  2. - varname1: value1
  3. - varname2: value2

示例:

  1. ---
  2. - hosts: 10.1.210.51
  3. remote_user: root
  4. vars:
  5. - pkg_name: httpd
  6. - pkg_cmd: /usr/sbin/httpd
  7. tasks:
  8. - name: restart httpd
  9. service: name={{ pkg_name }} state=restarted
  10. - name: copy cmd
  11. copy: src={{ pkg_cmd }} dest=/tmp/

运行playbook:

4.命令行中定义变量

  1. ansible-playbook -e 'varname=value’ #该方式定义的变量优先级最高

5.通过文件引用变量。

  1. [root@app52 ~]# cat /tmp/myvars.yml
  2. var1: value1
  3. var2: value2

playbook中使用

  1. ---
  2. - hosts: 10.1.210.51
  3. remote_user: root
  4. vars_files:
  5. - /tmp/myvars.yml #变量文件
  6. tasks:
  7. - name: test myvar
  8. debug: msg="{{ var1 }}/{{ var2 }}" #这里的var1变量和var2变量来自于文件/tmp/myvars.yml

运行结果:

6.在roles中定义变量,后续在介绍roles在讲解。

Templates

  不同的远程主机配置文件是一般不相同的,不可能每次都要修改配置文件,所有就出现了模板技术(魔模块template实现)。通过定义一份模板,在进行配置文件推送时候,会根据我们定义好的变量进行替换,从而达到不同的主机配置文件是不同且符合我们预期的。 templates模版语法采用Jinja2模版语法,参考这里
使用方法:
1.定义模版,文件名称必须以.j2结尾
  1. vim /tmp/nginx.conf.j2
  2. user nginx;
  3. worker_processes {{ ansible_processor_vcpus }}; #使用cpu个数作为woeker数量
  4. error_log /var/log/nginx_error.log crit;
  5. pid /var/run/nginx.pid;
  6. include /usr/share/nginx/modules/*.conf;
  7. worker_rlimit_nofile 65535;
  8. events {
  9. worker_connections 1024;
  10. }
  11. http {
  12. include mime.types;
  13. default_type application/octet-stream;
  14. sendfile on;
  15. keepalive_timeout 65;
  16. gzip on;
  17. server
  18. {
  19. listen 80;
  20. server_name {{ ansible_fqdn }} #使用主机名作为hostname
  21. access_log /var/log/access.log;
  22. error_log /var/log/error.log;
  23. }
  24. }

2.playbook中使用

  1. ---
  2. - hosts: 10.1.210.53
  3. remote_user: root
  4. vars:
  5. - package_name: nginx
  6. - config_path: /etc/nginx/nginx.conf
  7. tasks:
  8. - name: install epel
  9. yum: name=epel-release
  10. - name: install package
  11. yum: name={{ package_name }}
  12. - name: copy config file
  13. template: src=/tmp/nginx.conf.j2 dest={{ config_path }} backup=yes #backup用于备份
  14. - name: start service
  15. service: name={{ package_name }} state=started

运行playbook

Handlers和notify

  Handlers是task列表,这些task与tasks中的task并没有本质上的不同,用于当关注的资源发生变化时,才会采取一定的操作。而触发的行为则是由notify产生,可以理解为notify通知Handlers触发某个task。
    需要注意的是,不管发生多少次notify行为,等到play中的所有task执行完成之后,handlers中的task才会被执行,而这些task只会被执行一次;
   notify可以定义多个,多个notify用列表方式来表示。
示例:
  1. - hosts: 10.1.210.53
  2. remote_user: root
  3. vars:
  4. - package_name: nginx
  5. - config_path: /etc/nginx/nginx.conf
  6. tasks:
  7. - name: install epel
  8. yum: name=epel-release
  9. - name: install package
  10. yum: name={{ package_name }}
  11. - name: copy config file
  12. template: src=/tmp/nginx.conf.j2 dest={{ config_path }} backup=yes
  13. notify: #当配置文件发生变化时候,通知hanler触发task
  14. - stop service
  15. - start service
  16. handlers:
  17. - name: stop service
  18. service: name={{ package_name }} state=stopped
  19. - name: start service
  20. service: name={{ package_name }} state=started

运行playbook

三、playbook进阶技巧 

使用tag标记任务

为某个任务打一个标签,可以使用ansible-playbook 命令选择性的执行任务。打标签可通过tags标签指定,可以是与其相关的参数:
  • --list-tags :列出playbook中所有的tag
  • --skip-tags:跳过playbook中被标记的tag任务
  • -t 或 --tags:指定运行playbook中被标记的tag任务
 
以下playbook中分别定义了将两个tag进行了标记:
  1. ---
  2. - hosts: 10.1.210.51
  3. remote_user: root
  4. tasks:
  5. - name: exec tag1
  6. shell: cat /etc/passwd
  7. tags: tag1
  8. - name: exec tag2
  9. file: path=/tmp/b.txt state=touch
  10. tags: tag2
  11. - name: view password
  12. shell: cat /etc/passwd

列出tag

指定运行某个tag任务

跳过tag任务运行

从以上结果可看出,跳过了tag1和tag2任务,只运行了view password任务。

 

条件测试when

when关键字可实现根据某些条件判断当前的task是否需要执行。when可根据变量、facts(setup)或此前任务的执行结果来作为判断依据,同时支持逻辑运算符操作,比较强大和实用。
需要注意的是:
  • when判断的对象是task,所以和tas k在同一列表层次。它的判断结果决定它所在task是否执行,而不是它下面 的task是否执行。
  • when中引用变量的时候不需要加{{ }}符号。

示例一:使用facts变量测试

  1. tasks:
  2. - name: "shut down Debian flavored systems"
  3. command: /sbin/shutdown -t now
  4. when: ansible_facts['os_family'] == "Debian" #当操作系统是Debian才执行该任务

示例二:使用逻辑运算进行多条件判断

  1. tasks:
  2. - name: "shut down CentOS 6 and Debian 7 systems"
  3. command: /sbin/shutdown -t now
  4. when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or
  5. (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")

两个条件使用and还可以如下表示

  1. tasks:
  2. - name: "shut down CentOS 6 systems"
  3. command: /sbin/shutdown -t now
  4. when:
  5. - ansible_facts['distribution'] == "CentOS"
  6. - ansible_facts['distribution_major_version'] == "6" #同时满足系统是CentOS且版本是6

示例三:根据任务的执行结果状态来判断任务是否执行,这里使用register来保存了任务执行结果,后续会介绍。

  1. ---
  2. - hosts: 10.1.210.51
  3. name: test when
  4. remote_user: root
  5. tasks:
  6. - name: task1
  7. command: /bin/false
  8. register: result #将本次任务结果保存在变量中
  9. ignore_errors: True #忽略错误执行,可确保任务错误也能执行下一个任务
  10.  
  11. - name: pre task failed
  12. file: path=/tmp/failed.txt state=touch
  13. when: result is failed #判断result结果是否是failed
  14.  
  15. - name: pre task successed state=touch
  16. file: path=/tmp/success.txt
  17. when: result is succeeded #判断result结果是否是succeeded
  18.  
  19. - name: pre task skipped state=touch
  20. file: path=/tmp/skip.txt
  21. when: result is skipped #判断结果是否是skipped

运行一下:

如上图,红色的部分任务都跳过了未执行,这是因为result结果是failded,所以只执行了“pre task failed”这个任务。

示例4:使用变量进行测试

  1. ---
  2. - hosts: 10.1.210.51
  3. name: test var when
  4. remote_user: root
  5. vars:
  6. - flag: true #变量定义
  7. tasks:
  8. - name: test server
  9. ping:
  10. when: flag #判断变量flag为true执行
  11.  
  12. - name: create file
  13. file: path=/tmp/myfile.txt state=touch
  14. when: not flag #变量flag不为true执行

执行结果:

同样可以看到create file 任务被跳过了。

使用register保存任务结果

  register注册变量,用于保存任务结果,可用于保存配置或when关键字判断,这是非常有用了,在上个示例中使用了任务结果作为判断条件。例如,你可以将文件中的配置或者json内容保存在变量中,可以供后续使用:

  1. ---
  2. - hosts: 10.1.210.51
  3. name: test register
  4. remote_user: root
  5. tasks:
  6. - name: read conf
  7. shell: cat /tmp/nginx.conf
  8. register: nginx_conf #注册变量
  9.  
  10. - name: copy conf
  11. copy: content={{ nginx_conf }} dest=/etc/nginx/nginx.conf #使用变量

还可以配合循环使用,以下示例展示了批量创建软链接:

  1. - name: registered variable usage as a loop list
  2. hosts: all
  3. tasks:
  4. - name: retrieve the list of home directories
  5. command: ls /home
  6. register: home_dirs
  7. - name: add home dirs to the backup spooler
  8. file:
  9. path: /mnt/bkspool/{{ item }}
  10. src: /home/{{ item }}
  11. state: link
  12. loop: "{{ home_dirs.stdout_lines }}"

使用迭代进行重复性操作或任务

当有需要重复性执行的任务时,可以使用迭代机制。其迭代的内容会保存在特殊变量item中,ansible提供很多迭代指令,大多都是以with_开头,以下是几种常见的循环。 
1.with_items迭代列表
示例一:创建多个用户 
  1. ---
  2. - hosts: localhost
  3. remote_user: root
  4. tasks:
  5. - name: create user
  6. user: name={{ item }} state=present
  7. with_items:
  8. - zabbix
  9. - admin

执行结果:

示例二:循环中使用register注册变量

  1. - hosts: localhost
  2. remote_user: root
  3. tasks:
  4. - command: echo {{ item }}
  5. with_items: [ 0, 2, 4, 6, 8, 10 ]
  6. register: num
  7. - debug: msg="{% for i in num.results %} {{i.stdout}} {% endfor %}"

注意,将with_items迭代后的结果注册为变量时,其注册结果也是列表式的,且其key"results"。具体的结果比较长,可以使用debug模块的varmsg参数观察变量的结果。以上示例运行结果如下:

2.with_dict迭代字典

使用"with_dict "可以迭代字典项。迭代时,使用"item.key"表示字典的key"item.value"表示字典的值。
示例一:
  1. ---
  2. - hosts: localhost
  3. remote_user: root
  4. tasks:
  5. - debug: msg="{{ item.key }} / {{ item.value }}"
  6. with_dict: { ip: 10.1.210.51, hostname: app52, gateway: 10.1.210.1}

以上示例中字典是已经存在了,除此之外字典可以来源于变量、facts等。例如使用facts进行迭代

  1. ---
  2. - hosts: localhost
  3. remote_user: root
  4. tasks:
  5. - debug: msg="{{item.key}} / {{item.value}}"
  6. with_dict: "{{ ansible_cmdline }}"

 

使用include简化playbook

 如果将所有的play都写在一个playbook中,很容易导致这个playbook文件变得臃肿庞大,且不易读。因此,可以将多个不同任务分别写在不同的playbook中,然后使用include将其包含进去即可。include可以导入两种文件:导入task文件、导入playbook。

示例:创建task.yml任务列表

  1. vim task.yml
  2. - name: task1
  3. debug: msg="exec task1"
  4. - name: task2
  5. debug: msg="exec task2"

在目标playbook中倒入任务

  1. ---
  2. - hosts: 10.1.210.51
  3. remote_user: root
  4. tasks:
  5. - include: task.yml

执行playbook:

示例2:直接导入其他playbook,不过在ansible2.8将移除,2.8中将使用import_playbook 

  1. ---
  2. - hosts: 10.1.210.51
  3. remote_user: root
  4. tasks:
  5. - include: task.yml
  6. - include: test_when.yml
  7. - include: test_with.yml

四、roles(角色)介绍

简介

  roles 就字面上来说有角色、作用的意思,但它的全名其实是 Playbooks Roles,我们可把它当成是 playbooks 的延伸使用,可以降低 playbooks 的复杂性,更可以增加 playbooks 的可用性。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。

使用场景

  • 同时安装多个不同的软件如:LNMP环境
  • 不同服务器组需要安装不同服务
  • 复杂的playbook,使用role可以具有阅读性 

目录结构

一个角色中目录包含以下目录:
  • files:用来存放由copy模块或script模块调用的文件。
  • templates:用来存放jinjia2模板,template模块会自动在此目录中寻找jinjia2模板文件。
  • tasks:此目录应当包含一个main.yml文件,用于定义此角色的任务列表,此文件可以使用include包含其它的位于此目录的task文件。
  • handlers:此目录应当包含一个main.yml文件,用于定义此角色中触发条件时执行的动作。
  • vars:此目录应当包含一个main.yml文件,用于定义此角色用到的变量。
  • defaults:此目录应当包含一个main.yml文件,用于为当前角色设定默认变量。
  • meta:此目录应当包含一个main.yml文件,用于定义此角色的元数据信息。

   例如:一个nginx角色的目录结构可以是:

  1. .
  2. └── nginx
  3. ├── default
  4. ├── files
  5. ├── handlers
  6. ├── meta
  7. ├── tasks
  8. ├── templates
  9. └── vars

多个role目录:

  1. ├── httpd #http role
  2. ├── default
  3. ├── files
  4. ├── handlers
  5. ├── meta
  6. ├── tasks
  7. ├── templates
  8. └── vars
  9. └── nginx #nginx role
  10. ├── default
  11. ├── files
  12. ├── handlers
  13. ├── meta
  14. ├── tasks
  15. ├── templates
  16. └── vars

演示:使用role安装nginx

一、创建对应的目录结构:

  1. [root@app52 ~]# mkdir -pv roles/nginx/{files,templates,vars,tasks,handlers,meta,default}
  2. mkdir: 已创建目录 "roles"
  3. mkdir: 已创建目录 "roles/nginx"
  4. mkdir: 已创建目录 "roles/nginx/files"
  5. mkdir: 已创建目录 "roles/nginx/templates"
  6. mkdir: 已创建目录 "roles/nginx/vars"
  7. mkdir: 已创建目录 "roles/nginx/tasks"
  8. mkdir: 已创建目录 "roles/nginx/handlers"
  9. mkdir: 已创建目录 "roles/nginx/meta"
  10. mkdir: 已创建目录 "roles/nginx/default”

二、定义变量

  1. [root@app52 ~]# vi roles/nginx/vars/main.yml
  2. pkg_name: nginx #安装包名称
  3. listen_port: 80 #监听端口

三、编写任务

这里可以把任务模块化,最后在main.yml包含它们

  1. [root@app52 ~]# vi roles/nginx/tasks/yum.yml
  2. - name: install epel
  3. yum: name=epel-release state=present
  4. - name: install nginx pkg
  5. yum: name={{ pkg_name }} state=present
  6. [root@app52 ~]# vi roles/nginx/tasks/copy.yml
  7. - name: copy nginx.conf
  8. template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
  9. - name: copy index.html
  10. copy: src=index.html dest=/var/www/html/
  11. notify: reload
  12. [root@app52 ~]# vi roles/nginx/tasks/start_service.yml
  13. - name: start nginx
  14. service: name=nginx state=restarted
  15. [root@app52 ~]# vi roles/nginx/tasks/main.yml
  16. - include: yum.yml
  17. - include: copy.yml
  18. - include: start_service.yml

四、准备配置文件以及index.html

  1. #index.html
  2. [root@app52 ~]# vi roles/nginx/files/index.html
  3. <h1>Hello wd</h1>
  4.  
  5. #配置文件模版
  6. [root@app52 ~]# vi roles/nginx/templates/nginx.conf.j2
  7. user nginx;
  8. worker_processes {{ ansible_processor_vcpus }}; #使用cpu个数作为woeker数量
  9. error_log /var/log/nginx_error.log crit;
  10. pid /var/run/nginx.pid;
  11. include /usr/share/nginx/modules/*.conf;
  12. worker_rlimit_nofile 65535;
  13. events {
  14. worker_connections 1024;
  15. }
  16. http {
  17. include mime.types;
  18. default_type application/octet-stream;
  19. sendfile on;
  20. keepalive_timeout 65;
  21. gzip on;
  22. server
  23. {
  24. listen {{ listen_port }};
  25. server_name {{ ansible_all_ipv4_addresses[0] }} ; #使用IP地址作为server name
  26. root /var/www/html ;
  27. access_log /var/log/access.log;
  28. error_log /var/log/error.log;
  29. }
  30. }

五、编写handlers

如果在task中使用了notify,则就需要写对应的handlers,上述我使用了reload这个handler,所以这里需要定义:

  1. [root@app52 ~]# vi roles/nginx/handlers/main.yml
  2. - name: reload
  3. service: name=nginx state=reloaded

六、在角色同级目录编写playbook引入角色

  1. [root@app52 ~]# vi roles/install_nginx.yml
  2. - hosts: web #指定使用role的主机或主机组
  3. remote_user: root #指定用户
  4. roles: #使用的role,可以有多个
  5. - nginx

最后的目录结构为:

  1. [root@app52 ~]# tree roles/
  2. roles/
  3. ├── install_nginx.yml
  4. └── nginx
  5. ├── default
  6. ├── files
  7. └── index.html
  8. ├── handlers
  9. └── main.yml
  10. ├── meta
  11. ├── tasks
  12. ├── copy.yml
  13. ├── main.yml
  14. ├── start_service.yml
  15. └── yum.yml
  16. ├── templates
  17. └── nginx.conf.j2
  18. └── vars
  19. └── main.yml
  20. 8 directories, 9 files

七、运行playbook并测试

如红色部分,curl测试nginx 安装成功。 

原文链接:http://www.cnblogs.com/wdliu/p/10592397.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号