死磕ansible系列--playbook

playbook是由一个或多个“play”组成的列表。play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联合起来按事先编排的机制完成某一任务。

playbook 分为四大任务块:

  • Target section: 定义将要执行playbook的远程主机组。
  • Variable section: 定义playbook运行时需要使用的变量。
  • Task section: 定义将要在远程主机上执行的任务列表。
  • Handler section: 定义task执行完成以后需要调用的任务。

一个完整的例子:

1
2
3
4
5
6
7
8
9
10
11
- hosts: mfs_node
user: root
vars:
- motd_warning: 'WARNING: Use by ACME Employees ONLY'
tasks:
- name: setup a MOTD
copy: dest=/etc/motd content="{{ motd_warning }}"
notify: say someting
handlers:
- name: say someting
command: echo "copy OK"

命令行执行

1
2
3
ansible-playbook -i inventory/devlop site.yaml
ansible-playbook -i inventory/devlop site.yaml -l linux-node2
ansible-playbook -i inventory/devlop -e target=gsg_tmp -e role=php56 role.yml

ansible-playbook 常用参数

  • -C :只显示哪些有变更,但是不执行任何参数。
  • -D: 通常和-C参数一起使用,显示变化的内容。
  • -t:匹配tags。
  • -e: 增加扩展的参数。
  • -v: 显示详细内容。
  • -vvv: 显示更详细的内容。

Target section

  • hosts:定义远程的主机组。
  • user:执行该任务组的用户。
  • remote_user:与user相同。
  • sudo:如果设置为yes,执行该任务组的用户在执行任务的时候,获取root权限。
  • sudo_user:如果你设置user为tom,sudo为yes,sudo_user为jerry,则tom用户则会获取jerry用户的权限。
  • connection:通过什么方式连接到远程主机,默认为ssh。
  • gather_facts:除非你明确说明不需要在远程主机上执行setup模块,否则默认会自动执行。如果你确实不需要setup模块所传递过来的变量,你可以启用该选项。

Variable section

vars
vars_files
vars_prompt
setup

Task section

task任务的编写方法

1
2
3
4
5
6
7
8
9
10
11
tasks:
- name: install apache
action: yum name=httpd state=installed #第一种方法

- name: configure apache
copy: src=files/httpd.conf dest=/etc/httpd/conf/httpd.conf #第二种方法

- name: restart apache
service:
name: httpd
state: restarted #第三种方法

task任务中的常用模块

template

template模块是Ansible中最常用的模块之一。它可以让你设计一个框架式的配置文件,如何把Anisble需要的值插入到合适的位置。其中Jinja2模板尤为复杂,其中可以包含条件、循环、宏。

1
2
3
tasks:
- name: copy config file to remote server
template: src=named.conf.j2 dest=/etc/named.conf

