安装:
先安装EPEL源
yum install ansible -y
Ansible配置和测试
第一步是修改主机与配置组,文件位置 /etc/ansible/hosts, 格式是ini,添加两台主机IP 同时定义两个IP到webserver组 内容如下:
【/etc/ansible/hosts】
#web1.aa.com
#web2.aa.com
192.168.137.8
192.168.137.9
[webserver]
192.168.137.8
192.168.137.9
通过PING魔窟测试主机的连通性,分别对单主机及组进行PING操作 初夏如下结果 表示安装测试成功
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
[root@localhost ansible]
# ansible webserver -m ping -k
SSH password:
192.168.137.8 | SUCCESS => {
"changed"
:
false
,
"ping"
:
"pong"
}
192.168.137.9 | SUCCESS => {
"changed"
:
false
,
"ping"
:
"pong"
}
[root@localhost ansible]
#
#运行命令如下:
[root@localhost ansible]
# ansible webserver -a "uptime"
192.168.137.8 | SUCCESS | rc=0 >>
16:43:39 up 26 min, 1 user, load average: 0.03, 0.01, 0.00
127.168.137.9 | SUCCESS | rc=0 >>
16:43:40 up 49 min, 4
users
, load average: 0.14, 0.06, 0.01
[root@localhost ansible]
#
|
由于主控端与被控主机未配置SSH证书信任,需要在执行ansible命令时添加 -k 参数,要求提供root密码 即在提示 SSH password:时输入 很多人更倾向于使用Linux普通用户账户进行连接并使用sudo命令实现root权限,格式为:ansible webserver -m ping -u ansible -sudo.
为了避免Ansible下发指令时输入目标主机密码,通过证书签名达到SSH无密码是一个方案,推荐使用ssk-keygen 与 ssh-copy-id来实现快速证书的生成及公钥下发,其中ssh-keygen生成一对秘钥,使用ssh-copy-id来下发生成的公钥。具体操作如下:
在主控端主机创建秘钥 执行:ssk-keygen -t rsa, 有询问直接回车键即可 将在/root/.ssh目录下生成一对秘钥,其中 id_rsa为私钥,id_rsa.pub为公钥,公钥需要下发到被控主机用户的.ssh目录,同时要求重命名成authorized_keys文件。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[root@localhost ansible]
# ssh-keygen -t rsa -b 4096
Generating public
/private
rsa key pair.
Enter
file
in
which
to save the key (
/root/
.
ssh
/id_rsa
):
Created directory
'/root/.ssh'
.
Enter passphrase (empty
for
no passphrase):
Enter same passphrase again:
Your identification has been saved
in
/root/
.
ssh
/id_rsa
.
Your public key has been saved
in
/root/
.
ssh
/id_rsa
.pub.
The key fingerprint is:
36:ca:5e:14:6b:ef:ab:12:4d:82:be:33:d0:70:3c:f4 root@localhost
The key's randomart image is:
+--[ RSA 4096]----+
| |
| . |
| o o . |
| . = E .o |
| = . +S |
| . o..=.o |
| . .o.. . |
| +... . |
| o....o. |
+-----------------+
[root@localhost ansible]
#
|
接下来同步公钥文件id_rsa.pub到目标主机,推荐使用ssh-copy-id公钥拷贝工具,命令格式:
/usr/bin/ssh-copy-id [-i[identity_file]][user@]machine 本例中使用一下命令:
1
2
|
ssh
-copy-
id
-i
/root/
.
ssh
/id_rsa
.pub root@192.168.137.8
ssh
-copy-
id
-i
/root/
.
ssh
/id_rsa
.pub root@192.168.137.9
|
效验SSH无密码是否配置成功 ,运行 ssh root@192.168.137.8 如果直接进入目标root账号提示符,则署名配置成功。
定义主机与组规则
Ansible通过定义好的主机与组规则(Inventory)对匹配的目标主机进行远程操作,配置规则文件默认是/etc/ansible/hosts.
定义主机与组
所有定义的主机与组规则都在/etc/ansible/hosts文件中. 为ini文件格式 主机可以用域名、IP、别名进行标识,其中webserver、dbserver 为组名 紧跟着的主机为其成员 格式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
mail.example.com
192.168.1.21:2135
[webserver]
foo.example.com
bar.example.com
192.168.1.22
[dbserver]
one.example.com
two.example.com
three.example.com
192.168.1.23
|
其中 192.168.1.21:2135 的意思是定义一个SSH服务端口为2135的主机,当然我们也可以使用别名来描述一台主机。
jumper ansible_ssh_port=22 ansible_ssh_host=192.168.1.50
jumper为定义一个s别名,ansible_ssh_port为主机SSH服务端口,ansible_ssh_host为目标主机 更多保留主机变量如下:
ansible_ssh_host 目标主机地址
ansible_ssh_port 目标主机SSH端口 端口22无需指定
ansible_ssh_user 目标主机用户名
ansible_ssh_pass 目标主机密码
ansible_connection 目标主机连接类型 可以视local、ssh 或者paramiko
ansible_ssh_private_key_file 目标主机SSH私钥
ansible_*_interpreter 指定采用非Python的其他脚本语言 如 Ruby Perl 或者其他
组成员主机名称支持正则描述 示例如下:
1
2
3
4
5
|
[webserver]
www[01:20].example.com
[dbserver]
db-[a:f].example.com
|
定义主机变量
主机可以指定变量,以便后面供Playbooks配置使用 比如定义主机hosts1 和 hosts2 上Apache参数http_port 及 maxRequestsPerChild,目的是让两台主机产生Apache配置文件httpd.conf差异化,定义格式如下:
1
2
3
|
[atlanta]
host1 http_port=80 maxRequestsPerChild=808
host2 http_port=8080 maxRequestsPerChild=909
|
定义组变量
组变量的作用域是覆盖组所有成员,通过定义一个新块,块名由组名+":vars" 组成,定义格式如下:
1
2
3
4
5
6
7
|
[atlanta]
host1
host2
[atlanta:vars]
ntp_server=ntp.atlanta.example.com
proxy=proxy.atlanta.example.com
|
同时Ansible支持组嵌套组 ,通过定义一个新块,块名由组名+":children"组成,格式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
[atlanta]
host1
host2
[raleigh]
host2
host3
[southeast:children]
atlanta
raleigh
[southeast:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2
[usa:children]
southeast
northeast
southwest
southeast
|
分离主机与组特定数据
为了更好规范定义的主机与组变量,Ansible支持将/etc/ansible/hosts定义的主机名与组变量单独剥离出来存放到指定文件夹中.将采用YAML格式存放,存放位置规定: "/etc/ansible/group_vars/+组名" 和 "/etc/ansible/host_vars/+主机名" 分别存放指定组名或者主机名定义的变量。如下:
/etc/ansible/group_vars/dbservers
/etc/ansible/group_vars/webservers
定义的dbserver变量格式为:
[/etc/ansible/group_vars/dbservers]
---
ntp_server:acme.example.org
database_server:storage.example.org
Ansible常用模块
-
远程命令模块
1
2
3
|
ansible webserver -m
command
-a
"free -m"
#远程命令
ansible webserver -m script -a
"/home/test.sh"
#远程主机执行主控服务器脚本
ansible webserver -m shell -a
"/home/test.sh"
#执行远程主机脚本
|
2. copy模块
实现主控端想目标主机拷贝文件
以下例子实现拷贝/home/test.sh文件到webserver组目标主机/tmp目录下 并更新文件属主和权限
1
|
ansible webserver -m copy -a
"src=/home/test.sh dest=/tmp/ owner=root group=root mode=0755"
|
3. stat模块
获取远程文件状态信息,包括atime、ctime、mtime、MD5、uid、gid等信息
例子:
1
|
ansible webserver -m stat -a
"path=/etc/sysctl.conf"
|
4. get_url模块
实现在远程主机下载指定URL到本地 支持sha256sum文件效验
实例:
1
|
ansible webserver -m get_url -a
"url=http://www.baidu.com dest=/tmp/index.html mode=0440 force=yes "
|
5. yum功能
Linux平台软件包管理操作 常见的有yum apt 管理方式
实例 :
1
2
|
ansible webserver -m yum -a
"name=curl state=latest"
ansible webserver -m apt -a
"pkg=curl state=latest"
|
6. cron模块
远程主机crontab配置
实例:
1
|
ansible webserver -m
cron
-a
"'name=check dirs' hour='5,2' job='ls -alh > /dev/null'"
|
结果:
1
2
|
#Ansible: test_check
* 5,2 * * *
ls
-alh >
/root/ll
.txt
|
7. mount模块
远程主机分区挂载
实例:
1
|
ansible webserver -m
mount
-a
"name=/mnt/data src=/dev/sd0 fstype=ext3 opts=ro state=present"
|
8. service模块
远程主机系统服务管理
实例:
1
2
3
|
ansible webserver -m service -a
"name=nginx state=stopped"
ansible webserver -m service -a
"name=nginx state=restarted"
ansible webserver -m service -a
"name=nginx state=reloaded"
|
9. sysctl包管理模块
远程主机sysctl配置
实例:
1
|
ansible webserver -m sysctl -a "name=kernel.panic value=3 sysctl_file=
/etc/sysctl
.conf checks=before reload=yessalt
'*'
pkg.upgrade
|
10 . user模块
远程主机系统用户管理
实例:
1
2
|
ansible webserver -m user -a
"name=johnd comment='John Doe'"
#添加用户
ansible webserver -m user -a
"name=johnd state=absent remove=yes"
#删除用户
|
playbook介绍:
playbook是一个不同于使用ansible命令行执行方式的模式,其功能更强大灵活。 简单来说 playbook是一个非常简单的配置管理和多主机部署系统,不同于任何已经存在的模式,可作为合适部署复杂应用程序的基础。playbook可以定制配置,可以按指定的操作步骤有序执行,支持同步和异步方式,官方有提供大量的例子参考。https://github.com/ansible/ansible-examples 。playbook是通过YAML格式来进行描述定义的,可以实现多台主机的应用部署,定在在webserver及dbserver组上执行特定指令步骤。下面介绍一个实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
---
- hosts: webserver
vars:
worker_processes: 4
num_cpus: 4
max_open_file: 65506
root:
/data
remote_user: root
tasks:
- name ensure nginx is at the latest version
yum: pkg=nginx state=latest
- name: write the nginx config
file
template: src=
/home/test/nginx2
.conf dest=
/etc/nginx/nginx
.conf
notify:
- restart nginx
- name: ensure nginx is running
service: name=nginx start=started
handlers:
- name: restart nginx
service: name=nginx state=restarted
|
以上playbook定制了一个简单的Nginx软件包管理 内容包括安装 配置模板 状态管理等。下面进行说明。
定义主机和用户:
在playbook执行时,可以为主机或组定义变量,比如指定远程登录用户。一下为webserver组定义的相关变量,变量的作用域只限于webserver组下的主机。
1
2
3
4
5
6
|
- hosts: webserver
vars:
worker_processes: 4
num_cpus: 4
max_open_file: 65506
remote_user: root
|
hosts参数的作用为定义的操作对象,可以视主机或组,本示例定义的为webserver组,同事通过vars定义了4个变量 配置模板用到,其中remote_user 为制动远程操作的用户名,默认为root账号,支持sudo方式运行,通过添加sudo:yes即可。注意 remote_user参数在Ansible1.4或更高版本才引入。
任务列表:
所有定义的任务列表tasks list ,playbook将按定义的配置文件自上而下的顺序执行,定义的主机都将得到相同的任务,但执行的返回结果不一定保持一致,取决于主机的环境及程序包状态。建议每个任务事件都定义一个name标签增强可读性,也便于观察输出结果了解运行的位置,默认使用action(具体的执行动作)来替换name作为输入。下列是一个简单的任务定义示例:
1
2
3
|
tasks:
- name:
make
sure nginx is running
service: name=nginx state=started
|
功能实际检测Nginx服务是否处于运行状态,如果没有则启动。其中name标签对下面的action进行描述,action部分可以视ansible的任意模块,本示例为service模块,参数使用key=value的格式,如 name=httpd 在定义任务时也可以引用变量 格式如下:
1
2
3
|
tasks:
- name: create a virtual host
file
for
{{ vhost }}
template: src=somefile.j2 dest=
/etc/httpd/conf
.d/{{ vhost }}
|
在playbook可以通过template模块对本地配置模板文件进行渲染并同步到目标主机。已nginx配置文件为例,定义如下:
1
2
3
4
|
- name: write the nginx config
file
template: src=
/etc/ansible/nginx/nginx2
.conf dest=
/etc/nginx/nginx
.conf
notify:
- restart nginx
|
其中scr=/etc/ansible/nginx/nginx2.conf位管理端模板文件的存放位置 dest=/etc/nginx/nginx.conf为目标主机nginx配置的文件的存放位置,通过下面Nginx模板文件让大家对模板的定义有个基本的概念。
1
2
3
4
5
6
7
8
9
10
11
12
|
user nginx;
worker_processes {{ worker_processes}};
{%
if
num_cpus == 2 %}
worker_cpu_affinity 01 10;
{%
elif
num_cpus == 4 %}
worker_cpu_affinity 1000 0100 0010 0001;
{%
elif
num_cpus >= 8 %}
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
{%
else
%}
worker_cpu_affinity 1000 0100 0010 0001;
{% endif %}
worker_rlimit_nofile {{ max_open
file
}};
|
Ansible会根据定义好的模板渲染成真实的配置文件 ,模板使用YAML语法 最终生成的nginx.conf如下:
1
2
3
4
|
user nginx;
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;
worker_rlimit_nofile 65506;
|
档目标主机配置文件发生变化后,通知处理程序Handlers 来触发后面的动作,比如重启nginx服务。handlers中定义的处理程序没有通知触发时时不会执行的,触发后也只会运行一次。触发时通过Handlers定义的name标签来识别的,比如下面的notify中的 restart nginx 与handlers中的 name:restart nginx保持一致。
1
2
3
4
5
|
notify:
- restart nginx
handlers:
- name: restart nginx
service: name=nginx state=restarted
|
执行playbook
执行playbook可以通过ansible-playbook命令实现,格式: ansible-playbook playbookfiel.yml [参数] 如启用10个并行进程数执行playbook:
ansible-playbook /etc/ansible/nginx/nginx.yml -f 10
其他参数:
-u REMOTE_USER:手工指定远程执行playbook的系统用户
--syntax-check 检查playbook语法
--list-hosts playbooks 匹配到的主机列表
-T TIMEOUT 定义playbook执行超时时间
--setp 以单任务分步骤运行,方便做每一个不的确认工作。
更多参数运行 ansible-playbook -help获取。
以下是一个例子的执行结果 vsftpd.yml文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[root@localhost ansible]
# cat vsftpd.yml
---
- hosts: webserver
remote_user: root
tasks:
- name: Install Vsftpd Server
yum: pkg=vsftpd state=latest
notify:
- restart vsftpd
- name: Check Vsftpd is Running
service: name=vsftpd state=started
handlers:
- name: restart vsftpd
service: name=vsftpd stated=restarted
[root@localhost ansible]
#
|
执行结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
[root@localhost ansible]
# ansible-playbook vsftpd.yml -f 10
PLAY [webserver] ***************************************************************
TASK [setup] *******************************************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
TASK [Install Vsftpd Server] ***************************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
TASK [Check Vsftpd is Running] *************************************************
ok: [192.168.137.8]
ok: [192.168.137.9]
PLAY RECAP *********************************************************************
192.168.137.8 : ok=3 changed=0 unreachable=0 failed=0
192.168.137.9 : ok=3 changed=0 unreachable=0 failed=0
[root@localhost ansible]
#
|
角色定义
已经了解了变量,任务和处理程序的定义 有什么方法更好的进行组织或抽象 让其复用性更强、功能根据模块化?答案就是角色,角色是Ansible定制好的一种标准规范,以不同级别目录层次及文件对角色、变量、任务、处理程序等拆分、为后续功能扩展、可维护性打下基础 一个定向的角色目录结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
site.yml
webserver.yml
fooserver.yml
roles/
common/
files/
templates/
tasks/
handlers/
vars/
meta/
webserver/
files/
templates/
tasks/
handlers/
vars/
meta/
|
在playbook是这样引用的:
1
2
3
4
5
6
|
[site.yml]
---
- hosts:webserver
roles:
-common
-webserver
|
角色定制已下规范,其中X为角色名
如roles/x/tasks/main.yml 文件存在。其中列出的任务江北添加到执行队列
如roles/x/handlers/main.yml 文件存在 其中所列出的处理程序将被添加到执行队列;
如roles/x/vars/main.yml 文件存在 其中列出的变量将被添加到执行队列;
如roles/x/meta/main.yml 文件存在 所列任何作用的依赖关系将被添加到角色的列表
任何副本任务可以引用roles/x/files/无需写路径 默认相对或绝对医用;
任何脚本任务可以引用roles/x/files/无需写路径 默认相对或绝对医用;
任何模板任务可以引用文件中的roles/x/templates/无需写路径 默认相对或绝对医用;
为了便于更好的理解 以下示例以角色的形式存在。同时添加了一个公共角色common 从角色全局作用域中抽取出公共的部分,一般为系统的基础服务,比如 ntp iptables、selinux、sysctl等。本示例是针对ntp服务的管理
playbook目录结构
playbook目录包括变量定义目录group_vars、主机组定义文件hotst、全局配置文件site.yml 角色功能目录,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
|
[root@localhost playbook]
# tree nginx/
nginx/
├── group_Vars
├── hosts
├── roles
│ ├── common
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ └── ntp.conf.js
│ │ └── vars
│ │ └── main.yml
│ └── web
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ └── nginx2.conf
└── site.yml
11 directories, 9 files
[root@localhost playbook]
#
|
1.新建目录nginx
2.定义主机组
以下定义了一个业务组webserver 成员时两台主机
【nginx/hosts】
1
2
3
|
[webserver]
192.168.137.8
192.168.137.9
|
注意:这个非必选配置,默认将引用/etc/ansible/hosts的参数 角色中自定义组与主机文件将通过 -i file 命令行参数调用。如 ansible-playbook -i hosts 来调用
3.定义主机组或变量
group_vars为定义变量目录,目录当中的文件名要与组名保持一致,组变量文件定义的变量作为域值受限于该组,all代表所有主机
【nginx/group_vars/all】
1
2
|
---
nptserver:ntp.sjtu.edu.cn
|
【nginx/group_vars/webserver】
1
2
3
4
5
|
---
worker_processes:4
num_cpus:4
max_open_file:65536
root:
/data
|
4. 全局配置文件site.yml
全局配置文件引用了两个角色块,角色应用方位及实现的功能功都不一样:
【nginx/site.yml】
1
2
3
4
5
6
7
8
9
|
---
- name:apply common configuration to all nodes
hosts:all
roles:
- common
- name:configure and deploy the webserver and application code
hosts:webserver
roles:
- we
|
全局配置文件site.yml引用了两个角色,一个为公共类的common,另一个为web类,分别对应nginx/common 、nginx/web目录 以此类推,可以引用更多的角色,如dbserver、nosql、Hadoop等,前提是我们要先进行定义。通常情况下一个角色对应着一个特定功能服务。通过hosts参数来绑定角色对应的主机或组。
5. 角色common的定义
角色common定义了 handlers tasks templates vars 4个功能类,分别存放处理程序、任务列表、模板、变量的配置文件main.yml。需要注意的是 vars/main.yml中定义的变量优先级高于/nginx/group_vars/all定义的 可以从ansible-playbook的执行结果中得到验证。各功能模块配置文件如下:
[handlers/main.yml]
1
2
|
- name:restart ntp
service:name=ntpd state=restarted
|
[tasks/main.yml]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
- name:Install ntp
yum:name=ntp state=present
- name:Configure ntp
file
template:src=ntp.conf.js dest
/etc/ntp
.conf
notify:restart ntp
- name:Start the ntp service
service: name=ntpd state=started enabled=
true
- name:
test
to see
if
selinux is running
command
:getenforce
register:sestatus
changed_when:
false
|
其中template:src=ntp.conf.js引用是唔需要写路径。默认在上级的templates目录中查找。
[templates/ntp.conf.j2]
1
2
3
4
5
6
|
driftfile
/var/lib/ntp/drift
restrict 127.0.0.1
restrict -6 ::1
server {{ ntpserver }}
includefile
/etc/ntp/crypto/pw
keys
/etc/ntp/keys
|
此处` ntpserver `将引用vars/main.yml定义的ntpserver变量。
[vars/main.yml]
1
2
|
---
ntpserver:210.72.145.44
|
6. 角色WEB的定义
角色web定义了handlers tasks templates三个功能类 具体如下:
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
|
【handlers
/main
.yml】
- name: restart nginx
service: name=nginx state=restarted
【tasks
/main
.yml】
- name: Install nginx
yum: pkg=nginx state=latest
- name: write the nginx config
file
template: src=nginx2.conf dest=
/etc/nginx/nginx
.conf
notify:
- restart nginx
- name: check nginx is running
service:name=nginx state=started
【template
/nginx2
.conf】
user nginx;
worker_processes {{ worker_processes }};
{%
if
num_cpus == 2 %}
worker_cpu_affinity 01 10;
{%
if
num_cpus == 4 %}
worker_cpu_affinity 1000 0100 0010 0001;
{%
if
num_cpus >= 8 %}
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
{%
else
%}
worker_cpu_affinity 1000 0100 0010 0001;
{% endif %}
worker_rlimit_nofile {{ max_open_file }};
....
|
7 运行角色
cd /etc/ansible/playbook/nginx
ansible-playbook -i hosts site.yml -f 10
我自己的测试执行结果如下:
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
40
41
42
43
44
45
46
47
48
49
50
51
|
[root@localhost nginx]
# ansible-playbook -i hosts site.yml -f 10
PLAY [comment settings] ********************************************************
TASK [setup] *******************************************************************
ok: [192.168.137.8]
ok: [192.168.137.9]
TASK [common : Install ntp] ****************************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
TASK [common : write ntp config
file
] ******************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
TASK [common : start ntp server] ***********************************************
ok: [192.168.137.8]
ok: [192.168.137.9]
TASK [common : check selinux diabled] ******************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
PLAY [Deploy webserver] ********************************************************
TASK [setup] *******************************************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
TASK [web : Copy nginx yum repo
file
] ******************************************
ok: [192.168.137.8]
ok: [192.168.137.9]
TASK [web : Install Nginx Web Server] ******************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
TASK [web : write nginx config] ************************************************
ok: [192.168.137.8]
ok: [192.168.137.9]
TASK [web : check nginx running] ***********************************************
ok: [192.168.137.9]
ok: [192.168.137.8]
PLAY RECAP *********************************************************************
192.168.137.8 : ok=10 changed=0 unreachable=0 failed=0
192.168.137.9 : ok=10 changed=0 unreachable=0 failed=0
[root@localhost nginx]
#
|
目录结构和文件内容如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
├── group_vars
│ └── webserver
├── hosts
├── roles
│ ├── common
│ │ ├── handlers
│ │ │ └── main.yml
│ │ ├── tasks
│ │ │ └── main.yml
│ │ ├── templates
│ │ │ └── ntp.conf.j2
│ │ └── vars
│ │ └── main.yml
│ └── web
│ ├── files
│ │ └── nginx.repo
│ ├── handlers
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ └── nginx2.conf
└── site.yml
|
文件内容如下:
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
|
[root@localhost playbook]
# cat nginx/group_vars/webserver
---
worker_processes: auto
num_cpus: 2
max_open_file: 65536
root:
/data
[root@localhost playbook]
# cat nginx/hosts
[webserver]
192.168.137.8
192.168.137.9
[root@localhost playbook]
# cat nginx/roles/common/handlers/main.yml
- name: restart ntp
service: name=ntpd state=restarted
[root@localhost playbook]
# cat nginx/roles/common/tasks/main.yml
- name: Install ntp
yum: name=ntp state=present
- name: write ntp config
file
template: src=ntp.conf.j2 dest=
/etc/ntp
.conf
notify: restart ntp
- name: start ntp server
service: name=ntpd state=started enabled=
true
- name: check selinux diabled
command
: getenforce
register: sestatus
changed_when:
false
[root@localhost playbook]
# cat nginx/roles/common/templates/ntp.conf.j2
driftfile
/var/lib/ntp/drift
restrict 127.0.0.1
restrict -6 ::1
server {{ ntpserver }}
includefile
/etc/ntp/crypto/pw
keys
/etc/ntp/keys
[root@localhost playbook]
# cat nginx/roles/common/vars/main.yml
---
ntpserver: ntp.sjtu.edu.cn
[root@localhost playbook]
# cat nginx/roles/web/handlers/main.yml
- name: restart nginx
service: name=nginx state=restarted
[root@localhost playbook]
# cat nginx/roles/web/tasks/main.yml
- name: Copy nginx yum repo
file
copy: src=
/root/playbook/nginx/roles/web/files/nginx
.repo dest=
/etc/yum
.repos.d/ owner=root group=root mode=0755
- name: Install Nginx Web Server
yum: name=nginx state=latest
- name: write nginx config
template: src=nginx2.conf dest=
/etc/nginx/nginx
.conf
notify:
- restart nginx
- name: check nginx running
service: name=nginx state=started
[root@localhost playbook]
# cat nginx/roles/web/templates/nginx2.conf
user nginx;
worker_processes {{ worker_processes }};
{%
if
num_cpus == 2 %}
worker_cpu_affinity 01 10;
{%
elif
num_cpus == 4 %}
worker_cpu_affinity 1000 0100 0010 0001;
{%
elif
num_cpus >= 8 %}
worker_cpu_affinity 00000001 00000010 00000100 00001000 00010000 00100000 01000000 10000000;
{%
else
%}
worker_cpu_affinity 1000 0100 0010 0001;
{% endif %}
worker_rlimit_nofile {{ max_open_file }};
error_log
/var/log/nginx/error
.log warn;
pid
/var/run/nginx
.pid;
events {
worker_connections 204800;
use epoll;
multi_accept on;
}
http {
include
/etc/nginx/mime
.types;
default_type application
/octet-stream
;
log_format main
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'
;
access_log
/var/log/nginx/access
.log main;
sendfile on;
server_tokens off;
tcp_nopush on;
keepalive_timeout 10;
client_header_timeout 10;
client_body_timeout 10;
reset_timedout_connection on;
send_timeout 10;
#limit_conn_zone $binary_remote_addr zone=addr:10m;
#limit_conn addr 100;
gzip
on;
gzip_min_length 1000;
gzip_comp_level 4;
include
/etc/nginx/conf
.d/*.conf;
}
[root@localhost playbook]
# cat nginx/site.yml
---
- name: comment settings
hosts: all
roles:
- common
- name: Deploy webserver
hosts: webserver
roles:
- web
[root@localhost playbook]
#
|
获取远程主机系统信息:Facts
facts是一个非常有用的组件,类似于Saltstack的Grains功能,实现获取远程主机的系统信息,包括主机名,IP地址,操作系统,分区信息,硬件信息等。可以配合playbook实现更加个性化和灵活的要求。比如在httpd.conf模板中引用Facts的主机名信息作为ServerName参数的值。通过运行 ansible-playbook -m setup 可获取Facts信息 例如 获取192.168.137.8 的Facts信息需要运行
ansible-playbook 192.168.137.8 -m setup 结果如下:
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
[root@localhost ~]
# ansible 192.168.137.8 -m setup
192.168.137.8 | SUCCESS => {
"ansible_facts"
: {
"ansible_all_ipv4_addresses"
: [
"192.168.137.8"
],
"ansible_all_ipv6_addresses"
: [
"fe80::20c:29ff:fe7a:596d"
],
"ansible_architecture"
:
"x86_64"
,
"ansible_bios_date"
:
"05/20/2014"
,
"ansible_bios_version"
:
"6.00"
,
"ansible_cmdline"
: {
"KEYBOARDTYPE"
:
"pc"
,
"KEYTABLE"
:
"us"
,
"LANG"
:
"en_US.UTF-8"
,
"SYSFONT"
:
"latarcyrheb-sun16"
,
"crashkernel"
:
"auto"
,
"quiet"
:
true
,
"rd_LVM_LV"
:
"vg_serv/lv_root"
,
"rd_NO_DM"
:
true
,
"rd_NO_LUKS"
:
true
,
"rd_NO_MD"
:
true
,
"rhgb"
:
true
,
"ro"
:
true
,
"root"
:
"/dev/mapper/vg_serv-lv_root"
},
"ansible_date_time"
: {
"date"
:
"2016-07-26"
,
"day"
:
"26"
,
"epoch"
:
"1469546980"
,
"hour"
:
"23"
,
"iso8601"
:
"2016-07-26T15:29:40Z"
,
"iso8601_basic"
:
"20160726T232940834874"
,
"iso8601_basic_short"
:
"20160726T232940"
,
"iso8601_micro"
:
"2016-07-26T15:29:40.835071Z"
,
"minute"
:
"29"
,
"month"
:
"07"
,
"second"
:
"40"
,
"time"
:
"23:29:40"
,
"tz"
:
"CST"
,
"tz_offset"
:
"+0800"
,
"weekday"
:
"Tuesday"
,
"weekday_number"
:
"2"
,
"weeknumber"
:
"30"
,
"year"
:
"2016"
},
"ansible_default_ipv4"
: {
"address"
:
"192.168.137.8"
,
"alias"
:
"eth1"
,
"broadcast"
:
"192.168.137.255"
,
"gateway"
:
"192.168.137.1"
,
"interface"
:
"eth1"
,
"macaddress"
:
"00:0c:29:7a:59:6d"
,
"mtu"
: 1500,
"netmask"
:
"255.255.255.0"
,
"network"
:
"192.168.137.0"
,
"type"
:
"ether"
},
.......................省略
|
在模板文件中这样引用Facts信息
` ansible_device`.`sda`.`model `
` ansible_hostname `
Jinja2过滤器
jinja2是Python下一个广泛应用的模板引擎,他的设计类似于Django的模板引擎,并扩展了其语法和一系列强大的功能,官网地址 http://jinja.pocoo.org/ 简要说明Ansible 使用Jinja2强大的过滤器Filters功能
使用格式:{{ 变量名 | 过滤方法 }}
示例:获取一个文件路径变量过滤出文件名
{{ path | basename }}
获取文件所处的目录名:
{{ path | dirname }}
下面为一个完整的示例,实现从/etc/profile 中过滤出文件名 profile 并输出重定向到/tmp/testshell文件中
1
2
3
4
5
6
7
|
---
- hosts: 192.168.1.21
vars:
filename:
/etc/profile
tasks:
- name:
"shell"
shell:
echo
{{ filename |
basename
}} >>
/tmp/testshell
|
本地Facts
我们可以通过Facts来获取目标主机的系统信息,当这些信息不能满足我们功能需求时,可以通过编写自定义的facts模块来实现。当然 还有一个更简单的办法 就是通过本地facts来实现。 只需要在目标设备/etc/ansible/facts.d目录定义JSON、INI或可执行文件的JSON输出 文件扩展名使用“.fact” 这些文件都可以作为Ansible本地Facts 例如 在目标设备 192.168.137.9 定义三个变量 供以后playbook使用
【/etc/ansible/facts.d/preferences.fact】
[general]
max_memory_size=32
max_user_processes=3730
open_files=65535
在主控端运行ansible 192.168.1.21 -m setup -a "filter=ansible_local" 可以看到定义的结果 返回结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[root@localhost ~]
# ansible 192.168.137.9 -m setup -a "filter=ansible_local"
192.168.137.9 | SUCCESS => {
"ansible_facts"
: {
"ansible_local"
: {
"preferences"
: {
"general"
: {
"max_memory_size"
:
"32"
,
"max_user_processes"
:
"3730"
,
"open_files"
:
"65535"
}
}
}
},
"changed"
:
false
}
[root@localhost ~]
#
|
注册变量:
变量的另一个用途是将一条命令的运行结果保存到变量中,供后面的playbook使用 下面是一个简单的示例
1
2
3
4
5
6
7
|
- hosts: webserver
tasks:
- shell:
/usr/bin/foo
register: foo_result
ignore_errors:True
- shell:
/usr/bin/bar
when: foo_result.rc == 5
|
上述例子注册了一个foo_result的变量 值为shell:/usr/bin/foo的运行结果。ingore_error为忽略错误。变量注册完成后。就可以在后面的playbook中使用了。当条件语句when:foo_result.rc == 5成立时 shell:/usr/bin/bar命令才会运行,其中foo_result.rc为返回/usr/bin/foo的resultcode 返回码 例如 返回rc=0的返回码
1
2
3
4
5
6
7
8
|
[root@localhost ~]
# ansible 192.168.137.9 -a "free -m"
192.168.137.9 | SUCCESS | rc=0 >>
total used
free
shared buffers cached
Mem: 1989 601 1387 0 63 252
-/+ buffers
/cache
: 285 1704
Swap: 2015 0 2015
[root@localhost ~]
#
|
条件语句:
有时候一个playbook的结果取决于一个变量 或者取决于上一个任务的执行结果.在某些情况下 一个变量的值可以依赖于其他变量的值 当然也会影响Ansible的执行过程。
下面主要介绍when声明:
有时候我们想跳过某些主机的执行步骤 比如符合特定版本的操作系统将不安装某个软件。在Ansible中很容易做到这一点 通过when 子句实现 其中将引用jinja2表达式 如下示例:
1
2
3
4
|
tasks:
- name:
"shutdown Debian flavored systems"
command
:
/sbin/shutdown
-t now
when: ansible_os_family ==
"Debian"
|
通过定义任务的facts本地变量 ansible_os_family 操作系统版本名称是否为Debian 结果将返回BOOL类型值,为True时将执行上一条语句 command:/sbin/shutdown -t now 为False时该条语句都不会触发 。看另一个示例 通过判断一条命令执行结果做不同的处理。
1
2
3
4
5
6
7
8
9
10
|
tasks:
-
command
:
/bin/false
register: result
ingore_error: True
-
command
:
/bin/someting
when: result|failed
-
command
:
/bin/someting_else
when: result|success
-
command
:
/bin/still/someting
when: result|skipped
|
when:result|success的意思当变量result的执行结果为成功状态时 将执行/bin/someting_else命令 其他同理。其中success为ansible内部过滤器方法 返回True代表运行成功。
循环:
通常一个任务会做很多事情,例如创建大量的用户,安装很多包 或者重复特定步骤 直到某种结果条件为止.ansible为我们提供了此支持。示例如下:
1
2
3
4
5
|
- name: add several
users
user: name={{ item }} state=present
groups
=wheel
with_items:
- testuser1
- testuser2
|
这个示例实现了一个批量创建用户的功能 with_items会自动循环执行上面的语句 user: name=` item ` state=present groups=wheel 循环次数为with_items的元素个数。这里有两个元素 分别是testuser1 和 testuser2 会分别替换 ` item `项 这个示例与下面的语句是等价的:
1
2
3
4
|
- name: add user testuser1
user: name=testuser1 state=present
groups
=wheel
- name: add user testuser2
user: name=testuser2 state=present
groups
=wheel
|
当然 元素也支持字典的形式 如下:
1
2
3
4
5
|
- name: add several
users
user: name={{ item.name }} state=present
groups
={{ item.
groups
}}
with_items:
- { name:
'testuser1'
,
groups
:
'wheel'
}
- { name:
'testuser2'
,
groups
:
'root'
}
|
循环也支持list形式 不过是通过with_flattened语句实现 如下示例:
1
2
3
4
5
6
|
---
packages_base:
- [
'foo-packages'
,
'bar-packages'
]
packages_apps:
- [ [
'one-packages'
,
'two-packages'
] ]
- [ [
'red-packages'
],[
'blue-packages'
] ]
|
以上定义了两个列表变量,分别是需要安装的软件包名 以便后面如下引用:
1
2
3
4
5
|
- name: flattened loop demo
yum: name={{ item }} state=installed
with_flattened:
- packages_base
- packages_apps
|