关于 Ansible 中的一些奇技淫巧整理

举报
山河已无恙 发表于 2023/03/24 09:41:53 2023/03/24
【摘要】 写在前面分享一些 Ansible 中日常剧本中不常用但是需要知道的一些知识点博文适合了解 Ansible 的小伙伴,可以用作温习理解不足小伙伴帮忙指正 对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的懦弱回归,是随波逐流,是对内心的恐惧 ——赫尔曼·黑塞《德米安》ansible-pullansibl...

写在前面


  • 分享一些 Ansible 中日常剧本中不常用但是需要知道的一些知识点
  • 博文适合了解 Ansible 的小伙伴,可以用作温习
  • 理解不足小伙伴帮忙指正

对每个人而言,真正的职责只有一个:找到自我。然后在心中坚守其一生,全心全意,永不停息。所有其它的路都是不完整的,是人的逃避方式,是对大众理想的懦弱回归,是随波逐流,是对内心的恐惧 ——赫尔曼·黑塞《德米安》


ansible-pull

ansible-pull 该指令用于 Ansible 的另一种工作模式:pull 模式(Ansible默认使用 push模式 )。这和通常使用的 push 模式工作机理刚好相反。

用通俗的话讲,传统的方式(push),通过控制节点来控制受控节点,会在控制节点生成需要执行的 python 脚本,然后复制到受控节点,在受控节点执行,所以说是 push ,即在发布订阅模式中, Ansible 所在机器是 发布者,其他被控制的机器为订阅者。

ansible-pull 使用的是另一种方式, Ansible 所在机器在发布订阅模式中的是 订阅者,这里的发布者是当前 Git 仓库。当 Git 发生改变时,Ansible 会执行 Git 仓库中的的 ploybook

ansible-pull 其适用于以下场景:

  • 你有数量巨大的机器需要配置,即使使用高并发线程依旧要花费很多时间;
  • 你要在刚启动的、没有网络连接的主机上运行Anisble。

ansible-pull 命令使用格式如下: 可以通过 man ansible-pull 命令查看帮助信息。

ansible-pull [options] [playbook.yml]
# -C 分支 -d 目的地  -i  主机路径  -e 添加参数 -o 只有git仓库发生改变才执行playbook

这里我们看一个 Demo,配置一个定时任务,每 5分钟 执行一次,当 Git 仓库指定分支发生变更时,会拉取指定的剧本,并且执行它。

*/5 * * * * ansible-pull  -C master -d /root/test -i /opt/ansible-pull-test/hosts -U <github仓库>  playbook.yml -o

帮助文档查看

通过 ansible-doc -l 查看 ansible 的所有模块,如果忘记了全名,可以 grep

┌──[root@liruilongs.github.io]-[~]
└─$ ansible-doc -l
┌──[root@liruilongs.github.io]-[~]
└─$ ansible-doc -l |  grep k8s
k8s_scale                                                     Set a new size for a Deployment, ReplicaSet, Repl...
k8s_auth                                                      Authenticate to Kubernetes clusters which require...
k8s_info                                                      Describe Kubernetes (K8s) objects
k8s                                                           Manage Kubernetes (K8s) objects
k8s_service                                                   Manage Services on Kubernetes
┌──[root@liruilongs.github.io]-[~]
└─$

通过 ansible-doc -s k8s 可以查看详细信息

┌──[root@liruilongs.github.io]-[~]
└─$ ansible-doc -s k8s

或者直接写模块也可以

┌──[root@liruilongs.github.io]-[~]
└─$ ansible-doc  k8s

通过下面的路径可以 查看 jinja2ansible 过滤器对应文档

具体的过滤器列表,可以在下面的路劲看到,当在内网的时候,可以直接查找:

  • jinja2 /usr/lib/python3.6/site-packages/jinja2/filters.py
  • Ansible /usr/lib/python3.6/site-packages/ansible/plugins/filter/core.py

有网络时过滤器具体的说明文档:

插件的文档,通过下面的命令查看插件的文档

┌──[root@liruilongs.github.io]-[~]
└─$ ansible-doc -t lookup | grep ***
┌──[root@liruilongs.github.io]-[~]
└─$ ansible-doc -t lookup password

