Ansible Playbooks 基本功能介绍及示例代码

一、基本结构

用于安装或升级 apache 服务的 playbook 完整示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
---
- hosts: webservers
remote_user: root
vars:
http_port: 80
max_clients: 200
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
notify:
- restart apache
- name: ensure apache is running
service:
name: httpd
state: started
handlers:
- name: restart apache
service:
name: httpd
state: restarted

target

target 部分用于指定执行 playbook 任务时面向的目标主机和远程用户。

1
2
3
---
- hosts: webservers
remote_user: root

远程用户也可以在特定的 task 中额外进行定义:

1
2
3
4
5
6
7
---
- hosts: webservers
remote_user: root
tasks:
- name: test connection
ping:
remote_user: yourname

执行任务时允许远程用户通过 sudo 提升权限:

1
2
3
4
5
---
- hosts: webservers
remote_user: yourname
become: yes
become_method: sudo

执行任务时切换到其他用户身份:

1
2
3
4
5
---
- hosts: webservers
remote_user: yourname
become: yes
become_user: postgres

variable

variable 部分用于定义变量,作用域为当前 play

1
2
3
vars:
http_port: 80
max_clients: 200

通过 vars_files 定义变量:

1
2
3
4
vars_files:
conf/country-AU.yml
conf/datacenter-SYD.yml
conf/cluster-mysql.yml

交互式地从用户输入中获取变量的值。如将用户输入赋值给变量 https_passphrase

1
2
3
4
vars_prompt:
- name: https_passphrase
prompt: Key Passphrase
private: yes

task

task 部分包含了一系列我们希望在目标机器上执行的动作。

1
2
3
4
5
tasks:
- name: ensure apache is at the latest version
yum:
name: httpd
state: latest

handler

handler 与 task 有着相同的语法和类似的功能。但是 handler 只可以在特定的条件下由 task 调用后执行。

如配置文件替换成功后触发重启服务的动作:

1
2
3
4
5
6
7
8
9
10
11
12
tasks:
- name: copy the DHCP config
copy:
src: dhcp/dhcpd.conf
dest: /etc/dhcp/dhcpd.conf
notify: restart dhcp

handlers:
- name: restart dhcp
service:
name: dhcpd
state: restarted

可以在单个 task 中同时调用多个 handler:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
---
- hosts: qroud
tasks:
- name: checkout Qroud
git:
repo:git@github.com:smarthall/Qroud.git
dest: /opt/apps/Qroud force=no
notify:
- migrate db
- generate static
- restart httpd

handlers:
- name: migrate db
command: ./manage.py migrate –all
args:
chdir: /opt/apps/Qroud

- name: generate static
command: ./manage.py collectstatic -c –noinput
args:
chdir: /opt/apps/Qroud

- name: restart httpd
service:
name: httpd
state: restarted

二、playbook 模块

template

template 模块一般用于定义配置文件的主体框架,并为 Ansible 中的变量预留好位置以便在需要时渲染。类似于 Flask 应用依据模板文件动态地生成 HTML 文档。
其模板功能由 Jinja2 提供,支持条件语句、for 循环和宏等高级语法。

1
2
3
{% for ip in ansible_all_ipv4_addresses %}
{{ ip }};
{% endfor %}

1
2
3
4
5
tasks:
- name: write the apache config file
template:
src: /srv/httpd.j2
dest: /etc/httpd.conf
pause

pause 模块会将执行中的 playbook 暂停一段时间。比如部署了一个新版本的 web 应用,需要用户手动确认一切正常才能继续执行后面的任务。

1
2
3
4
5
6
7
8
9
---
- hosts: localhost
tasks:
- name: wait on user input
pause:
prompt: "Warning! Press ENTER to continue or CTRL-C to quit."
- name: timed wait
pause:
seconds: 30

wait_for

等待某个特定的 TCP 端口可以被远程主机访问连通。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
- hosts: webapps
tasks:
- name: Install Tomcat
yum:
name: tomcat7
state: latest

- name: Start Tomcat
service:
name: tomcat7
state: started

- name: Wait for Tomcat to start
wait_for:
port: 8080
state: started

group_by

group_by 模块可以在 task 中基于收集到的 facts 信息动态地对 hosts 进行分组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
---
- name: Create operating system group
hosts: all
tasks:
- group_by: key=os_{{ ansible_distribution }}

- name: Run on CentOS hosts only
hosts: os_CentOS
tasks:
- name: Install Apache
yum: name=httpd state=latest

- name: Run on Ubuntu hosts only
hosts: os_Ubuntu
tasks:
- name: Install Apache
apt: pkg=apache2 state=latest

三、Playbooks 进阶

Looping
1
2
3
4
5
6
7
8
9
10
11
tasks:
- name: Secure config files file
file:
path: "/etc/{{ item }}"
mode: 0600
owner: root
group: root
with_items:
- my.cnf
- shadow
- fstab
条件语句
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
---
- name: Install VIM
hosts: all
tasks:
- name: Install VIM via yum
yum:
name: vim-enhanced
state: latest
when: ansible_os_family == "RedHat"