named.conf.j2 模板的内容

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
28
29
30
31
32
33
34
35
36
37
38
39
# {{ ansible_managed }}
options {
listen-on port 53 {
127.0.0.1;
{% for ip in ansible_all_ipv4_addresses %}
{{ ip }};
{% endfor %}
};
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
};
zone "." IN {
type hint;
file "named.ca";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
{# Variables for zone config #}

{% if 'authorativenames' in group_names %}
{% set zone_type = 'master' %}
{% set zone_dir = 'data' %}

{% else %}
{% set zone_type = 'slave' %}
{% set zone_dir = 'slaves' %}
{% endif %}

zone "internal.example.com" IN {
type {{ zone_type }};
file "{{ zone_dir }}/internal.example.com";

{% if 'authorativenames' not in group_names %}
masters { 192.168.2.2; };
{% endif %}
};

set_fact

set_fact模块可以让你在远程受管机器上执行脚本的过程中来计算我们需要的值,这些值可以被用在模板或者变量中。这些值有点类似setup模块中的参数,只不过setup模块是以单台主机为单位的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- hosts: mysqlservers
tasks:
- name: install MySql
yum: name=mysql-server state=installed
- name: Calculate InnoDB buffer pool size
set_fact: innodb_buffer_pool_size_mb="{{ ansible_memtotal_mb /2 }}"
- name: Configure MySQL
template: src=templates/my.cnf.j2 dest=/etc/my.cnf owner=root group=root mode=0644
notify: restart mysql
- name: Start MySQL
service: name=mysqld state=started enabled=yes

handlers:
- name: restart mysql
service: name=mysqld state=restarted
1
2
3
4
5
6
7
8
9
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
symbolic-links=0
innodb_buffer_pool_size = {{ innodb_buffer_pool_size_mb|default(128) }}M

[mysqld_safe]
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid

补充一个官方的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Example setting host facts using key=value pairs
- set_fact: one_fact="something" other_fact="{{ local_var * 2 }}"

# Example setting host facts using complex arguments
- set_fact:
one_fact: something
other_fact: "{{ local_var * 2 }}"

# As of 1.8, Ansible will convert boolean strings ('true', 'false', 'yes', 'no')
# to proper boolean values when using the key=value syntax, however it is still
# recommended that booleans be set using the complex argument style:
- set_fact:
one_fact: true
other_fact: false

pause

暂停模块可以让我们在playbook中暂停一段时间,可以指定一个时间段,或者提示用户继续。在命令行模式中,这没什么用,但是在playbook中,这很有用处。
暂停模块通常被用在当我们需要用户来提供一个确认来继续的时候,或者在一个特殊的时间点手动确认。比如你更新一个web应用程序之后,你需要用户在接受用户的连接之前,手工确认一切Ok。这也可以用来提示用户一些可能出现的问题,并提供选项继续。Ansible会打印出服务器的名字,要求用户确认之后继续。如果在目标选项中设置了串行参数,Ansible会询问组里面的每一个主机。这种方式可以让用户在部署的时候,灵活的控制整个节奏,并监控整个交互过程。

1
2
3
4
5
- name: wait on user input
pause: prompt="Warning! Detected slight issue. ENTER to continue CTRL-C to quit."

- name: timed wait
pause: seconds=30

wait_for

wait_for模块用来检测一个tcp端口是否准备好接收远程连接,这是由远程主机来完成的。如果你只指定了端口,或者主机参数被设置为localhost,它会尝试连接远程受管主机。你可以用local_action参数来指定从控制机器来运行命令,并使用ansible_hostname做为主机参数来连接远程受管主机。这个模块在后台运行某些程序,或者启动某些进程需要一些时间的时候特别有用。

1
2
3
4
5
6
7
8
9
10
- hosts: webapps
tasks:
- name: Install Tomcat
yum: name=tomcat6 state=installed

- name: Start Tomcat
service: name=tomcat6 state=started

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

assemble

assemble组装模块把多个受管主机的文件合并成一个文件,当配置文件不允许包含的时候,这非常有用,特别是在设置root用户的authorized_keys文件的时候。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- hosts: all

tasks:
- name: Make a Directory in /opt
file: path=/opt/sshkeys state=directory owner=root group=root mode=0700
- name: Copy SSH keys over
copy: src=keys/{{ item }}.pub dest=/opt/sshkeys/{{ item }}.pub owner=root group=root mode=0600
with_items:
- dan
- kate
- mal
- name: Make the root users SSH config directory
file: path=/root/.ssh state=directory owner=root group=root mode=0700
- name: Build the authorized_keys file
assemble: src=/opt/sshkeys dest=/root/.ssh/authorized_keys

add_host

add_host 添加主机模块是playbook中一个强大的模块,它可以让你动态的添加受管主机到一个play中。我们可以使用uri模块从CMDB中获得主机信息然后添加他们。它还可以将主机加到组里面,如果组不存在的话还会自动创建它。这个模块仅需要主机名和组名2个参数,跟主机库存清单的配置一样,我们还可以添加扩展的参数像ansible_ssh_user , ansible_ssh_port等等。

group_by

group_by 模块可以让我们根据主机的真实特性来进行分组,真实特性可以通过add_fact来实现(前面已经介绍过set_fact)。group_by模块只接受一个参数,key,同样组名的机器就被分到一个组里面。如果我们在这里使用变量,我们就可以把同一个操作系统类型、同一个拓扑结构的、或者其他我们希望的拥有同样特性的主机分成一组,组可以在子play中、模板中的目标选项被使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 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

get_url

将文件从HTTP,HTTPS或FTP下载到远程服务器。远程服务器必须能够直接访问远程资源。默认情况下,如果在目标主机上设置了环境变量 _proxy,那么请求将通过该代理发送。

1
2
3
4
5
- name: download foo.conf
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf mode=0440

- name: download file with sha256 check
get_url: url=http://example.com/path/file.conf dest=/etc/foo.conf sha256sum=b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c

debug

该模块在执行期间打印语句,可用于调试变量或表达式,而无需停止操作手册。用于与’when:’指令一起调试。

1
2
3
4
5
6
7
8
9
10
# Example that prints the loopback address and gateway for each host
- debug: msg="System {{ inventory_hostname }} has uuid {{ ansible_product_uuid }}"

- debug: msg="System {{ inventory_hostname }} has gateway {{ ansible_default_ipv4.gateway }}"
when: ansible_default_ipv4.gateway is defined

- name: Display all variables/facts known for a host
debug: var=hostvars[inventory_hostname]

debug: msg="The varibale is {{ info['stdout'] }}"

查看shell的输出

1
2
3
4
5
- name: check oenresty
shell: openresty -t
register: message
- debug: var=message.stdout_lines
- debug: var=message.stderr_lines

fail

该模块无法通过自定义消息取得进展。当使用何时满足某个条件时,它可以用于救援。

1
2
3
# Example playbook using fail and when together
- fail: msg="The system may not be provisioned according to the CMDB status."
when: cmdb_status != "to-be-staged"

Handler section

Handler 模块提供回调功能,在所有tasks任务执行完成后,才会执行Handler模块。

1
2
3
4
5
6
7
8
9
10
11
12
tasks:
- name: template configuration file
template: src=template.j2 dest=/etc/foo.conf
notify:
- restart memcached
- restart apache

handlers:
- name: restart memcached
service: name= memcached state=restarted
- name: restart apache
service: name=httpd state=restarted
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- hosts: all
user: root

tasks:
- name: install memcached
yum: name=memcached state=installed
- name: set memcached size
set_fact: memcached_size="{{ ansible_memtotal_mb /4 }}"
- name: copy configurations
template: src=templates/memcached.j2 dest=/etc/sysconfig/memcached
notify:
- restart memcached

handlers:
- name: restart memcached
service: name=memcached state=restarted enabled=yes

·Handlers只会在Play的末尾运行一次;如果想在一个Playbook的中间运行Handlers,则需要使用meta模块来实现,例如:-meta:flush_handlers。

·如果一个Play在运行到调用Handlers的语句之前失败了,那么这个Handlers将不会被执行。我们可以使用mega模块的–force-handlers选项来强制执行Handlers,即使是Handlers所在的Play中途运行失败也能执行。

serial 参数的作用

你可以定义Ansible可以在一个特定时间同时控制多少主机,使用serial 关键词:

1
2
3
- name: test play
hosts: webservers
serial: 3

在上面的例子,如果我们有100台主机,3 台主机定义在组’webservers’ 可以在下面3个主机开始之前完全完成。

这个serial关键词在Ansible 1.8 以后可以被定义为百分数,用来定义每一次操作一个play中百分之多少主机:

1
2
3
- name: test play
hosts: websevers
serial: "30%"

如果主机数不能被passes数量整除,最后的pass将会包含提醒信息。

ansible disable swap

https://germaniumhq.com/2019/02/14/2019-02-14-Disabling-Swap-for-Kubernetes-in-an-Ansible-Playbook/

参考文档

ansible-examples
ansible学习之四:Playbooks
galaxy ansible 分享role配置文件的路径
ansible-playbook command tools