ansible-console

ansible-console 是 Ansible 为用户提供的一款交互式工具,用户可以在 ansible-console 虚拟出来的终端上像Shell 一样使用 Ansible 内置的各种命令,这为习惯于使用 Shell 交互方式的用户提供了便捷。

下面打码中 root@all (1)[f:5]$ 是提示符,该提示符表示 当前的使用用户@当前所在的Inventory中定义的组,默认是all分组(Inventory中all组所有主机的数量)[forks:线程数],默认为5个。

┌──[root@liruilongs.github.io]-[~/ansible]
└─$ ansible-console -i host
Welcome to the ansible console.
Type help or ? to list commands.

root@all (1)[f:5]$ systemctl is-active docker
192.168.26.55 | CHANGED | rc=0 >>
active
root@all (1)[f:5]$ systemctl is-active docker
192.168.26.55 | FAILED | rc=3 >>
inactivenon-zero return code
root@all (1)[f:5]$

通过上面的命令可以看到,可以执行正常 shell 命令

ansible 正则表达式使用

ansible 在指定主机清单的时候,可以通过 正则表达式 来匹配对应的主机清单。使用正则表达式需要以 ~ 开头

┌──[root@liruilongs.github.io]-[~/ansible]
└─$ cat host
192.168.26.55
┌──[root@liruilongs.github.io]-[~/ansible]
└─$ ansible ~^192\.168\..* -m ping -i host
192.168.26.55 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
┌──[root@liruilongs.github.io]-[~/ansible]
└─$ ansible ~^172\.25\..* -m ping -i host
[WARNING]: Could not match supplied host pattern, ignoring: ~^172.25..*
[WARNING]: No hosts matched, nothing to do
┌──[root@liruilongs.github.io]-[~/ansible]
└─$

ansible ad-hoc 的命令执行原理解析

下面为一个基本命令的执过程,这里对日志做了做了简单处理,只留下关键的部分,然后来看下,ad-hoc 在执行的时候,实际发生了什么。

┌──[root@liruilongs.github.io]-[~/ansible]
└─$ ansible all -a 'hostname' -i host  -vvv
ansible 2.9.27
  config file = /etc/ansible/ansible.cfg
  configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/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)]
Using /etc/ansible/ansible.cfg as config file
host_list declined parsing /root/ansible/host as it did not pass its verify_file() method
auto declined parsing /root/ansible/host as it did not pass its verify_file() method
Parsed /root/ansible/host inventory source with ini plugin
Skipping callback 'actionable', as we already have a stdout callback.
...............
Skipping callback 'yaml', as we already have a stdout callback.
META: ran handlers
.........
执行的命令 :  '/bin/sh -c '"'"'echo ~ && sleep 0'"'"''
执行的命令 :  '/bin/sh -c '"'"'( umask 77 && mkdir -p "` echo /root/.ansible/tmp `"&& mkdir "` echo /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595 `" && echo ansible-tmp-1678587386.14-50626-86000960670595="` echo /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595 `" ) && sleep 0'"'"''
执行的命令 :  '/bin/sh -c '"'"'echo PLATFORM; uname; echo FOUND; command -v '"'"'"'"'"'"'"'"'/usr/bin/python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python3.5'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.7'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python2.6'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/libexec/platform-python'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'/usr/bin/python3'"'"'"'"'"'"'"'"'; command -v '"'"'"'"'"'"'"'"'python'"'"'"'"'"'"'"'"'; echo ENDFOUND && sleep 0'"'"''
执行的命令 :  '/bin/sh -c '"'"'/usr/bin/python && sleep 0'"'"''
Using module file /usr/lib/python2.7/site-packages/ansible/modules/commands/command.py
<192.168.26.55> PUT /root/.ansible/tmp/ansible-local-506175w_TRB/tmpyPhPJB TO /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595/AnsiballZ_command.py
执行的命令 :   sftp  sftp 参数 '[192.168.26.55]'
执行的命令 :  '/bin/sh -c '"'"'chmod u+x /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595/ /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595/AnsiballZ_command.py && sleep 0'"'"''
执行的命令 :  '/bin/sh -c '"'"'/usr/bin/python /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595/AnsiballZ_command.py && sleep 0'"'"''
执行的命令 :  '/bin/sh -c '"'"'rm -f -r /root/.ansible/tmp/ansible-tmp-1678587386.14-50626-86000960670595/ > /dev/null 2>&1 && sleep 0'"'"''
192.168.26.55 | CHANGED | rc=0 >>
liruilongs.github.io
META: ran handlers
META: ran handlers
┌──[root@liruilongs.github.io]-[~/ansible]
└─$