- name: Install VIM via apt
apt:
name: vim
state: latest
when: ansible_os_family == "Debian"

- name: Unexpected OS family
debug:
msg: "OS Family {{ ansible_os_family }} is not supported"
fail: yes
when: ansible_os_family != "RedHat" and ansible_os_family != "Debian"
1
2
3
4
5
6
# Copy a file to the remote server if the hosts file doesn't exist
tasks:
- stat: path=/etc/hosts
register: hosts_file
- copy: src=path/to/local/file dest=/path/to/remote/file
when: not hosts_file.stat.exists
1
2
3
4
5
6
# Downgrade PHP version if the current version contains '7.0'.
tasks:
- shell: php --version
register: php_version
- shell: yum -y downgrade php*
when: "'7.0' in php_version.stdout"
注册变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- name: Using register
hosts: ansibletest
remote_user: root
tasks:
- name: Get /tmp info
file:
dest: /tmp
state: directory
register: tmp

- name: Set mode on /tmp/subtmp
file:
dest: /tmp/subtmp
mode: "{{ tmp.mode }}"
state: directory

debug 查看变量的值

1
2
3
4
- name: capture output of id command
command: id -un
register: login
- debug: msg="Logged in as user {{ login.stdout }}"

filter

Jinja2 模板提供的 filter 功能可以将原始数据转换为用户需要的格式

1
2
3
4
5
6
7
8
9
10
11
12
---
- name: Create user accounts
hosts: all
vars:
users:
tasks:
- name: Create accounts
user: name={{ item|lower }} state=present
with_items:
- Fred
- John
- DanielH

常用 filter:

  • min:参数为列表,返回列表中最小的项目
  • max:参数为列表,返回列表中最大的项目
  • random:参数为列表,随机返回列表中的某个值
  • default(x):指定变量不存在时,以 x 为该变量的默认值
  • unique:参数为列表,返回该列表去除重复项后的版本
  • replace(x, y):将字符串中出现的所有 x 替换为 y
  • join(x):参数为列表,返回由 x 拼接所有列表项后形成的字符串

针对文件路径的常用 filter:

  • basename:返回路径中包含的文件名
  • dirname:返回文件所在的目录
  • expanduser:将路径中包含的 ~ 替换为 home 目录
  • realpath:文件的真实路径
1
2
3
4
5
vars:
homepage: /usr/share/nginx/html/index.html
tasks:
- name: copy home page
copy: src=files/{{ homepage | basename }} dest={{ homepage }}
Lookup

lookups 允许 Ansible 从多种类型的源头(如文本文档、CSV 文件等)读取配置数据。

file:读取文本文件内容作为参数

1
2
- name: Add my public key as an EC2 key
ec2_key: name=mykey key_material="{{ lookup('file', '/Users/lorin/.ssh/id_rsa.pub') }}"

pipe:在远程机器上执行某个外部程序并关联其标准输出

1
2
- name: get SHA of most recent commit
debug: msg="{{ lookup('pipe', 'git rev-parse HEAD') }}"

env:获取远程机器上的某个环境变量

1
2
- name: get the current shell
debug: msg="{{ lookup('env', 'SHELL') }}"

password:生成随机密码,并将该密码写入指定文件

1
2
3
4
- name: create deploy postgres user
postgresql_user:
name: deploy
password: "{{ lookup('password', 'deploy-password.txt') }}"

redis-kv:获取 redis 服务器上某个 key 的值(需要在远程机器上安装 redis Python 库)

1
2
- name: look up value in Redis
debug: msg="{{ lookup('redis_kv', 'redis://localhost:6379,weather') }}"

复杂循环
名称 输入 循环规则
with_items 列表 遍历所有列表项
with_lines 命令 遍历命令输出的所有行
with_fileglob Glob 遍历文件(文件名可使用通配符)
with_first_found 路径列表 路径中检索到的第一个文件目标
with_dict 字典 遍历字典中的数据项
with_flattened 多层列表 遍历展开后的多层列表
with_inventory Host 模式 遍历匹配的主机

with_lines

1
2
3
4
5
# files/turing.txt
Leslie Lamport
Silvio Micali
Shafi Goldwasser
Judea Pearl

1
2
3
4
5
6
7
- name: Send out a slack message
slack:
domain: example.slack.com
token: "{{ slack_token }}"
msg: "{{ item }} was in the list"
with_lines:
- cat files/turing.txt

with_fileglob

1
2
3
4
5
- name: add public keys to account
authorized_key: user=deploy key="{{ lookup('file', item) }}"
with_fileglob:
- /var/keys/*.pub
- keys/*.pub

with_dict

1
2
3
- name: iterate over ansible_eth0
debug: msg={{ item.key }}={{ item.value }}
with_dict: "{{ ansible_eth0.ipv4 }}"

参考资料

Ansible Configuration Management - Second Edition