暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Ansible playbook 编程详解

OpenInfo 2020-04-20
109

主机规划


添加用户账号

说明:


1、 运维人员使用的登录账号;


2、 所有的业务都放在 app/ 下「yun用户的家目录」,避免业务数据乱放;


3、 该用户也被 ansible 使用,因为几乎所有的生产环境都是禁止 root 远程登录的(因此该 yun 用户也进行了 sudo 提权)。

    # 使用一个专门的用户,避免直接使用root用户
    # 添加用户、指定家目录并指定用户密码
    # sudo提权
    # 让其它普通用户可以进入该目录查看信息
    useradd -u 1050 -d app yun && echo '123456' | usr/bin/passwd --stdin yun
    echo "yun ALL=(ALL) NOPASSWD: ALL" >> etc/sudoers
    chmod 755 app/


    Ansible 配置清单Inventory

    之后文章都是如下主机配置清单

      [yun@ansi-manager ansible_info]$ pwd
      /app/ansible_info
      [yun@ansi-manager ansible_info]$ cat hosts_key
      # 方式1、主机 + 端口 + 密钥
      [manageservers]
      172.16.1.180:22


      [proxyservers]
      172.16.1.18[1:2]:22


      # 方式2:别名 + 主机 + 端口 + 密码
      [webservers]
      web01 ansible_ssh_host=172.16.1.183 ansible_ssh_port=22
      web02 ansible_ssh_host=172.16.1.184 ansible_ssh_port=22
      web03 ansible_ssh_host=172.16.1.185 ansible_ssh_port=22


      条件判断-when

      when 判断在 ansible 任务中的使用频率非常高。


      例如判断主机是否已经安装指定的软件包;对机器的操作系统进行判断然后再根据不同的方法「yum或apt等」进行软件包安装;根据操作系统的版本判断进行软件包的安装「是安装MySQL还是Mariadb」等。


      示例:根据主机名的不同,下载不同的文件

        [yun@ansi-manager object04]$ pwd
        /app/ansible_info/object04
        [yun@ansi-manager object04]$ ll
        total 4
        -rw-rw-r-- 1 yun yun 950 Oct 26 10:22 test_when.yml
        [yun@ansi-manager object04]$ cat test_when.yml
        ---
        # 根据 hostname 的不同下载不同的图片
        # 特殊组 all,对所有机器有效
        - hosts: all


        tasks:
        - name: "download picture jvm-01-01.png"
        get_url:
        url: http://www.zhangblog.com/uploads/jvm/jvm-01-01.png
        dest: tmp/
        when: ansible_hostname == "ansi-haproxy01"


        - name: "download picture jvm-01-02.png"
        get_url:
        url: http://www.zhangblog.com/uploads/jvm/jvm-01-02.png
        dest: tmp/
        when: ansible_hostname == "ansi-haproxy02"


        - name: "other download picture jvm-01-03.png"
        get_url:
        url: http://www.zhangblog.com/uploads/jvm/jvm-01-03.png
        dest: tmp/
        # 从 facts 中获取的变量,ansible_facts['ansible_hostname'] != "ansi-haproxy01" 错误写法;ansible_hostname != "ansi-haproxy01" 正确写法
        #when: (ansible_hostname != "ansi-haproxy01") and (ansible_hostname != "ansi-haproxy02") # 写法一
        #或者如下3行 列表之间关系是 (and 与) 等同于上一行
        #when:
        # - ansible_hostname != "ansi-haproxy01"
        # - ansible_hostname != "ansi-haproxy02"
        #when: ansible_hostname is not match "ansi-haproxy0*" # 写法二
        when: (ansible_hostname is match "ansi-manager") or (ansible_hostname is match "ansi-web*") # 写法三


        [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_when.yml # 语法检测
        [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_when.yml # 预执行,测试执行
        [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_when.yml # 执行


        PLAY [all] *******************************************************************************************************


        TASK [Gathering Facts] *******************************************************************************************
        ok: [web01]
        ok: [web02]
        ok: [web03]
        ok: [172.16.1.180]
        ok: [172.16.1.181]
        ok: [172.16.1.182]


        TASK [download picture jvm-01-01.png] ****************************************************************************
        skipping: [172.16.1.180]
        skipping: [web01]
        skipping: [web02]
        skipping: [web03]
        skipping: [172.16.1.182]
        changed: [172.16.1.181]


        TASK [download picture jvm-01-02.png] ****************************************************************************
        skipping: [172.16.1.180]
        skipping: [web01]
        skipping: [web02]
        skipping: [web03]
        skipping: [172.16.1.181]
        changed: [172.16.1.182]


        TASK [other download picture jvm-01-03.png] **********************************************************************
        skipping: [172.16.1.181]
        skipping: [172.16.1.182]
        changed: [web02]
        changed: [web01]
        changed: [172.16.1.180]
        changed: [web03]


        PLAY RECAP *******************************************************************************************************
        172.16.1.180 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
        172.16.1.181 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
        172.16.1.182 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
        web01 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
        web02 : ok=2 changed=1 unreachable=0 failed=0 skipped=2 rescued=0 ignored=0
        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 方式【推荐】

          [yun@ansi-manager object04]$ pwd
          /app/ansible_info/object04
          [yun@ansi-manager object04]$ ll
          total 20
          -rw-rw-r-- 1 yun yun 594 Aug 23 22:10 test_loop.yml
          [yun@ansi-manager object04]$ cat test_loop.yml
          ---
          # 启动多个服务 和下载多个文件
          - hosts: proxyservers


          tasks:
          - name: "start httpd, rpcbind, network server"
          service:
          name: "{{ item }}" # 需要用引号引起来
          state: started
          loop:
          - httpd
          - rpcbind
          - network


          - name: "download multiple file"
          get_url:
          url: "{{ item }}" # 需要用引号引起来
          dest: tmp/
          loop:
          - http://www.zhangblog.com/uploads/jvm/jvm-01-01.png
          - http://www.zhangblog.com/uploads/jvm/jvm-01-02.png
          - http://www.zhangblog.com/uploads/jvm/jvm-01-03.png


          [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop.yml # 语法检测
          [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop.yml # 预执行,测试执行
          [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop.yml # 执行


          备注:以上方法可用在 yum 模块中。


          使用 with_items 方式


          其中 playbook 文件中仅把 loop 变为了 with_items。

            [yun@ansi-manager object04]$ pwd
            /app/ansible_info/object04
            [yun@ansi-manager object04]$ ll
            total 20
            -rw-rw-r-- 1 yun yun 594 Aug 23 22:10 test_with_items.yml
            [yun@ansi-manager object04]$ cat test_with_items.yml
            ---
            # 启动多个服务 和下载多个文件
            - hosts: proxyservers


            tasks:
            - name: "start httpd, rpcbind, network server"
            service:
            name: "{{ item }}" # 需要用引号引起来
            state: started
            with_items:
            - httpd
            - rpcbind
            - network


            - name: "download multiple file"
            get_url:
            url: "{{ item }}" # 需要用引号引起来
            dest: tmp/
            with_items:
            - http://www.zhangblog.com/uploads/jvm/jvm-01-01.png
            - http://www.zhangblog.com/uploads/jvm/jvm-01-02.png
            - http://www.zhangblog.com/uploads/jvm/jvm-01-03.png


            [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items.yml # 语法检测
            [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items.yml # 预执行,测试执行
            [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items.yml # 执行


            如果用在 yum 模块中则会报如下弃用告警,因此该方法不适用于 yum 模块。


            遍历哈希列表

            如果我们需要创建多个用户并且每个用户都有指定的附加组;或者要创建多个文件,每个文件属主、属组、权限不一样;或者需要拷贝文件,但是每个文件的位置不一样,且属主、属组、权限不一样等等;那之前所学的简单循环就不能满足我们的需求了。这时「哈希列表循环」就闪亮登场了。


            示例:


            使用 loop 方式【推荐】

              [yun@ansi-manager object04]$ pwd
              /app/ansible_info/object04
              [yun@ansi-manager object04]$ ll
              total 16
              drwxrwxr-x 2 yun yun 56 Oct 26 16:03 file
              -rw-rw-r-- 1 yun yun 1205 Oct 26 16:02 test_loop_hash.yml
              [yun@ansi-manager object04]$ cat file/config_test.conf.j2
              111
              [yun@ansi-manager object04]$ cat file/yml_test_j2.yml
              222
              [yun@ansi-manager object04]$ cat test_loop_hash.yml
              ---
              # 使用循环字典创建多个用户,创建多个文件,拷贝多个文件
              - hosts: proxyservers


              tasks:
              - name: "Create multiple user"
              user:
              name: "{{ item.user }}"
              groups: "{{ item.groups }}"
              loop:
              - { user: "testuser1", groups: "root" }
              - { user: "testuser2", groups: "root,yun" }


              - name: "Create multiple file or dir"
              file:
              path: "{{ item.path }}"
              owner: "{{ item.owner }}"
              group: "{{ item.group }}"
              mode: "{{ item.mode }}"
              state: "{{ item.state }}"
              loop:
              - { path: "/tmp/with_items_testdir", owner: "yun", group: "root", mode: "755", state: "directory" }
              - { path: "/tmp/with_items_testfile", owner: "bin", group: "bin", mode: "644", state: "touch" }


              - name: "copy multiple file"
              copy:
              src: "{{ item.src }}"
              dest: "{{ item.dest }}"
              owner: "{{ item.owner }}"
              group: "{{ item.group }}"
              loop:
              - { src: "./file/config_test.conf.j2", dest: "/tmp/with_items_testdir/", owner: "yun", group: "root" }
              - { src: "./file/yml_test_j2.yml", dest: "/tmp/yml_test.yml", owner: "yun", group: "yun" }


              [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop_hash.yml # 语法检测
              [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop_hash.yml # 预执行,测试执行
              [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop_hash.yml # 执行


              使用 with_items 方式


              其中 playbook 文件中仅把 loop 变为了 with_items。

                [yun@ansi-manager object04]$ pwd
                /app/ansible_info/object04
                [yun@ansi-manager object04]$ ll
                total 16
                drwxrwxr-x 2 yun yun 56 Oct 26 16:03 file
                -rw-rw-r-- 1 yun yun 1205 Oct 26 16:02 test_with_items_hash.yml
                [yun@ansi-manager object04]$ cat file/config_test.conf.j2
                111
                [yun@ansi-manager object04]$ cat file/yml_test_j2.yml
                222
                [yun@ansi-manager object04]$ cat test_with_items_hash.yml
                ---
                # 使用循环字典创建多个用户,创建多个文件,拷贝多个文件
                - hosts: proxyservers


                tasks:
                - name: "Create multiple user"
                user:
                name: "{{ item.user }}"
                groups: "{{ item.groups }}"
                with_items:
                - { user: "testuser1", groups: "root" }
                - { user: "testuser2", groups: "root,yun" }


                - name: "Create multiple file or dir"
                file:
                path: "{{ item.path }}"
                owner: "{{ item.owner }}"
                group: "{{ item.group }}"
                mode: "{{ item.mode }}"
                state: "{{ item.state }}"
                with_items:
                - { path: "/tmp/with_items_testdir", owner: "yun", group: "root", mode: "755", state: "directory" }
                - { path: "/tmp/with_items_testfile", owner: "bin", group: "bin", mode: "644", state: "touch" }


                - name: "copy multiple file"
                copy:
                src: "{{ item.src }}"
                dest: "{{ item.dest }}"
                owner: "{{ item.owner }}"
                group: "{{ item.group }}"
                with_items:
                - { src: "./file/config_test.conf.j2", dest: "/tmp/with_items_testdir/", owner: "yun", group: "root" }
                - { src: "./file/yml_test_j2.yml", dest: "/tmp/yml_test.yml", owner: "yun", group: "yun" }


                [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items_hash.yml # 语法检测
                [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items_hash.yml # 预执行,测试执行
                [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items_hash.yml # 执行


                遍历字典

                示例:


                使用 loop 方式【推荐】

                  [yun@ansi-manager object04]$ pwd
                  /app/ansible_info/object04
                  [yun@ansi-manager object04]$ ll
                  total 28
                  -rw-rw-r-- 1 yun yun 452 Oct 26 16:46 test_loop_dict.yml
                  [yun@ansi-manager object04]$ cat test_loop_dict.yml
                  ---
                  # 打印信息
                  - hosts: manageservers
                  vars:
                  users:
                  alice:
                  name: Alice Appleworth
                  telephone: 123-456-7890
                  bob:
                  name: Bob Bananarama
                  telephone: 987-654-3210


                  tasks:
                  - name: "print user info"
                  debug:
                  msg: "User {{ item.key }}, userfullname: {{ item.value.name }} ({{ item.value.telephone }})"
                  # 将字典转换为适合循环的项表 第一种方式推荐
                  loop: "{{ users|dict2items }}"
                  #loop: "{{ lookup('dict', users) }}"


                  [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_loop_dict.yml # 语法检测
                  [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_loop_dict.yml # 预执行,测试执行
                  [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_loop_dict.yml # 执行


                  使用 with_items 方式

                    [yun@ansi-manager object04]$ pwd
                    /app/ansible_info/object04
                    [yun@ansi-manager object04]$ ll
                    total 28
                    -rw-rw-r-- 1 yun yun 458 Oct 26 16:47 test_with_items_dict.yml
                    [yun@ansi-manager object04]$ cat test_with_items_dict.yml
                    ---
                    # 打印信息
                    - hosts: manageservers
                    vars:
                    users:
                    alice:
                    name: Alice Appleworth
                    telephone: 123-456-7890
                    bob:
                    name: Bob Bananarama
                    telephone: 987-654-3210


                    tasks:
                    - name: "print user info"
                    debug:
                    msg: "User {{ item.key }}, userfullname: {{ item.value.name }} ({{ item.value.telephone }})"
                    # with_dict 会直接解析字典
                    with_dict: "{{ users }}"


                    [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_with_items_dict.yml # 语法检测
                    [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_with_items_dict.yml # 预执行,测试执行
                    [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key test_with_items_dict.yml # 执行


                    变量循环-vars

                    针对yum 安装多个包很有用,其他则会报出警告。

                      [yun@ansi-manager object04]$ pwd
                      /app/ansible_info/object04
                      [yun@ansi-manager object04]$ ll
                      total 36
                      -rw-rw-r-- 1 yun yun 252 Oct 26 17:46 test_cycle_vars.yml
                      [yun@ansi-manager object04]$ cat test_cycle_vars.yml
                      ---
                      # 批量包安装
                      - hosts: proxyservers


                      tasks:
                      - name: "Install multiple packages"
                      yum:
                      name: "{{ multi_package }}"
                      state: present
                      vars:
                      multi_package:
                      - tree
                      - nc
                      - tcpdump


                      [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key --syntax-check test_cycle_vars.yml # 语法检测
                      [yun@ansi-manager object04]$ ansible-playbook -b -i ../hosts_key -C test_cycle_vars.yml # 预执行,测试执行
                      [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 。

                        [yun@ansi-manager object05]$ pwd
                        /app/ansible_info/object05
                        [yun@ansi-manager object05]$ ll
                        total 24
                        drwxrwxr-x 2 yun yun 129 Aug 24 11:41 file
                        -rw-rw-r-- 1 yun yun 1029 Aug 24 11:57 test_handlers.yml
                        [yun@ansi-manager object05]$ ll file/ # 涉及配置文件
                        total 20
                        -rw-r--r-- 1 yun yun 11767 Aug 24 11:41 httpd.conf.j2
                        [yun@ansi-manager object05]$ vim file/httpd.conf.j2 # 配置文件修改的地方
                        …………
                        # Change this to Listen on specific IP addresses as shown below to
                        # prevent Apache from glomming onto all bound IP addresses.
                        #
                        #Listen 12.34.56.78:80
                        ###### 端口改为变量
                        Listen {{ httpd_port }}


                        …………
                        [yun@ansi-manager object05]$ cat test_handlers.yml # yml 文件
                        ---
                        # 比如安装配置启动 httpd。当我们修改配置文件,重启 httpd 服务
                        # 要求:修改配置,重启一个或多个服务
                        - hosts: proxyservers
                        # 这里为了演示方便,因此变量直接就写在了该文件中
                        vars:
                        - httpd_port: 8081


                        tasks:
                        - name: "Install httpd"
                        yum:
                        name: "{{ packages }}"
                        state: present
                        vars:
                        packages:
                        - httpd
                        - httpd-tools


                        - name: "Httpd config"
                        template:
                        src: ./file/httpd.conf.j2
                        dest: etc/httpd/conf/httpd.conf
                        # 一个通知
                        # notify: "Restart httpd server"
                        # 多个通知
                        notify:
                        - "Restart httpd server"
                        - "Restart crond server"


                        - name: "Start httpd server"
                        systemd:
                        name: httpd
                        state: started
                        enabled: yes


                        handlers:
                        - name: "Restart httpd server"
                        systemd:
                        name: httpd
                        state: restarted


                        - name: "Restart crond server"
                        systemd:
                        name: crond
                        state: restarted


                        [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_handlers.yml # 语法检测
                        [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_handlers.yml # 预执行,测试执行
                        [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 也不会被执行」;多个标签使用逗号分开

                          [yun@ansi-manager object05]$ pwd
                          /app/ansible_info/object05
                          [yun@ansi-manager object05]$ ll
                          total 8
                          drwxrwxr-x 2 yun yun 27 Oct 26 18:07 file
                          -rw-rw-r-- 1 yun yun 1004 Oct 26 19:21 test_tags.yml
                          [yun@ansi-manager object05]$ cat test_tags.yml
                          ---
                          # tags 标签测试
                          - hosts: proxyservers
                          # 这里为了演示方便,因此变量直接就写在了该文件中
                          vars:
                          - httpd_port: 8081


                          tasks:
                          - name: "Install httpd"
                          yum:
                          name: "{{ packages }}"
                          state: present
                          vars:
                          packages:
                          - httpd
                          - httpd-tools
                          tags:
                          - httpd_server
                          - httpd_install


                          - name: "Httpd config"
                          template:
                          src: ./file/httpd.conf.j2
                          dest: etc/httpd/conf/httpd.conf
                          notify: "Restart httpd server"
                          tags:
                          - httpd_server
                          - httpd_config


                          - name: "Start httpd server"
                          systemd:
                          name: httpd
                          state: started
                          enabled: yes
                          tags:
                          - httpd_server
                          - httpd_start


                          - name: "Create dir"
                          file:
                          path: tmp/with_items_testdir
                          state: directory
                          tags: create_dir


                          handlers:
                          - name: "Restart httpd server"
                          systemd:
                          name: httpd
                          state: restarted


                          playbook 标签查看

                            [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_tags.yml  # 语法检测
                            ## 查看 playbook 中的任务和标签
                            [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --list-tasks


                            playbook: test_tags.yml


                            play #1 (proxyservers): proxyservers TAGS: []
                            tasks:
                            Install httpd TAGS: [httpd_install, httpd_server]
                            Httpd config TAGS: [httpd_config, httpd_server]
                            Start httpd server TAGS: [httpd_server, httpd_start]
                            Create dir TAGS: [create_dir]
                            ## 查看 playbook 中的标签
                            [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --list-tags


                            playbook: test_tags.yml


                            play #1 (proxyservers): proxyservers TAGS: []
                            TASK TAGS: [create_dir, httpd_config, httpd_install, httpd_server, httpd_start]


                            playbook 执行

                              ## 单个标签执行
                              [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml -t httpd_install
                              [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml -t httpd_server
                              ## 多个标签执行
                              [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml -t httpd_install,httpd_config,httpd_start
                              ## 跳过哪些标签不执行「标签作用于 task 上,即使该 task 还有其他标签,这个 task 也不会被执行」
                              [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --skip-tags httpd_server
                              [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_tags.yml --skip-tags httpd_install,create_dir
                              ## 执行整个 playbook
                              [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 模块:这个模块还将支持一段时间,但在不久的将来可能会弃用「最好不要使用这个模块」。


                              示例

                                [yun@ansi-manager object05]$ pwd
                                /app/ansible_info/object05
                                [yun@ansi-manager object05]$ ll
                                total 32
                                drwxrwxr-x 2 yun yun 103 Aug 24 19:51 file_yml
                                -rw-rw-r-- 1 yun yun 518 Aug 24 19:56 test_include.yml
                                [yun@ansi-manager object05]$ ll file_yml/
                                total 16
                                -rw-rw-r-- 1 yun yun 136 Aug 24 19:51 httpd_config.yml
                                -rw-rw-r-- 1 yun yun 133 Aug 24 19:41 httpd_install.yml
                                -rw-rw-r-- 1 yun yun 80 Aug 24 19:42 httpd_restart.yml
                                -rw-rw-r-- 1 yun yun 93 Aug 24 19:41 httpd_start.yml
                                ## 每个小 yml 文件的具体内容
                                [yun@ansi-manager object05]$ cat file_yml/httpd_install.yml
                                - name: "Install httpd"
                                yum:
                                name: "{{ packages }}"
                                state: present
                                vars:
                                packages:
                                - httpd
                                - httpd-tools


                                [yun@ansi-manager object05]$ cat file_yml/httpd_config.yml
                                - name: "Httpd config"
                                template:
                                src: ./file/httpd.conf.j2
                                dest: etc/httpd/conf/httpd.conf
                                notify: "Restart httpd server"


                                [yun@ansi-manager object05]$ cat file_yml/httpd_start.yml
                                - name: "Start httpd server"
                                systemd:
                                name: httpd
                                state: started
                                enabled: yes


                                [yun@ansi-manager object05]$ cat file_yml/httpd_restart.yml
                                - name: "Restart httpd server"
                                systemd:
                                name: httpd
                                state: restarted


                                ###### 主调用 yml 文件内容 ######
                                [yun@ansi-manager object05]$ cat test_include.yml
                                ---
                                # 调用其他 yml 文件
                                - hosts: proxyservers
                                # 这里为了演示方便,因此变量直接就写在了该文件中
                                vars:
                                - httpd_port: 8083


                                tasks:
                                - include_tasks: ./file_yml/httpd_install.yml
                                - include_tasks: ./file_yml/httpd_config.yml
                                - include_tasks: ./file_yml/httpd_start.yml


                                handlers:
                                # 使用 import 进行预处理,这样防止 notify 时,在 handlers 找不到对应的信息
                                - import_tasks: ./file_yml/httpd_restart.yml


                                [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_include.yml # 语法检测
                                [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_include.yml # 预执行,测试执行
                                [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_include.yml # 执行


                                忽略错误-ignore_errors

                                在 playbook 执行过程中,默认情况下如果有错误发生,那么后面的 task 就不执行,并且退出当前的 playbook。


                                如果我们对某些 task 执行结果不关心,不管执行是否成功,后面的 task 也要继续执行。那就需要通过 ignore_errors 来忽略当前 task 的错误结果,让后面的 task 继续往下执行。

                                  [yun@ansi-manager object05]$ pwd
                                  /app/ansible_info/object05
                                  [yun@ansi-manager object05]$ ll
                                  total 36
                                  -rw-rw-r-- 1 yun yun 479 Aug 26 09:24 test_ignore_errors.yml
                                  [yun@ansi-manager object05]$ cat test_ignore_errors.yml
                                  ---
                                  # ignore_errors 测试
                                  - hosts: proxyservers


                                  tasks:
                                  - name: "Install httpd"
                                  yum:
                                  name: "{{ packages }}"
                                  state: present
                                  vars:
                                  packages:
                                  - httpd
                                  - httpd-tools


                                  - name: "Shell false"
                                  shell: bin/false
                                  # 是否忽略该 task 的错误 「打开或关闭注释,对比」
                                  ignore_errors: True


                                  - name: "Create dir"
                                  file:
                                  path: tmp/with_items_testdir
                                  state: directory


                                  [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_ignore_errors.yml # 语法检测
                                  [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_ignore_errors.yml # 预执行,测试执行
                                  [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_ignore_errors.yml # 执行


                                  默认情况


                                  使用了 ignore_errors 的情况


                                  自定义错误判定条件-failed_when

                                  命令不依赖返回状态码来判定是否执行失败,而是要查看命令返回内容来决定,比如返回内容中包括 command not found 字符串,则判定为失败。

                                    [yun@ansi-manager object05]$ pwd
                                    /app/ansible_info/object05
                                    [yun@ansi-manager object05]$ ll
                                    total 48
                                    -rw-rw-r-- 1 yun yun 369 Aug 29 16:12 test_custom_error.yml
                                    [yun@ansi-manager object05]$ cat test_custom_error.yml
                                    ---
                                    # 自定义错误条件
                                    - hosts: proxyservers


                                    tasks:
                                    - name: "this command prints 'command not found' if not find"
                                    shell: "kkk -x" # 测试一
                                    #shell: "/bin/kkk -x" # 测试二
                                    register: shell_result
                                    failed_when: "'command not found' in shell_result['stderr']"


                                    - name: "print shell_result info"
                                    debug:
                                    msg: "{{ shell_result['stderr'] }}"


                                    [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_custom_error.yml # 语法检测
                                    [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_custom_error.yml # 预执行,测试执行
                                    [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。最终结果就是配置改变了,但是就是没有重启服务。显然不符合我们的初衷。

                                      [yun@ansi-manager object05]$ pwd
                                      /app/ansible_info/object05
                                      [yun@ansi-manager object05]$ ll
                                      total 40
                                      drwxrwxr-x 2 yun yun 129 Aug 24 14:28 file
                                      -rw-rw-r-- 1 yun yun 909 Aug 29 12:23 test_error_deal.yml
                                      [yun@ansi-manager object05]$ cat test_error_deal.yml
                                      ---
                                      # 即使 task 执行错误,之前已 notify 的 handlers 必须被执行
                                      - hosts: proxyservers
                                      # 这里为了演示方便,因此变量直接就写在了该文件中
                                      vars:
                                      - httpd_port: 8087
                                      # 即使 task 执行错误,之前已 notify 的 handlers 必须被执行
                                      force_handlers: yes


                                      tasks:
                                      - name: "Install httpd"
                                      yum:
                                      name: "{{ packages }}"
                                      state: present
                                      vars:
                                      packages:
                                      - httpd
                                      - httpd-tools


                                      - name: "Httpd config"
                                      template:
                                      src: ./file/httpd.conf.j2
                                      dest: /etc/httpd/conf/httpd.conf
                                      notify: "Restart httpd server"


                                      - name: "Start httpd server"
                                      systemd:
                                      name: httpd
                                      state: started
                                      enabled: yes


                                      # /bin/false 返回状态码为1,不为0
                                      - name: "Shell task"
                                      shell: /bin/false


                                      - name: "Create dir"
                                      file:
                                      path: /tmp/with_items_testdir
                                      state: directory


                                      handlers:
                                      - name: "Restart httpd server"
                                      systemd:
                                      name: httpd
                                      state: restarted


                                      [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_error_deal.yml # 语法检测
                                      [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_error_deal.yml # 预执行,测试执行
                                      [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 模块,只是我们常用于这两个模块而已。

                                        [yun@ansi-manager object05]$ pwd
                                        /app/ansible_info/object05
                                        [yun@ansi-manager object05]$ ll
                                        total 44
                                        -rw-rw-r-- 1 yun yun 299 Aug 29 14:47 test_changed_when.yml
                                        [yun@ansi-manager object05]$ cat test_changed_when.yml
                                        ---
                                        # 使用 changed_when: false 抑制 changed 状态
                                        - hosts: proxyservers


                                        tasks:
                                        - name: "Shell task"
                                        shell: netstat -lntp | grep 'httpd'
                                        register: check_httpd
                                        # changed_when: false # 任何时候,都不为 changed 状态
                                        #### check_httpd['stdout'] 不包含 httpd 为 true,否则 false
                                        changed_when: "'httpd' not in check_httpd['stdout']" # 结果为 false


                                        - name: "Debug output"
                                        debug:
                                        msg: "{{ check_httpd.stdout }}"


                                        [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key --syntax-check test_changed_when.yml # 语法检测
                                        [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key -C test_changed_when.yml # 预执行,测试执行
                                        [yun@ansi-manager object05]$ ansible-playbook -b -i ../hosts_key test_changed_when.yml # 执行




                                        ———END———

                                        如果觉得不错就关注下呗 (-^O^-) !


                                        文章转载自OpenInfo,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                                        评论