默认情况下,通过日志,可以看到整个执行步骤的说明:

  • 执行环境配置加载解析
  • 建立 SSH 连接,确定连接后的目录 echo ~ && sleep 0
  • 远程环境 mkdir 执行文件夹
  • 确定远程执行的 python 环境是否支持
  • python 环境测试确认
  • 本地生成远程需要执行的 python 脚本
  • 通过 sftp 复制本地 python 脚本到远程环境
  • 为远程执行的目录和 python 脚本赋予执行权限
  • 执行 python 脚本
  • 删除远程临时 python 脚本

在上面的ssh,包括 sftp 过程中,涉及 Ansible 的传递参数:

<192.168.26.55> SSH: EXEC ssh -C 
-o ControlMaster=auto #表示将使用现有连接(如果可用),否则将为您打开一个连接。多条连接共享
-o ControlPersist=60s  # 长连接
-o KbdInteractiveAuthentication=no 
-o PreferredAuthentications=gssapi-with-mic,gssapi-keyex,hostbased,publickey # 
-o PasswordAuthentication=no #关闭密码登录模式
-o ConnectTimeout=10  # 建立连接超时时间
-o ControlPath=/root/.ansible/cp/da9dc192eb  #连接共享选项
192.168.26.55

执行并发数控制

执行并发数控制: -f 45, 默认为5

┌──[root@liruilongs.github.io]-[~/ansible]
└─$ ansible all -a 'hostname' -i host -f 45

变量注册器register

任何一个任务都可以注册一个变量用来 存储其运行结果,该注册变量在随后的任务中将像其他普通变量一样被使用。

大部分情况下,我们使用 注册器 用来接收 shell 命令的返回结果,结果中包含标准输出(stdout)和错误输出(stderr)。可以调用注册器来获取shell命令的返回结果。

- shell : hostname
  register: output_var

结合 when 条件使用

when 语句和 注册变量 结合起来的时候,其功能将更为强大。举例来说,我们想检查一个应用的运行状态,并判断返回的状态值,当状态为 ready 时,再执行下一步操作。任务代码如下:

- command: hostname 
  register: output_var
- command: do-something-to-my-app 
  when: " 'liruilongs.github.io' in output_var.stdout "

结合 changed_whenfailed_when 条件判断

- name: Install dependencies via Composer.
  command: "/usr/local/bin/composer global require phpunit/phpunit--prefer-dist"
  register: composer 
  changed_when: 'Nothing to install or update' not in composer.stdout

当PHP Composer安装或升级了某些软件的时候,也就是其运行结果中不包含“Nothing to install or update”字段的时候,Ansible才会返回运行状态为changed,这更符合我们的需要。

- name: 通过CLI导入Jenkins任务
  shel1: >
       java-jar/opt/jenkins-cli.jar-s http://localhost:8080/
       create-job"My Job"</usr/local/my-job.xm1
  register: import
  failed_when: "import.stderr and ' already exists' not in import.stderr"

当命令返回错误信息并且返回的错误信息中不包含“already exists”的内容时,再通知Anisble显示命令运行失败。

ignore_errors 条件判断

在有些情况下,一些运行的命令或脚本会报一些错误,甚至直接导致 Playbook 运行中断,但是这些错误并不是一定要解决,它是一些可预知的报错。

可以在相关任务中添加 ignore_erors:true 来屏蔽所有错误信息,Ansible 也将视该任务运行成功,不再报错,这样就不会对接下来要运行的任务造成额外困扰。

在日常剧本中, ignore_errors 一般结合 assert 使用,尤其是在 loop 中很方便。

