Skip to content
SRE运维进阶之路SRE运维进阶之路
github icon
  • Jenkins

    • Jenkins 学习笔记
      • 1 Jenkins 简介

        • 1.1 DevOps、CI、CD都是什么鬼?
          • 1.2 Jenkins简介
          • 2 安装 Jenkins

            • 2.1 yum 安装 jenkins
              • 2.2 war 包安装 jenkins
                • 2.3 使用 ansible 安装 jenkins
                  • 2.4 使用docker安装jenkins
                  • 3 使用 Jenkins

                    • 3.1 Jenkins用户权限管理
                      • 3.2 访问 API
                      • 4 流水线核心语法

                        • 4.1 开始编写Jenkinsfile
                          • 4.2 Jenkins流水线语法
                            • 4.3 Groovy 简明教程
                              • 4.4 Jenkins共享库应用
                                • 4.5 共享库之钉钉消息推送
                                  • 4.6 流水线开发工具
                                    • 4.7 pipeline 生产配置实例
                                      • 4.8 在 VS Code 中校验 Jenkinsfile
                                      • 5 流水线集成篇

                                        • 5.1 构建发布工具集成
                                          • 5.2 用户认证系统集成
                                          • 6 管理 jenkins

                                            • 6.1 Jenkins 关闭和重启的实现方式
                                            • 7 日常维护

                                              • 7.1 Jenkins Job迁移
                                              • 8 Jenkins 常见问题
                                              • Ansible

                                                • Ansible 学习笔记
                                                  • 1 初识Ansible
                                                    • 2 Ansible Inventory配置详解
                                                      • 3 Ansible Ad-hoc命令集

                                                        • 3.1 Ansible Ad-hoc 常用命令集
                                                          • 3.2 Ansible lineinfile模块详解
                                                          • 4 Ansible Playbook

                                                            • 4.1 Playbook的结构及handler用法
                                                              • 4.2 Playbook循环语句
                                                                • 4.3 Playbook条件语句
                                                                  • 4.4 Playbook高级用法
                                                                    • 4.5 Playbook之tags
                                                                      • 4.6 巧用Roles
                                                                        • 1 如何重用Playbook
                                                                          • 2 roles目录结构
                                                                            • 3 组织 task
                                                                              • 4 组织handler
                                                                                • 5 组织变量
                                                                                  • 5.1 vars_files
                                                                                    • 5.2 include_vars
                                                                                      • 5.3 --extra-vars选项
                                                                                      • 6 组织playbook文件
                                                                                        • 7 playbook 调用
                                                                                          • 8 playbook解析、动态加载和静态加载
                                                                                            • 9 Ansible Galaxy和Collection
                                                                                            • 4.7 Jinja2 模板
                                                                                              • 4.8 yaml语法
                                                                                              • 5 Ansible变量

                                                                                                • 5.1 自定义变量
                                                                                                  • 5.2 Fact变量
                                                                                                    • 5.3 魔法变量
                                                                                                      • 5.4 使用lookup生成变量
                                                                                                        • 5.5 使用vault配置加密
                                                                                                        • 6 Ansible使用优化
                                                                                                          • 7 常见问题
                                                                                                            • 8 综合项目

                                                                                                              • 使用 ansible 快速安装 k8s 机器

                                                                                                            4.6 巧用Roles

                                                                                                            author iconClaycalendar icon2020年3月20日category icon
                                                                                                            • 自动化工具
                                                                                                            tag icon
                                                                                                            • Ansible
                                                                                                            timer icon大约 19 分钟

                                                                                                            此页内容
                                                                                                            • 1 如何重用Playbook
                                                                                                            • 2 roles目录结构
                                                                                                            • 3 组织 task
                                                                                                            • 4 组织handler
                                                                                                            • 5 组织变量
                                                                                                              • 5.1 vars_files
                                                                                                              • 5.2 include_vars
                                                                                                              • 5.3 --extra-vars选项
                                                                                                            • 6 组织playbook文件
                                                                                                            • 7 playbook 调用
                                                                                                            • 8 playbook解析、动态加载和静态加载
                                                                                                            • 9 Ansible Galaxy和Collection

                                                                                                            # 4.6 巧用Roles

                                                                                                            # 1 如何重用Playbook

                                                                                                            不能站在巨人肩膀上的编程语言不是好语言,支持重用机制会节省重复的调研工作上浪费大量的时间,当然也会提高可维护性。

                                                                                                            Playbook支持两种重用机制,一种是重用静态Playbook脚本,另外一种是类似于编程语言中函数的机制。

                                                                                                            • include语句 - 重用静态的Playbook脚本,使用起来简单、直接。
                                                                                                            • role语言 - Playbook的“函数机制”,使用方法稍复杂、功能强大。是Playbook脚本的共享平台ansible galaxy主要的分享方式

                                                                                                            组织文件的方式均使用include指令,但随着版本的更迭,Ansible对这方面做了更为细致的区分。虽然目前仍然支持include,但早已纳入废弃的计划,所以现在不要再使用include指令。

                                                                                                            • 对于playbook(或play)或task,可以使用include_xxx或import_xxx指令:

                                                                                                              (1).include_tasks和import_tasks用于引入外部任务文件;

                                                                                                              (2).import_playbook用于引入playbook文件;

                                                                                                              (3).include可用于引入几乎所有内容文件,但建议不要使用它;

                                                                                                            • 对于handler,因为它本身也是task,所以它也能使用include_tasks、import_tasks来引入,但是这并不是想象中那么简单,后文再细说。

                                                                                                            • 对于variable,使用include_vars(这是核心模块提供的功能)或其它组织方式(如vars_files),没有对应的import_vars。

                                                                                                            后文要介绍的Role,使用include_role或import_role或roles指令。

                                                                                                            既然某类内容文件既可以使用include_xxx引入,也可以使用import_xxx引入,对于我们来说,就有必要去搞清楚它们有什么区别。

                                                                                                            本文最后我会详细解释它们,现在我先把结论写在这:

                                                                                                            (1).include_xxx指令是在遇到它的时候才加载文件并解析执行,所以它是动态解析的;

                                                                                                            (2).import_xxx是在解析playbook的时候解析的,也就是说在执行playbook之前就已经解析好了,所以它也称为静态加载。

                                                                                                            # 2 roles目录结构

                                                                                                            Role可以组织任务、变量、handler以及其它一些内容,所以一个完整的Role里包含的目录和文件可能较多,手动去创建所有这些目录和文件是一件比较烦人的事,好在可以使用ansible-galaxy init ROLE_NAME命令来快速创建一个符合Role文件组织规范的框架。

                                                                                                            例如,下面创建了一个名为first_role的Role:

                                                                                                            $ ansible-galaxy init first_role
                                                                                                            $ tree first_role/
                                                                                                            first_role/            \\ 角色名称
                                                                                                            ├── defaults           \\ 为当前角色设定默认变量时使用此目录,应当包含一个main.yml文件;
                                                                                                            │   └── main.yml        
                                                                                                            ├── files              \\ 存放有copy或script等模块调用的文件
                                                                                                            ├── handlers           \\ 此目录应当包含一个main.yml文件,用于定义各角色用到的各handler
                                                                                                            │   └── main.yml
                                                                                                            ├── meta               \\ 应当包含一个main.yml,用于定义角色的特殊设定及其依赖关系;1.3及以后版本支持
                                                                                                            │   └── main.yml
                                                                                                            ├── README.md
                                                                                                            ├── tasks              \\ 至少包含一个名为main.yml的文件,定义了此角色的任务列表
                                                                                                            │   └── main.yml
                                                                                                            ├── templates          \\ template模块会自动在此目录中寻找Jinja2模板文件
                                                                                                            ├── tests
                                                                                                            │   ├── inventory
                                                                                                            │   └── test.yml
                                                                                                            └── vars              \\ 应当包含一个main.yml,用于定义此角色用到的变量
                                                                                                                └── main.yml
                                                                                                            
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15
                                                                                                            16
                                                                                                            17
                                                                                                            18
                                                                                                            19
                                                                                                            20

                                                                                                            Role中有两个地方可以定义变量:

                                                                                                            (1).roles/xxx/vars/main.yml

                                                                                                            (2).roles/xxx/defaults/main.yml

                                                                                                            从目录名称中可以看出,defaults/main.yml中用于定义Role的默认变量,那么显然,vars/main.yml用于定义其它变量。这两个文件之间的区别在于,defaults/main.yml中定义的变量优先级低于vars/main.yml中定义的变量。事实上,defaults/main.yml中的变量优先级几乎是最低的,基本上其它任何地方定义的变量都可以覆盖它。

                                                                                                            通常会将每个Role放在一个称为roles的目录下。

                                                                                                            可以使用ansible-galaxy init --help查看更多选项。比如,使用--init-path选项指定创建的Role路径:

                                                                                                            ansible-galaxy init --init-path /etc/ansible/roles first_role
                                                                                                            
                                                                                                            1

                                                                                                            不难发现文件大多命名为main.yml,这是Role在使用到它们的时候默认加载的文件名,如果换成其它名称,则需要手动使用include_xxx或import_xxx去加载。另一方面,这些目录下可能还包含其它yml文件,比如tasks目录下有多个任务文件,那么需要在这些main.yml文件中使用include_xxx或import_xxx去加载其它外部文件。

                                                                                                            有了Role之后,就可以将Role当作一个不可分割的任务整体来对待,一个Role相当于是一个完整的功能。但在此需要明确一个层次上的概念,Role只是用于组织一个或多个任务,原来在play级别中使用tasks指令来定义任务,现在使用roles指令来引入Role中定义的任务。当然,roles指令和tasks指令并不冲突,它们可以共存。通过下面的图,应能帮助理解Role的角色。

                                                                                                            image-20210316113538941

                                                                                                            既然Role是一个完整的任务体系,拥有Role之后就可以去使用它,或者也可以分发给别人使用,但是一个Role仅仅只是目录而已,如何去使用这个Role呢?

                                                                                                            # 3 组织 task

                                                                                                            在此前的所有示例中,一直都是将所有任务编写在单个playbook文件中。但Ansible允许我们将任务分离到不同的文件中,然后去引入外部任务文件。

                                                                                                            用示例来解释会非常简单。

                                                                                                            假设,两个playbook文件pb1.yml和pb2.yml。pb1.yml文件内容如下:

                                                                                                            ---
                                                                                                            - name: play1
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              tasks:
                                                                                                                - name: task1 in play1
                                                                                                                  debug:
                                                                                                                    msg: "task1 in play1"
                                                                                                            
                                                                                                              # - include_tasks: pb2.yml
                                                                                                                - import_tasks: pb2.yml
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11

                                                                                                            pb2.yml文件内容如下:

                                                                                                            - name: task2 in play1
                                                                                                              debug:
                                                                                                                msg: "task2 in play1"
                                                                                                            
                                                                                                            - name: task3 in play1
                                                                                                              debug:
                                                                                                                msg: "task3 in play1"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7

                                                                                                            上面是在pb1.yml文件中通过import_tasks引入了额外的任务文件pb2.yml,对于此处来说,将import_tasks替换成include_tasks也能正确工作,不会有任何影响。

                                                                                                            执行结果有差别:import_tasks,相当于有三个task,include_tasks,相当于有四个task,多了一个included的任务

                                                                                                            但如果是在循环中(比如loop),则只能使用include_tasks而不能再使用import_tasks。

                                                                                                            在循环中include文件

                                                                                                            修改pb1.yml和pb2.yml文件内容:

                                                                                                            pb1.yml内容如下,注意该文件中的include_tasks指令:

                                                                                                            ---
                                                                                                            - name: play1
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              tasks:
                                                                                                                - name: task1 in play1
                                                                                                                  debug:
                                                                                                                    msg: "task1 in play1"
                                                                                                            
                                                                                                                - name: include two times
                                                                                                                  include_tasks: pb2.yml
                                                                                                                  loop:
                                                                                                                    - ONE
                                                                                                                    - TWO
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14

                                                                                                            pb2.yml内容如下,注意该文件中的变量引用:

                                                                                                            - name: task2 in play1
                                                                                                              debug:
                                                                                                                msg: "task2 in {{item}}"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3

                                                                                                            执行pb1.yml文件,观察执行结果:

                                                                                                            PLAY [play1] *****************************************************************************************************************************************************************************************************************************
                                                                                                            
                                                                                                            TASK [task1 in play1] ********************************************************************************************************************************************************************************************************************
                                                                                                            ok: [localhost] => {
                                                                                                                "msg": "task1 in play1"
                                                                                                            }
                                                                                                            
                                                                                                            TASK [include two times] *****************************************************************************************************************************************************************************************************************
                                                                                                            included: /etc/ansible/playbooks/pb2.yml for localhost
                                                                                                            included: /etc/ansible/playbooks/pb2.yml for localhost
                                                                                                            
                                                                                                            TASK [task2 in play1] ********************************************************************************************************************************************************************************************************************
                                                                                                            ok: [localhost] => {
                                                                                                                "msg": "task2 in ONE"
                                                                                                            }
                                                                                                            
                                                                                                            TASK [task2 in play1] ********************************************************************************************************************************************************************************************************************
                                                                                                            ok: [localhost] => {
                                                                                                                "msg": "task2 in TWO"
                                                                                                            }
                                                                                                            
                                                                                                            PLAY RECAP *******************************************************************************************************************************************************************************************************************************
                                                                                                            localhost                  : ok=5    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15
                                                                                                            16
                                                                                                            17
                                                                                                            18
                                                                                                            19
                                                                                                            20
                                                                                                            21
                                                                                                            22
                                                                                                            23

                                                                                                            上面是在loop循环中加载两次pb2.yml文件,该文件中的任务被执行了两次,并且在pb2.yml中能够引用外部文件(pb1.yml)中定义的变量。

                                                                                                            分析一下上面的执行流程:

                                                                                                            (1).解析playbook文件pb1.yml

                                                                                                            (2).执行第一个play

                                                                                                            (3).当执行到pb1.yml中的第二个任务时,该任务在循环中,且其作用是加载外部任务文件pb2.yml

                                                                                                            (4).开始循环,每轮循环都加载、解析并执行pb2.yml文件中的所有任务

                                                                                                            (5).退出正是因为include_tasks指令是在遇到它的时候才进行加载解析以及执行,所以在pb2.yml中才能使用变量。

                                                                                                            如果将上面loop循环中的include_tasks换成import_tasks呢?语法会报错,后面我会详细解释。

                                                                                                            # 4 组织handler

                                                                                                            handler其本质也是task,所以也可以使用include_tasks或import_tasks来加载外部任务文件。但是它们引入handler任务文件的方式有很大的差别。

                                                                                                            先看include_tasks引入handler任务文件的示例:

                                                                                                            pb1.yml的内容:

                                                                                                            ---
                                                                                                            - name: play1
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              handlers:
                                                                                                                - name: h1
                                                                                                                  include_tasks: handler1.yml
                                                                                                            
                                                                                                              tasks:
                                                                                                                - name: task1 in play1
                                                                                                                  debug:
                                                                                                                    msg: "task1 in play1"
                                                                                                                  changed_when: true
                                                                                                                  notify:
                                                                                                                    - h1
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15

                                                                                                            注意在tasks的任务中加了一个指令changed_when: true,它用来强制指定它所在任务的changed状态,如果条件为真,则changed=1,否则changed=0。使用这个指令是因为debug模块默认不会引起changed=1行为,所以只能使用该指令来强制其状态为changed=1。

                                                                                                            当Ansible监控到了changed=1,notify指令会生效,它会去触发对应的handler,它触发的handler的名称是handler1,其作用是使用include_tasks指令引入handler1.yml文件。

                                                                                                            下面是handler1.yml文件的内容:

                                                                                                            ---
                                                                                                            - name: h11
                                                                                                              debug:
                                                                                                                msg: "task h11"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4

                                                                                                            注意两个名称,一个是notify触发handler的任务名称("h1"),一个是引入文件中任务的名称("h11"),它们是两个任务。再来看import_tasks引入handler文件的示例,注意观察名称的不同点。

                                                                                                            ---
                                                                                                            - name: play1
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              handlers:
                                                                                                                - name: h2
                                                                                                                  import_tasks: handler2.yml
                                                                                                            
                                                                                                              tasks:
                                                                                                                - name: task1 in play1
                                                                                                                  debug:
                                                                                                                    msg: "task1 in play1"
                                                                                                                  changed_when: true
                                                                                                                  notify:
                                                                                                                    - h22
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15

                                                                                                            下面是使用import_tasks引入的handler2.yml文件的内容:

                                                                                                            ---
                                                                                                            - name: h22
                                                                                                              debug:
                                                                                                                msg: "task h22"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4

                                                                                                            在引入handler任务文件的时候,include_tasks和import_tasks的区别表现在:

                                                                                                            (1).使用include_tasks时,notify指令触发的handler名称是include_tasks任务本身的名称

                                                                                                            (2).使用import_tasks时,notify指令触发的handler名称是import_tasks所引入文件内的任务名称

                                                                                                            其实分析一下就很容易理解为什么notify触发的名称要不同:

                                                                                                            (1).include_tasks是在遇到这个指令的时候才引入文件的,所以notify不可能去触发外部handler文件里的名称(h11),外部handler文件中的名称在其引入之前根本就不存在

                                                                                                            (2).import_tasks是在解析playbook的时候引入的,换句话说,在执行play之前就已经把外部handler文件的内容引入并替换在handler的位置处,而原来的名称(h2)则被覆盖了

                                                                                                            最后,不要忘了import_tasks或include_tasks自身也是任务,既然是任务,就能使用task层次的指令。

                                                                                                            但这两个指令对task层次指令的处理方式不同,相关细节仍然保留到后文统一解释。

                                                                                                            # 5 组织变量

                                                                                                            在Ansible中有很多种定义变量的方式,想要搞清楚所有这些散布各个角落的知识,是一个很大的难点。好在,我们没必要去过多关注,只需要掌握几个常用的变量定义和应用的方式即可。此处我要介绍的是将变量定义在外部文件中,然后去引入这些外部文件中的变量。

                                                                                                            引入保存了变量的文件有两种方式:include_vars和vars_files。此外,还可以在命令行中使用-e或--extra-vars选项来引入。

                                                                                                            # 5.1 vars_files

                                                                                                            先介绍vars_files,它是一个play级别的指令,可用于在解析playbook的阶段引入一个或多个保存了变量的外部文件。

                                                                                                            例如,pb.yml文件如下:

                                                                                                            ---
                                                                                                            - name: play1
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              vars_files:
                                                                                                                - varfile1.yml
                                                                                                                - varfile2.yml
                                                                                                              tasks:
                                                                                                                - debug:
                                                                                                                    msg: "var in varfile1: {{var1}}"
                                                                                                                - debug:
                                                                                                                    msg: "var in varfile2: {{var2}}"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12

                                                                                                            pb.yml文件通过vars_files引入了两个变量文件,变量文件的写法要求遵守YAML或JSON格式。下面是这两个文件的内容:

                                                                                                            # 下面是varfile1.yml文件的内容
                                                                                                            ---
                                                                                                            var1: "value1"
                                                                                                            var11: "value11"
                                                                                                            
                                                                                                            # 下面是varfile2.yml文件的内容
                                                                                                            ---
                                                                                                            var2: "value2"
                                                                                                            var22: "value22"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9

                                                                                                            需要说明的是,vars_files指令是play级别的指令,且是在解析playbook的时候加载并解析的,所以所引入变量的变量是play范围内可用的,其它play不可使用这些变量。

                                                                                                            # 5.2 include_vars

                                                                                                            include_vars指令也可用于引入外部变量文件,它和vars_files不同。一方面,include_vars是模块提供的功能,它是一个实实在在的任务,所以在这个任务执行之后才会创建变量。另一方面,既然include_vars是一个任务,它就可以被一些task级别的指令控制,如when指令。

                                                                                                            例如:

                                                                                                            ---
                                                                                                            - name: play1
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              tasks:
                                                                                                                - name: include vars from files
                                                                                                                  include_vars: varfile1.yml
                                                                                                                  when: 3 > 2
                                                                                                                   
                                                                                                                - debug:
                                                                                                                    msg: "var in varfile1: {{var1}}"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11

                                                                                                            上面示例中引入变量文件的方式是直接指定文件名include_vars: varfile1.yml,也可以明确使用file参数来指定路径。

                                                                                                            - name: include vars from files
                                                                                                              include_vars:
                                                                                                                file: varfile1.yml
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3

                                                                                                            如果想要引入多个文件,可以使用循环的方式。例如:

                                                                                                            - name: include two var files
                                                                                                              include_vars:
                                                                                                                file: "{{item}}"
                                                                                                              loop:
                                                                                                                - varfile1.yml
                                                                                                                - varfile2.yml
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6

                                                                                                            需要注意,include_vars在引入文件的时候要求文件已经存在,如果有多个可能的文件但不确定文件是否已经存在,可以使用with_first_found指令或lookup的first_found插件,它们作用相同,都用于从文件列表中找出存在的文件,找到后立即停止,之前就曾提到过with_xxx的本质是调用lookup对应的插件。

                                                                                                            例如:

                                                                                                            tasks:
                                                                                                              - name: include vars from files
                                                                                                                include_vars:
                                                                                                                  file: "{{item}}"
                                                                                                                with_first_found:
                                                                                                                  - varfile1.yml
                                                                                                                  - varfile2.yml
                                                                                                                  - default.yml
                                                                                                            
                                                                                                            # 等价于:
                                                                                                            tasks:
                                                                                                              - name: include vars from files
                                                                                                                include_vars:
                                                                                                                  file: "{{ lookup('first_found',any_files) }}"
                                                                                                                vars:
                                                                                                                  any_files:
                                                                                                                    - varfile1.yml
                                                                                                                    - varfile2.yml
                                                                                                                    - default.yml
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15
                                                                                                            16
                                                                                                            17
                                                                                                            18
                                                                                                            19

                                                                                                            此外,include_vars还能从目录中导入多个文件,默认会递归到子目录中。例如:

                                                                                                            - name: Include all files in vars/all
                                                                                                              include_vars:
                                                                                                                dir: vars/all
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3

                                                                                                            # 5.3 --extra-vars选项

                                                                                                            ansible-playbook命令的-e选项或--extra-vars选项也可以用来定义变量或引入变量文件。

                                                                                                            # 定义单个变量
                                                                                                            $ ansible-playbook -e 'var1="value1"' xxx.yml
                                                                                                            
                                                                                                            # 定义多个变量
                                                                                                            $ ansible-playbook -e 'var1="value1" var2="value2"' xxx.yml
                                                                                                            
                                                                                                            # 引入单个变量文件
                                                                                                            $ ansible-playbook -e '@varfile1.yml' xxx.yml
                                                                                                            
                                                                                                            # 引入多个变量文件
                                                                                                            $ ansible-playbook -e '@varfile1.yml' -e '@varfile2.yml' xxx.yml
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11

                                                                                                            因为是通过选项的方式来定义变量的,所以它所定义的变量是全局的,对所有play都有效。

                                                                                                            通常来说不建议使用-e选项,因为这对用户来说是不透明也不友好的,要求用户记住要定义哪些变量。

                                                                                                            # 6 组织playbook文件

                                                                                                            当单个playbook文件中的任务过多时,或许就是将任务划分到多个文件中的时刻。

                                                                                                            import_playbook指令可用于引入playbook文件,它是一个play级别的指令,其本质是引入外部文件中的一个或多个play。

                                                                                                            例如,pb.yml是入口playbook文件,此文件中引入了其它playbook文件,其内容如下:

                                                                                                            ---
                                                                                                            # 引入其它playbook文件
                                                                                                            - import_playbook: pb1.yml
                                                                                                            - import_playbook: pb2.yml
                                                                                                            
                                                                                                            # 文件本身的play
                                                                                                            - name: play in self
                                                                                                              hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              tasks:
                                                                                                                - debug: 'msg="file pb.yml"'
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11

                                                                                                            pb1.yml,pb2.yml文件都是一个完整的playbook,它可以包含一个或多个play

                                                                                                            # 7 playbook 调用

                                                                                                            ansible-playbook -i /root/xxx.yml  /root/app/main.yml  --limit "lala_xxx" -e "user=wawo"
                                                                                                            
                                                                                                            1

                                                                                                            解析: -i 指定要运行的主机清单 --limit 指定运行的ip地址 -e 指定运行的外部参数

                                                                                                            运行的控制 YAML 文件为: /root/app/main.yml

                                                                                                            ---
                                                                                                            - hosts: all
                                                                                                              roles:
                                                                                                                - xxx
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4

                                                                                                            hosts指定所有(all)的主机,但是由于在外部已经指定了主机的配置,所以all由外部指定参数来进行

                                                                                                            roles指定要执行的具体剧本

                                                                                                            **roles **

                                                                                                            1. 首先执行meta下的main.yml文件内容 可以设置该role和其它role之前的关联关系。 dependencies

                                                                                                            2. gather_facts任务

                                                                                                            3. pre_tasks指令中的任务

                                                                                                            4. pre_tasks中触发的所有handler

                                                                                                            5. roles指令加载的Role,执行tasks下的main.yml文件内容

                                                                                                            6. tasks指令中的任务

                                                                                                            7. roles和tasks中触发的所有handler, 使用了notify后,会调用 handlers 目录下的main.yml文件

                                                                                                            8. post_tasks指令中的任务

                                                                                                            9. post_tasks中触发的所有handler

                                                                                                            用到的变量,会直接加载defaults目录下的main.yml文件,或者vars目录下,

                                                                                                            用到的需要拷贝到远程机器的文件,会放到files目录下,

                                                                                                            用到模板文件,会放到 templates 目录下

                                                                                                            写好Role之后就是使用Role,即在一个入口playbook文件中去加载Role。

                                                                                                            加载Role的方式有多种:

                                                                                                            (1).roles指令:play级别的指令,在playbook解析阶段加载对应文件,这是传统的引入Role的方式

                                                                                                            (2).import_role指令:task级别的指令,在playbook解析阶段加载对应文件

                                                                                                            (3).include_role指令:task级别的指令,在遇到该指令的时候才加载Role对应文件

                                                                                                            上面通过roles指令来定义要解析和执行的Role,可以同时指定多个Role,且也可以加上role:参数,例如:

                                                                                                            roles:
                                                                                                              - first_role
                                                                                                              - role: seconde_role
                                                                                                              - role: third_role
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4

                                                                                                            也可以使用include_role和import_role来引入Role,但需注意,这两个指令是tasks级别的,也正因为它们是task级别,使得它们可以和其它task共存。

                                                                                                            例如:

                                                                                                            ---
                                                                                                            - hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              tasks:
                                                                                                                - debug:
                                                                                                                    msg: "before first role"
                                                                                                                - import_role:
                                                                                                                     name: first_role
                                                                                                                - include_role:
                                                                                                                  name: second_role
                                                                                                                - debug:
                                                                                                                   msg: "after second role"
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12

                                                                                                            这三种引入Role的方式都可以为对应的Role传递参数,例如:

                                                                                                            ---
                                                                                                            - hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              roles:
                                                                                                                - role: first_role
                                                                                                                  varvar: "valuevalue"
                                                                                                                  vars:
                                                                                                                    var1: value1
                                                                                                            
                                                                                                            tasks:
                                                                                                            - import_role:
                                                                                                                name: second_role
                                                                                                              vars:
                                                                                                                var1: value1
                                                                                                            - include_role:
                                                                                                                name: third_role
                                                                                                              vars:
                                                                                                                var1: value1
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15
                                                                                                            16
                                                                                                            17
                                                                                                            18

                                                                                                            有时候需要让某个Role按需执行,比如对于目标节点是CentOS 7时执行Role7而不执行Role6,目标节点是CentOS 6时执行Role6而不是Role7,这可以使用when指令来控制。

                                                                                                            例如:

                                                                                                            ---
                                                                                                            - hosts: localhost
                                                                                                              gather_facts: false
                                                                                                              roles:
                                                                                                              # 下面是等价的,分别采用YAML和Json语法书写
                                                                                                                - role: first_role
                                                                                                                  when: xxx
                                                                                                                - {role: ffirst_role, when: xxx}
                                                                                                              tasks:
                                                                                                              - import_role:
                                                                                                                  name: second_role
                                                                                                                when: xxx
                                                                                                              - include_role:
                                                                                                                  name: third_role
                                                                                                                when: xxx
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15

                                                                                                            注意,在roles、import_role和include_role三种方式中,when指令的层次。

                                                                                                            通常来说,无论使用哪种方式来引入Role都可以,只是某些场景下需要小心一些陷阱。

                                                                                                            # 8 playbook解析、动态加载和静态加载

                                                                                                            还是这个结论:

                                                                                                            1. import_xxx是在playbook的解析阶段加载文件

                                                                                                            2. include_xxx是遇到指令的时候加载文件

                                                                                                            只要理解了这两个结论,所有相关的现象都能理解。

                                                                                                            那么playbook的解析是什么意思,它做了什么事呢?

                                                                                                            第一个要明确的是playbook解析处于哪个阶段执行:inventory解析完成后、play开始执行前的阶段。

                                                                                                            第二个要明确的是playbook解析做了哪些哪些事。一个简单又直观的描述是,playbook解析过程中,会扫描playbook文件中的内容,然后检查语法并转换成Ansible认识的内部格式,以便让Ansible去执行。

                                                                                                            更具体一点,在解析playbook期间:

                                                                                                            1.当在playbook文件中遇到了roles、include、import_xxx指令,则会将它们指定的文件内容"插入到"指令位置处,也即原文替换,这个过程对我们来说是透明的。实际上并非真的会插入替换,稍后我会再补充,但这样理解会更容易些;

                                                                                                            • (1).roles、include、import_xxx同属一类,它们都是静态加载,都在playbook解析阶段加载文件,而include_xxx属于另一类,是动态加载,遇到指令的时候临时去加载文件;

                                                                                                            • (2).之所以有这么多看似功能重复的指令,这和Ansible版本的发展有关,不同的版本可能会小有区别;

                                                                                                            • (3).早期版本只有include指令,所以它的行为有些混乱,建议不要对其做太多考究,也尽量不要使用该指令;

                                                                                                            1、ansible中的include, include_tasks 和 import_tasks 的差别
                                                                                                            include 被 deprecated(不建议使用)了. 建议使用 include_tasks 和 import_tasks
                                                                                                            
                                                                                                            include_tasks
                                                                                                            是动态的: 在运行时展开. when只应用一次. 被include的文件名可以使用变量.
                                                                                                            
                                                                                                            import_tasks
                                                                                                            是静态的: 在加载时展开. when在被import的文件里的每个task, 都会重新检查一次. 因为是加载时展开的, 文件名的变量不能是动态设定的.
                                                                                                            请确保文件名中使用到的变量被定义在vars中、vars_files中、或者extra-vars中,静态的import不支持其他方式传入的变量。
                                                                                                            
                                                                                                            When using static includes, ensure that any variables used in their names are defined in vars/vars_files or extra-vars passed in from the command line. Static includes cannot use variables from inventory sources like group or host vars.
                                                                                                            除了上述不同之处,在使用"循环操作"和"条件判断"时,"include_tasks"和"import_tasks"也有很多不同点需要注意,注意点如下。
                                                                                                            如果想要对包含的任务列表进行循环操作,则只能使用"include_tasks"关键字,不能使用"import_tasks"关键字,"import_tasks"并不支持循环操作,
                                                                                                            也就是说,使用"loop"关键字或"with_items"关键字对include文件进行循环操作时,只能配合"include_tasks"才能正常运行。
                                                                                                            when关键字对"include_tasks"和"import_tasks"的实际操作有着本质区别,区别如下:
                                                                                                            当对"include_tasks"使用when进行条件判断时,when对应的条件只会应用于"include_tasks"任务本身,当执行被包含的任务时,不会对这些被包含的任务重新进行条件判断。
                                                                                                            当对"import_tasks"使用when进行条件判断时,when对应的条件会应用于被include的文件中的每一个任务,当执行被包含的任务时,会对每一个被包含的任务进行同样的条件判断。
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15
                                                                                                            16
                                                                                                            17

                                                                                                            我想各位已经意识到了,使用include_tasks时,这个指令自身占用一个任务,使用import_tasks的时候,这个指令自身没有任务,它所在的任务会在解析playbook的时候被其加载的子任务覆盖。

                                                                                                            • 无法使用--list-tags列出include_xxx中的tags,无法使用--list-tasks列出include_xxx中的任务,因为它们都是临时动态加载的。

                                                                                                            # 9 Ansible Galaxy和Collection

                                                                                                            很多时候我们想要实现的Ansible部署需求其实别人已经写好了,所以我们自己不用再动手写(甚至不应该自己写),直接去网上找别人已经写好的轮子即可。

                                                                                                            Ansible Galaxy(https://galaxy.ansible.com/ )是一个Ansible官方的Role仓库,世界各地的人都在里面分享自己写好的Role,我们可以直接去Galaxy上搜索是否有自己想要的Role,如果有符合自己心意的,直接安装便可。当然,我们也可以将写好的Role分享出去给别人使用。

                                                                                                            Ansible提供了一个ansible-galaxy命令行工具,可以快速创建、安装、管理由该工具维护的Role。它常用的命令有:

                                                                                                            # 安装Role:
                                                                                                            ansible-galaxy install username.role_name
                                                                                                            
                                                                                                            # 移除Role:
                                                                                                            ansible-galaxy remove username.role_name
                                                                                                            
                                                                                                            # 列出已安装的Role:
                                                                                                            ansible-galaxy list
                                                                                                            
                                                                                                            # 查看Role信息:
                                                                                                            ansible-galaxy info username.role_name
                                                                                                            
                                                                                                            # 搜索Role:
                                                                                                            ansible-galaxy search role_name
                                                                                                            
                                                                                                            # 创建Role
                                                                                                            ansible-galaxy init role_name
                                                                                                            
                                                                                                            # 此外还有:'delete','import', 'setup', 'login'
                                                                                                            # 它们都用于管理galaxy.ansible.com个人账户或里面的Role
                                                                                                            # 无视它们
                                                                                                            
                                                                                                            1
                                                                                                            2
                                                                                                            3
                                                                                                            4
                                                                                                            5
                                                                                                            6
                                                                                                            7
                                                                                                            8
                                                                                                            9
                                                                                                            10
                                                                                                            11
                                                                                                            12
                                                                                                            13
                                                                                                            14
                                                                                                            15
                                                                                                            16
                                                                                                            17
                                                                                                            18
                                                                                                            19
                                                                                                            20
                                                                                                            21

                                                                                                            虽然Ansible Galaxy中有大量的Role,但有时候我们也会在Github上搜索Role,而且Galaxy仓库上的Role大多也都在Github上。ansible-galaxy install也可以直接从git上下载安装Role。

                                                                                                            ansible-galaxy install -p roles/ git+https://github.com/chusiang/helloworld.ansible.role.git
                                                                                                            
                                                                                                            1

                                                                                                            Ansible Collection

                                                                                                            对于文件组织结构,在Ansible 2.8以前只支持Role的概念,但Ansible 2.8中添加了一项目前仍处于实验性的功能Collection,它以包的管理模式来结构化管理Ansible playbook涉及到的各个文件。

                                                                                                            比如,我们可以将整个写好的功能构建、打包,然后分发出去,别人就可以使用ansible-galaxy(要求Ansible 2.9)去安装这个打包好的文件,这为自动化构建和部署带来了很大的便利。

                                                                                                            参考链接:

                                                                                                            https://blog.51cto.com/cloumn/blog/1567

                                                                                                            edit icon编辑此页open in new window
                                                                                                            上次编辑于: 2022/4/27 15:33:00
                                                                                                            贡献者: clay-wangzhi
                                                                                                            上一页
                                                                                                            4.5 Playbook之tags
                                                                                                            下一页
                                                                                                            4.7 Jinja2 模板
                                                                                                            备案号:冀ICP备2021007336号
                                                                                                            Copyright © 2023 Clay