Playbook 核心组件
官方文档
https://docs.ansible.com/ansible/latest/reference_appendices/playbooks_keywords.
html#playbook-keywords
一个playbook 中有多个组件组成,其中所用到的常见组进类型如下:
- Hosts 执行的远程主机列表
- Tasks 任务集,由多个task的元素组成的列表实现,每个task是一个字典,一个完整的代码块功能最少需包括 name和task,一个name只能包括一个task
- Variables 内置变量或自定义变量在playbook中调用
- Templates 模板,可替换模板文件中的变量并实现一些简单逻辑的文件
- Handlers 和 notify 结合使用,由特定条件触发的操作,满足条件才执行,否则不执行
- tags 标签,指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常的长。此时,如果确信其没有变化,就可以通过tags跳过此代码片段
1.1 playbook 组件
1.1.1 hosts 组件
Hosts:playbook中的每一个play的目的都是为了让特定主机以某个指定的用户身份执行任务,hosts用于指定要执行指定任务的主机,须事先定义在主机清单中
Websrvs:dbsrvs #或者,两个组的并集
Websrvs:&dbsrvs #与,两个组的交集
webservers:!dbsrvs #在websrvs组,但不在dbsrvs组
#案例
- hosts: websrvs:appsrvs
1.1.2 remote_user 组件
remote_user:可用于host和task中,也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某个任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户
- hosts: websrvs
remote_user: root
tasks:
- name: test connection
ping:
remote_user: lee
sudo: yes #默认sudo为root
sudo_user: lee #sudo为lee
1.1.3 task列表和action组件
play的主体部分是task list,task list中有一个或多个task,各个task按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个task后,再开始第二个task
task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量,模块执行是幂等的,这意味着多次执行是安全的,因为结果均一致
每个task都应该有其name,用于playbook的执行结果输出,具有其内容能清晰地描述任务执行步骤,如果未提供name,则action的结果将用于输出。
task的两种格式:
acion: module arguments # action: shell echo hello
module: arguments #建议使用此方式, shell: echo hello
注意: shell和command模块后面跟命令,而非key=value
范例:
[root@ansible playbook]#cat hello.yml
---
- hosts: web
remote_user: root
gather_facts: no #不收集系统信息,提高执行效率
tasks:
- name: test network connection
ping:
- name: Excute command
command: wall "hello,world, yaml!"
[root@ansible playbook]#cat install_httpd.yml
---
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
tasks:
- name: install httpd
yum: name=httpd
- name: start httpd
service: name=httpd state=started enabled=yes
[root@ansible playbook]#cat remove_httpd.yaml
---
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
tasks:
- name: remove httpd
yum: name=httpd state=absent
1.1.4 其他组件说明
某任务的状态再运行后为changed时,可通过 "notify" 通知给相应的handlers任务,还可以通过 "tags" 给task 打标签,可在ansible-playbook命令上使用 -t 指定进行调用
1.1.5 Shell 脚本 VS playbook 案例
#SHELL脚本实现
# 安装Apache
yum install --quiet -y httpd
# 复制配置文件
cp /tmp/httpd.conf /etc/httpd/conf/httpd.conf
cp/tmp/vhosts.conf /etc/httpd/conf.d/
# 启动Apache,并设置开机启动
systemctl enable --now httpd
#Playbook实现
---
- hosts: websrvs
remote_user: root
gather_facts: no
tasks:
- name: "安装Apache"
yum: name=httpd
- name: "复制配置文件"
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/
- name: "复制配置文件"
copy: src=/tmp/vhosts.conf dest=/etc/httpd/conf.d/
- name: "启动Apache,并设置开机启动"
service: name=httpd state=started enabled=yes
1.1.6 playbook 命令
格式
ansible-playbook [options] <filename.yml>
#常见选项
--syntax-check #语法检查,可缩写成--syntax,相当于bash -n
-C --check #模拟执行,只检测可能发生的改变,但不真正执行操作
--list-hosts #列出运行任务的主机
--list-tags #列出tag
--list-tasks #列出task
--limit 主机列表 #只针对主机列表中的特定主机执行
-i inventory #指定主机清单文件,通常一个项目对应一个主机清单文件
--start-at-task start_at_task #从指定task开始执行,而非从头开始,start_at_task为任务的name
-v -vv -vvv #显示过程
1.2 Playbook 初步
1.2.1 利用 playbook 创建mysql用户
---
- hosts: db
remote_user: root
gather_facts: no
tasks:
- name: create group
group: name=mysql system=yes gid=306
- name: create user
user: name=mysql shell=/sbin/nologin system=yes group=mysql uid=306 home/data/mysql create_home=no
1.2.2 利用playbook 安装 / 卸载 nginx
---
# install nginx
- hosts: web
remote_user: root
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
# 当前目录拷贝nginx.conf到files,并且创建index.html页面文件
- name: Config file
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
- name: web page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
---
# remove nginx
- hosts: web
remote_user: root
tasks:
- name: stop nginx
service: name=nginx state=stopped
- name: del user nginx
user: name=nginx state=absent
- name: remove Nginx
yum: name=nginx state=absent
- name: remove web page
file: name=/usr/share/nginx/index.html state=absent
- name: remove Config file
file: name=/etc/nginx/nginx.conf state=absent
1.2.3 忽略错误 ignore_errors
如果一个task出错,默认将不会继续执行后续的其他task
利用 ignore_errors: yes 可以忽略此task错误,继续向下执行其他task
---
- hosts: web
remote_user: root
ignore_errors: yes #忽略错误
tasks:
- name: error
command: /bin/false
- name: continue
command: wall continue
1.2.4 playbook 中使用handlers 和notify
Handlers 主要用于当关注的资源发生变化时,才会采取一定的操作,而notify对应的action可用于在每个play的最后被触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性的执行指定操作。
注意:
- 如果多个task通知了相同的handlers,此handlers仅会在所有task结束后运行一次
- 只有notifly对应的task发生改变了才会通知hangdlers,没有改变则不会触发handlers
- handlers 是在所有前面的task都成功执行才会执行,如果前面任何一个task失败,会导致handlers跳过执行,可以使用 force_handlers: yes 强制执行handler
---
# install nginx
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
force_handlers: yes # task中任何一个action失败,仍强制执行 handlers
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Config file
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
notify:
- Restart Nginx
- name: web page
copy: src=files/index1.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
handlers:
- name: Restart Nginx
service: name=nginx state=restarted
1.2.5 playbook中使用tags
在playbook文件中,可以利用tags组件,为特定 task 指定标签, 当在执行playbook时,可以只执行特定tags的task,而非整个 playbook文件
可以一个task对应多个tag,也可以多个task对应一个tag
---
# install nginx
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: no
force_handlers: yes
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Config file
copy: src=files/nginx.conf dest=/etc/nginx/nginx.conf
tags: conf # 指定tag 标签
notify:
- Restart Nginx
- name: web page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
handlers:
- name: Restart Nginx
service: name=nginx state=restarted
# 指定执行 tag标签 conf, 其他的task不会重复执行
[root@ansible playbook]#ansible-playbook -t conf install_nginx.yml
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>
TASK [Config file] </strong>***********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
RUNNING HANDLER [Restart Nginx] </strong>**********************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.0.7 : ok=2 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.6 playbook中使用变量
playbook中同样支持变量
变量名: 仅能由字母,数字和下划线组成,且只能以字母开头
变量调用方式:
通过 {{ variable_name }} 调用变量,且变量名前后建议加空格,有时用"{{ variable_name }}" 才生效
变量来源:
- ansible 的 setup模块 远程主机的所有变量都可以直接调用
- 通过命令行指定变量,优先级最高
ansible-playbook -e varname=value test.yml
- 在playbook文件中定义
- 在独立的变量yaml 文件中定义
- 在主机清单文件中定义
- 在项目中针对主机和主机组定义
- 在role中定义
变量的优先级从高到低如下
-e 选项定义变量 --> playbook中vars_files --> playbook中vars变量定义 --> host_vars/主机名文件 --> 主机清单中主机变量 --> group_vars/主机组名文件 --> group_vars/all文件 --> 主机清单组文件
1.2.6.1 使用setup 模块中的变量
[root@ansible ~]#ansible 10.0.0.27 -m setup -a "filter=ansible_default_ipv4"
10.0.0.27 | SUCCESS => {
"ansible_facts": {
"ansible_default_ipv4": {
"address": "10.0.0.27",
"alias": "eth0",
"broadcast": "10.0.0.255",
"gateway": "10.0.0.2",
"interface": "eth0",
"macaddress": "00:0c:29:aa:78:a9",
"mtu": 1500,
"netmask": "255.255.255.0",
"network": "10.0.0.0",
"type": "ether"
},
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false
}
[root@ansible playbook]#vim var1.yml
---
- hosts: web
remote_user: root
gather_facts: yes # 默认 yes,可不写
tasks:
- name: create log file
file: name=/data/{{ ansible_nodename }}.log state=touch
1.2.6.2 在playbook命令行中定义变量,-e指定变量
[root@ansible playbook]#vim var2.yml
---
- hosts: web:!10.0.0.202
remote_user: root
tasks:
- name: install package
yum: name={{ pkname }} state=present
[root@ansible playbook]#ansible-playbook -e pkname=vsftpd var2.yml
1.2.6.3 在playbook文件中定义变量
[root@ansible playbook]#vim var3.yml
---
- hosts: web:!10.0.0.202
remote_user: root
vars:
username: user1
groupname: group1
tasks:
- name: Create {{ groupname }}
group: name={{ groupname }} state=present
- name: Create {{ username }}
user: name={{ username }} group={{ groupname }} state=present
# -e 命令行指定的变量优先级更高
[root@ansible playbook]#ansible-playbook -e "username=user2 groupname=group2" var3.yml
1.2.6.4 使用变量文件
可以在一个独立的playbook文件中定义变量,在另一个playbook文件中引用文件中的变量,比playbook中定义的变量优先级高
[root@ansible playbook]#vim vars.yml
---
var1: vsftpd
var2: httpd
# var4 中引用 vars文件中的变量
[root@ansible playbook]#vim var4.yml
---
- hosts: web:!10.0.0.202
vars_files: vars.yml
tasks:
- name: touch apps dir
file: path=/apps state=directory
- name: Create vsftpd log
file: name=/apps/{{ var1 }}.log state=touch
- name: Create httpd log
file: name=/apps/{{ var2 }}.log state=touch
1.2.6.5 针对主机和主机组的变量
- 在主机清单中针对所有项目的主机和主机组定义变量
所有项目的主机变量
在inventory 主机清单文件中为指定的主机定义变量,在playbook中使用
[root@ansible playbook]#vim /etc/ansible/hosts
[web]
10.0.0.7 hname=www1 domain=magedu.io # 为单个主机设置变量
10.0.0.17 hname=www2
10.0.0.202
[web:vars] # 给web组设置变量
mark="-"
[all:vars] # 为所有组设置变量
domain=magedu.org
[root@ansible playbook]#ansible web -m hostname -a 'name={{ hname }}{{ mark }}{{ domain }}'
10.0.0.202 | FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: 'hname' is undefined"
}
10.0.0.17 | CHANGED => {
"ansible_facts": {
"ansible_domain": "org",
"ansible_fqdn": "www2-magedu.org",
"ansible_hostname": "www2-magedu",
"ansible_nodename": "www2-magedu.org",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"name": "www2-magedu.org"
}
10.0.0.7 | CHANGED => {
"ansible_facts": {
"ansible_domain": "io",
"ansible_fqdn": "www1-magedu.io",
"ansible_hostname": "www1-magedu",
"ansible_nodename": "www1-magedu.io",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"name": "www1-magedu.io"
}
- 针对当前项目的主机和主机组的变量
生产建议在项目目录中创建额外的两个变量目录,分别是 host_vars 和 group_vars
host_vars下面的文件名和主机清单主机名一致,针对单个主机进行变量定义,格式:host_vars/hostname
group_vars下面的文件名和主机清单中组名一致,针对单个组进行变量定义,格式:group_vars/groupname
group_vars/all 文件内定义的变量对所有组都有效
[root@ansible ansible]#tree
.
├── group_vars
│ ├── all
│ └── web
├── host_vars
│ ├── 10.0.0.17
│ └── 10.0.0.7
└── project_vars.yml
2 directories, 5 files
[root@ansible ansible]#cat host_vars/10.0.0.17
id: 2
[root@ansible ansible]#cat host_vars/10.0.0.7
id: 1
[root@ansible ansible]#cat group_vars/web
name: web
[root@ansible ansible]#cat group_vars/all
domain: magedu.cn
[root@ansible ansible]#vim project_vars.yml
---
- hosts: web:!10.0.0.202
remote_user: root
tasks:
- name: Get variable
command: echo "{{name}}{{id}}.{{domain}}"
register: result # 定义一个名为 result变量
- name: print variable
debug:
msg: "{{result.cmd}}"
[root@ansible ansible]#ansible-playbook project_vars.yml
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>
TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.17]
ok: [10.0.0.7]
TASK [Get variable] </strong>**********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
TASK [print variable] </strong>********************************************************************************************************************<strong>
ok: [10.0.0.7] => {
"msg": [
"echo",
"web1.magedu.cn"
]
}
ok: [10.0.0.17] => {
"msg": [
"echo",
"web2.magedu.cn"
]
}
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.0.7 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.6.6 register 注册变量
在playbook中可以使用register将捕获的输出保存在临时变量中,然后使用 debug 模块进行显示输出
范例: 利用debug 模块输出变量
[root@ansible ansible]#vim register.yml
---
- hosts: web:!10.0.0.202
tasks:
- name: get variable
shell: hostname
register: name
- name: print variable
debug:
msg: "{{ name }}" #输出register注册的name变量的全部信息,注意变量要加" "引起来
#msg: "{{ name.cmd }}" #显示命令
#msg: "{{ name.rc }}" #显示命令成功与否
#msg: "{{ name.stdout }}" #显示命令的输出结果为字符串形式
#msg: "{{ name.stdout_lines }}" #显示命令的输出结果为列表形式
#msg: "{{ name.stdout_lines[0] }}" #显示命令的输出结果为列表中的第一个元素
#说明
第一个 task中,使用了 register注册变量名为 name,当shell模块执行完毕后,会将数据放到该变量中
第二个 task中,使用了 debug模块,并从变量 name中获取数据
[root@ansible ansible]#ansible-playbook register.yml
PLAY [web:!10.0.0.202] *********************************************************************************************************************<strong>
TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.17]
ok: [10.0.0.7]
TASK [get variable] </strong>**********************************************************************************************************************<strong>
changed: [10.0.0.7]
changed: [10.0.0.17]
TASK [print variable] </strong>********************************************************************************************************************<strong>
ok: [10.0.0.7] => {
"msg": {
"changed": true,
"cmd": "hostname", #显示命令
"delta": "0:00:00.005647",
"end": "2022-06-12 22:12:28.347194",
"failed": false,
"rc": 0, #显示命令成功与否
"start": "2022-06-12 22:12:28.341547",
"stderr": "", #显示命令的错误输出结果,为字符串形式
"stderr_lines": [], #显示命令的错误输出结果,为列表形式
"stdout": "www1-magedu.io", #显示命令的输出结果,为字符串形式
"stdout_lines": [ #显示命令的输出结果,为列表形式
"www1-magedu.io"
]
}
}
ok: [10.0.0.17] => {
"msg": {
"changed": true,
"cmd": "hostname",
"delta": "0:00:00.004464",
"end": "2022-06-12 22:12:28.440166",
"failed": false,
"rc": 0,
"start": "2022-06-12 22:12:28.435702",
"stderr": "",
"stderr_lines": [],
"stdout": "www2-magedu.org",
"stdout_lines": [
"www2-magedu.org"
]
}
}
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.17 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
10.0.0.7 : ok=3 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
范例: 使用 register 注册变量创建文件
[root@ansible ansible]#vim register1.yml
---
- hosts: 10.0.0.7
tasks:
- name: get variable
shell: hostname
register: name
- name: create file
file: dest=/data/{{ name.stdout }}.log state=touch
[root@ansible ansible]#ansible-playbook register1.yml
# register 和 debug 模块
[root@ansible apps]#vim debug_register.yml
---
- hosts: 10.0.0.202
tasks:
- name: get var1
shell: echo hello world
register: say_hi
- name: get var2
shell: "awk -F: 'NR==1{print $1}' /etc/passwd"
register: user
- debug: var=say_hi.stdout #自定义输出变量代替msg
- debug: var=user.stdout #自定义输出变量代替msg
[root@ansible apps]#ansible-playbook debug_register.yml
PLAY [10.0.0.202] **************************************************************************************************************************<strong>
TASK [Gathering Facts] </strong>*******************************************************************************************************************<strong>
ok: [10.0.0.202]
TASK [get var1] </strong>**************************************************************************************************************************<strong>
changed: [10.0.0.202]
TASK [get var2] </strong>**************************************************************************************************************************<strong>
changed: [10.0.0.202]
TASK [debug] </strong>*****************************************************************************************************************************<strong>
ok: [10.0.0.202] => {
"say_hi.stdout": "hello world"
}
TASK [debug] </strong>*****************************************************************************************************************************<strong>
ok: [10.0.0.202] => {
"user.stdout": "root"
}
PLAY RECAP </strong>*********************************************************************************************************************************
10.0.0.202 : ok=5 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
1.2.6.7 案例: 利用变量批量部署MySQL5.7 或8.0
[root@ansible ansible]#cat install_mysql5.7or8.0.yml
---
# install mysql-5.7.17-linux-glibc2.5-x86_64.tar.gz
# install mysql-8.0.19-linux-glibc2.12-x86_64.tar.xz
#
- hosts: db
remote_user: root
gather_facts: yes
vars:
mysql_version: 5.7.17
mysql_file: mysql-{{mysql_version}}-linux-glibc2.5-x86_64.tar.gz
mysql_root_password: 123456
tasks:
- name: install packages
yum: name=libaio,numactl-libs state=latest
- name: create mysql group
group: name=mysql gid=306
- name: create mysql user
user: name=mysql uid=306 group=mysql shell=/sbin/nologin system=yes create_home=no home=/data/mysql
- name: 复制MySQL压缩包到远程主机并解压缩到指定目录
unarchive: src=/data/files/{{ mysql_file }} dest=/usr/local/ owner=root group=root
- name: 创建软链接 /usr/local/mysql
file: src=/usr/local/mysql-{{mysql_version}}-linux-glibc2.5-x86_64 dest=/usr/local/mysql state=link
- name: data dir
shell: /usr/local/mysql/bin/mysqld --initialize-insecure --user=mysql --datadir=/data/mysql
tags: data
- name: Config my.cnf
copy: src=/data/files/my.cnf dest=/etc/my.cnf
- name: service script
shell: /bin/cp /usr/local/mysql/support-files/mysql.server /etc/init.d/mysqld
- name: Path variable
copy: content='PATH=/usr/local/mysql/bin:$PATH' dest=/etc/profile.d/mysql.sh
- name: 使 Path variable生效
shell: /bin/source /etc/profile.d/mysql.sh
- name: enable service
shell: chkconfig --add mysqld;/etc/init.d/mysqld start
tags: service
- name: change password
shell: /usr/local/mysql/bin/mysqladmin -uroot password {{mysql_root_password}}
1.3 template 模板
模块是一个文本文件,可以做为生成文件的模板,并且模板中还可嵌套jinja2语法
template功能:可以根据和参考模块文件,动态生成相类似的配置文件
template文件必须存放于template目录下,且命名为 .j2 结尾
yaml / yml 文件需和template目录平级,目录结构如下示例:
./
├── temnginx.yml
└── templates
└── nginx.conf.j2
范例: 利用template 同步nginx配置文件
#准备 templates/nginx.conf.j2文件
[root@ansible ansible]#mkdir templates
[root@ansible ansible]#cp files/nginx.conf templates/nginx.conf.j2
[root@ansible ansible]#tree
.
├── ansible.cfg
├── files
│ ├── index.html
│ └── nginx.conf
├── install_nginx.yml
├── remove_nginx.yml
└── templates
└── nginx.conf.j2
[root@ansible ansible]#vim install_nginx.yml
---
# install nginx
- hosts: web:!10.0.0.202
remote_user: root
gather_facts: yes
force_handlers: yes
tasks:
- name: add group nginx
group: name=nginx state=present
- name: add user nginx
user: name=nginx state=present group=nginx
- name: Install Nginx
yum: name=nginx state=present
- name: Config file
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
tags: conf
notify:
- Restart Nginx
- name: web page
copy: src=files/index.html dest=/usr/share/nginx/html/index.html
- name: start Nginx
service: name=nginx state=started enabled=yes
handlers:
- name: Restart Nginx
service: name=nginx state=restarted
[root@ansible ansible]#vim templates/nginx.conf.j2
...
worker_processes {{ ansible_processor_vcpus+2 }};
...
1.3.1 template中使用流程控制 for 和 if
1.3.1.1 for 循环
格式
{% for i in EXPR %}
...
{% endfor %}
范例:
#templates/nginx.conf1.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost }}
}
{% endfor %}
#for.yml
---
- hosts: web
remote_user: root
vars:
nginx_vhosts:
- 81
- 82
- 83
tasks:
- name: template config
template: src=nginx.conf1.j2 dest=/data/nginx.conf
[root@ubuntu2004 data]#cat nginx.conf
server {
listen 81
}
server {
listen 82
}
server {
listen 83
}
范例: for 通过key=value读取变量值
#
[root@ansible ansible]#vim for1.yml
---
- hosts: web
remote_user: root
vars:
nginx_vhosts:
- listen: 8080
server_name: "web1.abc.com"
root: "/var/www/nginx/web1/"
- listen: 8081
server_name: "web2.abc.com"
root: "/var/www/nginx/web2/"
- {listen: 8082, server_name: "web3.abc.com",root: "/var/www/nginx/web3/"}
tasks:
- name: template config
template: src=for1.j2 dest=/data/for1.conf
[root@ansible ansible]#vim templates/for1.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost.listen }}
server_name {{ vhost.server_name }}
root {{ vhost.root }}
}
{% endfor %}
#执行结果
[root@ansible ansible]#ansible-playbook for1.yml --limit 10.0.0.202
[root@ubuntu2004 ~]#cat /data/for1.conf
server {
listen 8080
server_name web1.abc.com
root /var/www/nginx/web1/
}
server {
listen 8081
server_name web2.abc.com
root /var/www/nginx/web2/
}
server {
listen 8082
server_name web3.abc.com
root /var/www/nginx/web3/
}
1.3.1.2 if 条件判断
在模板文件使用 if 条件判断, 决定是否生成相关的配置信息
范例:
[root@ansible ansible]#vim if.yml
---
- hosts: web
remote_user: root
gather_facts: yes
vars:
nginx_vhosts:
- web1:
listen: 8080
root: "/var/www/nginx/web1/"
- web2:
listen: 8080
root: "/var/www/nginx/web2/"
- web3:
listen: 8080
server_name: "web3.abc.com"
root: "/var/www/nginx/web3/"
tasks:
- name: template config
template: src=if.conf.j2 dest=/data/if.conf
# if.conf.j2
[root@ansible ansible]#vim templates/if.conf.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhosts.listen }}
{% if vhosts.server_name is defined %}
server_name {{ vhosts.server_name }} #注意不缩进,顶格
{% endif %}
root {{ vhosts.root }} #注意不缩进,顶格
}
{% endfor %}
#生成的结果
[root@ubuntu2004 ~]#cat /data/if.conf
server {
listen 8080
root /var/www/nginx/web1/
}
server {
listen 8080
root /var/www/nginx/web2/
}
server {
listen 8080
server_name web3.abc.com
root /var/www/nginx/web3/
}
1.4 使用循环迭代
迭代:当有需要重复性执行的任务时,可以使用迭代机制
1.4.1 迭代 with_items (loop)
对迭代项的引用,固定内置变量名为 "item"
要在task中使用 with_items给定要迭代的元素类别
注意:ansible2.5版本后,可以用loop代替with_items
列表元素格式:
- 字符串
- 字典
范例:
# loop.yml
---
- hosts: web
remote_user: root
tasks:
- name: add server users
user: name={{ item }} state=present
loop: # ansible2.5版本前使用 with_items
- user1
- user2
- user3
#上面语句功能等同于下面的语句
- name: add server users
user: name=testuser1 state=present
- name: add several users
user: name=testuser2 state=present
- name: add several users
user: name=testuser3 state=present
1.4.2 迭代嵌套子变量
在迭代中,还可以嵌套子变量,关联多个变量在一起使用
示例: 批量创建用户
# 通过item 键值对取子变量
[root@ansible ansible]#vim items_user.yml
---
- hosts: web
tasks:
- name: add groups
group: name={{ item }} state=present
with_items:
- nginx
- mysql
- redis
- name: add users
user: name={{ item.user }} group={{ item.group }} uid={{ item.uid }} state=present
with_items:
- { user: 'nginx', group: 'nginx', uid: '80' }
- { user: 'mysql', group: 'mysql', uid: '3306' }
- { user: 'redis', group: 'redis', uid: '6379' }
1.4.3 until 循环
范例:
# until 为false时才会执行循环,为true则 退出循环
[root@ansible ansible]#vim until.yml
---
- hosts: localhost
gather_facts: false
tasks:
- debug: msg="until"
until: false
retries: 3 #默认值,重复3次
delay: 1 #延迟1S
1.4.4 with_lines 逐行处理
# with_lines 逐行处理
[root@ansible ansible]#vim with_lines.yml
- hosts: localhost
tasks:
- debug: msg={{ item }}
with_lines: free -h #类似 with_items集合
1.5 playbook 中使用 when
when语句可以实现条件测试
# when的列表形式表示and关系
[root@ansible ansible]#vim when.yml
---
#关闭Ubuntu版本的主机
- hosts: web
tasks:
- name: 关闭 Ubuntu 主机
reboot:
when:
- ansible_facts['distribution'] == "Ubuntu"
- ansible_facts['distribution_major_version'] == "20" # and关系
1.6 分组 block
当想在满足一个条件下,执行多个任务时,就需要分组,而不再每个任务都用when
[root@ansible ansible]#vim block.yml
---
- hosts: web
tasks:
- block:
- debug: msg="first"
- debug: msg="second"
when:
- ansible_facts['distribution'] == "CentOS"
- ansible_facts['distribution_major_version'] == "7"
[root@ansible ansible]#ansible-playbook block.yml
...
TASK [debug] *******************************************************************************************************************************<strong>
ok: [10.0.0.7] => {
"msg": "first"
}
ok: [10.0.0.17] => {
"msg": "first"
}
skipping: [10.0.0.202]
TASK [debug] </strong>*******************************************************************************************************************************
ok: [10.0.0.7] => {
"msg": "second"
}
ok: [10.0.0.17] => {
"msg": "second"
}
skipping: [10.0.0.202]
...
1.7 changed_when
检查task返回结果,决定是否继续向下执行
[root@ansible ansible]#vim changed_when.yml
---
- hosts: 10.0.0.17
remote_user: root
gather_facts: yes
tasks:
- name: install nginx
yum: name=nginx
- name: config file
template: src=nginx.conf.j2 dest="/etc/nginx/nginx.conf"
notify: restart nginx
- name: check config
shell: /usr/sbin/nginx -t
register: check_nginx_config
changed_when:
- (check_nginx_config.stdout.find('successful')) #如果/usr/sbin/nginx -t执行结果中有successful字符串,则继续执行,没有则停止向下执行
- false #/usr/sbin/nginx -t 每次成功执行是changed状态,关闭此change状态
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart nginx
service: name=nginx state=restarted
1.8 yaml 文件的相互调用
利用include 或 include_tasks 可以在某个task中调用其他的只有task内容的yaml 文件
[root@ansible ansible]#vim a.yml
---
- hosts: web
tasks:
- name: run a job
command: wall run a job
- name: join b.yml
include: b.yml #调用另一个只有task内容的yaml文件
[root@ansible ansible]#vim b.yml
- name: run b job
command: wall run b job
使用 import_playbook 将多个包含完整内容的yml文件由一个yml统一调用
[root@ansible ansible]#cat total_tasks.yml
- import_playbook: task1.yml
- import_playbook: task2.yml
[root@ansible ansible]#cat task1.yml task2.yml
---
- hosts: web
gather_facts: false
tasks:
- name: run task1 job
command: wall run task1 job
---
- hosts: web
gather_facts: false
tasks:
- name: run task2 job
command: wall run task2 job
2 roles 角色
roles就是通过分别将变量、文件、任务、模板及处理器放置在单独的目录中,并可以使用便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,也可以用于构建守护进程等场景中。
roles: 多个角色的集合目录,可以将多个role,分别放在roles目录下的独立子目录中,代码复用度高
[root@ansible ansible]#tree roles/
roles/
├── mysql
│ ├── files
│ │ └── main.yml
│ ├── handlers
│ │ └── main.yml
│ ├── services
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ │ └── main.yml
│ └── vars
│ └── main.yml
└── nginx
├── files
│ └── main.yml
├── handlers
│ └── main.yml
├── services
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
│ └── main.yml
└── vars
└── main.yml
默认存放roles路径
/root/.ansible/roles
/usr/share/ansible/roles
/etc/ansible/roles
2.1 创建 role
- 创建roles的目录结构,在以roles命名目录下分别创建以各角色名称命名的目录,如nginx,mysql等,在每个角色命名的目录中分别创建相关目录和文件,如tasks、files、handlers、templates、vars等目录;
- 编写和准备role的功能文件
- 编写playbook文件调用需要的角色应用于指定的主机
[root@ansible ansible]#mkdir roles/{nginx,mysql}/{tasks,files,templates,vars,handlers} -pv
[root@ansible ansible]#touch roles/{nginx,mysql}/{tasks,vars,templates,files,handlers}/main.yml
2.2 playbook 调用角色
[root@ansible ansible]#vim role.yml # 和roles目录平级创建调用角色文件
---
- hosts: localhost
roles:
- nginx
- mysql
2.3 案例: nginx 角色
#创建task文件
[root@ansible nginx]#cat tasks/main.yml
- include: group.yml
- include: user.yml
- include: install.yml
- include: config.yml
- include: data.yml
- include: service.yml
[root@ansible nginx]#cat tasks/user.yml
- name: add user nginx
user: name=nginx state=present group=nginx
[root@ansible nginx]#cat tasks/install.yml
- name: Install Nginx
yum: name=nginx state=present
[root@ansible nginx]#cat tasks/config.yml
- name: Config file
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
tags: conf
notify:
- Restart Nginx
[root@ansible nginx]#cat tasks/data.yml
- name: web page
copy: src=index.html dest=/usr/share/nginx/html/index.html
[root@ansible nginx]#cat tasks/service.yml
- name: start Nginx
service: name=nginx state=started
#创建handlers文件
[root@ansible nginx]#cat handlers/main.yml
- name: Restart Nginx
service: name=nginx state=restarted
#创建file文件,也可以跨角色调用文件
[root@ansible nginx]#cat files/index.html
<h1> nginx websize </h1>
#创建templates文件
[root@ansible nginx]#vim templates/nginx.conf.j2
...省略...
user {{ web_user }};
worker_processes {{ ansible_processor_vcpus+2 }};
...省略...
#创建vars变量文件
[root@ansible nginx]#cat vars/main.yml
web_user: nginx
#目录结构如下
[root@ansible nginx]#tree
.
├── files
│ └── index.html
├── handlers
│ └── main.yml
├── tasks
│ ├── config.yml
│ ├── data.yml
│ ├── group.yml
│ ├── install.yml
│ ├── main.yml
│ ├── service.yml
│ └── user.yml
├── templates
│ └── nginx.conf.j2
└── vars
└── main.yml
#在playbook中调用角色
[root@ansible ~]#vim /data/ansible/role_nginx.yml
---
# nginx role
- hosts: web
roles:
- nginx