assert:
  fail_msg: "Checksum FAIL: {{ item.name }}"
  success_msg: "Checksum PASS: {{ item.name }}"
  that:
    - post_result == pre_result
loop: "{{ files }}"
ignore_errors: yes

但是要注意的是,不应过度依赖 ignore_erors,因为它会隐藏所有的报错信息。

任务间的流程控制

任务委托

所谓任务委托,即把一些 tasks 委托给 delegate_to 指定的对应的机器执行。

- hosts: webservers 
  tasks: 
    - name: Add server to Munin monitoring configuration.
      command: monitor-server webservers {{ inventory_hostname })
      delegate_to: "{{ monitoring_master }}"

本地委托

本地委托,即所委托的机器为 Ansible 中执行剧本的 localhost 机器,可以通过下面两种不同的方式

- name: Remove server from load balancer.
  command: remove-from-1b {{ inventory_hostname }}
  delegate_to: 127.0.0.1
- name: Remove server from load balancer.
  localaction: command remove-from-1b{{ inventory _hostname }}

任务委托常常结合 delegate_factsrun_once,事实收集委托和只运行一次任务来执行一些特殊要求的命令,

- hosts: test_server
  tasks:
    - name: test
      shell: echo "test" > /root/test.list
      delegate_to: "servera"
      run_once: true
      delegate_facts: True

指定该 task 只能在委托的某一台机器或委托的组内机器上执行一次.同时对委托机器做实事收集

剧本执行顺序

Ansible 按照以下顺序运行 Play 的不同部分:

  • pre_tasks
  • pre_tasks 部分中通知的处理程序 handlers
  • roles
  • tasks
  • rolestasks 部分中通知的处理程序 handlers
  • post_tasks
  • post_tasks 部分中通知的处理程序 handlers

这些部分在 Play 中的编写顺序不会修改以上列出的执行顺序。

控制批处理的大小

在剧本中使用 serial 关键字来指定每个 批处理 中应当有多少个主机。通过 serial 特性可以实现部分 GitOps 的滚动更新。

---
- name: 滚动更新
  hosts: all
  serial: 2
  tasks:
    - name: update web
      shell: sleep 2

上面为一个简单的剧本,这个清单为 6台机器,可以看到 serial: 2,所以两台机器批量执行

ansible-playbook  serial.yaml

PLAY [滚动更新] **************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [servera]
ok: [serverb]

TASK [update web] ********************************************************************************************
changed: [servera]
changed: [serverb]

PLAY [滚动更新] **************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [serverd]
ok: [serverc]

TASK [update web] ********************************************************************************************
changed: [serverc]
changed: [serverd]

PLAY [滚动更新] **************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************
ok: [servere]
ok: [serverf]

TASK [update web] ********************************************************************************************
changed: [servere]
changed: [serverf]

PLAY RECAP ***************************************************************************************************
servera                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverb                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverc                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverd                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
servere                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
serverf                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

还可以为 serial 关键字设置为百分比:

- name: 滚动更新
  hosts: all
  serial: 25%
  tasks:
    - name: update web
      shell: sleep 2

运行时更改批处理大小

还可以在 Play 运行时更改批处理大小,即对每个批次进行定义。

---
- name: 滚动更新
  hosts: all
  serial:
    - 25%
    - 3
    - 100%
  tasks:
    - name: update web
      shell: sleep 2

Ansible 在开始下一批主机之前, 将全程通过 剧本任务 处理的每一批主机,如果当前 批处理 中的 所有主机都失败,则整个剧本 将中止,且 Ansible 不会启动下一批次处理。

这里可以通过 指定容错 的方式来提前终止剧本。通过将 max_fail_percentage 关键字添加到剧本 ,改变 Ansible 的失败行为

- name: 滚动更新
  hosts: all
  max_fail_percentage: 30%
  serial:
    - 25%
    - 3
    - 100%
  tasks:
    - name: update web
      shell: sleep 2

下面的配置,即所有机器里 30% 的机器执行 tasks 任务失败,那个会提前终止 剧本。

**这里需要注意下**:

