ansible是目前最受运维欢迎的自动化运维工具,基于Python开发,集合了众多运维工具(SaltStack puppet、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
ansible是基于 paramiko 开发的,并且基于模块化工作,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。ansible不需要在远程主机上安装client/agents,因为它们是基于ssh来和远程主机通讯的。ansible目前已经已经被红帽官方收购,是自动化运维工具中大家认可度最高的,并且上手容易,学习简单。是每位运维工程师必须掌握的技能之一。
部署简单,只需在主控端部署Ansible环境,被控端无需做任何操作;
默认使用SSH协议对设备进行管理;
有大量常规运维操作模块,可实现日常绝大部分操作;
配置简单、功能强大、扩展性强;
支持API及自定义模块,可通过Python轻松扩展;
通过Playbooks来定制强大的配置、状态管理;
轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
提供一个功能强大、操作性强的Web管理界面和REST API接口——AWX平台。
上图中我们看到的主要模块如下:
Ansible
:Ansible核心程序。HostInventory
:记录由Ansible管理的主机信息,包括端口、密码、ip等。Playbooks
:“剧本”YAML格式文件,多个任务定义在一个文件中,定义主机需要调用哪些模块来完成的功能。CoreModules
:核心模块,主要操作是通过调用核心模块来完成管理任务。CustomModules
:自定义模块,完成核心模块无法完成的功能,支持多种语言。ConnectionPlugins
:连接插件,Ansible和Host通信使用
Ansible 系统由控制主机对被管节点的操作方式可分为两类,即ad-hoc
和playbook
:
ad-hoc模式(点对点模式) 使用单个模块,支持批量执行单条命令。ad-hoc 命令是一种可以快速输入的命令,而且不需要保存起来的命令。就相当于bash中的一句话shell。
playbook模式(剧本模式) 是Ansible主要管理方式,也是Ansible功能强大的关键所在。playbook通过多个task集合完成一类功能,如Web服务的安装部署、数据库服务器的批量备份等。可以简单地把playbook理解为通过组合多条ad-hoc操作的配置文件。 类似于shell脚本,可以进行多个步骤
role模式(角色模式) 类似于多个脚本导入
简单理解就是Ansible在运行时, 首先读取ansible.cfg
中的配置, 根据规则获取Inventory
中的管理主机列表, 并行的在这些主机中执行配置的任务, 最后等待执行返回的结果。
加载自己的配置文件,默认/etc/ansible/ansible.cfg
;
查找对应的主机配置文件,找到要执行的主机或者组;
加载自己对应的模块文件,如 command;
通过ansible将模块或命令生成对应的临时py文件(python脚本), 并将该文件传输至远程服务器;
对应执行用户的家目录的.ansible/tmp/XXX/XXX.PY
文件;
给文件 +x 执行权限;
执行并返回结果;
删除临时py文件,sleep 0
退出;
ansible安装常用两种方式,yum 安装
和 pip 程序安装
。
yum 安装是我们很熟悉的安装方式了。我们需要先安装一个epel-release
包,然后再安装我们的 ansible 即可。
yum install epel-release -y yum install ansible –y
安装目录如下(yum安装): 配置文件目录:/etc/ansible/ 执行文件目录:/usr/bin/ Lib库依赖目录:/usr/lib/pythonX.X/site-packages/ansible/ Help文档目录:/usr/share/doc/ansible-X.X.X/ Man文档目录:/usr/share/man/man1/
ansible与我们其他的服务在这一点上有很大不同,这里的配置文件查找是从多个地方找的,顺序如下:
检查环境变量ANSIBLE_CONFIG
指向的路径文件(export ANSIBLE_CONFIG=/etc/ansible.cfg);
~/.ansible.cfg
,检查当前目录下的ansible.cfg配置文件;
/etc/ansible.cfg
检查etc目录的配置文件。
ansible 的配置文件为/etc/ansible/ansible.cfg
,ansible 有许多参数,下面我们列出一些常见的参数:
inventory = /etc/ansible/hosts #这个参数表示资源清单inventory文件的位置 library = /usr/share/ansible #指向存放Ansible模块的目录,支持多个目录方式,只要用冒号(:)隔开就可以 forks = 5 #并发连接数,默认为5 sudo_user = root #设置默认执行命令的用户 remote_port = 22 #指定连接被管节点的管理端口,默认为22端口,建议修改,能够更加安全 host_key_checking = False #设置是否检查SSH主机的密钥,值为True/False。关闭后第一次连接不会提示配置实例 timeout = 60 #设置SSH连接的超时时间,单位为秒 log_path = /var/log/ansible.log #指定一个存储ansible日志的文件(默认不记录日志)
在配置文件中,我们提到了资源清单,这个清单就是我们的主机清单,里面保存的是一些 ansible 需要连接管理的主机列表。我们可以来看看他的定义方式:
1、 直接指明主机地址或主机名: # green.example.com # blue.example.com # 192.168.100.1 # 192.168.100.10 2、 定义一个主机组[组名]把地址或主机名加进去 [mysql_test] 192.168.253.159 192.168.253.160 192.168.253.153 192.168.153.[199:202] 3. [web] web1 web2 [web:vars] ansible_ssh_root="root" ansible_ssh_pass="1" 4. [web] web1 web2 [db] db1 db2 [host:children] web db ansibel host --list-hosts
需要注意的是,这里的组成员可以使用通配符来匹配,这样对于一些标准化的管理来说就很轻松方便了。 我们可以根据实际情况来配置我们的主机列表,具体操作如下:
[root@server ~]# vim /etc/ansible/hosts [web] 192.168.37.122 192.168.37.133
/usr/bin/ansible
Ansibe AD-Hoc 临时命令执行工具,常用于临时命令的执行
/usr/bin/ansible-doc
Ansible 模块功能查看工具
/usr/bin/ansible-galaxy
下载/上传优秀代码或Roles模块 的官网平台,基于网络的/usr/bin/ansible-playbook
Ansible 定制自动化的任务集编排工具
/usr/bin/ansible-pull
Ansible远程执行命令的工具,拉取配置而非推送配置(使用较少,海量机器时使用,对运维的架构能力要求较高)
/usr/bin/ansible-vault
Ansible 文件加密工具
/usr/bin/ansible-console
Ansible基于Linux Console界面可与用户交互的命令执行工具
其中,我们比较常用的是/usr/bin/ansible
和/usr/bin/ansible-playbook
。
ansible-doc 命令常用于获取模块信息及其使用帮助,一般用法如下:
ansible-doc -l #获取全部模块的信息 ansible-doc -s MOD_NAME #获取指定模块的使用帮助
我们也可以查看一下ansible-doc的全部用法:
[root@server ~]# ansible-doc Usage: ansible-doc [options] [module...] Options: -h, --help show this help message and exit # 显示命令参数API文档 -l, --list List available modules #列出可用的模块 -M MODULE_PATH, --module-path=MODULE_PATH #指定模块的路径 specify path(s) to module library (default=None) -s, --snippet Show playbook snippet for specified module(s) #显示playbook制定模块的用法 -v, --verbose verbose mode (-vvv for more, -vvvv to enable # 显示ansible-doc的版本号查看模块列表: connection debugging) --version show program's version number and exit
我们可以来看一下,以mysql相关的为例:
[root@server ~]# ansible-doc -l |grep mysql mysql_db Add or remove MySQL databases from a remote... mysql_replication Manage MySQL replication mysql_user Adds or removes a user from a MySQL databas... mysql_variables Manage MySQL global variables [root@server ~]# ansible-doc -s mysql_user
命令的具体格式如下:
ansible[-f forks] [-m module_name] [-a args] ansible 主机清单中的主机组 -f 开启线程数 -m 模块 -a 模块需要的参数
也可以通过ansible -h
来查看帮助,下面我们列出一些比较常用的选项,并解释其含义:
-a MODULE_ARGS
#模块的参数,如果执行默认COMMAND的模块,即是命令参数,如: “date”,“pwd”等等-k
,--ask-pass
#ask for SSH password。登录密码,提示输入SSH密码而不是假设基于密钥的验证--ask-su-pass
#ask for su password。su切换密码-K
,--ask-sudo-pass
#ask for sudo password。提示密码使用sudo,sudo表示提权操作--ask-vault-pass
#ask for vault password。假设我们设定了加密的密码,则用该选项进行访问-B SECONDS
#后台运行超时时间-C
#模拟运行环境并进行预运行,可以进行查错测试-c CONNECTION
#连接类型使用-f FORKS
#并行任务数,默认为5-i INVENTORY
#指定主机清单的路径,默认为/etc/ansible/hosts
--list-hosts
#查看有哪些主机-m MODULE_NAME
#执行模块的名字,默认使用 command 模块,所以如果是只执行单一命令可以不用 -m参数-o
#压缩输出,尝试将所有结果在一行输出,一般针对收集工具使用-s
#用 sudo 命令-U SUDO_USER
#指定 sudo 到哪个用户,默认为 root 用户-T TIMEOUT
#指定 ssh 默认超时时间,默认为10s,也可在配置文件中修改-u REMOTE_USER
#远程用户,默认为 root 用户-v
#查看详细信息,同时支持-vvv
,-vvvv
可查看更详细信息
上面我们已经提到过 ansible 是基于 ssh 协议实现的,所以其配置公私钥的方式与 ssh 协议的方式相同,具体操作步骤如下:
#1.生成私钥 [root@server ~]# ssh-keygen #2.向主机分发私钥 [root@server ~]# ssh-copy-id [email protected] [root@server ~]# ssh-copy-id [email protected]
这样的话,就可以实现无密码登录,我们的实验过程也会顺畅很多。 注意,如果出现了一下报错:
-bash: ssh-copy-id: command not found
那么就证明我们需要安装一个包:
yum -y install openssh-clients
把包安装上即可。
我们使用ansible web -m ping
命令来进行主机连通性测试,效果如下:
[root@server ~]# ansible web -m ping 192.168.37.122 | SUCCESS => { "changed": false, "ping": "pong" } 192.168.37.133 | SUCCESS => { "changed": false, "ping": "pong" }
这样就说明我们的主机是连通状态的。接下来的操作才可以正常进行。
这个模块可以直接在远程主机上执行命令,并将结果返回本主机。 举例如下:
[root@server ~]# ansible web -m command -a 'ss -ntl' 192.168.37.122 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:111 *:* LISTEN 0 5 192.168.122.1:53 *:* LISTEN 0 128 *:22 *:* LISTEN 0 128 127.0.0.1:631 *:* LISTEN 0 128 *:23000 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 :::111 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 ::1:631 :::* LISTEN 0 100 ::1:25 :::* 192.168.37.133 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:111 *:* LISTEN 0 128 *:22 *:* LISTEN 0 128 127.0.0.1:631 *:* LISTEN 0 128 *:23000 *:* LISTEN 0 100 127.0.0.1:25 *:* LISTEN 0 128 :::111 :::* LISTEN 0 128 :::22 :::* LISTEN 0 128 ::1:631 :::* LISTEN 0 100 ::1:25 :::*
命令模块接受命令名称,后面是空格分隔的列表参数。给定的命令将在所有选定的节点上执行。它不会通过shell进行处理,比如$HOME和操作如"<",">","|",";","&" 工作(需要使用(shell)模块实现这些功能)。注意,该命令不支持| 管道命令。 下面来看一看该模块下常用的几个命令:
chdir # 在执行命令之前,先切换到该目录
executable # 切换shell来执行命令,需要使用命令的绝对路径
free_form # 要执行的Linux指令,一般使用Ansible的-a参数代替。
creates # 一个文件名,当这个文件存在,则该命令不执行,可以用来做判断
removes # 一个文件名,这个文件不存在,则该命令不执行
下面我们来看看这些命令的执行效果:
[root@server ~]# ansible web -m command -a 'chdir=/data/ ls' #先切换到/data/ 目录,再执行“ls”命令 192.168.37.122 | SUCCESS | rc=0 >> aaa.jpg fastdfs mogdata tmp web wKgleloeYoCAMLtZAAAWEekAtkc497.jpg 192.168.37.133 | SUCCESS | rc=0 >> aaa.jpg fastdfs mogdata tmp web wKgleloeYoCAMLtZAAAWEekAtkc497.jpg [root@server ~]# ansible web -m command -a 'creates=/data/aaa.jpg ls' #如果/data/aaa.jpg存在,则不执行“ls”命令 192.168.37.122 | SUCCESS | rc=0 >> skipped, since /data/aaa.jpg exists 192.168.37.133 | SUCCESS | rc=0 >> skipped, since /data/aaa.jpg exists [root@server ~]# ansible web -m command -a 'removes=/data/aaa.jpg cat /data/a' #如果/data/aaa.jpg存在,则执行“cat /data/a”命令 192.168.37.122 | SUCCESS | rc=0 >> hello 192.168.37.133 | SUCCESS | rc=0 >> hello
shell模块可以在远程主机上调用shell解释器运行命令,支持shell的各种功能,例如管道等。
[root@server ~]# ansible web -m shell -a 'cat /etc/passwd |grep "keer"' 192.168.37.122 | SUCCESS | rc=0 >> keer:x:10001:1000:keer:/home/keer:/bin/sh 192.168.37.133 | SUCCESS | rc=0 >> keer:x:10001:10001::/home/keer:/bin/sh
只要是我们的shell命令,都可以通过这个模块在远程主机上运行,这里就不一一举例了。
这个模块用于将文件复制到远程主机,同时支持给定内容生成文件和修改权限等。 其相关选项如下:
src
#被复制到远程主机的本地文件。可以是绝对路径,也可以是相对路径。如果路径是一个目录,则会递归复制,用法类似于"rsync"
content
#用于替换"src",可以直接指定文件的值
dest
#必选项,将源文件复制到的远程主机的绝对路径
backup
#当文件内容发生改变后,在覆盖之前把源文件备份,备份文件包含时间信息
directory_mode
#递归设定目录的权限,默认为系统默认权限
force
#当目标主机包含该文件,但内容不同时,设为"yes",表示强制覆盖;设为"no",表示目标主机的目标位置不存在该文件才复制。默认为"yes"
others
#所有的 file 模块中的选项可以在这里使用
用法举例如下: ① 复制文件:
[root@server ~]# ansible web -m copy -a 'src=~/hello dest=/data/hello' 192.168.37.122 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/hello", "gid": 0, "group": "root", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "mode": "0644", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512437093.55-228281064292921/source", "state": "file", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/hello", "gid": 0, "group": "root", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "mode": "0644", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512437093.74-44694985235189/source", "state": "file", "uid": 0 }
② 给定内容生成文件,并制定权限
[root@server ~]# ansible web -m copy -a 'content="I am keer\n" dest=/data/name mode=666' 192.168.37.122 | SUCCESS => { "changed": true, "checksum": "0421570938940ea784f9d8598dab87f07685b968", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "497fa8386590a5fc89090725b07f175c", "mode": "0666", "owner": "root", "size": 10, "src": "/root/.ansible/tmp/ansible-tmp-1512437327.37-199512601767687/source", "state": "file", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "checksum": "0421570938940ea784f9d8598dab87f07685b968", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "497fa8386590a5fc89090725b07f175c", "mode": "0666", "owner": "root", "size": 10, "src": "/root/.ansible/tmp/ansible-tmp-1512437327.55-218104039503110/source", "state": "file", "uid": 0 }
我们现在可以去查看一下我们生成的文件及其权限:
[root@server ~]# ansible web -m shell -a 'ls -l /data/' 192.168.37.122 | SUCCESS | rc=0 >> total 28 -rw-rw-rw- 1 root root 12 Dec 6 09:45 name 192.168.37.133 | SUCCESS | rc=0 >> total 40 -rw-rw-rw- 1 root root 12 Dec 5 09:45 name
可以看出我们的name文件已经生成,并且权限为666。 ③ 关于覆盖 我们把文件的内容修改一下,然后选择覆盖备份:
[root@server ~]# ansible web -m copy -a 'content="I am keerya\n" backup=yes dest=/data/name mode=666' 192.168.37.122 | SUCCESS => { "backup_file": "/data/name.4394.2017-12-06@09:46:25~", "changed": true, "checksum": "064a68908ab9971ee85dbc08ea038387598e3778", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "8ca7c11385856155af52e560f608891c", "mode": "0666", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512438383.78-228128616784888/source", "state": "file", "uid": 0 } 192.168.37.133 | SUCCESS => { "backup_file": "/data/name.5962.2017-12-05@09:46:24~", "changed": true, "checksum": "064a68908ab9971ee85dbc08ea038387598e3778", "dest": "/data/name", "gid": 0, "group": "root", "md5sum": "8ca7c11385856155af52e560f608891c", "mode": "0666", "owner": "root", "size": 12, "src": "/root/.ansible/tmp/ansible-tmp-1512438384.0-170718946740009/source", "state": "file", "uid": 0 }
现在我们可以去查看一下:
[root@server ~]# ansible web -m shell -a 'ls -l /data/' 192.168.37.122 | SUCCESS | rc=0 >> total 28 -rw-rw-rw- 1 root root 12 Dec 6 09:46 name -rw-rw-rw- 1 root root 10 Dec 6 09:45 name.4394.2017-12-06@09:46:25~ 192.168.37.133 | SUCCESS | rc=0 >> total 40 -rw-rw-rw- 1 root root 12 Dec 5 09:46 name -rw-rw-rw- 1 root root 10 Dec 5 09:45 name.5962.2017-12-05@09:46:24~
可以看出,我们的源文件已经被备份,我们还可以查看一下name
文件的内容:
[root@server ~]# ansible web -m shell -a 'cat /data/name' 192.168.37.122 | SUCCESS | rc=0 >> I am keerya 192.168.37.133 | SUCCESS | rc=0 >> I am keerya
证明,这正是我们新导入的文件的内容。
该模块主要用于设置文件的属性,比如创建文件、创建链接文件、删除文件等。 下面是一些常见的命令:
force
#需要在两种情况下强制创建软链接,一种是源文件不存在,但之后会建立的情况下;另一种是目标软链接已存在,需要先取消之前的软链,然后创建新的软链,有两个选项:yes|no
group
#定义文件/目录的属组。后面可以加上
mode
:定义文件/目录的权限
owner
#定义文件/目录的属主。后面必须跟上
path
:定义文件/目录的路径
recurse
#递归设置文件的属性,只对目录有效,后面跟上
src
:被链接的源文件路径,只应用于state=link
的情况
dest
#被链接到的路径,只应用于state=link
的情况
state
#状态,有以下选项:
directory
:如果目录不存在,就创建目录link
:创建软链接hard
:创建硬链接touch
:如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新其最后修改时间absent
:删除目录、文件或者取消链接文件
用法举例如下: ① 创建目录:
[root@server ~]# ansible web -m file -a 'path=/data/app state=directory' 192.168.37.122 | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/data/app", "size": 6, "state": "directory", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "gid": 0, "group": "root", "mode": "0755", "owner": "root", "path": "/data/app", "size": 4096, "state": "directory", "uid": 0 }
我们可以查看一下:
[root@server ~]# ansible web -m shell -a 'ls -l /data' 192.168.37.122 | SUCCESS | rc=0 >> total 28 drwxr-xr-x 2 root root 6 Dec 6 10:21 app 192.168.37.133 | SUCCESS | rc=0 >> total 44 drwxr-xr-x 2 root root 4096 Dec 5 10:21 app
可以看出,我们的目录已经创建完成。 ② 创建链接文件
[root@server ~]# ansible web -m file -a 'path=/data/bbb.jpg src=/data/aaa.jpg state=link' 192.168.37.122 | SUCCESS => { "changed": true, "dest": "/data/bbb.jpg", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 7, "src": "aaa.jpg", "state": "link", "uid": 0 } 192.168.37.133 | SUCCESS => { "changed": true, "dest": "/data/bbb.jpg", "gid": 0, "group": "root", "mode": "0777", "owner": "root", "size": 7, "src": "aaa.jpg", "state": "link", "uid": 0 }
我们可以去查看一下:
[root@server ~]# ansible web -m shell -a 'ls -l /data' 192.168.37.122 | SUCCESS | rc=0 >> total 28 -rw-r--r-- 1 root root 5649 Dec 5 13:49 aaa.jpg lrwxrwxrwx 1 root root 7 Dec 6 10:25 bbb.jpg -> aaa.jpg 192.168.37.133 | SUCCESS | rc=0 >> total 44 -rw-r--r-- 1 root root 5649 Dec 4 14:44 aaa.jpg lrwxrwxrwx 1 root root 7 Dec 5 10:25 bbb.jpg -> aaa.jpg
我们的链接文件已经创建成功。 ③ 删除文件
[root@server ~]# ansible web -m file -a 'path=/data/a state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "path": "/data/a", "state": "absent" } 192.168.37.133 | SUCCESS => { "changed": true, "path": "/data/a", "state": "absent" }
我们可以查看一下:
[root@server ~]# ansible web -m shell -a 'ls /data/a' 192.168.37.122 | FAILED | rc=2 >> ls: cannot access /data/a: No such file or directory 192.168.37.133 | FAILED | rc=2 >> ls: cannot access /data/a: No such file or directory
发现已经没有这个文件了。
该模块用于从远程某主机获取(复制)文件到本地。 有两个选项:
dest
:用来存放文件的目录
src
:在远程拉取的文件,并且必须是一个file,不能是目录
具体举例如下:
[root@server ~]# ansible web -m fetch -a 'src=/data/hello dest=/data' 192.168.37.122 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/192.168.37.122/data/hello", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "remote_checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "remote_md5sum": null } 192.168.37.133 | SUCCESS => { "changed": true, "checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "dest": "/data/192.168.37.133/data/hello", "md5sum": "6f5902ac237024bdd0c176cb93063dc4", "remote_checksum": "22596363b3de40b06f981fb85d82312e8c0ed511", "remote_md5sum": null }
我们可以在本机上查看一下文件是否复制成功。要注意,文件保存的路径是我们设置的接收目录下的被管制主机ip
目录下:
[root@server ~]# cd /data/ [root@server data]# ls 1 192.168.37.122 192.168.37.133 fastdfs web [root@server data]# cd 192.168.37.122 [root@server 192.168.37.122]# ls data [root@server 192.168.37.122]# cd data/ [root@server data]# ls hello [root@server data]# pwd /data/192.168.37.122/data
该模块适用于管理cron
计划任务的。 其使用的语法跟我们的crontab
文件中的语法一致,同时,可以指定以下选项:
day=
#日应该运行的工作( 1-31, , /2, )
hour=
# 小时 ( 0-23, , /2, )
minute=
#分钟( 0-59, , /2, )
month=
# 月( 1-12, *, /2, )
weekday=
# 周 ( 0-6 for Sunday-Saturday,, )
job=
#指明运行的命令是什么
name=
#定时任务描述
reboot
# 任务在重启时运行,不建议使用,建议使用special_time
special_time
#特殊的时间范围,参数:reboot(重启时),annually(每年),monthly(每月),weekly(每周),daily(每天),hourly(每小时)
state
#指定状态,present表示添加定时任务,也是默认设置,absent表示删除定时任务
user
# 以哪个用户的身份执行
举例如下: ① 添加计划任务
[root@server ~]# ansible web -m cron -a 'name="ntp update every 5 min" minute=*/5 job="/sbin/ntpdate 172.17.0.1 &> /dev/null"' 192.168.37.122 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] } 192.168.37.133 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] }
我们可以去查看一下:
[root@server ~]# ansible web -m shell -a 'crontab -l' 192.168.37.122 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null 192.168.37.133 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null
可以看出,我们的计划任务已经设置成功了。 ② 删除计划任务 如果我们的计划任务添加错误,想要删除的话,则执行以下操作: 首先我们查看一下现有的计划任务:
[root@server ~]# ansible web -m shell -a 'crontab -l' 192.168.37.122 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null #Ansible: df everyday * 15 * * * df -lh >> /tmp/disk_total &> /dev/null 192.168.37.133 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null #Ansible: df everyday * 15 * * * df -lh >> /tmp/disk_total &> /dev/null
然后执行删除操作:
[root@server ~]# ansible web -m cron -a 'name="df everyday" hour=15 job="df -lh >> /tmp/disk_total &> /dev/null" state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] } 192.168.37.133 | SUCCESS => { "changed": true, "envs": [], "jobs": [ "ntp update every 5 min" ] }
删除完成后,我们再查看一下现有的计划任务确认一下:
[root@server ~]# ansible web -m shell -a 'crontab -l' 192.168.37.122 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null 192.168.37.133 | SUCCESS | rc=0 >> #Ansible: ntp update every 5 min */5 * * * * /sbin/ntpdate 172.17.0.1 &> /dev/null
我们的删除操作已经成功。
顾名思义,该模块主要用于软件的安装。 其选项如下:
name=
#所安装的包的名称
state=
#present
--->安装,latest
--->安装最新的,absent
---> 卸载软件。
update_cache
#强制更新yum的缓存
conf_file
#指定远程yum安装时所依赖的配置文件(安装本地已有的包)。
disable_gpg_check
#是否禁止GPG checking,只用于present
orlatest
。
disablerepo
#临时禁止使用yum库。 只用于安装或更新时。
enablerepo
#临时使用的yum库。只用于安装或更新时。
下面我们就来安装一个包试试看:
[root@server ~]# ansible web -m yum -a 'name=htop state=present' 192.168.37.122 | SUCCESS => { "changed": true, "msg": "", "rc": 0, "results": [ "Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n--> Running transaction check\n---> Package htop.x86_64 0:2.0.2-1.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n htop x86_64 2.0.2-1.el7 epel 98 k\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 98 k\nInstalled size: 207 k\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : htop-2.0.2-1.el7.x86_64 1/1 \n Verifying : htop-2.0.2-1.el7.x86_64 1/1 \n\nInstalled:\n htop.x86_64 0:2.0.2-1.el7 \n\nComplete!\n" ] } 192.168.37.133 | SUCCESS => { "changed": true, "msg": "Warning: RPMDB altered outside of yum.\n** Found 3 pre-existing rpmdb problem(s), 'yum check' output follows:\nipa-client-4.4.0-12.el7.centos.x86_64 has installed conflicts freeipa-client: ipa-client-4.4.0-12.el7.centos.x86_64\nipa-client-common-4.4.0-12.el7.centos.noarch has installed conflicts freeipa-client-common: ipa-client-common-4.4.0-12.el7.centos.noarch\nipa-common-4.4.0-12.el7.centos.noarch has installed conflicts freeipa-common: ipa-common-4.4.0-12.el7.centos.noarch\n", "rc": 0, "results": [ "Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\nResolving Dependencies\n--> Running transaction check\n---> Package htop.x86_64 0:2.0.2-1.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n htop x86_64 2.0.2-1.el7 epel 98 k\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 98 k\nInstalled size: 207 k\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : htop-2.0.2-1.el7.x86_64 1/1 \n Verifying : htop-2.0.2-1.el7.x86_64 1/1 \n\nInstalled:\n htop.x86_64 0:2.0.2-1.el7 \n\nComplete!\n" ] }
安装成功。
该模块用于服务程序的管理。 其主要选项如下:
arguments
#命令行提供额外的参数enabled
#设置开机启动。name=
#服务名称runlevel
#开机启动的级别,一般不用指定。sleep
#在重启服务的过程中,是否等待。如在服务关闭以后等待2秒再启动。(定义在剧本中。)state
#有四种状态,分别为:started
--->启动服务,stopped
--->停止服务,restarted
--->重启服务,reloaded
--->重载配置
下面是一些例子: ① 开启服务并设置自启动
[root@server ~]# ansible web -m service -a 'name=nginx state=started enabled=true' 192.168.37.122 | SUCCESS => { "changed": true, "enabled": true, "name": "nginx", "state": "started", …… } 192.168.37.133 | SUCCESS => { "changed": true, "enabled": true, "name": "nginx", "state": "started", …… }
我们可以去查看一下端口是否打开:
[root@server ~]# ansible web -m shell -a 'ss -ntl' 192.168.37.122 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:80 *:* 192.168.37.133 | SUCCESS | rc=0 >> State Recv-Q Send-Q Local Address:Port Peer Address:Port LISTEN 0 128 *:80 *:*
可以看出我们的80端口已经打开。 ② 关闭服务 我们也可以通过该模块来关闭我们的服务:
[root@server ~]# ansible web -m service -a 'name=nginx state=stopped' 192.168.37.122 | SUCCESS => { "changed": true, "name": "nginx", "state": "stopped", …… } 192.168.37.133 | SUCCESS => { "changed": true, "name": "nginx", "state": "stopped", …… }
一样的,我们来查看一下端口:
[root@server ~]# ansible web -m shell -a 'ss -ntl | grep 80' 192.168.37.122 | FAILED | rc=1 >> 192.168.37.133 | FAILED | rc=1 >>
可以看出,我们已经没有80端口了,说明我们的nginx服务已经关闭了。
该模块主要是用来管理用户账号。 其主要选项如下:
comment
# 用户的描述信息createhome
# 是否创建家目录force
# 在使用state=absent时, 行为与userdel –force一致.group
# 指定基本组groups
# 指定附加组,如果指定为(groups=)表示删除所有组home
# 指定用户家目录move_home
# 如果设置为home=时, 试图将用户主目录移动到指定的目录name
# 指定用户名non_unique
# 该选项允许改变非唯一的用户ID值password
# 指定用户密码,对密码加密可以使用python的crypt和passlibremove
# 在使用state=absent时, 行为是与userdel –remove一致shell
# 指定默认shellstate
# 设置帐号状态,不指定为创建,指定值为absent表示删除system
# 当创建一个用户,设置这个用户是系统用户。这个设置不能更改现有用户uid
# 指定用户的uid
举例如下: ① 添加一个用户并指定其 uid
[root@server ~]# ansible web -m user -a 'name=keer uid=11111' 192.168.37.122 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 11111, "home": "/home/keer", "name": "keer", "shell": "/bin/bash", "state": "present", "stderr": "useradd: warning: the home directory already exists.\nNot copying any file from skel directory into it.\nCreating mailbox file: File exists\n", "system": false, "uid": 11111 } 192.168.37.133 | SUCCESS => { "changed": true, "comment": "", "createhome": true, "group": 11111, "home": "/home/keer", "name": "keer", "shell": "/bin/bash", "state": "present", "stderr": "useradd: warning: the home directory already exists.\nNot copying any file from skel directory into it.\nCreating mailbox file: File exists\n", "system": false, "uid": 11111 }
添加完成,我们可以去查看一下:
[root@server ~]# ansible web -m shell -a 'cat /etc/passwd |grep keer' 192.168.37.122 | SUCCESS | rc=0 >> keer:x:11111:11111::/home/keer:/bin/bash 192.168.37.133 | SUCCESS | rc=0 >> keer:x:11111:11111::/home/keer:/bin/bash
② 删除用户
[root@server ~]# ansible web -m user -a 'name=keer state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "force": false, "name": "keer", "remove": false, "state": "absent" } 192.168.37.133 | SUCCESS => { "changed": true, "force": false, "name": "keer", "remove": false, "state": "absent" }
一样的,删除之后,我们去看一下:
[root@server ~]# ansible web -m shell -a 'cat /etc/passwd |grep keer' 192.168.37.122 | FAILED | rc=1 >> 192.168.37.133 | FAILED | rc=1 >>
发现已经没有这个用户了。
该模块主要用于添加或删除组。 常用的选项如下:
gid=
#设置组的GID号name=
#指定组的名称state=
#指定组的状态,默认为创建,设置值为absent
为删除system=
#设置值为yes
,表示创建为系统组
举例如下: ① 创建组
[root@server ~]# ansible web -m group -a 'name=sanguo gid=12222' 192.168.37.122 | SUCCESS => { "changed": true, "gid": 12222, "name": "sanguo", "state": "present", "system": false } 192.168.37.133 | SUCCESS => { "changed": true, "gid": 12222, "name": "sanguo", "state": "present", "system": false }
创建过后,我们来查看一下:
[root@server ~]# ansible web -m shell -a 'cat /etc/group | grep 12222' 192.168.37.122 | SUCCESS | rc=0 >> sanguo:x:12222: 192.168.37.133 | SUCCESS | rc=0 >> sanguo:x:12222:
可以看出,我们的组已经创建成功了。 ② 删除组
[root@server ~]# ansible web -m group -a 'name=sanguo state=absent' 192.168.37.122 | SUCCESS => { "changed": true, "name": "sanguo", "state": "absent" } 192.168.37.133 | SUCCESS => { "changed": true, "name": "sanguo", "state": "absent" }
照例查看一下:
[root@server ~]# ansible web -m shell -a 'cat /etc/group | grep 12222' 192.168.37.122 | FAILED | rc=1 >> 192.168.37.133 | FAILED | rc=1 >>
已经没有这个组的相关信息了。
该模块用于将本机的脚本在被管理端的机器上运行。 该模块直接指定脚本的路径即可,我们通过例子来看一看到底如何使用的: 首先,我们写一个脚本,并给其加上执行权限:
[root@server ~]# vim /tmp/df.sh #!/bin/bash date >> /tmp/disk_total.log df -lh >> /tmp/disk_total.log [root@server ~]# chmod +x /tmp/df.sh
然后,我们直接运行命令来实现在被管理端执行该脚本:
[root@server ~]# ansible web -m script -a '/tmp/df.sh' 192.168.37.122 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.37.122 closed.\r\n", "stdout": "", "stdout_lines": [] } 192.168.37.133 | SUCCESS => { "changed": true, "rc": 0, "stderr": "Shared connection to 192.168.37.133 closed.\r\n", "stdout": "", "stdout_lines": [] }
照例查看一下文件内容:
[root@server ~]# ansible web -m shell -a 'cat /tmp/disk_total.log' 192.168.37.122 | SUCCESS | rc=0 >> Tue Dec 5 15:58:21 CST 2017 Filesystem Size Used Avail Use% Mounted on /dev/sda2 47G 4.4G 43G 10% / devtmpfs 978M 0 978M 0% /dev tmpfs 993M 84K 993M 1% /dev/shm tmpfs 993M 9.1M 984M 1% /run tmpfs 993M 0 993M 0% /sys/fs/cgroup /dev/sda3 47G 33M 47G 1% /app /dev/sda1 950M 153M 798M 17% /boot tmpfs 199M 16K 199M 1% /run/user/42 tmpfs 199M 0 199M 0% /run/user/0 192.168.37.133 | SUCCESS | rc=0 >> Tue Dec 5 15:58:21 CST 2017 Filesystem Size Used Avail Use% Mounted on /dev/sda2 46G 4.1G 40G 10% / devtmpfs 898M 0 898M 0% /dev tmpfs 912M 84K 912M 1% /dev/shm tmpfs 912M 9.0M 903M 1% /run tmpfs 912M 0 912M 0% /sys/fs/cgroup /dev/sda3 3.7G 15M 3.4G 1% /app /dev/sda1 1.9G 141M 1.6G 9% /boot tmpfs 183M 16K 183M 1% /run/user/42 tmpfs 183M 0 183M 0% /run/user/0
可以看出已经执行成功了。
该模块主要用于收集信息,是通过调用facts组件来实现的。 facts组件是Ansible用于采集被管机器设备信息的一个功能,我们可以使用setup模块查机器的所有facts信息,可以使用filter来查看指定信息。整个facts信息被包装在一个JSON格式的数据结构中,ansible_facts是最上层的值。 facts就是变量,内建变量 。每个主机的各种信息,cpu颗数、内存大小等。会存在facts中的某个变量中。调用后返回很多对应主机的信息,在后面的操作中可以根据不同的信息来做不同的操作。如redhat系列用yum安装,而debian系列用apt来安装软件。 ① 查看信息 我们可以直接用命令获取到变量的值,具体我们来看看例子:
[root@server ~]# ansible web -m setup -a 'filter="*mem*"' #查看内存 192.168.37.122 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1116, "ansible_memory_mb": { "nocache": { "free": 1397, "used": 587 }, "real": { "free": 1116, "total": 1984, "used": 868 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1984 }, "changed": false } 192.168.37.133 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1203, "ansible_memory_mb": { "nocache": { "free": 1470, "used": 353 }, "real": { "free": 1203, "total": 1823, "used": 620 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1823 }, "changed": false }
我们可以通过命令查看一下内存的大小以确认一下是否一致:
[root@server ~]# ansible web -m shell -a 'free -m' 192.168.37.122 | SUCCESS | rc=0 >> total used free shared buff/cache available Mem: 1984 404 1122 9 457 1346 Swap: 3813 0 3813 192.168.37.133 | SUCCESS | rc=0 >> total used free shared buff/cache available Mem: 1823 292 1207 9 323 1351 Swap: 3813 0 3813
可以看出信息是一致的。 ② 保存信息 我们的setup模块还有一个很好用的功能就是可以保存我们所筛选的信息至我们的主机上,同时,文件名为我们被管制的主机的IP,这样方便我们知道是哪台机器出的问题。 我们可以看一看例子:
[root@server tmp]# ansible web -m setup -a 'filter="*mem*"' --tree /tmp/facts 192.168.37.122 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1115, "ansible_memory_mb": { "nocache": { "free": 1396, "used": 588 }, "real": { "free": 1115, "total": 1984, "used": 869 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1984 }, "changed": false } 192.168.37.133 | SUCCESS => { "ansible_facts": { "ansible_memfree_mb": 1199, "ansible_memory_mb": { "nocache": { "free": 1467, "used": 356 }, "real": { "free": 1199, "total": 1823, "used": 624 }, "swap": { "cached": 0, "free": 3813, "total": 3813, "used": 0 } }, "ansible_memtotal_mb": 1823 }, "changed": false }
然后我们可以去查看一下:
[root@server ~]# cd /tmp/facts/ [root@server facts]# ls 192.168.37.122 192.168.37.133 [root@server facts]# cat 192.168.37.122 {"ansible_facts": {"ansible_memfree_mb": 1115, "ansible_memory_mb": {"nocache": {"free": 1396, "used": 588}, "real": {"free": 1115, "total": 1984, "used": 869}, "swap": {"cached": 0, "free": 3813, "total": 3813, "used": 0}}, "ansible_memtotal_mb": 1984}, "changed": false}
get_url模块 用途: 用于将文件或软件从http、https或ftp下载到本地节点上 常用参数: dest: 指定将文件下载的绝对路径---必须 url: 文件的下载地址(网址)---必须 url_username: 用于http基本认证的用户名 url_password: 用于http基本认证的密码 validate_certs: 如果否,SSL证书将不会验证。这只应在使用自签名证书的个人控制站点上使用 owner: 指定属主 group: 指定属组 mode: 指定权限 ansible -i /etc/ansible/hosts zabbix -m get_url -a "url=ftp://10.3.131.50/soft/wechat.py dest=/tmp"
用途:检查文件或文件系统的状态 注意: 对于Windows目标,请改用win_stat模块 选项: path:文件/对象的完整路径(必须) 案例: name: install_apcu | Check if apcu local file is already configured. stat: path={{ php_apcu_file_path }} connection: local register: php_apcu_file_result 常用的返回值判断: exists: 判断是否存在 isuid: 调用用户的ID与所有者ID是否匹配
用途:从本地机器上复制存档后,将其解包。 说明: 该unarchive模块将解压缩一个存档。 默认情况下,它将在解包之前将源文件从本地系统复制到目标。 设置remote_src=yes为解包目标上已经存在的档案。 对于Windows目标,请改用win_unzip模块。 常用选项: dest:远程绝对路径,档案应该被解压缩 exec:列出需要排除的目录和文件 src:指定源 creates:一个文件名,当它已经存在时,这个步骤将不会被运行。 ansible -i /etc/ansible/hosts web -m unarchive -a 'src=/root/easy-springmvc-maven.zip dest=/tmp' - name: Extract foo.tgz into /var/lib/foo unarchive: src: foo.tgz dest: /var/lib/foo - name: Unarchive a file that is already on the remote machine unarchive: src: /tmp/foo.zip dest: /usr/local/bin remote_src: yes - name: Unarchive a file that needs to be downloaded (added in 2.0) unarchive: src: https://example.com/example.zip dest: /usr/local/bin remote_src: yes
playbook 是 ansible 用于配置,部署,和管理被控节点的剧本。 通过 playbook 的详细描述,执行其中的一系列 tasks ,可以让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的一系列 to-do-list ,而被控节点必须要完成。 也可以这么理解,playbook 字面意思,即剧本,现实中由演员按照剧本表演,在Ansible中,这次由计算机进行表演,由计算机安装,部署应用,提供对外服务,以及组织计算机处理各种各样的事情。
执行一些简单的任务,使用ad-hoc命令可以方便的解决问题,但是有时一个设施过于复杂,需要大量的操作时候,执行的ad-hoc命令是不适合的,这时最好使用playbook。 就像执行shell命令与写shell脚本一样, 也可以理解为批处理任务,不过playbook有自己的语法格式。 使用playbook你可以方便的重用这些代码,可以移植到不同的机器上面,像函数一样,最大化的利用代码。在你使用Ansible的过程中,你也会发现,你所处理的大部分操作都是编写playbook。可以把常见的应用都编写成playbook,之后管理服务器会变得十分简单。
playbook由YMAL语言编写。YAML( /ˈjæməl/ )参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822,Clark Evans在2001年5月在首次发表了这种语言,另外Ingy döt Net与OrenBen-Kiki也是这语言的共同设计者。 YMAL格式是类似于JSON的文件格式,便于人理解和阅读,同时便于书写。首先学习了解一下YMAL的格式,对我们后面书写playbook很有帮助。
以下为playbook常用到的YMAL格式: 1、文件的第一行应该以 "---" (三个连字符)开始,表明YMAL文件的开始。 2、在同一行中,#之后的内容表示注释,类似于shell,python和ruby。 3、YMAL中的列表元素以”-”开头然后紧跟着一个空格,后面为元素内容。 4、同一个列表中的元素应该保持相同的缩进。否则会被当做错误处理。 5、play中hosts,variables,roles,tasks等对象的表示方法都是键值中间以":"分隔表示,":"后面还要增加一个空格。
下面是一个举例:
--- #安装与运行mysql服务 - hosts: node1 remote_user: root tasks: - name: install mysql-server package yum: name=mysql-server state=present - name: starting mysqld service service: name=mysql state=started
我们的文件名称应该以.yml
结尾,像我们上面的例子就是mysql.yml
。其中,有三个部分组成:
host部分
:使用 hosts 指示使用哪个主机或主机组来运行下面的 tasks ,每个 playbook 都必须指定 hosts ,hosts也可以使用通配符格式。主机或主机组在 inventory 清单中指定,可以使用系统默认的/etc/ansible/hosts
,也可以自己编辑,在运行的时候加上-i
选项,指定清单的位置即可。在运行清单文件的时候,–list-hosts
选项会显示那些主机将会参与执行 task 的过程中。remote_user
:指定远端主机中的哪个用户来登录远端系统,在远端系统执行 task 的用户,可以任意指定,也可以使用 sudo,但是用户必须要有执行相应 task 的权限。tasks
:指定远端主机将要执行的一系列动作。tasks 的核心为 ansible 的模块,前面已经提到模块的用法。tasks 包含name
和要执行的模块
,name 是可选的,只是为了便于用户阅读,不过还是建议加上去,模块是必须的,同时也要给予模块相应的参数。
使用ansible-playbook运行playbook文件,得到如下输出信息,输出内容为JSON格式。并且由不同颜色组成,便于识别。一般而言 | 绿色代表执行成功,系统保持原样 | 黄色代表系统代表系统状态发生改变 | 红色代表执行失败,显示错误输出 执行有三个步骤:1、收集facts 2、执行tasks 3、报告结果
Playbook的核心元素:
Hosts
:主机组;Tasks
:任务列表;Variables
:变量,设置方式有四种;Templates
:包含了模板语法的文本文件;Handlers
:由特定条件触发的任务;
Playbooks配置文件的基础组件:
Hosts
:运行指定任务的目标主机
remote_user
:在远程主机上执行任务的用户;
sudo_user
:
tasks
:任务列表
模块,模块参数
:格式: (1) action: module arguments (2) module: arguments 注意:shell和command模块后面直接跟命令,而非key=value类的参数列表;
handlers
:任务,在特定条件下触发;接收到其它任务的通知时被触发;
(1) 某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers;
(2) 任务可以通过“tags“打标签,而后可在ansible-playbook命令上使用-t指定进行调用;
① 定义playbook
[root@server ~]# cd /etc/ansible [root@server ansible]# vim nginx.yml --- - hosts: web remote_user: root tasks: - name: install nginx yum: name=nginx state=present - name: copy nginx.conf copy: src=/tmp/nginx.conf dest=/etc/nginx/nginx.conf backup=yes notify: reload #当nginx.conf发生改变时,通知给相应的handlers tags: reloadnginx #打标签 - name: start nginx service service: name=nginx state=started tags: startnginx #打标签 handlers: #注意,前面没有-,是两个空格 - name: reload service: name=nginx state=restarted #为了在进程中能看出来
运行前三部曲 检查错误 ansible-playbook nginx.yml --syntax-check 列出所有任务 ansible-playbook nginx.yml --list-task 列出在那些机器执行 ansible-playbook nginx.yml --list-hosts 运行 ansible-playbook nginx.yml
② 测试运行结果 写完了以后,我们就可以运行了:
[root@server ansible]# ansible-playbook nginx.yml
现在我们可以看看两台机器的端口是否开启:
[root@server ansible]# ansible web -m shell -a 'ss -nutlp |grep nginx' 192.168.37.122 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=8304,fd=6),("nginx",pid=8303,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=9671,fd=6),("nginx",pid=9670,fd=6))
③ 测试标签 我们在里面已经打上了一个标签,所以可以直接引用标签。但是我们需要先把服务关闭,再来运行剧本并引用标签:
[root@server ansible]# ansible web -m shell -a 'systemctl stop nginx' [root@server ansible]# ansible-playbook nginx.yml -t startnginx
④ 测试notify 我们还做了一个notify
,来测试一下: 首先,它的触发条件是配置文件被改变,所以我们去把配置文件中的端口改一下:
[root@server ansible]# vim /tmp/nginx.conf listen 8080;
然后我们重新加载一下这个剧本:
发现我们执行的就是reload段以及我们定义的notify
部分。 我们来看一看我们的端口号:
[root@server ansible]# ansible web -m shell -a 'ss -ntlp | grep nginx' 192.168.37.122 | SUCCESS | rc=0 >> LISTEN 0 128 *:8080 *:* users:(("nginx",pid=2097,fd=6),("nginx",pid=2096,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> LISTEN 0 128 *:8080 *:* users:(("nginx",pid=3061,fd=6),("nginx",pid=3060,fd=6))
可以看出,我们的nginx端口已经变成了8080。
上文中,我们说到了variables
是变量,有四种定义方 法,现在我们就来说说这四种定义方法:
上一篇中,我们有说到setup
这个模块,这个模块就是通过调用facts组件来实现的。我们这里的variables
也可以直接调用facts
组件。 具体的facters
我们可以使用setup
模块来获取,然后直接放入我们的剧本中调用即可。
ansible_all_ipv4_addresses:仅显示ipv4的信息 ---> [u'192.168.95.143'] ansible_eth0['ipv4']['address']:仅显示ipv4的信息 ---> eth0 的ip地址 ansible_devices:仅显示磁盘设备信息 ansible_distribution:显示是什么系统,例:centos,suse等 ansible_distribution_version:仅显示系统版本 ansible_machine:显示系统类型,例:32位,还是64位 ansible_eth0:仅显示eth0的信息 ansible_hostname:仅显示主机名 ansible_kernel:仅显示内核版本 ansible_lvm:显示lvm相关信息 ansible_memtotal_mb:显示系统总内存 ansible_memfree_mb:显示可用系统内存 ansible_memory_mb:详细显示内存情况 ansible_swaptotal_mb:显示总的swap内存 ansible_swapfree_mb:显示swap内存的可用内存 ansible_mounts:显示系统磁盘挂载情况 ansible_processor:显示cpu个数(具体显示每个cpu的型号) ansible_processor_vcpus:显示cpu个数(只显示总的个数) ansible_python_version:显示python版本
例如:批量修改主机 host 文件
--- - hosts: web vars: IP: "{{ ansible_eth0['ipv4']['address'] }}" tasks: - name: 将原有的hosts文件备份 shell: mv /etc/hosts /etc/hosts_bak - name: 将ansible端的hosts复制到各自机器上 copy: src=/root/hosts dest=/etc/ owner=root group=root mode=0644 - name: 在新的hosts文件后面追加各自机器内网ip和hostname lineinfile: dest=/etc/hosts line="{{ IP }} {{ ansible_hostname }} "
我们也可以直接使用用户自定义变量,想要自定义变量有以下两种方式:
通过命令行传入
ansible-playbook
命令的命令行中的-e VARS, --extra-vars=VARS
,这样就可以直接把自定义的变量传入。
在playbook中定义变量
我们也可以直接在playbook中定义我们的变量:
vars: - var1: value1 - var2: value2
① 定义剧本 我们就使用全局替换把我们刚刚编辑的文件修改一下:
[root@server ansible]# vim nginx.yml
这样一来,我们的剧本就定义完成了。 ② 拷贝配置文件 我们想要在被监管的机器上安装什么服务的话,就直接在我们的server端上把该服务的配置文件拷贝到我们的/tmp/
目录下。这样我们的剧本才能正常运行。 我们就以keepalived
服务为例:
[root@server ansible]# cp /etc/keepalived/keepalived.conf /tmp/keepalived.conf
③ 运行剧本,变量由命令行传入
[root@server ansible]# ansible-playbook nginx.yml -e rpmname=keepalived
④ 修改剧本,直接定义变量 同样的,我们可以直接在剧本中把变量定义好,这样就不需要在通过命令行传入了。以后想要安装不同的服务,直接在剧本里把变量修改一下即可。
[root@server ansible]# vim nginx.yml
⑤ 运行定义过变量的剧本 我们刚刚已经把变量定义在剧本里面了。现在我们来运行一下试试看:
[root@server ansible]# ansible-playbook nginx.yml
发现这样也是可以的~
具体的,我们下文中说到 roles 的时候再详细说明。
我们也可以直接在主机清单中定义。 定义的方法如下:
向不同的主机传递不同的变量:
IP/HOSTNAME varaiable=value var2=value2
向组中的主机传递相同的变量:
[groupname:vars] variable=value
Ansible Inventory 内置参数
使用内置变量把用户名密码写在Inventory中,也就是/etc/ansible/hosts文件里,缺点就是暴露了账号密码,不安全。如果有多个主机需要使用同样的变量,可以用组变量的形式,书写格式如下:
[web] 192.168.100.10 192.168.100.11 192.168.100.12 [web:vars] #给名为webservers的组定义一个变量,:vars是固定格式 ansible_ssh_port=22 ansible_ssh_user='root' ansible_ssh_pass='1234.com'
模板是一个文本文件,嵌套有脚本(使用模板编程语言编写)。 Jinja2
:Jinja2是python的一种模板语言,以Django的模板语言为原本。 模板支持:
字符串:使用单引号或双引号; 数字:整数,浮点数; 列表:[item1, item2, ...] 元组:(item1, item2, ...) 字典:{key1:value1, key2:value2, ...} 布尔型:true/false 算术运算: +, -, *, /, //, %, ** 比较操作: ==, !=, >, >=, <, <= 逻辑运算: and, or, not
通常来说,模板都是通过引用变量来运用的。
① 定义模板 我们直接把之前定义的/tmp/nginx.conf
改个名,然后编辑一下,就可以定义成我们的模板文件了:
[root@server ansible]# cd /tmp [root@server tmp]# mv nginx.conf nginx.conf.j2 [root@server tmp]# vim nginx.conf.j2 worker_processes {{ ansible_processor_vcpus }}; listen {{ nginxport }};
② 修改剧本 我们现在需要去修改剧本来定义变量:
[root@server ansible]# vim nginx.yml
需要修改的部分如图所示。
copy模块 也需要修改为 template模块
③ 运行剧本 上面的准备工作完成后,我们就可以去运行剧本了:
[root@server ansible]# ansible-playbook nginx.yml -t reloadnginx PLAY [web] ********************************************************************* TASK [setup] ******************************************************************* ok: [192.168.37.122] ok: [192.168.37.133] TASK [copy nginx.conf] ********************************************************* ok: [192.168.37.122] ok: [192.168.37.133] PLAY RECAP ********************************************************************* 192.168.37.122 : ok=2 changed=0 unreachable=0 failed=0 192.168.37.133 : ok=2 changed=0 unreachable=0 failed=0
when语句:在task中使用,jinja2的语法格式。 举例如下:
tasks: - name: install conf file to centos7 template: src=files/nginx.conf.c7.j2 when: ansible_distribution_major_version == "7" - name: install conf file to centos6 template: src=files/nginx.conf.c6.j2 when: ansible_distribution_major_version == "6"
循环:迭代,需要重复执行的任务; 对迭代项的引用,固定变量名为"item",而后,要在task中使用with_items给定要迭代的元素列表; 举例如下:
tasks: - name: unstall web packages yum: name={{ item }} state=absent with_items: - httpd - php - php-mysql
ansible playbook 还支持字典功能。举例如下:
- name: install some packages yum: name={{ item }} state=present with_items: - nginx - memcached - php-fpm - name: add some groups group: name={{ item }} state=present with_items: - group11 - group12 - group13 - name: add some users user: name={{ item.name }} group={{ item.group }} state=present with_items: - { name: 'user11', group: 'group11' } - { name: 'user12', group: 'group12' } - { name: 'user13', group: 'group13' }
对于以上所有的方式有个弊端就是无法实现复用假设在同时部署Web、db、ha 时或不同服务器组合不同的应用就需要写多个yml文件。很难实现灵活的调用。 roles 用于层次性、结构化地组织playbook。roles 能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量(vars)、文件(file)、任务(tasks)、模块(modules)及处理器(handlers)放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。
角色集合:roles/ mysql/ httpd/ nginx/ files/:存储由copy或script等模块调用的文件; tasks/:此目录中至少应该有一个名为main.yml的文件,用于定义各task;其它的文件需要由main.yml进行“包含”调用; handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler;其它的文件需要由main.yml进行“包含”调用; vars/:此目录中至少应该有一个名为main.yml的文件,用于定义各variable;其它的文件需要由main.yml进行“包含”调用; templates/:存储由template模块调用的模板文本; meta/:此目录中至少应该有一个名为main.yml的文件,定义当前角色的特殊设定及其依赖关系;其它的文件需要由main.yml进行“包含”调用; default/:此目录中至少应该有一个名为main.yml的文件,用于设定默认变量;
1. 在roles目录下生成对应的目录结构
[root@server ansible]# cd roles/ [root@server roles]# ls [root@server roles]# mkdir -pv ./{nginx,mysql,httpd}/{files,templates,vars,tasks,handlers,meta,default} [root@server roles]# tree . ├── httpd │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars ├── mysql │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars └── nginx ├── default ├── files ├── handlers ├── meta ├── tasks ├── templates └── vars 24 directories, 0 files
2. 定义配置文件 我们需要修改的配置文件为/tasks/main.yml
,下面,我们就来修改一下:
[root@server roles]# vim nginx/tasks/main.yml - name: cp rpm for nginx copy: src=nginx-1.10.2-1.el7.ngx.x86_64.rpm dest=/tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm - name: install yum: name=/tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm state=latest - name: conf template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf tags: nginxconf notify: new conf to reload - name: start service service: name=nginx state=started enabled=true
http://nginx.org/packages/rhel/7/x86_64/RPMS/nginx-1.10.2-1.el7.ngx.x86_64.rpm
3. 放置我们所需要的文件到指定目录 因为我们定义的角色已经有了新的组成方式,所以我们需要把文件都放到指定的位置,这样,才能让配置文件找到这些并进行加载。 rpm包放在files
目录下,模板放在templates
目录下:
[root@server nginx]# cp /tmp/nginx-1.10.2-1.el7.ngx.x86_64.rpm ./files/ [root@server nginx]# cp /tmp/nginx.conf.j2 ./templates/ [root@server nginx]# tree . ├── default ├── files │ └── nginx-1.10.2-1.el7.ngx.x86_64.rpm ├── handlers ├── meta ├── tasks │ └── main.yml ├── templates │ └── nginx.conf.j2 └── vars 7 directories, 3 files
4. 修改变量文件 我们在模板中定义的变量,也要去配置文件中加上:
[root@server nginx]# vim vars/main.yml nginxprot: 9999
5. 定义handlers文件 我们在配置文件中定义了notify
,所以我么也需要定义handlers
,我们来修改配置文件:
[root@server nginx]# vim handlers/main.yml - name: new conf to reload service: name=nginx state=restarted
6. 定义剧本文件 接下来,我们就来定义剧本文件,由于大部分设置我们都单独配置在了roles里面,所以,接下来剧本就只需要写一点点内容即可:
[root@server ansible]# vim roles/roles.yml - hosts: web remote_user: root roles: - nginx
完成后的目录结构 # tree ./ ./ ├── httpd │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars ├── mysql │ ├── default │ ├── files │ ├── handlers │ ├── meta │ ├── tasks │ ├── templates │ └── vars ├── nginx │ ├── default │ ├── files │ │ └── nginx-1.10.2-1.el7.ngx.x86_64.rpm │ ├── handlers │ │ └── main.yml │ ├── meta │ ├── tasks │ │ └── main.yml │ ├── templates │ │ └── nginx.conf.j2 │ └── vars │ └── main.yml └── roles.yml
7. 启动服务 剧本定义完成以后,我们就可以来启动服务了:
[root@server ansible]# ansible-playbook roles.yml PLAY [web] ********************************************************************* TASK [setup] ******************************************************************* ok: [192.168.37.122] ok: [192.168.37.133] TASK [nginx : cp] ************************************************************** ok: [192.168.37.122] ok: [192.168.37.133] TASK [nginx : install] ********************************************************* changed: [192.168.37.122] changed: [192.168.37.133] TASK [nginx : conf] ************************************************************ changed: [192.168.37.122] changed: [192.168.37.133] TASK [nginx : start service] *************************************************** changed: [192.168.37.122] changed: [192.168.37.133] RUNNING HANDLER [nginx : new conf to reload] *********************************** changed: [192.168.37.122] changed: [192.168.37.133] PLAY RECAP ********************************************************************* 192.168.37.122 : ok=6 changed=4 unreachable=0 failed=0 192.168.37.133 : ok=6 changed=4 unreachable=0 failed=0
启动过后照例查看端口号:
[root@server ansible]# ansible web -m shell -a "ss -ntulp |grep 9999" 192.168.37.122 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:9999 *:* users:(("nginx",pid=7831,fd=6),("nginx",pid=7830,fd=6),("nginx",pid=7829,fd=6)) 192.168.37.133 | SUCCESS | rc=0 >> tcp LISTEN 0 128 *:9999 *:* users:(("nginx",pid=9654,fd=6),("nginx",pid=9653,fd=6),("nginx",pid=9652,fd=6))
可以看出我们的剧本已经执行成功。
Jinja2是基于python的模板引擎,功能比较类似于PHP的smarty,J2ee的Freemarker和velocity。它能完全支持unicode,并具有集成的沙箱执行环境,应用广泛。jinja2使用BSD授权
Jinja2的语法是由variables(变量)和statement(语句)组成,如下;
my_variables
{{ some_dudes_name | capitalize }}
if语句: {% if my_conditional %} ... {% endif %} for 语句: {% for item in all_items %} `item` …… {% endfor %}
从上面第二个variables的例子中可以看出,jinja2支持使用带过滤器的Unix型管道操作符,有很多的内置过滤器可供使用。我们可以仅仅用一堆简单if和for就可以建立几乎任何的常规配置文件,不过如果你有意更进一步,jinja2 documentation (Jinja — Jinja Documentation (3.2.x))包含了很多有趣的东西可供了解。我们可以看到ansible允许在模板中使用诸如绘制时间此类的一些额外的模板变量
第一个例子:引用变量
# tree . ├── site.yml ├── templates │ └── order.j2 └── vars └── main.yml 2 directories, 3 files
总调度yml文件:
# cat site.yml --- - hosts: 192.168.19.154 user: root vars: - PROJECT: "JAVA" SWITCH: "ON" DBPORT: "8080" tasks: - name: create{{ PROJECT }}directory file: path=/data/{{ PROJECT }} state=directory - name: template transfor java template: src=order.j2 dest=/data/{{ PROJECT }}/order.conf
注意:这里 - role: template 和 - template 是一样的!
其他yml文件,如下:
# cat templates/order.j2 project: {{ PROJECT }} switch: {{ SWITCH }} dbport: {{ DBPORT }} 测试: # ansible-playbook templates.yml --syntax-check playbook: templates.yml 执行: PLAY [192.168.19.154] ********************************************************** TASK [Gathering Facts] ********************************************************* ok: [192.168.19.154] TASK [createJAVAdirectory] ***************************************************** changed: [192.168.19.154] TASK [template transfor java] ************************************************** changed: [192.168.19.154] PLAY RECAP ********************************************************************* 192.168.19.154 : ok=3 changed=2 unreachable=0 failed=0 #cat /data/JAVA/order.conf project: JAVA switch: ON dbport: 8080
第二个例子:for 语句
为远程主机生成服务器列表,加入该列表从192.168.13.201 web01.test.com 到192.168.13.211 web11.test.com 结束,如果手动添加就很不科学了,这里需要使用jinja2语法的for循环通过模板批量生成对应的配置文件,如下:
ansible目录结构:
#cd /etc/ansible/roles/test_hosts . ├── meta │ └── main.yml ├── tasks │ ├── file1.yml │ └── main.yml ├── templates │ └── test1.j2 └── vars └── main.yml
各个目录下yml文件内容:
# cat tasks/file1.yml - name: ansible jinja2 template for hosts config template: src=test1.j2 dest=/etc/httpd/conf/httpd.conf.test # cat tasks/main.yml - include: file1.yml # cat templates/test1.j2 {% for id in range(201,212) %} 192.168.13.{{ id }} web{{ "%02d" |format(id-200) }}.test.com {% endfor %} 解释: {{ id }} 提取for循环中对应的变量id值 "%02d" 调用的是python内置的字符串格式化输出(%d格式化整数)因为是01,02这种格式,所以是保留2位,故用02 然后将结果通过管道符 “|” 传递给format 函数做二次处理。
执行结果:
#cat httpd.conf.test 192.168.13.201 web01.test.com 192.168.13.202 web02.test.com 192.168.13.203 web03.test.com 192.168.13.204 web04.test.com 192.168.13.205 web05.test.com 192.168.13.206 web06.test.com 192.168.13.207 web07.test.com 192.168.13.208 web08.test.com 192.168.13.209 web09.test.com 192.168.13.210 web10.test.com 192.168.13.211 web11.test.com
第三个例子:if语句
说明:如果定义端口号,就绑定定义的端口号,如果不定义端口号,就绑定默认端口号
ansible目录结果 #cd /etc/ansible/roles/mysql_cnf #tree . ├── meta │ └── main.yml ├── tasks │ └── main.yml ├── templates │ └── test3.j2 └── vars
主要的yml文件是templates目录下面的test3.j2
# cat templates/test3.j2 {% if PORT %} bind_address=10.0.90.27:{{ PORT }} {% else %} bind_address=10.0.90.27:3306 {% endif %}
playbook主文件
# cat jinj2_test.yml --- - hosts: 10.0.90.27 user: root gather_facts: false vars: PORT: 3136 tasks: - name: copy file to client template: src=/etc/ansible/roles/mysql_cnf/templates/test3.j2 dest=/root/my.cnf
执行:
# ansible-playbook jinj2_test.yml PLAY [10.0.90.27] ************************************************************** TASK [copy file to client] ***************************************************** changed: [10.0.90.27] PLAY RECAP ********************************************************************* 10.0.90.27 : ok=1 changed=1 unreachable=0 failed=0
查看
# cat my.cnf bind_address=10.0.90.27:3136
如果将vars变量去掉,执行结果:
# cat jinj2_test.yml --- - hosts: 10.0.90.27 user: root gather_facts: false vars: PORT: false tasks: - name: copy file to client template: src=/etc/ansible/roles/mysql_cnf/templates/test3.j2 dest=/root/my.cnf
查看:
# cat my.cnf bind_address=10.0.90.27:3306
精通程序编码的朋友皆知,default()默认值的设定有助于程序的健壮性和简洁性。所幸Jinja也支持该功能,上面的例子中生成Mysql配置文件中的端口定义,如果指定则PORT=3136,否则PORT=3306,我们将该案例改造为使用default()试试
编辑/etc/ansible/roles/mysql_cnf/templates/test3.j2内容如下,这种方法更简介。
bind_address=10.0.90.27:{{ PORT | default(3306) }}
#cd /etc/ansible/roles/apache_conf # tree ./ ./ ├── meta │ └── main.yml ├── tasks │ ├── file.yml │ └── main.yml ├── templates │ └── apache.config.j2 └── vars └── main.yml 4 directories, 5 files
#cat file.yml - name: ansible jinja2 template for apache config template: src=apache.config.j2 dest=/etc/httpd/conf/httpd.conf.template #cat main.yml - include: file.yml
#cat apache.config.j2 NameVirtualHost *:80 {% for vhost in apache_vhost %}ServerName {{ vhost.servername }} DocumentRoot {{ vhost.documentroot }} {% if vhost.serveradmin is defined %} ServerAdmin {{ vhost.serveradmin }} {% endif %} {% endfor %}AllowOverride All Options -Indexes FollowSymLinks Order allow,deny Allow from all
#cat vars/main.yml apache_vhost: - {servername: "apache.test1.com", documentroot: "/data/test1/"} - {servername: "apache.test2.com", documentroot: "/data/test2/"}
#cat /etc/ansible/apache_test.yml --- - hosts: 10.0.90.27 user: root gather_facts: no roles: - { role: apache_conf }
#ansible-playbook apache_test.yml --syntax-check playbook: apache_test.yml
#ansible-playbook apache_test.yml PLAY [10.0.90.27] ************************************************************** TASK [apache_conf : include] *************************************************** included: /etc/ansible/roles/apache_conf/tasks/file.yml for 10.0.90.27 TASK [apache_conf : ansible jinja2 template for apache config] ***************** changed: [10.0.90.27] PLAY RECAP ********************************************************************* 10.0.90.27 : ok=2 changed=1 unreachable=0 failed=0
#cat httpd.conf.template NameVirtualHost *:80ServerName apache.test1.com DocumentRoot /data/test1/ AllowOverride All Options -Indexes FollowSymLinks Order allow,deny Allow from all ServerName apache.test2.com DocumentRoot /data/test2/ AllowOverride All Options -Indexes FollowSymLinks Order allow,deny Allow from all
说明:为2台Nginx Proxy,1台Nginx Web通过一套模板生成对应的配置
# cd roles/nginx_conf/ #tree . ├── files ├── meta │ └── main.yml ├── tasks │ ├── file.yml │ └── main.yml ├── templates │ └── nginx.conf.j2 └── vars └── main.yml
#cat tasks/file.yml - name: nginx.j2 template transfer example template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf.template #cat tasks/main.yml - include: file.yml
#cat templates/nginx.conf.j2 {% if nginx_use_proxy %} {% for proxy in nginx_proxies %} upstream {{ proxy.name }} #server 127.0.0.1:{{ proxy.port }}; server {{ ansible_eth0.ipv4.address }}:{{ proxy.port }}; } {% endfor %} {% endif%} server { listen 80; servername {{ nginx_server_name }}; access_log off; error_log /etc/nginx/nginx_error.log; rewrite ^ https://$server_name$request_uri? permanent; } server { listen 443 ssl; server_name {{ nginx_server_name }}; ssl_certificate /etc/nginx/ssl/{{ nginx_ssl_cert_name }}; ssl_certificate_key /etc/nginx/ssl/{{ nginx_ssl_cert_key }}; root {{ nginx_web_root }}; index index.html index.html; {% if nginx_use_auth %} auth_basic "Restricted"; auth_basic_user_file /etc/nginx/{{ project_name }}.htpasswd; {% endif %} {% if nginx_use_proxy %} {% for proxy in nginx_proxies %} location {{ proxy.location }} { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto http; proxy_set_header X-Url-Scheme $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_redirect off; proxy_pass http://{{ proxy.name }}; break; } {% endfor %} {% endif %} {% if nginx_server_static %} location / { try_files $url $url/ =404; } {% endif %} }
cat vars/main.yml nginx_server_name: www.testnginx.com nginx_web_root: /data/html/ nginx_proxies: - name: suspicious location: / port: 1234 - name: suspicious-api location: /api port: 4567
#cat nginx_test.yml ##The first roles - name: Nginx Proxy Server's Config Dynamic Create hosts: "10.0.90.25:10.0.90.26" remote_user: root vars: nginx_use_proxy: true nginx_ssl_cert_name: ifa.crt nginx_ssl_cert_key: ifa.key nginx_use_auth: true project_name: suspicious nginx_server_static: true gather_facts: true roles: - role: nginx_conf ##The second roles - name: Nginx WebServer's Config Dynamic Create hosts: 10.0.90.27 remote_user: root vars: nginx_use_proxy: false nginx_ssl_cert_name: ifa.crt nginx_ssl_cert_key: ifa.crt nginx_use_auth: false project_name: suspicious nginx_server_static: false gather_facts: false roles: - role: nginx_conf
#ansible-playbook nginx_test.yml --syntax-check playbook: nginx_test.yml 执行: # ansible-playbook nginx_test.yml PLAY [Nginx Proxy Server's Config Dynamic Create] ****************************** TASK [setup] ******************************************************************* ok: [10.0.90.25] ok: [10.0.90.26] TASK [nginx_conf : include] **************************************************** included: /etc/ansible/roles/nginx_conf/tasks/file.yml for 10.0.90.25, 10.0.90.26 TASK [nginx_conf : nginx.j2 template transfer example] ************************* changed: [10.0.90.26] changed: [10.0.90.25] PLAY [Nginx WebServer's Config Dynamic Create] ********************************* TASK [nginx_conf : include] **************************************************** included: /etc/ansible/roles/nginx_conf/tasks/file.yml for 10.0.90.27 TASK [nginx_conf : nginx.j2 template transfer example] ************************* changed: [10.0.90.27] PLAY RECAP ********************************************************************* 10.0.90.25 : ok=3 changed=1 unreachable=0 failed=0 10.0.90.26 : ok=3 changed=1 unreachable=0 failed=0 10.0.90.27 : ok=2 changed=1 unreachable=0 failed=0
到Nginx Proxy 服务器查看配置文件
#cat nginx.conf.template upstream suspicious #server 127.0.0.1:1234; server 10.0.90.26:1234; } upstream suspicious-api #server 127.0.0.1:4567; server 10.0.90.26:4567; } server { listen 80; servername www.testnginx.com; access_log off; error_log /etc/nginx/nginx_error.log; rewrite ^ https://$server_name$request_uri? permanent; } server { listen 443 ssl; server_name www.testnginx.com; ssl_certificate /etc/nginx/ssl/ifa.crt; ssl_certificate_key /etc/nginx/ssl/ifa.key; root /data/html/; index index.html index.html; auth_basic "Restricted"; auth_basic_user_file /etc/nginx/suspicious.htpasswd; location / { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto http; proxy_set_header X-Url-Scheme $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_redirect off; proxy_pass http://suspicious; break; } location /api { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto http; proxy_set_header X-Url-Scheme $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_redirect off; proxy_pass http://suspicious-api; break; } location / { try_files $url $url/ =404; } }
到Nginx Web 服务器上查看配置文件
#cat nginx.conf.template server { listen 80; servername www.testnginx.com; access_log off; error_log /etc/nginx/nginx_error.log; rewrite ^ https://$server_name$request_uri? permanent; } server { listen 443 ssl; server_name www.testnginx.com; ssl_certificate /etc/nginx/ssl/ifa.crt; ssl_certificate_key /etc/nginx/ssl/ifa.crt; root /data/html/; index index.html index.html; }
到这里,就结束了。用同样的模板通过简单的if和变量设置就可以完成不同类型主机的Nginx conf配置,所以一方面在了解Ansible强大的模板功能的同时,也需要看到模板质量的重要性。