• 自动化利器 Ansible - 从了解到应用


     



    本文说明

    本系列使用 ansible 2.9.27 版本来说明和汇总相关信息。

    # cat /etc/system-release
    Red Hat Enterprise Linux Server release 7.8 (Maipo)
    # 
    # uname -a
    Linux test01 3.10.0-1160.36.2.el7.x86_64 #1 SMP Wed Jul 21 11:57:15 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
    # 
    # ansible --version  # ansible版本
    ansible 2.9.27
      config file = /etc/ansible/ansible.cfg
      configured module search path = [u'/home/vipxf/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
      ansible python module location = /usr/lib/python2.7/site-packages/ansible
      executable location = /bin/ansible
      python version = 2.7.5 (default, Nov 16 2020, 22:23:17) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)]
    # 
    # yum info ansible  # 包信息
    Loaded plugins: fastestmirror, product-id, search-disabled-repos, subscription-manager
    Repodata is over 2 weeks old. Install yum-cron? Or run: yum makecache fast
    Loading mirror speeds from cached hostfile
    Installed Packages
    Name        : ansible
    Arch        : noarch
    Version     : 2.9.27
    Release     : 1.el7
    Size        : 103 M
    Repo        : installed
    Summary     : SSH-based configuration management, deployment, and task execution system
    URL         : http://ansible.com
    License     : GPLv3+
    Description : Ansible is a radically simple model-driven configuration management,
                : multi-node deployment, and remote task execution system. Ansible works
                : over SSH and does not require any software or daemons to be installed
                : on remote nodes. Extension modules can be written in any language and
                : are transferred to managed machines automatically.
    
    #
    

    独立章节索引


    Ansible 概念介绍

    Ansible是自由开源的配置和自动化工具。

    主要特点

    - 无客户端模式(agentless),无侵入性,只需在主控端部署Ansible环境,被控端无需做任何操作
    - 丰富的内置模块满足绝大部分场景需求,调用特定模块可以完成特定任务
    - 基于Python研发,支持API及自定义模块,二次开发相对容易
    - 默认使用SSH协议和节点进行通信
    - 强大的playbook机制,配置语法使用yaml和Jinja模板语言,便于配置管理和实现复杂功能
    - 具有幂等性,自动跳过没有变化的部分,也就是一个操作执行一次和多次的结果相同
    - 对云计算平台和大数据提供了支持
    

    Ansible 使用模式

    - 结合CMDB:通过CMDB下发指令调用Ansible完成相关操作      
    - API调用:远程调用Ansible提供丰富的API接口
    - Ad-Hoc命令:直接通过Ad-Hoc命令集执行相关操作,多用于非固化需求、临时性操作和二次开发接口调用的场景
    - playbook执行:运行Playbook按序执行任务集
    

    Ansible由控制主机对被管节点的操作方式为 Ad-Hoc 和 Playbook。

    • Ad-Hoc模式使用单个模块,支持批量执行单条命令。
    • Playbook模式是Ansible主要管理方式,Playbook通过多个Task集合完成一类功能,可以简单地把 Playbook理解为通过组合多条Ad-Hoc操作的配置文件。

    Ansible 架构及流程

    # Ansible 主要由五部分组成
    - Ansible      Ansible核心
    - Modules      包括核心模块和自定义模块
    - Plugins      插件,对模块功能的补充,包含连接插件、邮件插件等
    - Playbooks    剧本,定义任务的配置文件
    - Inventory    定义管理主机的清单
    
    # ansible 命令执行过程
    1. Ansible加载配置文件,默认为/etc/ansible/ansible.cfg
    2. Ansible读取Inventory文件(默认为/etc/ansible/hosts),获取管理主机列表及相关变量信息
    3. 根据参数调用对应配置和模块,产生临时py文件
    4. 通过SSH方式传输到目标主机(默认为~/.ansible/tmp目录)
    5. 设置临时py文件的执行权限,执行操作并返回结果
    6. 删除临时py文件并退出
    

    Ansible 基础信息

    Ansible 主要目录

    # 配置文件目录
    /etc/ansible/
    - ansible.cfg  # Ansible 主配置文件
    - hosts  # Inventory,定义目标主机清单的文件
    - roles  # 角色目录,用于定义角色
    
    # 执行文件目录
    /usr/bin/
    
    # Lib库依赖目录
    /usr/lib/python2.7/site-packages/ansible
    
    # Help文档目录
    /usr/share/doc/ansible-2.9.27/
    
    # Ansible模块目录
    /usr/share/ansible/
    - collections  # 目录
    - plugins      # 插件模块目录
    - roles        # 角色模块目录
    

    Ansible 配置文件

    默认Ansible配置文件存放在 /etc/ansible/ansible.cfg

    # 常见参数
    inventory = /etc/ansible/hosts     # 主机清单inventory文件的位置
    library = /usr/share/ansible       # 存放Ansible模块的目录,支持用“:”隔开多个目录
    forks = 5                          # 并发连接数,默认为5
    remote_port = 22                   # 连接主机节点的端口,默认为22端口,建议修改
    host_key_checking = False          # 是否检查SSH主机的密钥,值为True/False
    timeout = 60                       # SSH连接的超时时间,单位为秒
    log_path = /var/log/ansible.log    # 存储ansible日志的文件(默认不开启)
    

    Ansible 执行文件

    $ ll /usr/bin/*ansible*  # 相关执行文件
    lrwxrwxrwx 1 root root    20 Mar 17  2022 /usr/bin/ansible -> /usr/bin/ansible-2.7
    lrwxrwxrwx 1 root root    20 Mar 17  2022 /usr/bin/ansible-2 -> /usr/bin/ansible-2.7
    -rwxr-xr-x 1 root root  5933 Jan 16  2022 /usr/bin/ansible-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-config -> ansible
    -rwxr-xr-x 1 root root 13432 Jan 16  2022 /usr/bin/ansible-connection
    lrwxrwxrwx 1 root root    28 Mar 17  2022 /usr/bin/ansible-console -> /usr/bin/ansible-console-2.7
    lrwxrwxrwx 1 root root    28 Mar 17  2022 /usr/bin/ansible-console-2 -> /usr/bin/ansible-console-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-console-2.7 -> ansible
    lrwxrwxrwx 1 root root    24 Mar 17  2022 /usr/bin/ansible-doc -> /usr/bin/ansible-doc-2.7
    lrwxrwxrwx 1 root root    24 Mar 17  2022 /usr/bin/ansible-doc-2 -> /usr/bin/ansible-doc-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-doc-2.7 -> ansible
    lrwxrwxrwx 1 root root    27 Mar 17  2022 /usr/bin/ansible-galaxy -> /usr/bin/ansible-galaxy-2.7
    lrwxrwxrwx 1 root root    27 Mar 17  2022 /usr/bin/ansible-galaxy-2 -> /usr/bin/ansible-galaxy-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-galaxy-2.7 -> ansible
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-inventory -> ansible
    lrwxrwxrwx 1 root root    29 Mar 17  2022 /usr/bin/ansible-playbook -> /usr/bin/ansible-playbook-2.7
    lrwxrwxrwx 1 root root    29 Mar 17  2022 /usr/bin/ansible-playbook-2 -> /usr/bin/ansible-playbook-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-playbook-2.7 -> ansible
    lrwxrwxrwx 1 root root    25 Mar 17  2022 /usr/bin/ansible-pull -> /usr/bin/ansible-pull-2.7
    lrwxrwxrwx 1 root root    25 Mar 17  2022 /usr/bin/ansible-pull-2 -> /usr/bin/ansible-pull-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-pull-2.7 -> ansible
    lrwxrwxrwx 1 root root    26 Mar 17  2022 /usr/bin/ansible-vault -> /usr/bin/ansible-vault-2.7
    lrwxrwxrwx 1 root root    26 Mar 17  2022 /usr/bin/ansible-vault-2 -> /usr/bin/ansible-vault-2.7
    lrwxrwxrwx 1 root root     7 Mar 17  2022 /usr/bin/ansible-vault-2.7 -> ansible
    $ 
    

    Ansible 常见模块

    Ansible基于模块工作,本身没有批量部署的能力。
    可以理解为Ansilbe提供了框架来运行具备批量部署能力的指定功能模块。

    # 通过 ansilbe-doc 命令查看模块帮助信息,类似man命令
    ansible-doc -l                  # List available plugins
    ansible-doc -l | grep "copy"    # 查找名称包含copy字符的模块
    ansible-doc        # 显示模块的说明信息
    ansible-doc -s     # Show playbook snippet for specified plugin(s)
    
    
    # 常见模块
    ## command     在指定主机上执行命令(默认模块),不支持变量、管道、重定向等shell特性
    ## ping        测试指定主机连接性
    ## shell       在指定主机上执行命令或运行脚本,打开远程主机的shell进程的一个子shell运行命令,支持shell的变量、管道、重定向等特性
    ## script      调用本地脚本在远程主机执行
    ## stat        获取文件信息
    ## setup       主机系统信息,收集facts
    ## copy        复制文件到远程主机的指定位置
    ## fetch       复制指定主机的文件到本地
    ## get_url     在远程主机下载指定的url地址,支持文件校验
    ## file        设置文件属性
    ## cron        设置计划任务
    ## group       管理用户组
    ## user        管理用户
    ## service     管理服务状态
    ## yum         管理程序包
    ## hostname    管理主机名称
    
    ## git         代码及版本管理
    ## B           后台管理
    ## assemble    文件组装,可以将多份配置文件组装成一份配置文件
    ## ini         ini文件管理模块
    ## url         web请求,发送HTTP协议请求并得到返回的状态码
    ## apt         APT包管理模块
    

    Ansible 命令讲解

    Ansible 命令集

    # 以下所有命令均可使用`-h`参数获取帮助信息
    ansible               # Define and run a single task 'playbook' against a set of hosts  # 常用命令
    ansible-config        # View ansible configuration.
    ansible-console       # REPL console for executing Ansible tasks.  # 交互式工具,相当linux中的shell,不常用
    ansible-doc           # plugin documentation tool  # 查询模块的文档说明,类似linux中的man,常用命令
    ansible-galaxy        # Perform various Role and Collection related operations.
    ansible-inventory     # Show Ansible inventory information, by default it uses the inventory script JSON format
    ansible-playbook      # Runs Ansible playbooks, executing the defined tasks on the targeted hosts.  # 执行系列任务,类似linux中的sh,常用命令
    ansible-pull          # pulls playbooks from a VCS repo and executes them for the local host
    ansible-vault         # encryption/decryption utility for Ansible data files
    

    Ansible 常用命令集

    # 常用命令集
    
    ansible
    # Ansible指令的核心部分,主要用于执行ad-hoc命令,也就是单条命令
    # 默认使用command模块,默认的使用模块可以在ansible.cfg中进行定义
    
    ansible-doc
    # 用于查看模块信息,
    # 参数 -l 列出可用的插件
    # 参数 -s 用于查看指定模块的用法,例如“ansible-doc -s shell”
    
    ansible-playbook
    # 读取playbook文件内容并执行定义的任务
    # 对于固化的需求通常采用playbook方式定义和实现
    
    ansible-vault
    # 主要应用于包含敏感信息的场景,可以加密和解密敏感信息
    # 经过加密后的playbook,编辑时需要输入事先设定的密码才能打开
    # 使用 --ask-vault-pass 参数执行加密后的playbook文件
    
    ansible-config
    # 查看ansible的配置
    # 参数 list 打印所有配置参数,参数 view 查看配置文件
    # 结合 grep 使用可以获取具体配置信息,例如“ansible-config view |grep -v "#" |grep host_key_checking”
    

    Ansible ad-hoc命令

    # ansible  [-f forks] [-m module_name] [-a args]
    #       命令生效的主机: //all 
    
    # 常用选项  通过 ansible -h 命令获取详细信息
    -v                    # 输出详细执行过程信息  verbose mode (-vvv for more, -vvvv to enable connection debugging)
    -i inventory_file     # 指定inventory文件,默认为/etc/ansible/hosts
    -f forks              # 指定并发线程数(一次处理多少个主机),默认5个线程
    -m module_name        # 指定执行使用的模块
    -M directory          # 指定模块存放路径,默认为/usr/share/ansible
    -a args               # 指定模块参数
    -l hosts_ip           # 限定主机(只在限定主机上执行任务)
    
    --become-method BECOME_METHOD    # 提权方法,默认为sudo,一般为su
    --become-user BECOME_USER        # 切换用户,默认为root
    -K, --ask-become-pass            # 请求提权密码
    
    --list-hosts        # 列出此次的主机列表,但不做任何改变
    --syntax-check      # 语法检查
    -C, --check         # 检验可能的错误,但不做任何改变
    
    
    # 主机名文件保留
    - "-t""--tree"选项可以将ansible的执行结果按主机名保存在指定目录下的文件中
    - 使用"-t"选项可以将第一次执行的结果按主机名保存在文件中, 此后即使不使用"-t"选项也能提升执行速度
    
    
    # 命令示例
    ansible 192.168.56.3 -a 'hostname'  # 默认使用command模块(不支持变量、管道等)
    ansible 192.168.56.3 -m command -a 'hostname'
    ansible Test --list  # 列出Test组所有的主机列表
    
    ansible Test:Test2 -m ping  # 通过ping模块检查Test组和Test2组的主机是否存活
    ansible Test:!Test2 -m ping  # 在Test组但不在Test2组的主机
    ansible Test:&Test2 -m ping  # 同时存在Test组和Test2组的主机
    ansible Tes* -m ping  # 组名开头为Tes的主机
    
    ansible all -m shell -a 'hostname && date'  # 通过shell模块在所有主机试行shell命令
    ansible all -f 10 -m shell -a 'hostname && date;cat /etc/system-release'  # shell模块支持变量、管道、if判断等复杂命令
    

    部分参数如果不指定,将采用ansible.cfg中的设置值,或者采用原始默认值。

    Ansible ad-hoc常用命令

    # ping        测试指定主机连接性
    ansible ta -m ping
    ansible -i /etc/ansible/hosts ta -m ping  #  指定inventory文件
    
    # command     在指定主机上执行命令(默认模块),不支持变量、管道、重定向等shell特性
    ansible ta -m command -a "chdir=/home/ ls ./"  # 在远程主机上切换到home目录执行ls命令 
    
    # shell       在指定主机上执行命令或运行脚本,打开远程主机的shell进程的一个子shell运行命令,支持shell的变量、管道、重定向等特性
    ansible ta -m shell -a 'hostname && date;cat /etc/system-release'  # 支持变量、管道、if判断等复杂命令
    ansible ta -m shell -a "echo 'this is a test' > test.txt" -C  # 验证可能的错误,但不做任何改变
    ansible ta -m shell -a 'echo "123456"|passwd --stdin root' -K  # 更改root密码
    
    # script      调用本地脚本在远程主机执行,但只能执行脚本,不能调用其他指令,且不支持管道命令
    ansible ta -m script -a "/home/vipxf/test.sh"
    ansible ta -m script -a "removes=/home/vipxf/test.sh /home/vipxf/test2.sh" -o  # 如果远程主机上test.sh文件存在就执行本地脚本test2.sh,否则就不执行
    ansible ta -m script -a "creates=/home/vipxf/test.sh /home/vipxf/test2.sh" -o  # 如果远程主机上test.sh文件存在就不执行本地脚本test2.sh,否则就执行
    
    # raw         类似shell,支持管道命令,可用于没有python环境的远程主机
    ansible ta -m raw -a "cd /home;pwd"
    
    # stat        获取文件信息
    ansible ta -m stat -a "path=/etc/ansible/hosts" 
    
    # setup       系统信息,收集facts
    ansible ta -m setup
    ansible ta -m setup | grep "xxx"  # 获取主机所有facts然后结合grep命令过滤
    ansible ta -m setup -a "filter=ansible_os_family"  # 通过filter参数来查看指定信息
    ansible ta -m setup -a 'filter="*mem*"' --tree ./facts  # 通过filter参数查看指定信息并按主机名保存到facts目录
    
    # copy        复制文件到远程主机的指定位置
    ansible ta -m copy -a "src=/etc/hosts dest=~/test.hosts mode=664 owner=vipxf group=vipxf"  # 复制本地文件到远程主机并设置权限
    ansible ta -m copy -a "content="this is a test" dest=~/test.hosts mode=664 owner=vipxf group=vipxf"  # 在远程主机创建指定内容文件并设置权限
    
    # fetch       复制指定主机的文件到本地
    ansible ta -m fetch -a "src=~/testfile.txt dest=~/"  # 将在指定目录下创建对应IP文件夹来存放文件,保留了原目录结构
    
    # get_url     在远程主机下载网络文件,支持文件校验
    ansible ta -m get_url -a "url=http://172.20.5.3/pub/soft/docker/docker-19.03.9.tgz dest=~/ mode=655 owner=vipxf"  # 下载文件到远程主机指定目录并设置权限
    
    # file        设置文件属性
    ansible ta -m file -a "path=~/testfile.txt mode=755 owner=vipxf group=vipxf"  # 更新文件属性
    ansible ta -m file -a "path=~/testfile.txt state=touch mode=644"  # 创建文件
    ansible ta -m file -a "path=~/testfile.txt state=absent"  # 删除文件
    ansible ta -m file -a "path=~/testdir mode=755 state=directory"  # 创建目录
    
    # cron        设置计划任务
    ansible ta -m cron -a "minute=0 hour=22 job='/home/vipxf/testcron.sh' name='test cron'"  # 创建计划任务
    ansible ta -m cron -a "name='test cron' state=absent"  # 删除计划任务
    
    # group       管理用户组
    ansible ta -b --become-user root --become-method su -m group -a "name=test system=yes" --ask-become-pass  # 创建用户组
    ansible ta -b --become-user root --become-method su -m group -a "name=test state=absent" --ask-become-pass  # 删除用户组
    
    # user        管理用户
    ansible ta -b --become-user root --become-method su -m user -a "name=test system=yes group=test password=pw@123" -K  # 创建用户
    ansible ta -b --become-user root --become-method su -m user -a "name=test state=absent force=yes" -K  # 删除用户
    
    # service     管理服务
    ansible ta -b --become-user root --become-method su -m service -a "name=sshd enabled=yes state=started" -K  # 启动httpd服务并且不设置开机启动
    ansible ta -b --become-user root --become-method su -m service -a "name=sshd state=stopped" -K  # 停止httpd服务并且不设置开机启动
    
    # yum         管理程序包
    ansible ta -b --become-user root --become-method su -m yum -a "name=httpd state=present" -K  # 安装包,也可以将state设为installed
    ansible ta -b --become-user root --become-method su -m yum -a "name=httpd state=latest disable_gpg_check=no" -K  # 关闭完整性校验,安装最新版包
    ansible ta -b --become-user root --become-method su -m yum -a "name=httpd state=absent" -K  # 卸载包,也可以将state设为removed
    ansible ta -b --become-user root --become-method su -m yum -a "name=nginx state=latest enablerepo=local" -K  # 启用本地yum源安装最新版软件包
    
    # hostname    管理主机名称
    ansible ta -b --become-user root --become-method su -m hostname -a "name=test" --ask-become-pass  # 更改远程主机名
    
    

    Ansible 变量使用

    • Ansible的变量名仅能由字母、数字和下划线组成,且只能以字母开头。
    • Python关键字和playbook关键字都不能作为有效的变量名。
    • Ansible的变量可以被定义在playbook、inventory、yaml格式文件、角色和命令行中,也可以将任务的输出定义为变量。

    Ansible 自定义变量

    # 方式1:在Inventory中为主机或组定义专用变量
    [groupname]
    192.168.56.1 variable_name=value
    [groupname:vars]
    variable_name=value
    [all:vars]
    ansible_ssh_port="2222"
    
    # 方式2:在playbook中通过关键字vars或vars_files定义变量
    vars:
      - var_name: value
      - var_name: value
    vars_files:
      - ./external_vars.yml  # 变量文件内容遵循yml格式
    
    # 方式3:在playbook中把任务的输出通过关键字register定义为变量,然后用于其他任务
    tasks:
        - name: test
          shell: /usr/bin/foo
          register: foo_result
    
    # 方式4:在playbook中使用变量,执行palybook时通过命令行传递变量,通过“-e”或“--extra-vars”参数传递的变量优先级更高
    ansible-playbook test.yml --extra-vars "hosts=Test user=anliven"
    
    # 方式5:指定主机角色时通过 roles 传递变量,并在角色内通过` var_name `调用
    roles:
      - { role: ROLE_NAME, var: value, ...}
    

    Ansible 系统变量

    # 远程主机的系统信息统称为facts
    # facts信息是JSON格式的数据结构,ansible_facts是最上层,可以使用 ansible_facts 变量查看所有内容
    # 此外默认情况下使用 ansible_ 前缀可以将一些fact作为顶级变量访问
    # 通过 setup 模块可以查看指定主机的所有facts信息,通过使用filter参数来查看指定信息
    
    ansible  -m setup  # 查看指定主机的facts信息
    ansible ta -m setup | grep "xxx"  # 获取主机所有facts然后结合grep命令过滤
    ansible ta -m setup -a "filter=ansible_os_family"  # 通过filter参数来查看指定信息
    ansible ta -m setup -a 'filter="*mem*"' --tree ./facts  # 通过filter参数查看指定信息并按主机名保存到facts目录
    

    Ansible 引用变量

    # 普通变量
    通过 {{ var_name }} 方式引用
    
    # 关键字register或系统fact变量
    - 返回结果集通常是嵌套yaml或者json的数据结构
    - 通过  {{ var_name.stdout }}  或 {{ ansible_facts["eth0"]["ipv4"]["address"] }} 的方式引用
    

    Ansible 变量优先级

    # 优先级
    1. 命令行中定义的变量(用-e或--extra-vars定义的变量)
    2. 在Inventory中定义的连接变量(比如:ansible_ssh_user)
    3. 普通变量(命令行转换、play中的变量、included的变量、role中的变量等)
    4. 在Inventory中定义的其他变量
    5. Facts变量
    

    Ansible 清单定义

    Ansible 主机清单(Inventory)

    Ansible通过Inventory(可管理的主机集合)对远端服务器或者主机进行统一操作和管理,默认将主机列在一个文本文件中,这个文件称为Inventory文件。

    • 默认的路径和文件为:/etc/ansible/hosts
    • 可以通过ANSIBLE_HOSTS环境变量、ansible.cfg文件中inventory参数来指定,或者运行ansible和ansible-playbook时使用-i参数临时指定
    • 可以有多个 inventory 文件,也可以通过 Dynamic Inventory 动态生成
    • inventory文件为 INI文件格式,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中
    • 组名建议使用"_"来连接字符,例如“aaa_bbb_ccc”

    Ansible Inventory文件

    192.168.56.1    # 未分组
    test.example.com  
    127.0.0.1 ansible_connection=local  # 直接访问本地主机,而不是通过SSH
    
    [Test]  # 已分组名称
    192.168.56.1
    192.168.56.2
    192.168.56.[3:4]  # 通过列表方式通配地址
    192.168.56.[5:6]  # 多个通配地址
    anliven-[a:d].example.com  # 通配主机域名
    
    [Test:vars]  # 定义组变量
    ansible_ssh_user="anliven"
    ansible_ssh_pass="anliven"
    ansible_ssh_port="22"
    
    [Test2]  # 在分组中指定变量
    192.168.56.[1:2] ansible_ssh_user=anliven ansible_ssh_pass=Anliven
    192.168.56.[3:4] ansible_ssh_user=anliven ansible_ssh_pass=Anliven
    192.168.56.[3:4]
    
    [Test3]  # 在分组中切换root用户 
    192.168.56.[1:2] ansible_ssh_port="22" ansible_ssh_user="anliven" ansible_ssh_pass="anliven"  ansible_become_pass="Anliven" 
    
    [Temp]
    192.168.56.1:2222  # 指定端口
    192.168.56.2 http_port=8080 var1=test1 var2=test2   # 定义主机时为其添加主机变量以便于在playbook中使用
    
    [Temp:vars]
    ansible_ssh_user="anliven"
    ansible_ssh_pass="anliven"
    
    [TestGroup:children]  # 嵌套组,TestGroup为名称,其他为固定格式 
    Test1
    Test2
    Test3
    
    [TestGroup:vars]  # 嵌套组的变量只能在ansible-playbook中使用
    var1="test1"
    var2="123"
    
    [all:vars]  # 类似全局变量,对inventory文件中所有节点都有效
    ansible_ssh_port="22"
    

    Ansible Inventory内置参数

    Ansible Inventory文件中可使用的行为参数

    可以在ansible.cfg中的[defaults]部分更改一些Inventory行为参数的默认值

    Ansible Dynamic Inventory

    • 动态Inventory也就是Ansible所有的Inventory文件里面的主机列表和变量信息都支持从外部拉取,例如CMDB等。
    • 避免主机列表不准确和频繁大量的手动更新的问题
    • 也可以通过设置ansible.cfg文件中的inventory参数为一个可执行脚本,将CMDB等其他系统的主机信息同步至Ansible中。
    • 对动态Inventory脚本的参数和输出必须遵循格式规则

    Ansible Inventory分割

    通过Inventory分割满足如下需要

    • 按不同业务/系统分割成的多个Inventory文件
    • 同时使用常规Inventory文件和动态Inventory脚本

    实现方式

    • 配置ansible.cfg文件的hostfile参数,指定放置inventory文件目录作为Inventory,Ansible将合并目录里所有文件为一个完整的Inventory
    • 也可以在命令行中使用-i命令来指定特定的Inventory文件
    [defaults]
    
    hostfile = /etc/ansible/inventory  # 指定目录作为Inventory
    

    Ansible 剧本执行

    Ansible Playbook 基础介绍

    剧本(Playbook) 是 ansible 用于配置,部署,和管理被控节点的剧本。

    • playbook类似Linux的shell脚本,用于实现和管理大量的、规律的、复杂的操作任务
    • playbook方便代码和配置的重用、移植性好,同时也易于管理

    Playbook格式

    # playbook文件由YMAL语言编写,需遵循yaml格式要求
      1. 第一行以 "---" 开始,表明YMAL文件的开始(非playbook强制要求,没有也能通过语法检查)
      2. 列表元素以”-”开头,后面带有一个空格,然后元素内容
      3. 对象的键与值以":"和一个空格来分隔
      4. 相同层次内容必须保持相同的缩进和对齐,否则会报错
      5. 在同一行中,“#” 之后的内容表示注释
      6. 文件名应该以.yml或.yaml结尾
    
    # 编写完成后可以通过 --syntax-check 子命令检查语法错误
    ansible-playbook --syntax-check sample.yaml
    

    Playbook组成

    • 一个Playbook可以包括一个或多个Play。
    • 一个Play由Host的无序集合与Task的有序列表组成。
    • 每一个Task仅由一个模块构成。
    # play
    - 一个Playbook包括一个或多个Play
    - 一个Play由Host的无序集合与Task的有序列表组成
    
    # 目标定义部分:host
    - 执行playbook的主机信息
    
    # 变量定义部分:variable
    - 执行playbook需要的变量
    - 设置的ansible配置变量,例如gather_facts
    
    # 任务定义部分:tasks & module
    - 在目标主机执行的任务列表和调用的模块
    - Play的主体部分,Task列表中的各任务按次序逐个在Hosts中的指定主机上执行
    - 每一个Task仅由一个模块构成
    - 建议为每一个Task设置一个Name,便于在运行Playbook时从输出结果辨别task信息
    - Ansible的自带模块Command模块和Shell模块无需使用key=value格式,可以直接编写要执行的命令
    
    # 触发器定义部分:handlers
    - task执行完成后需要调用的任务,通常结合关键字notify
    - 由通知者进行的Notify,如果没有被Notify,则Handlers不会执行,如果被Notify了,则Handlers被执行
    - 不管有多少个通知者进行了Notify,等到Play中的所有Task都执行完成之后,Handlers也只会被执行一次
    - 实质上也是Task的列表
    

    Ansible Playbook 示例

    [root@test01 ansible-test]# pwd
    /root/ansible-test
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ll
    total 4
    drwxr-xr-x 2 root root  24 Oct 17 09:47 conf
    -rw-r--r-- 1 root root 922 Oct 14 11:24 sample.yaml
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# tree
    .
    ├── conf
    │   └── httpd.conf
    └── sample.yaml
    
    1 directory, 2 files
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat conf/httpd.conf 
    MaxClients        {{ maxClients }}
    Listen        {{ httpd_port }}
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat sample.yaml 
    - hosts: ta
      remote_user: vipxf
      gather_facts: no
      vars:
        - package: vim
      tasks:
        - name: install vim 
          yum: name={{ package }} state=latest
        - name: install configuration file
          become: yes
          become_method: su
          become_flags: "-"
          become_user: root    
          template: src=/root/ansible-test/conf/httpd.conf dest=/root/test-template.conf
          notify: 
          - check-hostname-date
        - name: copy file
          copy: src=/root/ansible-test/conf/httpd.conf dest=/home/vipxf/test-copy.conf
      handlers:
        - name: check-hostname-date
          shell: hostname && date
          ignore_errors: True
    - hosts: ta
      remote_user: vipxf
      gather_facts: no
      become: yes
      become_method: su
      become_flags: "-"
      become_user: root
      vars:
        - newgroup: testgroup
        - newuser: testuser
      tasks:
        - name: create new group
          group: name={{ newgroup }} system=yes
        - name: create new user
          user: name={{ newuser }} group=testgroup system=yes
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat /etc/ansible/hosts
    [ta]
    172.20.8.247 ansible_ssh_port=2222 ansible_ssh_user=vipxf ansible_ssh_pass=Anliven09! 
    [ta:vars]
    httpd_port=80 maxClients=10
    [root@test01 ansible-test]# 
    

    Ansible playbook 示例说明

    - hosts: ta  # 指定执行指定任务的主机,可以通过一个或多个由冒号分隔主机组
      remote_user: vipxf  # 指定在远程主机执行任务的用户,也能用在task中
      gather_facts: no
      vars:
        - package: vim
      tasks:  # 任务列表,在指定主机上按顺序执行各任务
        - name: install vim  # 每个任务的名称,用于输出执行结果
          yum: name={{ package }} state=latest
        - name: install configuration file
          become: yes
          become_method: su
          become_flags: "-"
          become_user: root    
          template: src=/root/ansible-test/conf/httpd.conf dest=/root/test-template.conf
          notify:  # 此任务状态为changed时采取的操作,在handlers中定义
          - check-hostname-date
        - name: copy file
          copy: src=/root/ansible-test/conf/httpd.conf dest=/home/vipxf/test-copy.conf
      handlers:  # 触发器,在被指定任务通知和所有任务完成的情况下只执行一次,实质上也是按序执行的任务列表
        - name: check-hostname-date
          shell: hostname && date
          ignore_errors: True  # 如果命令或脚本的退出码不为零,使用ignore_errors来忽略错误信息
    
    - hosts: ta  # playbook文件可以有多个play
      remote_user: vipxf
      gather_facts: no
      become: yes
      become_method: su
      become_flags: "-"
      become_user: root
      vars:
        - newgroup: testgroup
        - newuser: testuser
      tasks:
        - name: create new group
          group: name={{ newgroup }} system=yes
        - name: create new user
          user: name={{ newuser }} group=testgroup system=yes
    

    相关命令

    # Ansible具有幂等性,会自动跳过没有变化的部分
    # 建议使用绝对路径来执行playbook的yaml文件
    # 语法检查 和 预测试 不能保证结果绝对正确,实际的运行测试是有必要的
    # 特别注意yml文件的内容格式(空格、缩进)、特殊字符等,必要时重写重建
    
    --------------------------------------------------------------------------------
    ## 列出主机列表、任务列表和标签列表
    ansible-playbook --list-hosts --list-tasks --list-tags sample.yaml
    
    --------------------------------------------------------------------------------
    ## 检查内容语法错误
    ansible-playbook --syntax-check sample.yaml
    ## 预测试(不改变目标主机的任何设置)
    ansible-playbook --check sample.yaml --ask-become-pass
    ansible-playbook -C sample.yaml --ask-become-pass
    
    --------------------------------------------------------------------------------
    ## 选择playbook开始执行的任务
    ansible-playbook sample.yaml --start-at-task="common"  # 从名称为common的任务开始执行playbook
    ## 详细模式下执行playbook,根据提示输入root密码
    ansible-playbook -v sample.yaml --ask-become-pass
    ## verbose mode (-vvv for more, -vvvv to enable connection debugging)
    ansible-playbook -vvv sample.yaml --ask-become-pass  
    

    返回结果

    # 一般以红色、黄色、绿色来表示执行结果
    
    - 绿色:执行成功,未更改目标主机状态
    - 黄色:执行成功,对目标主机完成变更,目标主机状态有变化
    - 紫色:执行结果出现警告或提示信息
    - 红色:执行失败,结果出现异常
    
    # 在playbook执行后,会明确列出任务执行状态的汇总信息
    # 任务执行状态包括:ok、changed、unreachable、failed、skipped、rescued、ignored  
    

    结果示例

    [root@test02 ~]# ip addr show
    1: lo:  mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
        link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
        inet 127.0.0.1/8 scope host lo
           valid_lft forever preferred_lft forever
        inet6 ::1/128 scope host 
           valid_lft forever preferred_lft forever
    2: ens192:  mtu 1500 qdisc mq state UP group default qlen 1000
        link/ether 00:50:56:a1:07:78 brd ff:ff:ff:ff:ff:ff
        inet 172.20.8.247/24 brd 172.20.8.255 scope global noprefixroute ens192
           valid_lft forever preferred_lft forever
        inet6 fe80::250:56ff:fea1:778/64 scope link 
           valid_lft forever preferred_lft forever
    [root@test02 ~]# 
    [root@test02 ~]# vim -h | head -n 1
    VIM - Vi IMproved 7.4 (2013 Aug 10, compiled Jun 14 2019 10:43:10)
    [root@test02 ~]# 
    [root@test02 ~]# pwd
    /root
    [root@test02 ~]# ll test-template.conf 
    -rw-r--r-- 1 root root 38 Oct 17 18:18 test-template.conf
    [root@test02 ~]# 
    [root@test02 ~]# ll /home/vipxf/test-copy.conf 
    -rw-rw-r-- 1 vipxf vipxf 66 Oct 17 18:18 /home/vipxf/test-copy.conf
    [root@test02 ~]# 
    [root@test02 ~]# cat /etc/group |grep test
    testgroup:x:995:
    testuser:x:994:
    [root@test02 ~]# cat /etc/passwd |grep test
    testuser:x:997:995::/home/testuser:/bin/bash
    [root@test02 ~]# 
    

    Ansible Playbook 条件判断(When)

    when语句可以将变量、facts或此前任务的执行结果作为指定task是否执行的前提条件,也可以判断变量是否被定义。

    示例-1:when

    - hosts: ta
      remote_user: vipxf
      gather_facts: yes
      tasks:
        - name: test
          shell: echo "test"
          when: ansible_os_family == "RedHat" and ansible_distribution_version == "7.8" # 可以使用facts、playbook 或Inventory中定义的变量
        - name: test1
          shell: echo "step1"
          register: result  # 将执行结果定义为变量
        - name: test2
          shell: echo "step2"
          when: result.rc == 0  # 基于先前任务的结果来执行
        - name: test3
          shell: echo "step3"
          when: result.stderr != ""
        - name: test4
          shell: echo "step4"
          when: result.changed == "true"
    

    示例-2:结合fail语句

    test.sh
    [vipxf@test02 ~]$ pwd
    /home/vipxf
    [vipxf@test02 ~]$ 
    [vipxf@test02 ~]$ ll test.sh 
    -rw-rw-r-- 1 vipxf vipxf 127 Oct 21 00:11 test.sh
    [vipxf@test02 ~]$ 
    [vipxf@test02 ~]$ cat test.sh
    #!/bin/bash
    
      if [ "$1" = "Anliven" ];then
         echo "Success"
      else
         echo "Failed"
      fi
    
    [vipxf@test02 ~]$ 
    

    when2.yaml

    [root@test01 ansible-test]# cat when2.yaml 
    - hosts: ta
      remote_user: vipxf
      gather_facts: yes
      vars:
        - teststring: Failed
      tasks:
        - name: test1
          shell: sh /home/vipxf/test.sh {{ testvar }} 
          register: result 
        - name: test2
          shell: echo "rc 0"
          when: result.rc == 0  # 基于先前任务的结果来执行
        - name: test3
          shell: echo "Success"
          when: '"Success" in result.stdout'  # 对stdout结果进行判断
        - name: test4
          fail: msg="Check Failed"  # 任务报错并抛出msg信息
          when: result.stdout == (( teststring ))  # 在when使用playbook中定义的变量
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook -v when2.yaml -e testvar=Anliven
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [Gathering Facts] ********************************************************************************************************************************************************************************
    ok: [172.20.8.247]
    
    TASK [test1] ******************************************************************************************************************************************************************************************
    changed: [172.20.8.247] => {"changed": true, "cmd": "sh /home/vipxf/test.sh Anliven", "delta": "0:00:00.003638", "end": "2022-10-21 18:05:00.273596", "rc": 0, "start": "2022-10-21 18:05:00.269958", "stderr": "", "stderr_lines": [], "stdout": "Success", "stdout_lines": ["Success"]}
    
    TASK [test2] ******************************************************************************************************************************************************************************************
    changed: [172.20.8.247] => {"changed": true, "cmd": "echo \"rc 0\"", "delta": "0:00:00.002893", "end": "2022-10-21 18:05:00.722340", "rc": 0, "start": "2022-10-21 18:05:00.719447", "stderr": "", "stderr_lines": [], "stdout": "rc 0", "stdout_lines": ["rc 0"]}
    
    TASK [test3] ******************************************************************************************************************************************************************************************
    changed: [172.20.8.247] => {"changed": true, "cmd": "echo \"Success\"", "delta": "0:00:00.002632", "end": "2022-10-21 18:05:01.172659", "rc": 0, "start": "2022-10-21 18:05:01.170027", "stderr": "", "stderr_lines": [], "stdout": "Success", "stdout_lines": ["Success"]}
    
    TASK [test4] ******************************************************************************************************************************************************************************************
    skipping: [172.20.8.247] => {"changed": false, "skip_reason": "Conditional result was False"}
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=4    changed=3    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook -v when2.yaml -e testvar="test"
    Using /etc/ansible/ansible.cfg as config file
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [Gathering Facts] ********************************************************************************************************************************************************************************
    ok: [172.20.8.247]
    
    TASK [test1] ******************************************************************************************************************************************************************************************
    changed: [172.20.8.247] => {"changed": true, "cmd": "sh /home/vipxf/test.sh test", "delta": "0:00:00.003612", "end": "2022-10-21 18:06:49.907767", "rc": 0, "start": "2022-10-21 18:06:49.904155", "stderr": "", "stderr_lines": [], "stdout": "Failed", "stdout_lines": ["Failed"]}
    
    TASK [test2] ******************************************************************************************************************************************************************************************
    changed: [172.20.8.247] => {"changed": true, "cmd": "echo \"rc 0\"", "delta": "0:00:00.002721", "end": "2022-10-21 18:06:50.338001", "rc": 0, "start": "2022-10-21 18:06:50.335280", "stderr": "", "stderr_lines": [], "stdout": "rc 0", "stdout_lines": ["rc 0"]}
    
    TASK [test3] ******************************************************************************************************************************************************************************************
    skipping: [172.20.8.247] => {"changed": false, "skip_reason": "Conditional result was False"}
    
    TASK [test4] ******************************************************************************************************************************************************************************************
    fatal: [172.20.8.247]: FAILED! => {"changed": false, "msg": "Check Failed"}
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=3    changed=2    unreachable=0    failed=1    skipped=1    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    

    changed_when  与 failed_when

    # changed_when
    # 在条件成立时,将对应任务的执行状态设置为changed
    # 先执行task,并对task返回的值进行判断,当满足changed_when指定的条件时说明是执行成功的
    # 默认情况下执行命令完成的主机状态都为changed
    
        - name: all host run this task
          shell: hostname
          register: info
          changed_when: '"webserver01" in info.stdout'   # 输出包含某个特定字符才能将主机状态设为changed
    
    # failed_when
    # 在条件成立时,将对应任务的执行状态设置为失败
    # 当执行失败后会将信息存在register的stderr中,通过判断指定的字符是否在stderr中来确定是否真的失败
    # 其实是ansible的一种错误处理机制:由fail模块组合了when条件语句的效果
    
        - name: this command prints FAILED when it fails
          command: echo "FAILED"
          register: command_result
          failed_when: "'FAILED' in command_result.stdout"
    

    Ansible Playbook 循环迭代(with_items)

    用来在playbook中实现循环迭代的功能。
    支持元素列表、文件名(with_fileglob)、复合(with_together)、步进(with_sequence)、随机(with_random_choice)、until等多种类型循环。

    - name: test with_items
      hosts: ta
      remote_user: vipxf
      gather_facts: yes
      tasks:
        - name: test1
          shell: echo {{ item }}  # 通过引用item变量来迭代
          with_items:
            - one
            - two
            - three
        - name: test2
          shell: echo {{ item.name }} with {{ item.value }}  # 通过字典来迭代
          with_items: 
            - { name: 'one', value: '111'}
            - { name: 'two', value: '222'}     
            - { name: 'three', value: '333'}  
        - name: test3
          copy: src={{ item }} dest=~/ mode=664 owner=vipxf group=vipxf  # 把文件名作为变量循环
          with_fileglob: 
            - /playbook/files/*
    

    Ansible Playbook 角色(Roles)

    • 角色(roles)用于层次性、结构化地组织playbook
    • 通过规范的目录存储结构将变量、文件、任务、模板以及触发器等放置于单独目录
    • 通过独立的role目录将不同的playbook任务单一化,从而实现高效的代码复用
    • 在playbook中调用roles,根据层次型结构自动转载roles的变量文件、tasks以及handlers等要素信息
    • 一般用于基于主机构建服务和进程的场景
    • 可以在 /etc/ansible/ansible.cfg 中设置 roles_path 来设置roles文件路径

    创建role

    1. 创建 roles 名称的目录
    2. 在 roles 目录中分别创建各角色命名的目录,如webserver等
    3. 在每个角色命名的目录中分别创建defaults、files、handlers、meta、tasks、templates和vars等目录;如果为空目录则不被引用
    4. 在roles的整体编排文件playbook中调用各角色
    

    目录结构说明

    site.yml  # playbook文件,roles的整体编排文件
    roles/  # 定义各角色的总目录
        common/  # 角色role模块名,在playbook中需要调用时使用的名称
            default/  # 默认变量文件目录(定义此角色的默认变量),至少包含一个main.yml文件,在main.yml文件中可以用include指令将其他yml文件包含进来
            files/  # 存放由copy或script等模块调用的文件
            handlers/  # 触发器文件目录,至少包含一个main.yml文件,在main.yml文件中可以用include指令将其他yml文件包含进来
            meta/  # 此角色的元数据(特殊设定及其依赖关系),至少包含一个main.yml文件,在main.yml文件中可以用include指令将其他yml文件包含进来
            tasks/  # 任务文件目录,至少包含一个main.yml文件,在main.yml文件中可以用include指令将其他yml文件包含进来
            templates/  # 模板文件目录,template模块会自动在此目录中寻找
            vars/  # 变量文件目录,至少包含一个main.yml文件,在main.yml文件中可以用include指令将其他yml文件包含进来
    
        webserver/  # 角色 webserver 目录
            default/
            files/  
            handlers/
            meta/
            tasks/
            templates/
            vars/
    

    调用方式
    在playbook中可以通过关键字 roles 来调用角色role。

    - hosts: webserver
      remote_user: anliven
      roles:  # 使用关键字roles调用角色role
        - common  # 调用role
        - webserver  # 调研多个role
    
    - hosts: webserver
      remote_user: anliven
      roles:
        - common
        - { role: foo_app_instance, dir:'/opt/a',port:5000}  # 可以向roles传递参数
        - { role: foo_app_instance, dir:'/opt/b',port:5001}
    
    - hosts:webserver
      remote_user: anliven
      roles:
        - { role: some_role, when: "ansible_so_family == 'RedHat" } # 指定调用roles的条件 
    

    Ansible Playbook 标签(Tags)

    • 通过标签(tags)可以选择运行或跳过playbook中的指定任务
    • 在playbook为任务定义一个"标签",在通过ansible-playbook命令执行此playbook时使用 --tags 或 --skip-tags选项选择运行或跳过这个任务
    • 可以使用系统特殊标签,也可以自定义标签
    • 可以为同一个任务设定多个标签,也可以为不同的任务设定相同的标签

    语法格式

    # 格式1
    tags:
     - testtag
     - t2
    
    # 格式2
    tags: testtag,t2
     
    # 格式3
    tags: ['testtag','t2']
    

    特殊标签

    always    除非--skip-tags选项指定,否则 always 标签的task会一直执行
    never     除非--tags选项指定,否则 never 标签的task都不会执行
    tagged    不包括never的所有标签
    untagged  所有无标签和always标签
    all       包括非never标签和无标签
    

    选项

    # 如果执行 ansible-playbook 时不指定标签,则会执行所有非 never 标签的任务
    
    --tags "tag1,tag2..."   执行指定标签和always标签的tasks
    
    --tags always      只执行always标签的tasks
    --tags all          执行所有非never标签和无标签的tasks
    --tags never        执行always和never标签的tasks
    --tags tagged       执行所有标签的tasks,但不包括never标签的tasks
    --tags untagged     执行所有无标签和always标签的tasks
    
    --skip-tags "tag1,tag2..."  跳过指定标签的tasks
    
    --list-tags   查看playbook中哪些tags会被执行
    

    tags示例

      tasks:
      - name: install package
        yum: name={{ packagename }} state=latest
        tags:
        - always
      - name: copy configuration file
        copy: src=/root/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
        tags: conf,http
    

    命令示例

    ansible-playbook nginx_tags.yaml  --tags "testtag,t2"
    

    Ansible Playbook 调试(Debug)

    • Print statements during execution
    • 在Ansible Playbook中常使用debug模块,可以在Playbook执行过程打印调试信息
    • 结合when条件语句一起使用时,可以调试特定条件下的执行过程

    注意:在setup模块中查询出来的变量,直接可以在debug中直接作为变量引用。

    # msg
    输出自定义信息,如果不指定或不写msg的话,默认也会输出“null”
    
    # var
    - 指定要打印的变量名,与msg参数互斥,二者只能有一个
    - var参数中的变量不需要使用{{}}表达式,而msg中需要
    
    # verbosity
    - debug的调试级别,默认0是全部显示,级别调整到3是忽略内容不显示
    - 在命令中使用-vvv参数,可以在设置为3情况下仍然显示debug内容
    

    示例

      tasks:
        - name: Host run this task
          debug: 'msg="{{ ansible_fqdn }} and {{ ansible_default_ipv4.address }}"'  # 打印必要信息
          when: ansible_memtotal_mb < 500 and ansible_processor_cores == 2  # 结合when使用
        - name: all host run this task
          shell: hostname
          register: info
        - name: Hostname is webserver01 Machie run this task
          debug: 'msg="{{ ansible_fqdn }}"'
          when: info['stdout'].startswith('Success') 
        - name: Show debug info
          debug: var=info verbosity=1    # 打印var变量信息,调试级别为1
    

    Ansilbe 加密解密

    ansible-vault 用途

    • encryption/decryption utility for Ansible data files
    • 主要应用于包含敏感信息的场景,可以加密和解密敏感信息
    • See 'ansible-vault --help' for more information on a specific command.
    # ansible-vault -h
    usage: ansible-vault [-h] [--version] [-v]
                         {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
                         ...
    
    encryption/decryption utility for Ansible data files
    
    positional arguments:
      {create,decrypt,edit,view,encrypt,encrypt_string,rekey}
        create              Create new vault encrypted file
        decrypt             Decrypt vault encrypted file
        edit                Edit vault encrypted file
        view                View vault encrypted file
        encrypt             Encrypt YAML file
        encrypt_string      Encrypt a string
        rekey               Re-key a vault encrypted file
    
    optional arguments:
      --version             show program's version number, config file location, configured module search path, module location, executable location and exit
      -h, --help            show this help message and exit
      -v, --verbose         verbose mode (-vvv for more, -vvvv to enable connection debugging)
    
    See 'ansible-vault <command> --help' for more information on a specific command.
    

    ansible-vault 常用命令

    # 加密文件
    ansible-vault encrypt test-vault.yml
    ansible-vault encrypt test-vault.yml --vault-password-file pwdfile
    
    # 解密文件
    ansible-vault decrypt test-vault.yml 
    ansible-vault decrypt test-vault.yml --vault-password-file pwdfile
    
    # 查看文件
    ansible-vault view test-vault.yml
    ansible-vault view test-vault.yml --vault-password-file pwdfile
    
    # 重置文件密码
    ansible-vault rekey test-vault.yml
    ansible-vault rekey test-vault.yml --vault-password-file pwdfile --new-vault-password-file pwdfilenew
    
    # 创建加密文件
    ansible-vault create test-vault.yml
    ansible-vault create test-vault.yml --vault-password-file pwdfile
    
    # 编辑加密文件
    ansible-vault edit test-vault.yml
    ansible-vault edit test-vault.yml --vault-password-file pwdfile
    
    # 加密字符串
    ansible-vault encrypt_string 'test123456'
    ansible-vault encrypt_string 'test123456' --name 'ansible_ssh_pass'
    ansible-vault encrypt_string 'test123456' --name 'ansible_ssh_pass' --vault-id anliven@pwdfile
    

    ansible-vault "--vault-id"选项

    # 从ansible2.4版本开始,官方推荐使用"--vault-id"选项代替"--vault-password-file"选项指定密码文件
    # “--vault-id prompt”功能上等同于"--ask-vault-pass"选项
    # 支持同时使用多个密码文件进行解密,适用于“引用其他文件”的场景
    # 可以在被加密文件中包含特定字符“做记号”
    
    ansible-vault encrypt_string 'test123456' --name 'ansible_ssh_pass' --vault-id pwdfile  # 加密字符串
    
    ansible-vault encrypt test-vault.yml --vault-id pwdfile  # 加密文件
    ansible-vault encrypt test-vault.yml --vault-id anliven@pwdfile  # 加密完成后的文件内容包含anliven字符
    
    ansible-vault decrypt test-vault.yml --vault-id pwdfile  # 解密文件
    ansible-vault view test-vault.yml --vault-id pwdfile  # 查看文件
    ansible-vault edit test-vault.yml --vault-id pwdfile  # 编辑文件
    
    ansible-vault rekey test-vault.yml --vault-id pwdfile  # 交互式密码重置
    ansible-vault rekey test-vault.yml --vault-id pwdfile  --new-vault-id pwdfilenew  # 通过新密码文件重置
    
    ansible-playbook test-vault.yml --vault-id pwdfile  # 运行playbook
    ansible-playbook test-vault.yml --vault-id pwdfile1 --vault-id pwdfile2   # 提供多个密码文件来解密,test-vault.yml中引用其他vault加密文件
    ansible-playbook test-vault1.yml test-vault2.yml --vault-id pwdfile1 --vault-id pwdfile2  # 提供多个加密文件来解密多个文件
    

    ansible-vault 示例

    示例-1 交互式密码

    [root@test01 ansible-test]# cat test-vault.yml 
    - hosts: ta
      gather_facts: no
      tasks:
      - debug:
          msg: "test ansible-vault"
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook test-vault.yml
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "test ansible-vault"
    }
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-vault encrypt test-vault.yml 
    New Vault password: 
    Confirm New Vault password: 
    Encryption successful
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat test-vault.yml 
    $ANSIBLE_VAULT;1.1;AES256
    32656239643632646139633938613430326139636636333235346361643161393131396661366534
    6636386331316239616632316137316266316266646432360a366366643232313033343835346638
    38616331636639643731633766333335613763623636333363336238353931616263313637313834
    3135656632343034340a316238656238336432386638373236653738306530383232626231333438
    38666338346130333561316535353637616230633634346162303730393166396230616533396435
    38346536306433653566373438303565373036663138366330313836356666656639393438396134
    35333465623365636531653562363366323065316238333333353863376236373362373832633636
    62613732666263306531653231353931326635303533623934633235396239613838613230323862
    3134
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-vault view test-vault.yml 
    Vault password: 
    - hosts: ta
      gather_facts: no
      tasks:
      - debug:
          msg: "test ansible-vault"
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook --ask-vault-pass test-vault.yml 
    Vault password: 
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "test ansible-vault"
    }
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-vault decrypt test-vault.yml 
    Vault password: 
    Decryption successful
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat test-vault.yml 
    - hosts: ta
      gather_facts: no
      tasks:
      - debug:
          msg: "test ansible-vault"
    [root@test01 ansible-test]# 
    

    示例-2 密码文件

    [root@test01 ansible-test]# echo "This-is_a#Test!2o22" > pwdfile
    echo "This-is_a#Testhistoryo22" > pwdfile
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat pwdfile 
    This-is_a#Testhistoryo22
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-vault encrypt test-vault.yml --vault-password-file pwdfile
    Encryption successful
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat test-vault.yml 
    $ANSIBLE_VAULT;1.1;AES256
    63343030376661643237653266366133313735363630353564363631376563613236383863346264
    6163303562643831636237633038373265616334343234630a613466663138396334303463623665
    30353632396236306435633062383864646466616261393064313633373635353633656161393266
    3234326635323438610a376631323634316663313130356466306238306638613261663138333663
    30363461616433643530656562313139303831346365346531303530666236373038306435636338
    39666432326465313834613164356436653366656138613634303339346130353033313330303733
    30643934383363333261646366396330343164393236633138383137316166643966393838396464
    64323863306539333534663938393962326231373137613630623635313534356163363261626262
    3765
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-vault view  test-vault.yml --vault-password-file pwdfile
    - hosts: ta
      gather_facts: no
      tasks:
      - debug:
          msg: "test ansible-vault"
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook test-vault.yml --vault-password-file pwdfile
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "test ansible-vault"
    }
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=1    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-vault decrypt test-vault.yml --vault-password-file pwdfile
    Decryption successful
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat test-vault.yml 
    - hosts: ta
      gather_facts: no
      tasks:
      - debug:
          msg: "test ansible-vault"
    [root@test01 ansible-test]# 
    

    示例-3 加密字符串

    [root@test01 ansible-test]# ansible-vault encrypt_string "test123456"
    New Vault password: 
    Confirm New Vault password: 
    !vault |
              $ANSIBLE_VAULT;1.1;AES256
              33383336353737346430653165326665393430346539376334396335336530613330643764313962
              3438366538366262316666353962663564666532393333300a333934633664393262653065343864
              63653361666133363862353061323238376335666165313130393664623761393033343136343265
              6166663630353038380a666164643565343336373062323135643038363436343938383363303632
              6230
    Encryption successful
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# vim test-encrypt_string.yaml 
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# cat test-encrypt_string.yaml 
    - hosts: ta
      gather_facts: no
      vars:
        test_user: "testuser"
        test_passwd: !vault |
              $ANSIBLE_VAULT;1.1;AES256
              33383336353737346430653165326665393430346539376334396335336530613330643764313962
              3438366538366262316666353962663564666532393333300a333934633664393262653065343864
              63653361666133363862353061323238376335666165313130393664623761393033343136343265
              6166663630353038380a666164643565343336373062323135643038363436343938383363303632
              6230
      tasks:
      - debug:
          msg: "{{test_user}}"
      - debug:
          msg: "{{test_passwd}}"
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook test-encrypt_string.yaml --ask-vault-pass
    Vault password: 
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "testuser"
    }
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "test123456"
    }
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    

    示例-4 通过密码文件加密字符串

    [root@test01 ansible-test]# ansible-vault encrypt_string "test123456" --name "test_passwd" --vault-id anliven@pwdfile
    test_passwd: !vault |
              $ANSIBLE_VAULT;1.2;AES256;anliven
              61646130623833333634646632393432326431383864663134356530323536663165303061313661
              3365343837623564343037663236316635666565613730350a393731646238376638363365363561
              35383465336137313134306363363139386537633839393363653465333161303634313832383136
              3038326464613935350a383565343261363833333631663862336464303538323561363237326637
              3431
    Encryption successful
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# vim test-encrypt_string.yaml
    [root@test01 ansible-test]# cat test-encrypt_string.yaml
    - hosts: ta
      gather_facts: no
      vars:
        test_user: "testuser"
        test_passwd: !vault |
              $ANSIBLE_VAULT;1.2;AES256;anliven
              61646130623833333634646632393432326431383864663134356530323536663165303061313661
              3365343837623564343037663236316635666565613730350a393731646238376638363365363561
              35383465336137313134306363363139386537633839393363653465333161303634313832383136
              3038326464613935350a383565343261363833333631663862336464303538323561363237326637
              3431
      tasks:
      - debug:
          msg: "{{test_user}}"
      - debug:
          msg: "{{test_passwd}}"
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook test-encrypt_string.yaml --vault-id pwdfile
    
    PLAY [ta] *********************************************************************************************************************************************************************************************
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "testuser"
    }
    
    TASK [debug] ******************************************************************************************************************************************************************************************
    ok: [172.20.8.247] => {
        "msg": "test123456"
    }
    
    PLAY RECAP ********************************************************************************************************************************************************************************************
    172.20.8.247               : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    

    Ansible 性能优化

    在默认设置的情况下,Ansible的执行效率已经可以满足大多数场景。
    面对巨量目标主机时,可以通过一些配置优化去再提高ansible的执行效率。

    Ansible 基本设置

    # 通过 time 命令统计执行时间
    time 
    
    
    # 关闭SSH密钥检测
    - 以SSH登录远程设备时,默认该设备会检查远程主机的公钥,并且将该公钥记录在~/.ssh/known_hosts文件中
    - 当下次该主机访问时会核对公钥,如果公钥不同则会发出警告,如果公钥相同,则会提示输入密码
    - 对主机公钥的检查是根据StrictHostKeyChecking变量的检查级别确定:no(不检查)、ask(是否检查要询问)、yes(每次都检查)、False(关闭检查)
    - 设置 /etc/ansible/ansible.cfg 中参数 host_key_checking = False
    
    
    # 并发数forks
    - Ansible默认只会创建5个进程并发执行任务,所以在任务中只能同时控制5台主机执行
    - forks是线程数,受限于CPU的核心数,加大forks值能让CPU尽量提升并发数量,一定程度上提高执行效率
    - 设置 /etc/ansible/ansible.cfg  forks 参数,默认值为5
    - 在执行命令时通过 -f 参数指定并发数
    
    
    # 关闭 gather_facts
    - playbook默认执行第一个tasks是gather_facts,用于收集主机信息
    - 如果任务并不需要调用主机信息变量,建议关闭可以减少Ansible在收集客户端信息的时间,提高运行速度
    - 可以在playbook文件中设置gather_facts参数为false或no(不区分大小写)
    gather_facts: False 
    gather_facts: no
    - 也可以设置 /etc/ansible/ansible.cfg 中参数
    gather_facts: no
    
    
    # 设置 facts缓存
    - 不过禁用facts,可以采用facts缓存保证一定的运行效率来使用facts信息
    - 在ansible.cfg中可以配置多种facts缓存的方式:json文件,redis,memcache等
    - 使用redis存储facts文件需安装redis和响应的python库
    - 以json文件方式为例
    gathering=smart
    fact_caching_timeout=86400
    fact_caching=jsonfile
    fact_caching_connection=/path/to/ansible_fact_cache
    
    
    # serial滚动更新
    - 并发可以在ansible.cfg里修改配置,也可以在playbook中限制服务端的并发数量
    - Ansible默认将尝试并行管理playbook中所有的机器,可以使用serial关键字定义一次应管理多少主机
    - 常应用与负载均衡环境下,不能一次性停止所有主机的场景
    - 在play或者task下都可以设置serial参数,但其值对应的主机数不应大于forks值 
    serial: "30%"  # 按百分比执行
    serial: 2      # 指定单次执行数量
    
    
    # OpenSSH链接优化(非必要,不建议)
    - 默认情况下,使用OpenSSH时,服务器端会根据客户端的IP地址进行DNS反向解析,然后根据获取到的主机名再次进行DNS查询得到IP地址,比较这两个IP地址是否一致
    - 可以在一定程度上提高安全性,但会花费更多时间,可以关闭这一特性来实现加速SSH链接速度
    - 设置 /etc/ssh/sshd_config 中参数 UseDNS no
    
    
    # 开启pipeling
    - 发送执行命令代替命令临时文件(针对不使用sudo的场景,建议以单独ansible.cfg文件方式开启)
    - 设置 /etc/ansible/ansible.cfg 中参数 pipelining = True
    

    Ansible 异步async与轮询polling特性

    # Ansible的异步async与轮询polling特性
    # 适合使用到ansible的polling特性的场景
    # - task需要运行很长的时间,这个task很可能会达到timeout
    # - 任务需要在大量的机器上面运行
    # - 任务是不需要等待它完成的
    
    # 不适合使用polling特性的场景
    # - task任务是需要运行完后才能继续另外的任务的
    # - task任务能很快的完成
    
    # 注意:需在ansible服务端的/etc/ansible/ansible.cfg文件里配置"host_key_checking = False"  
    
    - hosts: all
      max_fail_percentage: 30  # 主机数最大失败百分比,只有在大于时中止tasks的执行,结合serial参数则以serial值为基准
      serial: 7  # 定义同时执行操作的主机数,一般要小于forks的值,否则会按forks值进行“分片”执行任务
      tasks:
        - name: test-1
          shell: sleep 5 && echo "test-1 done" && hostname && date
          async: 9  # 任务执行时间的上限值,超时则任务失败,建议设置为略大于任务正常实际执行时长,设置为0时表示一直等待任务结束
          poll: 2  # 检查任务结果的间隔时长,建议设置为小于任务实际执行时长,设置为0表示不用等待结果继续执行后续任务
          register: script_result
        - name: test-2
          async_status: jid={{ script_result.ansible_job_id }}  # 通过模块async_status查看轮询结果
          register: job_result
          until: job_result.finished  #  
          retries: 10  # 重试10次
        - name: test-3  # 将在所有主机执行完成后一次性返回结果
          shell: echo "test-3 done" && hostname && date
    

    Ansible 图形界面

    Ansible Web UI目前主要有官方商业版Tower、官方开源版AWX 和 开源免费版semaphore。

    Ansible Tower

    Ansible AWX

    semaphore


    Ansible 技巧提示

    1 - Ansible 免密登录

    # 通过秘钥方式连接
    ssh-keygen -t rsa 
    ssh-copy-id -i /vipxf/.ssh/id_rsa.pub root@192.168.56.101
    ssh-copy-id -i /vipxf/.ssh/id_rsa.pub root@127.0.0.1
    

    2 - Ansible playbook输入root密码

    [root@test01 ansible-test]# cat test-temp.yaml 
    ---
    - name: test-root
      hosts: ta
      remote_user: vipxf
      gather_facts: no
      become: yes
      become_user: root
      become_method: su
      become_flags: "-"
      vars:
        ansible_become_pass: "{{ root_pass_input }}"
      tasks:
        - name: test
          shell: date && sleep 3
          ignore_errors: True
    [root@test01 ansible-test]# 
    [root@test01 ansible-test]# ansible-playbook  test-temp.yaml -e root_pass_input=redhat
    
    PLAY [test-root] **************************************************************************************************************************************************************************************************************
    
    TASK [test] *******************************************************************************************************************************************************************************************************************
    changed: [172.20.8.247]
    
    PLAY RECAP ********************************************************************************************************************************************************************************************************************
    172.20.8.247               : ok=1    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    
    [root@test01 ansible-test]# 
    

    3 - Ansible 简单的复用方式

    通过关键字include可以实现简单的代码复用。

    # 在playbook中,Include 指令可以跟task 混合在一起使用
      - tasks:
          - include: tasks/foo.yml  # 引用配置文件
    
    # 参数化的include
      - tasks: 
          - include: aaa.yml user=anliven  # 引用配置文件的同时传入变量
          - include: bbb.yml pwd=P@ssw0rd
    

    4 - Ansible task通知多个handlers任务

    通过关键字listen为handlers中的一个或多个触发器任务指定主题,task就可以按主题名进行通知。
    触发顺序按照handler定义的先后顺序执行。

    ---
    - hosts: ta
      remote_user: vipxf
      gather_facts: no
      vars:
        - package: vim
      tasks:
        - name: test-step one
          shell: echo "111" 
          notify: "test listen"  # task通知handlers主题
        - name: test-step two
          shell: echo "222"
      handlers:
        - name: check-hostname-date
          shell: hostname && date
          ignore_errors: True
          listen: "test listen"  # 按照定义顺序被触发
        - name: who-am-i
          shell: whoami && date
          ignore_errors: True
          listen: "test listen"  # 按照定义顺序被触发
    

  • 相关阅读:
    原生js 之 (DOM操作)
    一文搞懂漏洞严重程度分析
    基于微信小程序的健身小助手打卡预约教学系统(源码+lw+部署文档+讲解等)
    15.变量的存储类别
    REDIS篇(4)——命令执行过程(readQueryFromClient)
    C++静态成员(static)
    SQL数据分析之流程控制语句【if,case...when详解】
    MQTT Paho Android 支持SSL/TLS(亲测有效)
    基于激光雷达的深度图杆状物提取器在城市环境中长期定位方法
    计算机毕业设计ssm电商后台管理系统tgm41系统+程序+源码+lw+远程部署
  • 原文地址:https://www.cnblogs.com/anliven/p/16859401.html