默认情况下,Ansible尝试获取尽可能多的主机来完成play。如果某一任务对于某一主机失败,则它将从play中丢弃,但Ansible 将继续为其他主机运行play中剩余的任务。仅当所有主机都失败时,play才会停止。

但是,如果使用 serial 关键字将主机组织到批处理中,那么如果当前批处理中的所有主机都失败,则Ansible将停止所有剩余主机的 play,而不仅仅是当前批处理中剩余的主机。如果由于批处理中的所有主机失败而停止了该play的执行,则下一个批处理将不会启动。

Ansible 的 ansible_play_batch 变量中的保留每个批处理活动服务器列表。任何有任务失败的主机都将从 ansible_play_batch 列表中删除。Ansible会在每项任务后更新此列表。

控制任务暂停

在有些情况下,一些任务的运行需要 等待一些状态的恢复,比如某一台主机或者应用刚刚重启,我们需要等待它上面的某个端口开启,此时我们就不得不将正在运行的任务暂停,直到其状态满足我们需求。

可以通过 wait_for 来实现相关的功能。

  • 使用选项 host、port、timeout 的组合来判断一段时间内主机的端口是否可用
  • 使用 path 选项(可结合search_regx 选项进行正则匹配)和timeout选项来判断某个路径下的文件是否存在
  • 使用选项 host、port 和 state 选项的 drained 值来判断活动端口是否关闭
  • 使用 delay 选项来指定在 timeout 时间内进行检测的时间间隔,时间单位为秒。

下面是一些 官网的 Demo

#睡眠 300 秒并继续
- name: Sleep for 300 seconds and continue with play
  ansible.builtin.wait_for:
    timeout: 300
  delegate_to: localhost
# 等待主机上的8000端口打开,10秒后才开始检查
- name: Wait for port 8000 to become open on the host, don't start checking for 10 seconds
  ansible.builtin.wait_for:
    port: 8000
    delay: 10
# 等待任何IP的8000端口关闭活动连接,10秒后才开始检查
- name: Waits for port 8000 of any IP to close active connections, don't start checking for 10 seconds
  ansible.builtin.wait_for:
    host: 0.0.0.0
    port: 8000
    delay: 10
    state: drained

# 等待任何IP的8000端口关闭活动连接,忽略指定主机的连接
- name: Wait for port 8000 of any IP to close active connections, ignoring connections for specified hosts
  ansible.builtin.wait_for:
    host: 0.0.0.0
    port: 8000
    state: drained
    exclude_hosts: 10.2.1.2,10.2.1.3
# 等到/tmp/foo文件存在后再继续
- name: Wait until the file /tmp/foo is present before continuing
  ansible.builtin.wait_for:
    path: /tmp/foo

# 等待字符串"completed"在/tmp/foo文件中,再继续
- name: Wait until the string "completed" is in the file /tmp/foo before continuing
  ansible.builtin.wait_for:
    path: /tmp/foo
    search_regex: completed
# 直到正则表达式匹配到/tmp/foo文件中,然后打印出匹配的组
- name: Wait until regex pattern matches in the file /tmp/foo and print the matched group
  ansible.builtin.wait_for:
    path: /tmp/foo
    search_regex: completed (?P<task>\w+)
  register: waitfor
- ansible.builtin.debug:
    msg: Completed {{ waitfor['match_groupdict']['task'] }}
# 等待文件被移除
- name: Wait until the lock file is removed
  ansible.builtin.wait_for:
    path: /var/lock/file.lock
    state: absent
# 等待,直到进程完成并销毁pid
- name: Wait until the process is finished and pid was destroyed
  ansible.builtin.wait_for:
    path: /proc/3466/status
    state: absent
# 失败时输出自定义消息
- name: Output customized message when failed
  ansible.builtin.wait_for:
    path: /tmp/foo
    state: present
    msg: Timeout to find file /tmp/foo
...........    

交互式提示

虽然 Ansible 提供了 Ansible-vault 来保存秘密相关的,但是动态的秘密数据不方便保存,不同用户有可能有不同的需求,比如输入用户自己的账号和密码或者输入不同的版本号会触发不同的后续操作等。Ansiblevars prompt 关键字就是用来处理上述这种与用户交互的情况的。

