Ansible playbook 编程详解与各种小案例
主机规划
添加用户账号
说明:
1、 运维人员使用的登录账号;
2、 所有的业务都放在 /app/ 下「yun用户的家目录」,避免业务数据乱放;
3、 该用户也被 ansible 使用,因为几乎所有的生产环境都是禁止 root 远程登录的(因此该 yun 用户也进行了 sudo 提权)。
1 # 使用一个专门的用户,避免直接使用root用户 2 # 添加用户、指定家目录并指定用户密码 3 # sudo提权 4 # 让其它普通用户可以进入该目录查看信息 5 useradd -u 1050 -d /app yun && echo '123456' | /usr/bin/passwd --stdin yun 6 echo "yun ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers 7 chmod 755 /app/
Ansible 配置清单Inventory
之后文章都是如下主机配置清单
1 [yun@ansi-manager ansible_info]$ pwd 2 /app/ansible_info 3 [yun@ansi-manager ansible_info]$ cat hosts_key 4 # 方式1、主机 + 端口 + 密钥 5 [manageservers] 6 172.16.1.180:22 7 8 [proxyservers] 9 172.16.1.18[1:2]:22 10 11 # 方式2:别名 + 主机 + 端口 + 密码 12 [webservers] 13 web01 ansible_ssh_host=172.16.1.183 ansible_ssh_port=22 14 web02 ansible_ssh_host=172.16.1.184 ansible_ssh_port=22 15 web03 ansible_ssh_host=172.16.1.185 ansible_ssh_port=22
条件判断-when
when 判断在 ansible 任务中的使用频率非常高。
例如判断主机是否已经安装指定的软件包;对机器的操作系统进行判断然后再根据不同的方法「yum或apt等」进行软件包安装;根据操作系统的版本判断进行软件包的安装「是安装MySQL还是Mariadb」等。
示例:根据主机名的不同,下载不同的文件
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 4 5 -rw-rw-r-- 1 yun yun 950 Oct 26 10:22 test_when.yml 6 [yun@ansi-manager object04]$ cat test_when.yml 7 --- 8 # 根据 hostname 的不同下载不同的图片 9 # 特殊组 all,对所有机器有效 10 - hosts: all 11 12 tasks: 13 - name: "download picture jvm-01-01.png" 14 get_url: 15 url: http://www.zhangblog.com/uploads/jvm/jvm-01-01.png 16 dest: /tmp/ 17 when: ansible_hostname == "ansi-haproxy01" 18 19 - name: "download picture jvm-01-02.png" 20 get_url: 21 url: http://www.zhangblog.com/uploads/jvm/jvm-01-02.png 22 dest: /tmp/ 23 when: ansible_hostname == "ansi-haproxy02" 24 25 - name: "other download picture jvm-01-03.png" 26 get_url: 27 url: http://www.zhangblog.com/uploads/jvm/jvm-01-03.png 28 dest: /tmp/ 29 # 从 facts 中获取的变量,ansible_facts['ansible_hostname'] != "ansi-haproxy01" 错误写法;ansible_hostname != "ansi-haproxy01" 正确写法 30 #when: (ansible_hostname != "ansi-haproxy01") and (ansible_hostname != "ansi-haproxy02") # 写法一 31 #或者如下3行 列表之间关系是 (and 与) 等同于上一行 32 #when: 33 # - ansible_hostname != "ansi-haproxy01" 34 # - ansible_hostname != "ansi-haproxy02" 35 #when: ansible_hostname is not match "ansi-haproxy0*" # 写法二 36 when: (ansible_hostname is match "ansi-manager") or (ansible_hostname is match "ansi-web*") # 写法三 37 38 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_when.yml # 语法检测 39 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_when.yml # 预执行,测试执行 40 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_when.yml # 执行 41 42 PLAY [all] ******************************************************************************************************* 43 44 TASK [Gathering Facts] ******************************************************************************************* 45 ok: [web01] 46 ok: [web02] 47 ok: [web03] 48 ok: [172.16.1.180] 49 ok: [172.16.1.181] 50 ok: [172.16.1.182] 51 52 TASK [download picture jvm-01-01.png] **************************************************************************** 53 skipping: [172.16.1.180] 54 skipping: [web01] 55 skipping: [web02] 56 skipping: [web03] 57 skipping: [172.16.1.182] 58 changed: [172.16.1.181] 59 60 TASK [download picture jvm-01-02.png] **************************************************************************** 61 skipping: [172.16.1.180] 62 skipping: [web01] 63 skipping: [web02] 64 skipping: [web03] 65 skipping: [172.16.1.181] 66 changed: [172.16.1.182] 67 68 TASK [other download picture jvm-01-03.png] ********************************************************************** 69 skipping: [172.16.1.181] 70 skipping: [172.16.1.182] 71 changed: [web02] 72 changed: [web01] 73 changed: [172.16.1.180] 74 changed: [web03] 75 76 PLAY RECAP ******************************************************************************************************* 77 172.16.1.180 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 78 172.16.1.181 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 79 172.16.1.182 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 80 web01 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 81 web02 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0 82 web03 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
标准循环
注意:
1、循环语法有两种:loop 和 with_。
2、loop 是在ansible 2.5 添加的,with_ 是一直存在的,推荐使用 loop。在未来 with_ 可能被弃用。
简单列表循环
如果我们需要在 playbook 中启动多个服务,或者下载多个文件;按照之前所学的,那么我们需要写多个 task。但这样会使得 playbook 变得臃肿,因此这时我们就需要引进循环了。
示例:一次启动多个服务,下载多个文件
使用 loop 方式【推荐】
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 20 5 -rw-rw-r-- 1 yun yun 594 Aug 23 22:10 test_loop.yml 6 [yun@ansi-manager object04]$ cat test_loop.yml 7 --- 8 # 启动多个服务 和下载多个文件 9 - hosts: proxyservers 10 11 tasks: 12 - name: "start httpd, rpcbind, network server" 13 service: 14 name: "{{ item }}" # 需要用引号引起来 15 state: started 16 loop: 17 - httpd 18 - rpcbind 19 - network 20 21 - name: "download multiple file" 22 get_url: 23 url: "{{ item }}" # 需要用引号引起来 24 dest: /tmp/ 25 loop: 26 - http://www.zhangblog.com/uploads/jvm/jvm-01-01.png 27 - http://www.zhangblog.com/uploads/jvm/jvm-01-02.png 28 - http://www.zhangblog.com/uploads/jvm/jvm-01-03.png 29 30 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop.yml # 语法检测 31 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop.yml # 预执行,测试执行 32 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop.yml # 执行
备注:以上方法可用在 yum 模块中。
使用 with_items 方式
其中 playbook 文件中仅把 loop 变为了 with_items。
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 20 5 -rw-rw-r-- 1 yun yun 594 Aug 23 22:10 test_with_items.yml 6 [yun@ansi-manager object04]$ cat test_with_items.yml 7 --- 8 # 启动多个服务 和下载多个文件 9 - hosts: proxyservers 10 11 tasks: 12 - name: "start httpd, rpcbind, network server" 13 service: 14 name: "{{ item }}" # 需要用引号引起来 15 state: started 16 with_items: 17 - httpd 18 - rpcbind 19 - network 20 21 - name: "download multiple file" 22 get_url: 23 url: "{{ item }}" # 需要用引号引起来 24 dest: /tmp/ 25 with_items: 26 - http://www.zhangblog.com/uploads/jvm/jvm-01-01.png 27 - http://www.zhangblog.com/uploads/jvm/jvm-01-02.png 28 - http://www.zhangblog.com/uploads/jvm/jvm-01-03.png 29 30 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items.yml # 语法检测 31 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items.yml # 预执行,测试执行 32 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items.yml # 执行
如果用在 yum 模块中则会报如下弃用告警,因此该方法不适用于 yum 模块。
遍历哈希列表
如果我们需要创建多个用户并且每个用户都有指定的附加组;或者要创建多个文件,每个文件属主、属组、权限不一样;或者需要拷贝文件,但是每个文件的位置不一样,且属主、属组、权限不一样等等;那之前所学的简单循环就不能满足我们的需求了。这时「哈希列表循环」就闪亮登场了。
示例:
使用 loop 方式【推荐】
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 16 5 drwxrwxr-x 2 yun yun 56 Oct 26 16:03 file 6 -rw-rw-r-- 1 yun yun 1205 Oct 26 16:02 test_loop_hash.yml 7 [yun@ansi-manager object04]$ cat file/config_test.conf.j2 8 111 9 [yun@ansi-manager object04]$ cat file/yml_test_j2.yml 10 222 11 [yun@ansi-manager object04]$ cat test_loop_hash.yml 12 --- 13 # 使用循环字典创建多个用户,创建多个文件,拷贝多个文件 14 - hosts: proxyservers 15 16 tasks: 17 - name: "Create multiple user" 18 user: 19 name: "{{ item.user }}" 20 groups: "{{ item.groups }}" 21 loop: 22 - { user: "testuser1", groups: "root" } 23 - { user: "testuser2", groups: "root,yun" } 24 25 - name: "Create multiple file or dir" 26 file: 27 path: "{{ item.path }}" 28 owner: "{{ item.owner }}" 29 group: "{{ item.group }}" 30 mode: "{{ item.mode }}" 31 state: "{{ item.state }}" 32 loop: 33 - { path: "/tmp/with_items_testdir", owner: "yun", group: "root", mode: "755", state: "directory" } 34 - { path: "/tmp/with_items_testfile", owner: "bin", group: "bin", mode: "644", state: "touch" } 35 36 - name: "copy multiple file" 37 copy: 38 src: "{{ item.src }}" 39 dest: "{{ item.dest }}" 40 owner: "{{ item.owner }}" 41 group: "{{ item.group }}" 42 loop: 43 - { src: "./file/config_test.conf.j2", dest: "/tmp/with_items_testdir/", owner: "yun", group: "root" } 44 - { src: "./file/yml_test_j2.yml", dest: "/tmp/yml_test.yml", owner: "yun", group: "yun" } 45 46 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop_hash.yml # 语法检测 47 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop_hash.yml # 预执行,测试执行 48 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop_hash.yml # 执行
使用 with_items 方式
其中 playbook 文件中仅把 loop 变为了 with_items。
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 16 5 drwxrwxr-x 2 yun yun 56 Oct 26 16:03 file 6 -rw-rw-r-- 1 yun yun 1205 Oct 26 16:02 test_with_items_hash.yml 7 [yun@ansi-manager object04]$ cat file/config_test.conf.j2 8 111 9 [yun@ansi-manager object04]$ cat file/yml_test_j2.yml 10 222 11 [yun@ansi-manager object04]$ cat test_with_items_hash.yml 12 --- 13 # 使用循环字典创建多个用户,创建多个文件,拷贝多个文件 14 - hosts: proxyservers 15 16 tasks: 17 - name: "Create multiple user" 18 user: 19 name: "{{ item.user }}" 20 groups: "{{ item.groups }}" 21 with_items: 22 - { user: "testuser1", groups: "root" } 23 - { user: "testuser2", groups: "root,yun" } 24 25 - name: "Create multiple file or dir" 26 file: 27 path: "{{ item.path }}" 28 owner: "{{ item.owner }}" 29 group: "{{ item.group }}" 30 mode: "{{ item.mode }}" 31 state: "{{ item.state }}" 32 with_items: 33 - { path: "/tmp/with_items_testdir", owner: "yun", group: "root", mode: "755", state: "directory" } 34 - { path: "/tmp/with_items_testfile", owner: "bin", group: "bin", mode: "644", state: "touch" } 35 36 - name: "copy multiple file" 37 copy: 38 src: "{{ item.src }}" 39 dest: "{{ item.dest }}" 40 owner: "{{ item.owner }}" 41 group: "{{ item.group }}" 42 with_items: 43 - { src: "./file/config_test.conf.j2", dest: "/tmp/with_items_testdir/", owner: "yun", group: "root" } 44 - { src: "./file/yml_test_j2.yml", dest: "/tmp/yml_test.yml", owner: "yun", group: "yun" } 45 46 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items_hash.yml # 语法检测 47 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items_hash.yml # 预执行,测试执行 48 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items_hash.yml # 执行
遍历字典
示例:
使用 loop 方式【推荐】
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 28 5 -rw-rw-r-- 1 yun yun 452 Oct 26 16:46 test_loop_dict.yml 6 [yun@ansi-manager object04]$ cat test_loop_dict.yml 7 --- 8 # 打印信息 9 - hosts: manageservers 10 vars: 11 users: 12 alice: 13 name: Alice Appleworth 14 telephone: 123-456-7890 15 bob: 16 name: Bob Bananarama 17 telephone: 987-654-3210 18 19 tasks: 20 - name: "print user info" 21 debug: 22 msg: "User {{ item.key }}, userfullname: {{ item.value.name }} ({{ item.value.telephone }})" 23 # 将字典转换为适合循环的项表 第一种方式推荐 24 loop: "{{ users|dict2items }}" 25 #loop: "{{ lookup('dict', users) }}" 26 27 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop_dict.yml # 语法检测 28 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop_dict.yml # 预执行,测试执行 29 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop_dict.yml # 执行
使用 with_items 方式
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 28 5 -rw-rw-r-- 1 yun yun 458 Oct 26 16:47 test_with_items_dict.yml 6 [yun@ansi-manager object04]$ cat test_with_items_dict.yml 7 --- 8 # 打印信息 9 - hosts: manageservers 10 vars: 11 users: 12 alice: 13 name: Alice Appleworth 14 telephone: 123-456-7890 15 bob: 16 name: Bob Bananarama 17 telephone: 987-654-3210 18 19 tasks: 20 - name: "print user info" 21 debug: 22 msg: "User {{ item.key }}, userfullname: {{ item.value.name }} ({{ item.value.telephone }})" 23 # with_dict 会直接解析字典 24 with_dict: "{{ users }}" 25 26 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items_dict.yml # 语法检测 27 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items_dict.yml # 预执行,测试执行 28 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items_dict.yml # 执行
变量循环-vars
针对yum 安装多个包很有用,其他则会报出警告。
1 [yun@ansi-manager object04]$ pwd 2 /app/ansible_info/object04 3 [yun@ansi-manager object04]$ ll 4 total 36 5 -rw-rw-r-- 1 yun yun 252 Oct 26 17:46 test_cycle_vars.yml 6 [yun@ansi-manager object04]$ cat test_cycle_vars.yml 7 --- 8 # 批量包安装 9 - hosts: proxyservers 10 11 tasks: 12 - name: "Install multiple packages" 13 yum: 14 name: "{{ multi_package }}" 15 state: present 16 vars: 17 multi_package: 18 - tree 19 - nc 20 - tcpdump 21 22 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_cycle_vars.yml # 语法检测 23 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_cycle_vars.yml # 预执行,测试执行 24 [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_cycle_vars.yml # 执行
该方法不一定适用于其他模块
触发器-handlers
当我们修改了服务的配置文件时,这时我们需要去重启服务,那么 handlers 就可以派上用场了。
注意事项:
1、无论多少个 task 通知了相同的 handlers,handlers 仅会在所有 tasks 结束后运行一次。
2、只有 task 发生改变了才会通知 handlers,没有改变则不会通知和触发 handlers。
3、不能用 handlers 替代 task 。
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 24 5 drwxrwxr-x 2 yun yun 129 Aug 24 11:41 file 6 -rw-rw-r-- 1 yun yun 1029 Aug 24 11:57 test_handlers.yml 7 [yun@ansi-manager object05]$ ll file/ # 涉及配置文件 8 total 20 9 -rw-r--r-- 1 yun yun 11767 Aug 24 11:41 httpd.conf.j2 10 [yun@ansi-manager object05]$ vim file/httpd.conf.j2 # 配置文件修改的地方 11 ………… 12 # Change this to Listen on specific IP addresses as shown below to 13 # prevent Apache from glomming onto all bound IP addresses. 14 # 15 #Listen 12.34.56.78:80 16 ###### 端口改为变量 17 Listen {{ httpd_port }} 18 19 ………… 20 [yun@ansi-manager object05]$ cat test_handlers.yml # yml 文件 21 --- 22 # 比如安装配置启动 httpd。当我们修改配置文件,重启 httpd 服务 23 # 要求:修改配置,重启一个或多个服务 24 - hosts: proxyservers 25 # 这里为了演示方便,因此变量直接就写在了该文件中 26 vars: 27 - httpd_port: 8081 28 29 tasks: 30 - name: "Install httpd" 31 yum: 32 name: "{{ packages }}" 33 state: present 34 vars: 35 packages: 36 - httpd 37 - httpd-tools 38 39 - name: "Httpd config" 40 template: 41 src: ./file/httpd.conf.j2 42 dest: /etc/httpd/conf/httpd.conf 43 # 一个通知 44 # notify: "Restart httpd server" 45 # 多个通知 46 notify: 47 - "Restart httpd server" 48 - "Restart crond server" 49 50 - name: "Start httpd server" 51 systemd: 52 name: httpd 53 state: started 54 enabled: yes 55 56 handlers: 57 - name: "Restart httpd server" 58 systemd: 59 name: httpd 60 state: restarted 61 62 - name: "Restart crond server" 63 systemd: 64 name: crond 65 state: restarted 66 67 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_handlers.yml # 语法检测 68 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_handlers.yml # 预执行,测试执行 69 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_handlers.yml # 执行
任务标签-tags
默认情况下,当我们执行一个 playbook 时,会执行该 playbook 中所有的任务。如果只想执行一个 task 或者部分 task 用于调试或者需求就是执行部分 task。那么可以使用 ansible 的标签(tags)功能给单独 task 或者全部 task 打上标签。之后利用这些标签来指定要运行哪些 playbook 任务,或不运行哪些 playbook 任务。
打标签方式
对一个 task 打一个标签;
对一个 task 打多个标签;
对多个 task 打一个标签
标签如何运用
-t TAGS, --tags=TAGS:执行指定的 tag 标签任务;多个标签使用逗号分开
--skip-tags=SKIP_TAGS:跳过指定标签不执行,执行指定外的 task「标签作用于 task 上,即使该task还有其他标签,这个 task 也不会被执行」;多个标签使用逗号分开
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 8 5 drwxrwxr-x 2 yun yun 27 Oct 26 18:07 file 6 -rw-rw-r-- 1 yun yun 1004 Oct 26 19:21 test_tags.yml 7 [yun@ansi-manager object05]$ cat test_tags.yml 8 --- 9 # tags 标签测试 10 - hosts: proxyservers 11 # 这里为了演示方便,因此变量直接就写在了该文件中 12 vars: 13 - httpd_port: 8081 14 15 tasks: 16 - name: "Install httpd" 17 yum: 18 name: "{{ packages }}" 19 state: present 20 vars: 21 packages: 22 - httpd 23 - httpd-tools 24 tags: 25 - httpd_server 26 - httpd_install 27 28 - name: "Httpd config" 29 template: 30 src: ./file/httpd.conf.j2 31 dest: /etc/httpd/conf/httpd.conf 32 notify: "Restart httpd server" 33 tags: 34 - httpd_server 35 - httpd_config 36 37 - name: "Start httpd server" 38 systemd: 39 name: httpd 40 state: started 41 enabled: yes 42 tags: 43 - httpd_server 44 - httpd_start 45 46 - name: "Create dir" 47 file: 48 path: /tmp/with_items_testdir 49 state: directory 50 tags: create_dir 51 52 handlers: 53 - name: "Restart httpd server" 54 systemd: 55 name: httpd 56 state: restarted
playbook 标签查看
1 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_tags.yml # 语法检测 2 ## 查看 playbook 中的任务和标签 3 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --list-tasks 4 5 playbook: test_tags.yml 6 7 play #1 (proxyservers): proxyservers TAGS: [] 8 tasks: 9 Install httpd TAGS: [httpd_install, httpd_server] 10 Httpd config TAGS: [httpd_config, httpd_server] 11 Start httpd server TAGS: [httpd_server, httpd_start] 12 Create dir TAGS: [create_dir] 13 ## 查看 playbook 中的标签 14 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --list-tags 15 16 playbook: test_tags.yml 17 18 play #1 (proxyservers): proxyservers TAGS: [] 19 TASK TAGS: [create_dir, httpd_config, httpd_install, httpd_server, httpd_start]
playbook 执行
1 ## 单个标签执行 2 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml -t httpd_install 3 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml -t httpd_server 4 ## 多个标签执行 5 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml -t httpd_install,httpd_config,httpd_start 6 ## 跳过哪些标签不执行「标签作用于 task 上,即使该 task 还有其他标签,这个 task 也不会被执行」 7 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --skip-tags httpd_server 8 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --skip-tags httpd_install,create_dir 9 ## 执行整个 playbook 10 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml
文件引用/复用-include与import
在实际应用中,是不可能将所有 task 写在一个 playbook 中的,需要进行拆分,方便后期重复使用。这样后面写其他 playbook 的时候,如果有重复的,那么直接引用之前写的即可。
Includes 与 Imports
1、include 和 import 虽然功能相近,但是 ansible 执行引擎对他们的处理却截然不同。
2、所有 import* 语句都会在解析 playbook 时进行预处理。「提前准备好工具」
3、所有 include* 语句都是在执行 playbook 时遇到再处理。「需要什么工具,再拿什么工具」
PS:include 模块:这个模块还将支持一段时间,但在不久的将来可能会弃用「最好不要使用这个模块」。
示例
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 32 5 drwxrwxr-x 2 yun yun 103 Aug 24 19:51 file_yml 6 -rw-rw-r-- 1 yun yun 518 Aug 24 19:56 test_include.yml 7 [yun@ansi-manager object05]$ ll file_yml/ 8 total 16 9 -rw-rw-r-- 1 yun yun 136 Aug 24 19:51 httpd_config.yml 10 -rw-rw-r-- 1 yun yun 133 Aug 24 19:41 httpd_install.yml 11 -rw-rw-r-- 1 yun yun 80 Aug 24 19:42 httpd_restart.yml 12 -rw-rw-r-- 1 yun yun 93 Aug 24 19:41 httpd_start.yml 13 ## 每个小 yml 文件的具体内容 14 [yun@ansi-manager object05]$ cat file_yml/httpd_install.yml 15 - name: "Install httpd" 16 yum: 17 name: "{{ packages }}" 18 state: present 19 vars: 20 packages: 21 - httpd 22 - httpd-tools 23 24 [yun@ansi-manager object05]$ cat file_yml/httpd_config.yml 25 - name: "Httpd config" 26 template: 27 src: ./file/httpd.conf.j2 28 dest: /etc/httpd/conf/httpd.conf 29 notify: "Restart httpd server" 30 31 [yun@ansi-manager object05]$ cat file_yml/httpd_start.yml 32 - name: "Start httpd server" 33 systemd: 34 name: httpd 35 state: started 36 enabled: yes 37 38 [yun@ansi-manager object05]$ cat file_yml/httpd_restart.yml 39 - name: "Restart httpd server" 40 systemd: 41 name: httpd 42 state: restarted 43 44 ###### 主调用 yml 文件内容 ###### 45 [yun@ansi-manager object05]$ cat test_include.yml 46 --- 47 # 调用其他 yml 文件 48 - hosts: proxyservers 49 # 这里为了演示方便,因此变量直接就写在了该文件中 50 vars: 51 - httpd_port: 8083 52 53 tasks: 54 - include_tasks: ./file_yml/httpd_install.yml 55 - include_tasks: ./file_yml/httpd_config.yml 56 - include_tasks: ./file_yml/httpd_start.yml 57 58 handlers: 59 # 使用 import 进行预处理,这样防止 notify 时,在 handlers 找不到对应的信息 60 - import_tasks: ./file_yml/httpd_restart.yml 61 62 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_include.yml # 语法检测 63 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_include.yml # 预执行,测试执行 64 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_include.yml # 执行
忽略错误-ignore_errors
在 playbook 执行过程中,默认情况下如果有错误发生,那么后面的 task 就不执行,并且退出当前的 playbook。
如果我们对某些 task 执行结果不关心,不管执行是否成功,后面的 task 也要继续执行。那就需要通过 ignore_errors 来忽略当前 task 的错误结果,让后面的 task 继续往下执行。
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 36 5 -rw-rw-r-- 1 yun yun 479 Aug 26 09:24 test_ignore_errors.yml 6 [yun@ansi-manager object05]$ cat test_ignore_errors.yml 7 --- 8 # ignore_errors 测试 9 - hosts: proxyservers 10 11 tasks: 12 - name: "Install httpd" 13 yum: 14 name: "{{ packages }}" 15 state: present 16 vars: 17 packages: 18 - httpd 19 - httpd-tools 20 21 - name: "Shell false" 22 shell: /bin/false 23 # 是否忽略该 task 的错误 「打开或关闭注释,对比」 24 ignore_errors: True 25 26 - name: "Create dir" 27 file: 28 path: /tmp/with_items_testdir 29 state: directory 30 31 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_ignore_errors.yml # 语法检测 32 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_ignore_errors.yml # 预执行,测试执行 33 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_ignore_errors.yml # 执行
默认情况
使用了 ignore_errors 的情况
自定义错误判定条件-failed_when
命令不依赖返回状态码来判定是否执行失败,而是要查看命令返回内容来决定,比如返回内容中包括 command not found 字符串,则判定为失败。
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 48 5 -rw-rw-r-- 1 yun yun 369 Aug 29 16:12 test_custom_error.yml 6 [yun@ansi-manager object05]$ cat test_custom_error.yml 7 --- 8 # 自定义错误条件 9 - hosts: proxyservers 10 11 tasks: 12 - name: "this command prints 'command not found' if not find" 13 shell: "kkk -x" # 测试一 14 #shell: "/bin/kkk -x" # 测试二 15 register: shell_result 16 failed_when: "'command not found' in shell_result['stderr']" 17 18 - name: "print shell_result info" 19 debug: 20 msg: "{{ shell_result['stderr'] }}" 21 22 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_custom_error.yml # 语法检测 23 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_custom_error.yml # 预执行,测试执行 24 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_custom_error.yml # 执行
强制调用触发器-force_handlers
通常情况下,当 task 执行失败后,playbook 会终止。任何在此之前已经被 task notify 的 handlers 都不会被执行。
此时,如果你在 playbook 中设置了 force_handlers: yes 参数,则被通知的 handlers 就会被强制执行(有些特殊场景可能会使用到)。
如示例,在一个 playbook 中,如果配置文件的 task 已经被执行成功,并且 notify 了 handlers,之后必须重启服务。那么我们会强制要求:即使后续的 task 执行失败,之前被通知的 handlers 也必须执行。
如果不强制执行就变成了,第一次执行时:配置文件修改成功,但由于之后有 task 执行失败,导致 playbook 终止,后续 handlers 没有被调用,对应服务没有重启;第二次执行时:配置文件没发生改变「因此第一次已经更新了配置文件」,因此不会通知 handlers。最终结果就是配置改变了,但是就是没有重启服务。显然不符合我们的初衷。
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 40 5 drwxrwxr-x 2 yun yun 129 Aug 24 14:28 file 6 -rw-rw-r-- 1 yun yun 909 Aug 29 12:23 test_error_deal.yml 7 [yun@ansi-manager object05]$ cat test_error_deal.yml 8 --- 9 # 即使 task 执行错误,之前已 notify 的 handlers 必须被执行 10 - hosts: proxyservers 11 # 这里为了演示方便,因此变量直接就写在了该文件中 12 vars: 13 - httpd_port: 8087 14 # 即使 task 执行错误,之前已 notify 的 handlers 必须被执行 15 force_handlers: yes 16 17 tasks: 18 - name: "Install httpd" 19 yum: 20 name: "{{ packages }}" 21 state: present 22 vars: 23 packages: 24 - httpd 25 - httpd-tools 26 27 - name: "Httpd config" 28 template: 29 src: ./file/httpd.conf.j2 30 dest: /etc/httpd/conf/httpd.conf 31 notify: "Restart httpd server" 32 33 - name: "Start httpd server" 34 systemd: 35 name: httpd 36 state: started 37 enabled: yes 38 39 # /bin/false 返回状态码为1,不为0 40 - name: "Shell task" 41 shell: /bin/false 42 43 - name: "Create dir" 44 file: 45 path: /tmp/with_items_testdir 46 state: directory 47 48 handlers: 49 - name: "Restart httpd server" 50 systemd: 51 name: httpd 52 state: restarted 53 54 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_error_deal.yml # 语法检测 55 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_error_deal.yml # 预执行,测试执行 56 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_error_deal.yml # 执行
抑制changed状态-changed_when
ansible 会自动判断模块执行状态,command、shell 及其它模块如果修改了远程主机状态则被判定为 changed 状态,不过也可以自己决定达到 changed 状态的条件。
当我们在 playbook 中使用 shell 或者 command 模块时,每次 task 执行状态都是 changed。原因是因为每次我们都去执行获取当前数据,而不是一个固化的状态。
但在实际应用中,我们可能不需要 shell 或者 command 模块执行后的 changed 状态,这时我们就需要通过 changed_when: false 来抑制这个改变。
当然上述的 changed_when: false 可以在任何模块中使用,不局限于 shell 和 command 模块,只是我们常用于这两个模块而已。
1 [yun@ansi-manager object05]$ pwd 2 /app/ansible_info/object05 3 [yun@ansi-manager object05]$ ll 4 total 44 5 -rw-rw-r-- 1 yun yun 299 Aug 29 14:47 test_changed_when.yml 6 [yun@ansi-manager object05]$ cat test_changed_when.yml 7 --- 8 # 使用 changed_when: false 抑制 changed 状态 9 - hosts: proxyservers 10 11 tasks: 12 - name: "Shell task" 13 shell: netstat -lntp | grep 'httpd' 14 register: check_httpd 15 # changed_when: false # 任何时候,都不为 changed 状态 16 #### check_httpd['stdout'] 不包含 httpd 为 true,否则 false 17 changed_when: "'httpd' not in check_httpd['stdout']" # 结果为 false 18 19 - name: "Debug output" 20 debug: 21 msg: "{{ check_httpd.stdout }}" 22 23 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_changed_when.yml # 语法检测 24 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_changed_when.yml # 预执行,测试执行 25 [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_changed_when.yml # 执行
完毕!