Ansible 执行 Playbook
提权
设置提权
Ansible 通过 become
进行提权。become
有如下几个参数:
become
:设置为true
表示进行提权become_user
:设置提权到哪个用户become_method
:设置提权方法,默认为sudo
become_flags
:用于设置提权后使用的额外配置参数
以上四个参数都可以在 Playbook 或者 Task 级别进行设置。
下边是一个官网的提权示例:
- name: Run a command as nobody
command: somecommand
become: true
become_method: su
become_user: nobody
become_flags: '-s /bin/sh'
提权相关的连接变量
以下是提权相关的连接变量:
ansible_become
:是否提权ansible_become_method
:提权方法ansible_become_user
:提权到哪个用户ansible_become_password
:提权密码ansible_common_remote_group
:要给临时的 Ansible 目录设置的组
这个变量可以在主机清单中设置:
webserver ansible_user=manager ansible_become=true
提权相关命令行参数
--become
,-b
:是否提权--ask-become-pass
,-K
:提权密码--become-methos=BECOME_METHOD
:提权方法,默认sudo
--become-user=BECOME_USER
:要提权到的用户,默认root
通过标记控制任务执行范围
为任务设置标记
通过标记可以设置只执行部分任务。
通过 tags:
参数可以给 tasks
设置标记:
[root@study ansible]# cat tag.yml
- name: test tag
hosts: localhost
gather_facts: false
tasks:
- name: print one
ansible.builtin.debug:
msg: "one"
tags:
- example
- name: print two
ansible.builtin.debug:
msg: "two"
tags:
- example
- example2
- name: print three
ansible.builtin.debug:
msg: "three"
这里给第二个 tasks
添加了一个标记为 example
,此时如果直接执行 Playbook,结果如下:
[root@study ansible]# ansible-playbook tag.yml
PLAY [test tag] *************************************************************************************************************
TASK [print one] ************************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print two] ************************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
TASK [print three] **********************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP ******************************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以看到三个任务都执行了,如果指定 tag
后在执行,结果如下:
[root@study ansible]# ansible-playbook --tag example tag.yml
PLAY [test tag] *************************************************************************************************************
TASK [print one] ************************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print two] ************************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
PLAY RECAP ******************************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook --tag example2 tag.yml
PLAY [test tag] *************************************************************************************************************
TASK [print two] ************************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
PLAY RECAP ******************************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以看到只有第二个 tasks
执行了。
一个标记可以用于多个
tasks
,一个tasks
也可以打多个标记。
handlers
会忽略标记。如果要给
block
打标记,记得给rescue
和always
也打上标记,否则rescue
和always
的任务就不会执行。标记不仅仅支持
tasks
,也支持给 Playbook 打标记,跟hosts
一个层级。
为导入任务设置标记
include_*
include_*
是动态导入,有 include_tasks
和 include_role
,这里以 include_tasks
为例:
[root@study ansible]# cat include_tag.yml
- name: test tag
hosts: localhost
gather_facts: false
tasks:
- name: include
ansible.builtin.include_tasks: include_import_tasks.yml
tags: test_include
[root@study ansible]# cat include_import_tasks.yml
---
- name: print one
ansible.builtin.debug:
msg: "one"
tags:
- example
- name: print two
ansible.builtin.debug:
msg: "two"
tags:
- example
- example2
- name: print three
ansible.builtin.debug:
msg: "three"
# 执行任务测试
[root@study ansible]# ansible-playbook include_tag.yml --tag example2
PLAY [test tag] ****************************************************************************************************
PLAY RECAP *********************************************************************************************************
[root@study ansible]# ansible-playbook include_tag.yml --tag test_include
PLAY [test tag] ****************************************************************************************************
TASK [include] *****************************************************************************************************
included: /root/ansible/include_import_tasks.yml for localhost
PLAY RECAP *********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook include_tag.yml --tag example2,test_include
PLAY [test tag] ****************************************************************************************************
TASK [include] *****************************************************************************************************
included: /root/ansible/include_import_tasks.yml for localhost
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
可以看到 include_tasks
的 tags
只限制 include_tasks
是否执行,只使用 include_tasks
的 tags
虽然会执行任务动态导入,但是导入后的任务也只会执行带有对应 tags
的任务,使用时需要额外指定被导入任务里的 tags
。
简单测试了一下
include_role
应该跟include_tasks
一样。
import_*
import_*
是静态导入,有 import_tasks
、import_role
和 import_playbook
。
这里以 import_tasks
为例:
[root@study ansible]# cat import_tag.yml
- name: test tag
hosts: localhost
gather_facts: false
tasks:
- name: import
ansible.builtin.import_tasks: include_import_tasks.yml
tags: test_import
[root@study ansible]# cat include_import_tasks.yml
---
- name: print one
ansible.builtin.debug:
msg: "one"
tags:
- example
- name: print two
ansible.builtin.debug:
msg: "two"
tags:
- example
- example2
- name: print three
ansible.builtin.debug:
msg: "three"
# 执行任务测试
[root@study ansible]# ansible-playbook import_tag.yml --tag test_import
PLAY [test tag] ****************************************************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook import_tag.yml --tag example2
PLAY [test tag] ****************************************************************************************************
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
和动态导入不同,import_tasks
在导入任务的时候会将自己的 tags
附加给被导入的任务,使用 import_tasks
的 tags
会执行所有被导入的任务,因为是静态导入,所以直接使用被导入任务里的 tags
也能执行任务。
简单测试了一下,
import_role
和import_playbook
,还有通过roles
使用角色,效果都和import_tasks
一样。
为动态导入集成标记
include_*
默认不会将自己的 tags
传递给被导入的任务,想要将标记传递给被导入的任务可以使用如下两种方式:
- name: include
ansible.builtin.include_tasks:
file: include_import_tasks.yml
apply:
tags:
- test_apply
tags: test_include
或者
- block:
- name: include
ansible.builtin.include_tasks: include_import_tasks.yml
tags: test_apply
特殊标记
Ansible 有几个特殊标记:always
、never
、tagged
、untagged
和 all
。其中 always
和 never
用于标记任务。
always
:用于标记任务,表示不管指定什么tags
,这个任务都会执行never
:用于标记任务,表示如果每有显式指定tags
,则任务不会执行tagged
:用于ansible-playbook --tags
,表示执行所有被设置tags
的任务(带有never
标记任务除外)untagged
:用于ansible-playbook --tags
,表示执行所有未设置tags
的任务all
:用于ansible-playbook --tags
,执行所有带tags
和不带tags
的任务(带有never
标记任务除外)
always
和 never
就不做过多介绍了,可以自己测试,前者加上之后任务始终运行,后者任务始终不运行。
后边三个简单说一下:
[root@study ansible]# cat tag.yml
- name: test tag
hosts: localhost
gather_facts: false
tasks:
- name: print one
ansible.builtin.debug:
msg: "one"
tags:
- example
- always
- name: print two
ansible.builtin.debug:
msg: "two"
tags:
- example
- example2
- never
- name: print three
ansible.builtin.debug:
msg: "three"
[root@study ansible]# ansible-playbook tag.yml --tags tagged
PLAY [test tag] ****************************************************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook tag.yml --tags untagged
PLAY [test tag] ****************************************************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook tag.yml --tags all
PLAY [test tag] ****************************************************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook tag.yml --tags never
PLAY [test tag] ****************************************************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
上边可以看到,always
始终运行,tagged
执行所有带标记且不带 never
标记的任务,untagged
执行所有不带标记和带有 always
标记的任务
列出标记和跳过标记
通过 --list-tags
可以列出标记:
[root@study ansible]# ansible-playbook --list-tags tag.yml
playbook: tag.yml
play #1 (localhost): test tag TAGS: []
TASK TAGS: [always, example, example2, never]
通过 --skip-tags
可以跳过标记的任务:
[root@study ansible]# cat tag.yml
- name: test tag
hosts: localhost
gather_facts: false
tasks:
- name: print one
ansible.builtin.debug:
msg: "one"
tags:
- example
- always
- name: print two
ansible.builtin.debug:
msg: "two"
tags:
- example
- example2
- never
- name: print three
ansible.builtin.debug:
msg: "three"
[root@study ansible]# ansible-playbook --skip-tags example tag.yml
PLAY [test tag] ****************************************************************************************************
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
--skip-tags
优先级比--tags
高。
--skip-tags
可以跳过带有always
标记的任务。
设置 Playbook 开始位置和交互式确认
设置 Playbook 开始位置
通过 --start-at-task
和 Playbook 的名字来设置从哪开始执行 Playbook。
[root@study ansible]# cat tag.yml
- name: test tag
hosts: localhost
gather_facts: false
tasks:
- name: print one
ansible.builtin.debug:
msg: "one"
tags:
- example
- always
- name: print two
ansible.builtin.debug:
msg: "two"
tags:
- example
- example2
- name: print three
ansible.builtin.debug:
msg: "three"
[root@study ansible]# ansible-playbook tag.yml --start-at-task "print two"
PLAY [test tag] ****************************************************************************************************
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
交互式确认执行
通过 --step
设置交互式执行任务。
交互式有三种选项:
yes
:执行这个任务no
:不执行这个任务continue
:自动执行后续所有任务
[root@study ansible]# ansible-playbook tag.yml
PLAY [test tag] ****************************************************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook --step tag.yml
PLAY [test tag] ****************************************************************************************************
Perform task: TASK: print one (N)o/(y)es/(c)ontinue: y
Perform task: TASK: print one (N)o/(y)es/(c)ontinue: ***************************************************************
TASK [print one] ***************************************************************************************************
ok: [localhost] => {
"msg": "one"
}
Perform task: TASK: print two (N)o/(y)es/(c)ontinue: n
Perform task: TASK: print two (N)o/(y)es/(c)ontinue: ***************************************************************
Perform task: TASK: print three (N)o/(y)es/(c)ontinue: y
Perform task: TASK: print three (N)o/(y)es/(c)ontinue: *************************************************************
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@study ansible]# ansible-playbook --step tag.yml
PLAY [test tag] ****************************************************************************************************
Perform task: TASK: print one (N)o/(y)es/(c)ontinue: n
Perform task: TASK: print one (N)o/(y)es/(c)ontinue: ***************************************************************
Perform task: TASK: print two (N)o/(y)es/(c)ontinue: c
Perform task: TASK: print two (N)o/(y)es/(c)ontinue: ***************************************************************
TASK [print two] ***************************************************************************************************
ok: [localhost] => {
"msg": "two"
}
TASK [print three] *************************************************************************************************
ok: [localhost] => {
"msg": "three"
}
PLAY RECAP *********************************************************************************************************
localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Ansible Playbook 执行策略
设置主机执行策略
通过 strategy:
设置主机执行策略。
- name: test
hosts: all
gather_facts: false
strategy: free
tasks:
通过 ansible-doc -t strategy -l
可以查看有哪些策略插件:
[root@study ansible]# ansible-doc -t strategy -l
ansible.builtin.debug Executes tasks in interactive debug session
ansible.builtin.free Executes tasks without waiting for all hosts
ansible.builtin.host_pinned Executes tasks on each host without interruption
ansible.builtin.linear Executes tasks in a linear fashion
strategy 名称 | 特点 | 使用场景 | Ansible 版本 |
---|---|---|---|
linear |
默认策略,所有主机执行完某个任务后在继续执行下一个任务 | 一般情况 | 所有版本 |
free |
主机之间并发执行任务,不等其他主机完成 | 性能测试、节点独立部署 | 所有版本 |
host_pinned |
每个主机独立执行整个 play 中的所有任务(有点类似 serial: 1 ) |
针对节点完整生命周期操作 | Ansible 2.11+ |
debug |
每执行一步打印所有细节 | 排错场景 | 所有版本(较少用) |
-
linear:所有主机同步推进任务。
task1: host1 host2 host3 task2: host1 host2 host3 task3: host1 host2 host3
-
free:主机各自独立运行,不等待其他主机。
host1: task1 -> task2 -> task3 host2: task1 -> task2 -> task3 host3: task1 -> task2 -> task3
-
host_pinned:每台主机完整执行一遍 play,再轮到下一台主机。
host1: task1 -> task2 -> task3 | host2: └───task1 -> task2 -> task3 | host3: └───task1 -> task2 -> task3
- 每个主机的 play 中所有
task
、handler
、rescue
、always
等都会执行完整流程 - 主机之间严格串行执行
- 常见于需要:
- 整台机器部署生命周期控制
- 避免并发时资源冲突
- 精准排查单主机问题
- 每个主机的 play 中所有
示例:
[root@remote-host ansible]# ansible all --list-hosts
hosts (3):
base-k8s-master-1
base-k8s-worker-1
base-k8s-worker-2
# 默认情况
[root@remote-host ansible]# cat test.yml
- name: test environment
hosts: all
gather_facts: true
#strategy: host_pinned
tasks:
- name: get env
ansible.builtin.shell: /bin/env
register: shell_result
notify: test
- name: print env
ansible.builtin.debug:
var: shell_result.rc
- name: print env1
ansible.builtin.debug:
var: shell_result.rc
handlers:
- name: test
debug:
msg: "1"
# 执行结果
[root@remote-host ansible]# ansible-playbook test.yml
PLAY [test environment] ************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [base-k8s-worker-2]
ok: [base-k8s-worker-1]
ok: [base-k8s-master-1]
TASK [get env] *********************************************************************************************************
changed: [base-k8s-worker-2]
changed: [base-k8s-worker-1]
changed: [base-k8s-master-1]
TASK [print env] *******************************************************************************************************
ok: [base-k8s-master-1] => {
"shell_result.rc": "0"
}
ok: [base-k8s-worker-1] => {
"shell_result.rc": "0"
}
ok: [base-k8s-worker-2] => {
"shell_result.rc": "0"
}
TASK [print env1] ******************************************************************************************************
ok: [base-k8s-master-1] => {
"shell_result.rc": "0"
}
ok: [base-k8s-worker-1] => {
"shell_result.rc": "0"
}
ok: [base-k8s-worker-2] => {
"shell_result.rc": "0"
}
PLAY RECAP *************************************************************************************************************
base-k8s-master-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
# 修改策略为 host_pinned
[root@remote-host ansible]# cat test.yml
- name: test environment
hosts: all
gather_facts: true
strategy: host_pinned
tasks:
- name: get env
ansible.builtin.shell: /bin/env
register: shell_result
notify: test
- name: print env
ansible.builtin.debug:
var: shell_result.rc
- name: print env1
ansible.builtin.debug:
var: shell_result.rc
handlers:
- name: test
debug:
msg: "1"
# 执行结果
[root@remote-host ansible]# ansible-playbook test.yml
PLAY [test environment] ************************************************************************************************
TASK [Gathering Facts] *************************************************************************************************
ok: [base-k8s-worker-2]
ok: [base-k8s-master-1]
TASK [get env] *********************************************************************************************************
changed: [base-k8s-worker-2]
TASK [print env] *******************************************************************************************************
ok: [base-k8s-worker-2] => {
"shell_result.rc": "0"
}
TASK [Gathering Facts] *************************************************************************************************
ok: [base-k8s-worker-1]
TASK [print env1] ******************************************************************************************************
ok: [base-k8s-worker-2] => {
"shell_result.rc": "0"
}
TASK [get env] *********************************************************************************************************
changed: [base-k8s-master-1]
TASK [print env] *******************************************************************************************************
ok: [base-k8s-master-1] => {
"shell_result.rc": "0"
}
TASK [print env1] ******************************************************************************************************
ok: [base-k8s-master-1] => {
"shell_result.rc": "0"
}
TASK [get env] *********************************************************************************************************
changed: [base-k8s-worker-1]
TASK [print env] *******************************************************************************************************
ok: [base-k8s-worker-1] => {
"shell_result.rc": "0"
}
TASK [print env1] ******************************************************************************************************
ok: [base-k8s-worker-1] => {
"shell_result.rc": "0"
}
PLAY RECAP *************************************************************************************************************
base-k8s-master-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-1 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
base-k8s-worker-2 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
在使用
host_pinned
时,有的节点的Gathering Facts
任务可能会在其他节点的任务之后执行,这个和host_pinned
的单节点执行任务并不冲突,因为Gathering Facts
是收集信息不会影响其他节点执行任务。我没测试,这样的话估计会影响其他节点获取这个节点的
ansible_facts
变量。
设置并发数
Ansible 支持设置 Ansible 并发数(一次性最多对多少主机执行任务),并发数可以通过配置文件和 Ad-Hoc 设置:
配置文件:
[defaults]
forks = 30
Ad-Hoc:
[root@study ansible]# ansible-playbook -f 30 playbook.yml
设置批量管理主机数量
Ansible 通过队列执行任务,可以通过 serial
可以设置每个队列管理的主机数量(用于滚动更新)。
这个和 forks
不同,forks
是设置的是 Ansible 执行任务时一次性管理多少主机,serial
设置的是在 forks
基础上一个队列里最多有多少个主机执行任务。
serial
支持数量、百分比和列表三种种方式。
数量:
- name: test
hosts: all
serial: 1
一个队列执行一个主机。
百分比:
- name: test
hosts: all
serial: 30%
一个队列执行 30% 的主机(向下取整,最少一台)。
列表:
- name: test
hosts: all
serial:
- 1
- 5
- 10
- name: test
hosts: all
serial:
- 10%
- 20%
- 100%
第一个表示:
- 第一次队列执行 1 台
- 第二次队列执行 5 台
- 后续的队列每次 10 台
第二个表示(假设 hosts
为 all
有 10 台):
- 第一次队列执行
all
的 10% 的主机(1 台) - 第二次队列执行
all
的 20% 的主机(2 台) - 第三次队列执行
all
的剩余所有主机(7 台)
列表还可以组合使用:
- name: test
hosts: all
serial:
- 1
- 5
- 20%
第一次 1 台,第二次 5 台,后续每次 20%。
限制单个任务执行数量
forks
和 serial
都是在 Playbook 级别限制,如果想在一个 tasks
上限制可以使用 throttle
,不过这个可能占用大量 CPU 资源。
throttle
支持在 tasks
和 block
上设置。
tasks:
- command: /path/to/cpu_intensive_command
throttle: 1
如果将
throttls
和forks
或serial
同时使用,则throttle
要小于它俩。
设置主机执行顺序
通过 order
可以设置主机执行顺序,order
有如下几个选项:
inventory
:根据 Ansible 编译后的主机清单执行,主机顺序可预测但不可重现reverse_inventory
:和inventory
相反sorted
:根据字母顺序排序reverse_sorted
:跟sorted
相反shuffle
:随机排序
- name: test
hosts: all
order: sorted
设置任务只执行一次
通过 run_once
可以设置任务只执行一次(只在当前队列的第一台主机上执行
),通常与 delegate_to
组合使用,实现只在某个节点执行任务。
- name: run_once
ansible.builtin.debug:
msg: "run_once"
run_once: true