- hosts:all 
  vars_prompt:
- name: share_user 
  prompt: "What is your network username?"
- name: share_pass 
  prompt: "What is your network password?"
  private: yes

关键字vars prompt下面几个常用的选项总结如下

  • private:该值为yes,即用户所有的输入在命令中默认都是不可见的;而将其值设为no时,用户输入可见。
  • default:为变量设置默认值,以节省用户输入时间。
  • confirm:特别适合输入密码的情况,如果将值设为yes,则会要求用户输入两次,以增加输入的正确性。

但是需要注意的是,使用交互式是一种剧本的坏味道,Ansible 是为了自动化而存在,交互式打破了自动化。

标签

默认情况下,Ansible 在执行一个 Playbook 时,会执行 Playbook 中定义的所有任务。

Ansible 的标签(Tags) 功能可以给 角色(Roles)、文件、单独的任务甚至整个Playbook 打上标签,然后利用这些标签来指定要运行 Playbook 中的个别任务,或不执行指定的任务,并且它的语法非常简单。

特殊标签:

  • always:带有 always 标记的资源始终都会运行,除非明确指定--skip-tags always选项。
  • never:带有 never 特殊标记的资源不会运行,除非明确指定--tags never选项。
- name: tags Demo 1
  hosts: servera
  tags:
    - play-tag-1
    - never

命令行指定标签时的特定参数:

  • tagged : 标记将运行任何带有显式标记的资源
  • untagged : 标记将运行不带有显式标记的资源
  • all : 参数将包括 Play 中的所有任务, 无论是否带有标记, 这是默认行为。
ansible-playbook  tags-all.yaml  --tags=tagged

Block 块

在 Ansible 中 Block 块常常 结合 when 条件,用于批量的 task 执行

- hosts: web 
  tasks:
# Install and configure Apache on RedHat/Centos hosts.
    - block:
        - yum: name=httpd state=present
        - template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
        - service: name=httpd state=started enabled=yes 
      when: ansible_os_family =='RedHat'
    - block:
        - apt: name=apache2 state=present
        - template: src=httpd.conf.j2 dest=/etc/apache2/apache2.conf
        - service: name=apache2 state=started enabled=yes 
      when: ansible_os_family==' Debian'   

try-catch-fianlly 机制,ansible 剧本中提供了对应的类似编程语言的机制,用于处理异常。

tasks:
  - block:
      - name: shell commont
        script: shell.sh
    rescue:
      - debug: msg="报错会执行的"
    always:
      - debug: msg='总是会执行的'      

条件化动态、静态加载

Ansible 提供了条件化动态静态加载功能,即在满足一定条件时加载任务或者剧本角色。这个功能极大地提高 Ansible 功能的灵活性和可扩展性。同时可以正常执行

加载方式:

  • include_* : 包含(include_tasks, include_role),include 是动态加载,在运行到对应的 task 的时候才会加载
  • import_* : 包含(import_playbook, import_tasks,import_role),import 是静态加载,在 playbook 预解析阶段就会加载。

具体要考虑版本要求,不通的版本对应支持不同,当前最新版本支持上面所有,同时涉及到变量传递,headlers 的执行,具体问题需要具体分析。

    - name: include_tasks demo
      include_tasks: task.yaml
      vars:
        ansible_user: root
    - name: A task to include role_tasks_demo   here
      include_role:
        name: role_tasks_demo
#引用附加的任务,该任务只在运行时有效
- name: Check if extra_tasks.yml is present.
  stat: path=extras/extra-tasks.yml #判断extras 目录下extra-tasks.yml 文件是否存在
  register: extra_tasks_file #获取状态返回值
  connection: local

- include: tasks/extra-tasks.yml  #结合如下when条件,只有当extra_tasks_file 文件存在时再加载
  when: extra_tasks_file.stat.exists

限定特殊的 task 运行

有时候,你并不希望 Ansible 运行你 playbook 中的每一个 task ,特别是在你第一次编写和调试某个 playbook 的时候。Ansible 提供了一些命令行选项来让你控制哪个task 运行。

--step

