官当
https://ansible-tran.readthedocs.io/en/latest/docs/playbooks.html
https://docs.ansible.com/ansible/latest/getting_started/get_started_playbook.html
1. Ansible Playbook
1.1 Playbook简介
Playbook
与ad-hoc
相比,是一种完全不同的运用ansible的方式,类似与saltstack
的state
状态文件。ad-hoc
无法持久使用,playbook
可以持久使用。
playbook
是由一个或多个play
组成的列表,play
的主要功能在于将事先归并为一组的主机装扮成事先通过ansible
中的task
定义好的角色。从根本上来讲,所谓的task
无非是调用ansible
的一个module
。将多个play
组织在一个playbook
中,即可以让它们联合起来按事先编排的机制完成某一任务
1.2 Playbook核心元素
- hosts 执行的远程主机列表
- tasks 任务集
- varniables 内置变量或自定义变量在playbook中调用
- templates 模板,即使用模板语法的文件,比如配置文件等
- handlers 和notity结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
- tags 标签,指定某条任务执行,用于选择运行playbook中的部分代码。
1.3 Playbook语法
playbook
使用yaml
语法格式,后缀可以是yaml
,也可以是yml
。
- 在单一一个
playbook
文件中,可以连续三个连子号(---
)区分多个play
。还有选择性的连续三个点好(...
)用来表示play
的结尾,也可省略。 - 次行开始正常写
playbook
的内容,一般都会写上描述该playbook
的功能。 - 使用#号注释代码。
- 缩进必须统一,不能空格和
tab
混用。 - 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行实现的。
YAML
文件内容和Linux
系统大小写判断方式保持一致,是区分大小写的,k/v
的值均需大小写敏感k/v
的值可同行写也可以换行写。同行使用:分隔。v
可以是个字符串,也可以是一个列表- 一个完整的代码块功能需要最少元素包括
name: task
案例
# 创建playbook文件
[root@ansible ~/ansible]$ cat playbook01.yml
--- #固定格式
- hosts: node1 #定义需要执行主机
remote_user: root #远程用户
vars: #定义变量
http_port: 8088 #变量
tasks: #定义一个任务的开始
- name: create new file #定义任务的名称
file: name=/tmp/playtest.txt state=touch #调用模块,具体要做的事情
- name: create new user
user: name=test02 system=yes shell=/sbin/nologin
- name: install package
yum: name=httpd
- name: config httpd
template: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: #定义执行一个动作(action)让handlers来引用执行,与handlers配合使用
- restart apache #notify要执行的动作,这里必须与handlers中的name定义内容一致
- name: copy index.html
copy: src=/var/www/html/index.html dest=/var/www/html/index.html
- name: start httpd
service: name=httpd state=started
handlers: #处理器:更加tasks中notify定义的action触发执行相应的处理动作
- name: restart apache #要与notify定义的内容相同
service: name=httpd state=restarted #触发要执行的动作
# 创建playbook文件
[root@ansible ~/ansible]$ cat playbook01.yml
--- #固定格式
- hosts: node1 #定义需要执行主机
remote_user: root #远程用户
vars: #定义变量
http_port: 8088 #变量
tasks: #定义一个任务的开始
- name: create new file #定义任务的名称
file: name=/tmp/playtest.txt state=touch #调用模块,具体要做的事情
- name: create new user
user: name=test02 system=yes shell=/sbin/nologin
- name: install package
yum: name=httpd
- name: config httpd
template: src=./httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: #定义执行一个动作(action)让handlers来引用执行,与handlers配合使用
- restart apache #notify要执行的动作,这里必须与handlers中的name定义内容一致
- name: copy index.html
copy: src=/var/www/html/index.html dest=/var/www/html/index.html
- name: start httpd
service: name=httpd state=started
handlers: #处理器:更加tasks中notify定义的action触发执行相应的处理动作
- name: restart apache #要与notify定义的内容相同
service: name=httpd state=restarted #触发要执行的动作
1.4 Playbook运行方式
通过ansible-playbook
命令运行
格式:ansible-playbook <filename.yml> ... [options]
[root@ansible ~/ansible]$ ansible-playbook -h
#ansible-playbook常用选项:
--check or -C #只检测可能会发生的改变,但不真正执行操作
--list-hosts #列出运行任务的主机
--list-tags #列出playbook文件中定义所有的tags
--list-tasks #列出playbook文件中定义的所以任务集
--limit #主机列表 只针对主机列表中的某个主机或者某个组执行
-f #指定并发数,默认为5个
-t #指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags)
-v #显示过程 -vv -vvv更详细
[root@ansible ~/ansible]$ ansible-playbook -h
#ansible-playbook常用选项:
--check or -C #只检测可能会发生的改变,但不真正执行操作
--list-hosts #列出运行任务的主机
--list-tags #列出playbook文件中定义所有的tags
--list-tasks #列出playbook文件中定义的所以任务集
--limit #主机列表 只针对主机列表中的某个主机或者某个组执行
-f #指定并发数,默认为5个
-t #指定tags运行,运行某一个或者多个tags。(前提playbook中有定义tags)
-v #显示过程 -vv -vvv更详细
2. 定义模版
cd /etc/ansible/roles
mkdir hello
cd hello
mkdir templates defaults files handlers meta tasks vars
cd /etc/ansible/roles
mkdir hello
cd hello
mkdir templates defaults files handlers meta tasks vars
3.模块
1.正则
2.lineinfile
---
- hosts: all
gather_facts: true
remote_user: root
tasks:
- name: "修改ssh配置文件的安全选项"
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?UseDNS'
line: 'UseDNS no'
notify:
- restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
---
- hosts: all
gather_facts: true
remote_user: root
tasks:
- name: "修改ssh配置文件的安全选项"
lineinfile:
path: /etc/ssh/sshd_config
regexp: '^#?UseDNS'
line: 'UseDNS no'
notify:
- restart sshd
handlers:
- name: restart sshd
service:
name: sshd
state: restarted
4. 流程控制
4.1 handler
在Ansible中,handler是一个特殊的任务,它不会在playbook的主执行流程中直接运行,而是根据某些条件被触发执行。这些条件通常是由其他任务(task)的状态变化触发的,比如当某个任务修改了目标系统的状态(例如文件、服务或配置等)。
handler的主要作用是允许您在playbook的末尾一次性地执行某些操作,而不是在每个任务之后都执行。这可以提高效率,并避免不必要的重复操作。
handler通常与notify关键字一起使用。当您在一个任务中定义了notify,并且该任务的状态发生了改变(例如从未改变变为已改变),那么与notify相关联的handler就会在playbook的末尾被触发。
- hosts: web
gather_facts: false
tasks:
- name: copy ngx conf file
copy:
src: /tmp/backend.conf
dest: /etc/nginx/conf.d/
backup: yes
notify:
- ngx reload
handlers:
- name: ngx reload
systemd:
name: nginx
state: reloaded
- hosts: web
gather_facts: false
tasks:
- name: copy ngx conf file
copy:
src: /tmp/backend.conf
dest: /etc/nginx/conf.d/
backup: yes
notify:
- ngx reload
handlers:
- name: ngx reload
systemd:
name: nginx
state: reloaded
4.2 when
在Ansible中,when关键字用于条件判断,根据特定的条件来决定是否执行某个任务(task)。当条件为真时,任务将会执行;当条件为假时,任务将被跳过。
when关键字的使用非常简单,只需在单个任务的后面添加when条件判断语句即可。在when语句中,变量不需要使用
表达式来包裹。
在Ansible中,when语句用于条件判断,其判断语法关键字和表达式与Python的语法非常相似。Ansible的when语句可以支持多种条件表达式和逻辑运算符,以下是一些常用的判断语法关键字和表达式:
1.比较运算符
符号 | 作用 |
---|---|
== | 相等 |
!= | 不等 |
> | 大于 |
< | 小于 |
>= | 大于等于 |
<= | 小于等于 |
- hosts: all
tasks:
- debug:
msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 1000MB {{ ansible_memfree_mb }}"
when: ansible_memfree_mb >= 1000
- hosts: all
tasks:
- debug:
msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 1000MB {{ ansible_memfree_mb }}"
when: ansible_memfree_mb >= 1000
2.逻辑运算符
符号 | 作用 |
---|---|
and | 逻辑与 |
or | 逻辑或 |
not | 逻辑否 |
- hosts: all
tasks:
- debug:
msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 200MB 小于1000MB {{ ansible_memfree_mb }}"
when: ansible_memfree_mb <= 1000 and ansible_memfree_mb >= 200
- hosts: all
tasks:
- debug:
msg: "当前主机名 {{ ansible_hostname }} 可用内存大于 200MB 小于1000MB {{ ansible_memfree_mb }}"
when: ansible_memfree_mb <= 1000 and ansible_memfree_mb >= 200
3.组合条件
可以使用括号()来组合多个条件,以改变运算的优先级
4.is判断
符号 | 作用 |
---|---|
is exists | 文件、目录存在 |
is not exists | 文件、目录不存在 |
is number | 判断是否为数字 |
is string | 判断是否为字符串 |
is defined | 判断变量是否已定义,已定义则返回真 |
is undefined | 判断变量是否未定义,未定义则返回真 |
is none | 判断变量的值是否为空,如果变量已定义且值为空,则返回真 |
is match | 判断正则是否匹配 |
is file | 判断指定路径是否为一个文件,是则为真 |
is directory | 判断指定路径是否为一个目录,是则为真 |
is link | 判断指定路径是否为一个软链接,是则为真 |
is mount | 判断指定路径是否为一个挂载点,是则为真 |
- host: all
tasks:
- name: add ngx yum repo
yum_repository:
name: nginx-stable
description: " nginx yum repo"
baseurl: "http://nginx.org/packages/centos/$releasever/$basearch/"
enable: yes
gpgcheck: no
file: nginx
when: ( ansible_hostname is match("web|lb") )
- host: all
tasks:
- name: add ngx yum repo
yum_repository:
name: nginx-stable
description: " nginx yum repo"
baseurl: "http://nginx.org/packages/centos/$releasever/$basearch/"
enable: yes
gpgcheck: no
file: nginx
when: ( ansible_hostname is match("web|lb") )
5. register
拿到执行命令结果
- name: Nginx Check
hosts: all
tasks:
- name: Check if Nginx is installed
command: ls /sbin/nginx
register: ngx_exist
ignore_errors: true
- name: ngx status
debug:
msg: "{{ ngx_exist }}"
- name: Nginx service failure
debug:
msg: ngx is not installed
when: ngx_exist is failure
- name: Nginx service success
debug:
msg: ngx is installed
when: ngx_exist is failure
- name: Nginx Check
hosts: all
tasks:
- name: Check if Nginx is installed
command: ls /sbin/nginx
register: ngx_exist
ignore_errors: true
- name: ngx status
debug:
msg: "{{ ngx_exist }}"
- name: Nginx service failure
debug:
msg: ngx is not installed
when: ngx_exist is failure
- name: Nginx service success
debug:
msg: ngx is installed
when: ngx_exist is failure
❌ 注意
要使用failure判断,就要开启ignore_errors。默认情况下ignore_errors为false,如果脚本tasks有错误,会忽略接下来的任务,也就会使playbook获取不到failed的结果。所以,要使用 is failure 一定要开启 ignore_errors
- name: Check if Tomcat Webapp Exists
stat:
path: /opt/tomcat/webapps/ROOT/WEB-INF/web.xml
register: tomcat_extracted
- name: Download Apache Tomcat
get_url:
url: "https://downloads.apache.org/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
when: not tomcat_extracted.stat.exists
- name: Extract Tomcat
unarchive:
src: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: /opt/tomcat
remote_src: true
extra_opts: ["--strip-components=1"]
when: not tomcat_extracted.stat.exists
- name: Check if Tomcat Webapp Exists
stat:
path: /opt/tomcat/webapps/ROOT/WEB-INF/web.xml
register: tomcat_extracted
- name: Download Apache Tomcat
get_url:
url: "https://downloads.apache.org/tomcat/tomcat-9/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
when: not tomcat_extracted.stat.exists
- name: Extract Tomcat
unarchive:
src: "/tmp/apache-tomcat-{{ tomcat_version }}.tar.gz"
dest: /opt/tomcat
remote_src: true
extra_opts: ["--strip-components=1"]
when: not tomcat_extracted.stat.exists
4.3 with_items
在Ansible中,with_items 是一个循环迭代模块,用于在playbook的任务中处理一个列表或字典中的元素。他将遍历一个列表或字典,并对每个元素执行一次任务。
with_items 后面跟着的是要处理的列表或字典数据。任务会按照列表顺序依次执行,每次迭代时,列表中的一个元素会被传递给任务作为变量(通常使用 来引用当前迭代的元素)。
1.迭代列表
- hosts: all
tasks:
- name: 创建多个目录
file:
path: "{{ item }}"
state: directory
with_items:
- /tmp/dir1
- /tmp/dir2
- /tmp/dir3
- hosts: all
tasks:
- name: 创建多个目录
file:
path: "{{ item }}"
state: directory
with_items:
- /tmp/dir1
- /tmp/dir2
- /tmp/dir3
2.迭代字典
- hosts: all
tasks:
- name: 创建多个目录
file:
path: "{{ item.path }}/{{ item.name}}"
state: touch
with_items:
- {path: /tmp/dir1, name: gg}
- {path: /tmp/dir2, name: uu}
- {path: /tmp/dir3, name: oo}
- hosts: all
tasks:
- name: 创建多个目录
file:
path: "{{ item.path }}/{{ item.name}}"
state: touch
with_items:
- {path: /tmp/dir1, name: gg}
- {path: /tmp/dir2, name: uu}
- {path: /tmp/dir3, name: oo}
4.4 loop
在Ansible Playbook中,loop关键字用于迭代一个列表或字典,并对每个元素执行一次特定的任务
通过ansible-doc -t lookup 查看文档
1.迭代列表
- hosts: all
tasks:
- name: Install packages
yum:
name: "{{ item }}"
state: present
loop:
- httpd
- mysql-server
- php
- hosts: all
tasks:
- name: Install packages
yum:
name: "{{ item }}"
state: present
loop:
- httpd
- mysql-server
- php
2.迭代字典
- hosts: all
tasks:
- name: Create directories
file:
path: "{{ item.path }}/{{ item.name}}"
state: touch
loop:
- { path: "/tmp/directory1", name: "file1.txt" }
- { path: "/tmp/directory2", name: "file2.txt" }
- { path: "/tmp/directory3", name: "file3.txt" }
- hosts: all
tasks:
- name: Create directories
file:
path: "{{ item.path }}/{{ item.name}}"
state: touch
loop:
- { path: "/tmp/directory1", name: "file1.txt" }
- { path: "/tmp/directory2", name: "file2.txt" }
- { path: "/tmp/directory3", name: "file3.txt" }
文档:
https://docs.ansible.com/ansible/2.9/user_guide/playbooks_conditionals.html