ansible-ploybook --step ploybook.yaml

--step 参数将会让 Ansible 在执行每一个task之前都提示你,就像下面这样: Perform task;install packages(y/n/c)

  • 你可以选择执行这个task(y),
  • 跳过它(n)
  • 或者告诉 Ansible 继续执行剩下的 playbook 并且不再提示你(c)。

start-at-task

start-at-task taskname 参数告诉 Ansible 从指定的 task 开始运行playbook ,而不是从头开始运行。

ansible-ploybook --start-at-task='install packages' ploybook.yaml

如果你的 playbook 因为某一个 task 中有bug 而失败了,在你修复了这个 bug 后你希望从这个你修复的 task 开始再次运行playbook的时候,使用这个参数会非常便利。

控制主机执行顺序

Ansible根据剧本hosts指令确定要管理的主机。默认情况下,Ansible2.4和更高版本根据清单中主机列出的顺序运行剧本。您可以使用order指令更改该顺序。

order指令接受以下值:

  • inventory 清单顺序。这是默认值。
  • reverse_inventory 清单相反顺序。
  • sorted 主机按字母顺序排列。数字在字母前排序。
  • reverse_sorted 主机以相反的字母顺序排列。
  • shuffle 每次您执行剧本时,随机排序。
- name: Executing a role as a task
  hosts: all
  order: sorted
  tasks:
     - name: A normal task
       debug:
         msg: 'first task'

由于Ansible通常在多个主机上并行运行每个任务,因此 ansible-playbook 命令的输出可能无法反映预期的顺序:输出显示的是任务完成顺序,而不是执行顺序。

Jinja2 实现模板自定义

对于配置文件,可以通过 Jinja2 模板来实现。使用方式常用的有 变量、for循环、if-else

{% for item in all_items %)
{{ item }}
{% endfor %}
{% if PORT %}
bind-address=0.0.0.0:{{ PORT }}
{% else %}
bind-address=0.0.0.0:3306
{% endif %}

SSH批量免密配置

需要首先通过清单中配置用户密码连接机器,配置完SSH免密后,删除相关配置

[host_group_name:vars]
ansible_ssh_user='username'
ansible_ssh_pass='!QAZ2wsx'

通过 authorized_key 模块实现

ansible db -m authorized_key -a \
 "user=username key=' lookup('file', '/home/username/.ssh/id_rsa.pub') ) \
  path=/home/username/.ssh/authorized_keys \
  manage_dir=no"

通过 copy 模块实现

# copy公钥至远程主机/tmp目录下
ansible db -m copy -a "src=/home/username/.ssh/id_rsa.pub dest=/tmp/id_rsa.pub"
# 添加公钥
ansible db -m shell -a "cat /tmp/id_rsa.pub >> /home/username/.ssh/authorized_keys"

Ansible 调试

debug 模块

我们已经在本书中多次使用debug模块了。它可以称得上是Ansible版的print语句。

- debug: var=msg
- debug: msg=" {{ [hostvars]inventory_hostname }} 输出 {{ msg }}"

assert 模块

assert模块会在指定的条件不符合的时候返回错误并失败退出。例如,下面配置在没有eth1网卡情况下会让playbook直接返回失败:

- name: assert that ethl interface exists
assert:
  that:
    - ansible eth1 is defined

- name: stat /opt/foo
  stat: 
    path: /opt/foo
  register: st

- name: assert that /opt/foo is dit
  assert:
    that: st.stat.isdir    

帮助文档查看

┌──[root@liruilongs.github.io]-[~/.ssh]
└─$ ansible-doc -s assert
- name: Asserts given expressions are true
  assert:
      fail_msg:              # The customized message used for a failing assertion. This argument was called 'msg'
                               before Ansible 2.7, now it is renamed to 'fail_msg'
                               with alias 'msg'.
      quiet:                 # Set this to `yes' to avoid verbose output.
      success_msg:           # The customized message used for a successful assertion.
      that:                  # (required) A list of string expressions of the same form that can be passed to the
                               'when' statement.
┌──[root@liruilongs.github.io]-[~/.ssh]
└─$

一些 Demo

- name: After version 2.7 both 'msg' and 'fail_msg' can customize failing assertion message
  assert:
    that:
      - my_param <= 100
      - my_param >= 0
    fail_msg: "'my_param' must be between 0 and 100"
    success_msg: "'my_param' is between 0 and 100"

- name: use quiet to avoid verbose output
  assert:
    that:
      - my_param <= 100
      - my_param >= 0
    quiet: true

剧本检查

# 检查语法
ansible-ploybook --syntax-check ploybook.yaml
# 检查主机
ansible-ploybook --list-hosts ploybook.yaml
# 检查执行任务
ansible-ploybook --list-tasks ploybook.yaml
# 检查模式运行
ansible-ploybool -C ploybook.ymal
ansible-ploybook --check ploybook.yaml

-D--diff 参数将会为任何变更远程主机状态的文件输出差异信息。将它与--check 结合起来非常好用。将它们结合起来使用会展示正常运行情况下 Ansible 会如何修改文件。

## diff 显示修改的文件差异
ansible-ploybook -D --check ploybook.yaml
ansible-ploybook --diff --check ploybook.yaml

剧本执行优化

禁用facts收集

增加并行

不多讲 forks=20 的设置

使用软件包管理器模块避免循环

- name: Install the packages on the web servers
  hosts: all

  tasks:
    - name: Ensure the packages are installed
      yum:
        name:
          - httpd
          - mod_ssl
          - httpd-tools
          - mariadb-server
          - mariadb
          - php
          - php-mysqlnd
        state: absent

高效复制文件到受管主机

大量文件复制到受管主机时,使用 synchronize 模块更为高效,应为``synchronize` 模块使用可rsync来同步文件,会通过哈希值比较文件,如果文件存在,则不复制,速度非常快,所以大多数情况下此模块后台使用 rsync 速度比copy 模块快,copy模块本质上是scp,所以他不会对文件是否存在进行校验。

---
- name: Deploy the w eb content on the web servers
  hosts: all
  become: True
  gather_facts: False
  tasks:
    - name: copy demo
      synchronize:
        src: bigfile1
        dest: /tmp/

优化 SSH 连接

ControlMaster允许多个同时与远程主机连接的 SSH 会话使用单一网络连接。第一个 SSH 会话建立连接,与同一主机连接的其他会话则重复利用该连接,从而绕过较慢的初始过程。SSH 在最后一个会话关闭后,立即销毁共享的连接。

ControlPersist使连接在后台保持打开,而不是在上⼀次会话后销毁连接。此指令允许稍后的 SSH 会话重用该连接。ControlPersist 指示 SSH 应使空闲连接保持打开的时间长度,每个新会话将重置此空闲计时器。

配置文件设置

[ssh_connection]
ssh_args=-o ControlMaster=auto -o ControlPersist=60s

启用 Pipelining

提高 playbook 的性能,可以激活Pipelining功能,Ansible 将建立较少的 SSH 连接。若要启用 Pipelining ,将 Ansible 配置文件中的[ssh_connection] 部分:

[ssh_connection]
pipelining =True

此功能默认不启用,因为需要禁用受管主机中的 requiretty sudo 选项。requiretty表示即使没有交互式shell /会话也可以使用sudo

禁用需要找受管机做如下配置

$ cat /etc/sudoers | grep -C 4 visiblepw

#
# Refuse to run if unable to disable echo on the tty.
#
Defaults   !visiblepw
Defaults   !requiretty 
#
# Preserving HOME has security implications since many programs
# use it when searching for configuration files. Note that HOME

关于 Ansible的一些语法就和小伙伴分享,这里整理了 Ansible 相关的中文书籍分享给小伙伴,仅用于学习,有条件的小伙伴支持下作者 :)

  • 《Ansible自动化运维 技术与最佳实践》
  • 《Ansible 权威指南 》
  • 《Ansible:Up and Running》

获取方式,关注 訂 閱 號: 山河已无恙,回复 ansible 获取。


博文部分内容参考

© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知


https://docs.ansible.com/ansible/2.9/index.html

《Ansible 权威指南 》

《Ansible:Up and Running》


© 2018-2023 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。