1 ansible的介绍
文章目录
http://www.ansible.com.cn/ ansible中文手册
1. 简介
ansible是新出现的自动化运维工具,基于Python开发,集合了众多运维工具(puppet、cfengine、chef、func、fabric)的优点,实现了批量系统配置、批量程序部署、批量运行命令等功能。
2. 特性
- no agent: 不需要在被管控主机上安装任何软件,
- no server: 无服务器端,使用时直接运行命令即可,
- modules in any languages:基于模块工作,可使用任意语言开发模块,
- yaml,not code:使用yaml语言定制剧本playbook,
- ssh by default:基于SSH工作
- strong multi-tier solution:可实现多级指挥
3. 优点
- 轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
- 批量任务执行可以写成脚本,而且不用分发到远程就可以执行;
- 使用python编写,维护更简单,ruby语法过于复杂;
- 支持sudo。
ansible是基于模块工作的,本身没有批量部署的能力。真正具有批量部署的是ansible所运行的模块,ansible只是提供一种框架。主要包括:
- 连接插件connection plugins:负责和被监控端实现通信
- host inventory:指定操作的主机,是一个配置文件里面定义监控的主机
- 各种模块核心模块、command模块、自定义模块
- 借助于插件完成记录日志邮件等功能
- playbook:剧本执行多个任务时,非必需可以让节点一次性运行多个任务。
2. ansible的安装
ansible在epel(第三方社区)源中,安装时要先安装epel
[root@ansible-master ~]# yum install epel-* -y
[root@ansible-master ~]# yum install ansible -y
配置文件:
- ansible 应用程序的主配置文件:/etc/ansible/ansible.cfg
- Host Inventory 定义管控主机 :/etc/ansible/hosts
配置主机:
Ex1: 任何组的头部前面指定,不属于任何组的主机
## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10
Ex2:一批主机属于一个组,例如定义为 ‘webservers’ 的组
## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110
[webserver]
#192.168.44.112
ansible-node.com
192.168.44.113
当ansible管理主机时,可以基于ssh的用户密码验证,也可以基于用户的密钥验证基于密码验证:
[webserver]
192.168.10.201 ansible_ssh_pass=123456 asible_ssh_user=root
以下是Hosts部分中经常用到的变量部分:
- ansible_ssh_host #用于指定被管理的主机的真实IP
- ansible_ssh_port #用于指定连接到被管理主机的ssh端口号,默认是22
- ansible_ssh_user #ssh连接时默认使用的用户名
- ansible_ssh_pass #ssh连接时的密码
- ansible_sudo_pass #使用sudo连接用户时的密码
- ansible_sudo_exec #如果sudo命令不在默认路径,需要指定sudo命令路径
- ansible_ssh_private_key_file #秘钥文件路径,秘钥文件如果不想使用ssh-agent管理时可以使用此选项
- ansible_shell_type #目标系统的shell的类型,默认sh
- ansible_connection #SSH 连接的类型: local , ssh , paramiko,在 ansible 1.2 之前默认是 paramiko ,后来智能选择,优先使用基于 ControlPersist 的 ssh (支持的前提)
- ansible_python_interpreter #用来指定python解释器的路径,默认为/usr/bin/python 同样可以指定ruby 、perl 的路径
- ansible__interpreter #其他解释器路径,用法与ansible_python_interpreter类似,这里""可以是ruby或才perl等
基于密钥验证:
创建密钥,并且将公钥传输给被管控端
[root@ansible-master ~]# ssh-keygen
[root@ansible-master ~]# ssh-copy-id -i 192.168.44.113
配置ansible
[root@ansible-master ~]# vim /etc/ansible/hosts
[webserver]
192.168.44.113
3. ansible模块测试
1. ansible命令语法介绍
ansible HOST-PATTERN [-f FORKS] [-M MOD_NAME] [-a MOD_ARGS]
- -f FORKS:表示一批处理几台主机,也就是当被管控主机很多时,ansible不是对所有主机同时发起管理操作,而是一批处理几台,然后再换一批,直到所有主机被处理完成,如果不指定,则默认是5台
- -m MOD_NAME:指明调用哪个模块执行操作,各个模块所能实现的功能不同,如果不指定,默认是用-m command模块
- -a MOD_ARGS:指明使用该模块的执行操作时的参数
[root@master ~]# ansible-doc -l获取支持的模块
注意每个模块的用法可以使用 ansible-doc MOD
如:
[root@master ~]# ansible-doc copy
modules 具有”幂等”性,意思是如果你再一次地执行 moudle(译者注:比如遇到远端系统被意外改动,需要恢复原状),moudle 只会执行必要的改动,只会改变需要改变的地方.所以重复多次执行 playbook 也很安全.
2.ping模块 探测助主机是否在线
[root@ansible-master ~]# ansible webservers -m ping
ansible-node.com | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
3. command模块 在远程主机执行命令,不支持管道,重定向等shell特性
查看时间信息:
[root@ansible-master ~]# ansible webserver -m command -a 'date'
192.168.44.113 | CHANGED | rc=0 >>
Mon Sep 20 01:40:50 PDT 2021
[root@ansible-master ~]#
查看负载
[root@ansible-master ~]# ansible webserver -m command -a 'uptime'
192.168.44.113 | CHANGED | rc=0 >>
01:42:01 up 1:25, 4 users, load average: 0.00, 0.01, 0.05
常用参数:
- chdir= 表示指明命令在远程主机上哪个目录下运行,也就是在命令执行前切换到哪个目录下
- creates= 在命令运行时创建一个文件,如果文件已存在,则不会执行创建任务
- removes= 在命令运行时移除一个文件,如果文件不存在,则不会执行移除任务
- executeble= 指明运行命令的shell程序
[root@ansible-master ~]# ansible webserver -m command -a 'chdir=/tmp ls ./'
192.168.44.113 | CHANGED | rc=0 >>
ansible_command_payload_DN7wO0
hsperfdata_root
ks-script-EBBLfs
ks-script-msGuRH
systemd-private-6a7e8e83713e4db7ad5ec5b584e622b3-chronyd.service-w2Mhgq
systemd-private-6a7e8e83713e4db7ad5ec5b584e622b3-cups.service-S1DIEi
tracker-extract-files.0
vmware-root_580-2730496794
vmware-root_593-4022112081
vmware-root_601-3988687199
vmware-root_617-4022243191
vmware-root_640-2722304575
yum.log
yum_save_tx.2021-08-30.18-49.u0vWpN.yumtx
[root@ansible-master ~]#
不支持管道和重定向
[root@ansible-master ~]# ansible webserver -m command -a 'echo "hello" > /tmp/aa.txt'
192.168.44.113 | CHANGED | rc=0 >>
hello > /tmp/aa.txt
[root@master ~]# ansible webserver -m command -a 'cat /tmp/aa.txt'
[root@ansible-master ~]# ansible webserver -m command -a 'cat /tmp/aa.txt'
192.168.44.113 | FAILED | rc=1 >>
cat: /tmp/aa.txt: No such file or directorynon-zero return code
[root@ansible-master ~]# ansible webserver -a "ls /tmp"
192.168.44.113 | CHANGED | rc=0 >>
aa1.txt
aa2.txt
aa3.txt
ansible_command_payload_n2qOIz
[root@ansible-master ~]# ansible webserver -m command -a "ls -l chdir=/tmp creates=aa.txt"
192.168.44.113 | CHANGED | rc=0 >>
total 0
-rw-r--r--. 1 root root 0 Sep 20 02:09 aa1.txt
-rw-r--r--. 1 root root 0 Sep 20 02:09 aa2.txt
-rw-r--r--. 1 root root 0 Sep 20 02:09 aa3.txt
drwx------. 2 root root 41 Sep 20 02:10 ansible_command_payload_7kmZlS
[root@master ~]# ansible webserver -m command -a "ls -l chdir=/tmp creates=aaa.txt"
192.168.10.201 | SUCCESS | rc=0 >>
总用量 8
-rw-r--r-- 1 root root 6 11月 3 13:08 aa.txt
drwx------ 2 root root 4096 11月 3 13:19 ansible_xK_rU6
192.168.10.202 | SUCCESS | rc=0 >>
总用量 4
-rw-r--r-- 1 root root 0 11月 3 12:18 aa.txt
drwx------ 2 root root 4096 11月 3 12:20 ansible_LG642m
4. shell模块
在远程主机执行命令,相当于调用远程主机的shell进程,然后在该shell下打开一个子shell运行命令支持shell特性,如管道,重定向等
常见参数有:
- chdir= 表示指明命令在远程主机上哪个目录下运行
- creates= 在命令运行时创建一个文件,如果文件已存在,则不会执行创建任务
- removes= 在命令运行时移除一个文件,如果文件不存在,则不会执行移除任务
- executeble= 指明运行命令的shell程序
[root@master ~]# ansible webserver -m shell -a 'echo "hello" > /tmp/aa.txt'
192.168.44.201 | SUCCESS | rc=0 >>
192.168.44.202 | SUCCESS | rc=0 >>
[root@master ~]# ansible webserver -m shell -a 'cat /tmp/aa.txt'
192.168.10.201 | SUCCESS | rc=0 >>
hello
192.168.10.202 | SUCCESS | rc=0 >>
hello
5. copy模块
拷贝ansible管理端的文件到远程主机的指定位置
常见参数有:
- dest= 指明拷贝文件的目标目录位置,使用绝对路径,如果源是目录,则目标也要是目录,如果目标文件已存在,会覆盖原有内容
- src= 指明本地路径下的某个文件,可以使用相对路径和绝对路径,支持直接指定目录,如果源是目录,则目标也要是目录
- mode= 指明复制时,目标文件的权限
- owner= 指明复制时,目标文件的属主
- group= 指明复制时,目标文件的属组
- content= 指明复制到目标主机上的内容,不能与src一起使用,相当于复制content指明的数据,到目标文件中
[root@ansible-master ~]# ansible webservers -m copy -a 'src=/etc/passwd dest=/data mode=777 owner=root group=root'
ansible-node.com | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "22d6803f1e504f3ad27d57c055e5d7764d570028",
"dest": "/data/passwd",
"gid": 0,
"group": "root",
"md5sum": "4702f894dd1fd7be43ab58a448a270fd",
"mode": "0777",
"owner": "root",
"secontext": "system_u:object_r:default_t:s0",
"size": 2310,
"src": "/root/.ansible/tmp/ansible-tmp-1632190959.89-1845-74266020800987/source",
"state": "file",
"uid": 0
}
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"checksum": "22d6803f1e504f3ad27d57c055e5d7764d570028",
"dest": "/data/passwd",
"gid": 0,
"group": "root",
"md5sum": "4702f894dd1fd7be43ab58a448a270fd",
"mode": "0777",
"owner": "root",
"secontext": "system_u:object_r:default_t:s0",
"size": 2310,
"src": "/root/.ansible/tmp/ansible-tmp-1632190959.89-1847-23905901753958/source",
"state": "file",
"uid": 0
}
[root@ansible-master ~]# ansible webservers -m copy -a 'content="hello world" dest=/data/test2.txt mode=777'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"checksum": "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
"dest": "/data/test2.txt",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"path": "/data/test2.txt",
"secontext": "system_u:object_r:default_t:s0",
"size": 11,
"state": "file",
"uid": 0
}
6. cron模块
管理计划任务的模块
常见参数有:
- minute= 指明计划任务的分钟,支持格式:0-59,,/2等,与正常cron任务定义的一样的语法,省略时,默认为*,也就是每分钟都执行
- hour= 指明计划任务的小时,支持的语法:0-23,,/2等,省略时,默认为*,也就是每小时都执行
- day= 指明计划任务的天,支持的语法:1-31,,/2等,省略时,默认为*,也就是每天都执行
- month= 指明计划任务的月,支持的语法为:1-12,,/2等,省略时,默认为*,也就是每月都执行
- weekday= 指明计划任务的星期几,支持的语法为:0-6,等,省略时,默认为,也就是每星期几都执行
- reboot 指明计划任务执行的时间为每次重启之后
- name= 给该计划任务取个名称,必须要给明。每个任务的名称不能一样。删除任务时,只需要给明任务的名称即可
- job= 执行的任务是什么,当state=present时才有意义
- state=present|absent 表示这个任务是创建还是删除,present表示创建,absent表示删除,默认是present
[root@ansible-master ~]# ansible webservers -m cron -a 'minute=10 hour=10-20 day=10 name="test cron" job="ntpdate 192.168.44.112 &> /dev/null"'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"envs": [],
"jobs": [
"test cron"
]
}
[root@master ~]# ansible webserver -m shell -a "crontab -l"
192.168.44.113 | SUCCESS | rc=0 >>
#Ansible: test cron
10 10-20 10 * * ntpdate 192.168.10.200 &> /dev/null
[root@ansible-master ~]# ansible webservers -m cron -a 'minute=10 hour=10-20 day=10 name="test" job="ntpdate 192.168.44.112 &> /dev/null" state=absent'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"envs": [],
"jobs": []
}
[root@ansible-master ~]# ansible webservers -m shell -a "crontab -l"
192.168.44.113 | CHANGED | rc=0 >>
#Ansible: test cron
10 10-20 10 * * ntpdate 192.168.44.112 &> /dev/null
7. fetch模块
从远程主机拉取文件到本地
一般情况下,只会从一个远程节点拉取数据
常见参数有:
- dest= 从远程主机上拉取的文件存放在本地的位置,一般只能是目录
- src= 指明远程主机上要拉取的文件,只能是文件,不能是目录
[root@ansible-master ~]# ansible 192.168.44.113 -m fetch -a 'src=/data/test.sh dest=/tmp'
192.168.44.113 | CHANGED => {
"changed": true,
"checksum": "62a1e17a8ab950e59e56563ebf327d9a14520575",
"dest": "/tmp/192.168.44.113/data/test.sh",
"md5sum": "85239384c0243e32ef2e37e8cd3b3114",
"remote_checksum": "62a1e17a8ab950e59e56563ebf327d9a14520575",
"remote_md5sum": null
}
[root@ansible-master ~]# ansible 192.168.44.113 -m fetch -a 'src=/data/test.sh dest=/tmp'
192.168.44.113 | SUCCESS => {
"changed": false,
"checksum": "62a1e17a8ab950e59e56563ebf327d9a14520575",
"dest": "/tmp/192.168.44.113/data/test.sh",
"file": "/data/test.sh",
"md5sum": "85239384c0243e32ef2e37e8cd3b3114"
}
[root@master ~]# ls /tmp/
192.168.10.202
[root@master ~]# cat /tmp/192.168.10.202/tmp/test.txt
8. file模块
用于设定远程主机上的文件属性
常见参数有:
- path= 指明对哪个文件修改其属性
- src= 指明path=指明的文件是软链接文件,其对应的源文件是谁,必须要在state=link时才有用
- state=directory|link|absent 表示创建的文件是目录还是软链接
- owner= 指明文件的属主
- group= 指明文件的属组
- mode= 指明文件的权限
创建软链接的用法:
src= path= state=link
修改文件属性的用法:
path= owner= mode= group=
创建目录的用法:
path= state=directory
删除文件:
path= state=absent
创建软连接
[root@ansible-master ~]# ansible webservers -m file -a 'src=/data/test.sh path=/tmp/test.link state=link'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"dest": "/tmp/test.link",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"secontext": "unconfined_u:object_r:user_tmp_t:s0",
"size": 13,
"src": "/data/test.sh",
"state": "link",
"uid": 0
}
[root@ansible-master ~]# ansible webservers -m file -a 'src=/data/test.sh path=/tmp/test.link state=link'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"dest": "/tmp/test.link",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"secontext": "unconfined_u:object_r:user_tmp_t:s0",
"size": 13,
"src": "/data/test.sh",
"state": "link",
"uid": 0
}
[root@ansible-master ~]# ansible webservers -m shell -a 'ls -l /tmp/test.link'
192.168.44.113 | CHANGED | rc=0 >>
lrwxrwxrwx. 1 root root 13 Sep 21 10:41 /tmp/test.link -> /data/test.sh
删除文件
[root@ansible-master ~]# ansible webservers -m file -a 'path=/tmp/test.link state=absent'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"path": "/tmp/test.link",
"state": "absent"
}
[root@ansible-master ~]# ansible webservers -m file -a 'path=/tmp/test.link state=absent'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"path": "/tmp/test.link",
"state": "absent"
}
[root@ansible-master ~]#
修改文件属性
[root@ansible-master ~]# ansible webservers -m file -a 'path=/data/test.sh owner=nobody group=nobody mode=000'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"gid": 99,
"group": "nobody",
"mode": "0000",
"owner": "nobody",
"path": "/data/test.sh",
"secontext": "unconfined_u:object_r:default_t:s0",
"size": 32,
"state": "file",
"uid": 99
}
[root@master ~]# ansible webserver -m shell -a 'ls -l /tmp/test.txt'
192.168.10.201 | SUCCESS | rc=0 >>
---------- 1 nobody nobody 11 11月 3 13:24 /tmp/test.txt
192.168.10.202 | SUCCESS | rc=0 >>
---------- 1 nobody nobody 11 11月 3 12:24 /tmp/test.txt
创建目录
[root@ansible-master ~]# ansible webservers -m file -a 'path=/data/dir state=directory'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/data/dir",
"secontext": "unconfined_u:object_r:default_t:s0",
"size": 6,
"state": "directory",
"uid": 0
}
[root@ansible-master ~]# ansible webservers -m file -a 'path=/data/dir state=directory'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/data/dir",
"secontext": "unconfined_u:object_r:default_t:s0",
"size": 6,
"state": "directory",
"uid": 0
}
[root@ansible-master ~]#
[root@ansible-master ~]# ansible webservers -m shell -a 'ls -ld /data/dir'
192.168.44.113 | CHANGED | rc=0 >>
drwxr-xr-x. 2 root root 6 Sep 21 10:45 /data/dir
[root@ansible-master ~]# ansible webservers -m command -a 'ls -ld /data/dir'
192.168.44.113 | CHANGED | rc=0 >>
drwxr-xr-x. 2 root root 6 Sep 21 10:45 /data/dir
删除目录
[root@ansible-master ~]# ansible webservers -m file -a 'path=/data/dir state=absent'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"path": "/data/dir",
"state": "absent"
}
[root@ansible-master ~]# ansible webservers -m file -a 'path=/data/dir state=absent'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"path": "/data/dir",
"state": "absent"
}
[root@ansible-master ~]# ansible webservers -m shell -a 'ls -l /data/'
192.168.44.113 | CHANGED | rc=0 >>
total 4
----------. 1 nobody nobody 32 Sep 21 10:35 test.sh
[root@ansible-master ~]#
9. hostname模块
管理远程主机上的主机名
常用参数有
name= 指明主机名
[root@ansible-master ~]# ansible webservers -m shell -a 'hostname'
192.168.44.113 | CHANGED | rc=0 >>
ansible-node.com
[root@ansible-master ~]#
[root@ansible-master ~]# ansible 192.168.44.113 -m hostname -a 'name=ansible-node.com'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"ansible_domain": "com",
"ansible_fqdn": "ansible-node.com",
"ansible_hostname": "ansible-node",
"ansible_nodename": "ansible-node.com",
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"name": "ansible-node.com"
}
[root@ansible-master ~]#
[root@ansible-master ~]# ansible webservers -m shell -a 'hostname'
192.168.44.113 | CHANGED | rc=0 >>
ansible-node.com
[root@ansible-master ~]# ansible webservers -m shell -a 'hostname'
192.168.44.113 | CHANGED | rc=0 >>
ansible-node.com
[root@ansible-master ~]#
10. yum模块
基于yum机制,对远程主机管理程序包
常用参数有:
- name= 指明程序包的名称,可以带上版本号,不指明版本,就是默认最新版本。
- state=present|latest|absent 指明对程序包执行的操作,present表示安装程序包,latest表示安装最新版本的程序包,absent表示卸载程序包
- disablerepo= 在用yum安装时,临时禁用某个仓库,仓库的ID
- enablerepo= 在用yum安装时,临时启用某个仓库,仓库的ID
- conf_file= 指明yum运行时采用哪个配置文件,而不是使用默认的配置文件
- diable_gpg_check=yes|no 是否启用gpg-check
卸载软件包:
[root@ansible-master ~]# ansible webservers -m yum -a 'name=nmap state=absent'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"msg": "",
"rc": 0,
"results": [
"nmap is not installed"
]
}
[root@ansible-master ~]#
安装软件包:
[root@ansible-master ~]# ansible webservers -m yum -a 'name=nmap state=present'
192.168.44.113 | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": true,
"changes": {
"installed": [
"nmap"
]
},
"msg": "",
"rc": 0,
"results": [
"Loaded plugins: fastestmirror, langpacks\nLoading mirror speeds from cached hostfile\n * base: mirrors.ustc.edu.cn\n * extras: mirrors.cn99.com\n * updates: mirrors.ustc.edu.cn\nResolving Dependencies\n--> Running transaction check\n---> Package nmap.x86_64 2:6.40-19.el7 will be installed\n--> Finished Dependency Resolution\n\nDependencies Resolved\n\n================================================================================\n Package Arch Version Repository Size\n================================================================================\nInstalling:\n nmap x86_64 2:6.40-19.el7 base 3.9 M\n\nTransaction Summary\n================================================================================\nInstall 1 Package\n\nTotal download size: 3.9 M\nInstalled size: 16 M\nDownloading packages:\nRunning transaction check\nRunning transaction test\nTransaction test succeeded\nRunning transaction\n Installing : 2:nmap-6.40-19.el7.x86_64 1/1 \n Verifying : 2:nmap-6.40-19.el7.x86_64 1/1 \n\nInstalled:\n nmap.x86_64 2:6.40-19.el7 \n\nComplete!\n"
]
}
[root@ansible-master ~]# ansible webservers -m yum -a 'name=nmap state=present'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"msg": "",
"rc": 0,
"results": [
"2:nmap-6.40-19.el7.x86_64 providing nmap is already installed"
]
}
查询:
[root@ansible-master ~]# ansible webservers -m shell -a 'rpm -q nmap'
192.168.44.113 | CHANGED | rc=0 >>
nmap-6.40-19.el7.x86_64
[root@master ~]# ansible webserver -m yum -a 'name=httpd disable_gpg_check=yes disablerepo=yum state=present'
11. service模块
用来管理远程主机上的服务的模块
常见参数有:
- name= 被管理的服务名称
- state=started|stopped|restarted 表示启动或关闭或重启
- enabled=yes|no 表示要不要设定该服务开机自启动
- runlevel= 如果设定了enabled开机自动启动,则要定义在哪些运行级别下自动启动
[root@ansible-master ~]# ansible webservers -m service -a 'name=httpd state=started'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"name": "httpd",
"state": "started",
"status": {
"ActiveEnterTimestamp": "Tue 2021-09-21 11:28:23 CST",
"ActiveEnterTimestampMonotonic": "4749575015",
"ActiveExitTimestamp": "Tue 2021-09-21 11:28:21 CST",
"ActiveExitTimestampMonotonic": "4748442301",
"ActiveState": "active",
"After": "network.target remote-fs.target system.slice -.mount tmp.mount nss-lookup.target basic.target systemd-journald.socket",
"AllowIsolate": "no",
"AmbientCapabilities": "0",
"AssertResult": "yes",
[root@ansible-master ~]# ansible webservers -m shell -a 'netstat -anplt |grep :80'
192.168.44.113 | CHANGED | rc=0 >>
tcp6 0 0 :::80 :::* LISTEN 8197/httpd
[root@ansible-master ~]#
[root@ansible-master ~]# ansible webservers -m service -a 'name=httpd state=stopped'
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"name": "httpd",
"state": "stopped",
"status": {
"ActiveEnterTimestampMonotonic": "0",
"ActiveExitTimestampMonotonic": "0",
"ActiveState": "inactive",
"After": "systemd-journald.socket basic.target tmp.mount remote-fs.target nss-lookup.target system.slice network.target -.mount",
"AllowIsolate": "no",
"AmbientCapabilities": "0",
"AssertResult": "no",
[root@ansible-master ~]# ansible webservers -m service -a 'name=httpd state=restarted enabled=yes runlevel=2345'
[root@ansible-master ~]# ansible webservers -m shell -a 'ss -anmplt | grep httpd'
192.168.44.113 | CHANGED | rc=0 >>
LISTEN 0 128 [::]:80 [::]:* users:(("httpd",pid=8817,fd=4),("httpd",pid=8816,fd=4),("httpd",pid=8815,fd=4),("httpd",pid=8814,fd=4),("httpd",pid=8813,fd=4),("httpd",pid=8812,fd=4))
[root@ansible-master ~]#
12. uri模块
如果远端是web服务器,可以利用ansible直接请求某个网页
常见参数有:
- url= 指明请求的url的路径,如:http://10.1.32.68/test.jpg
- user= 如果请求的url需要认nstall.lo证,则认证的用户名是什么
- password= 如果请求的url需要认证,则认证的密码是什么
- method= 指明请求的方法,如GET、POST…
- body= 指明报文中实体部分的内容,一般是POST方法或PUT方法时用到
- HEADER_ 自定义请求报文中的添加的首部
[root@ansible-master ~]# ansible webservers -m uri -a 'url=http://192.168.44.113'
192.168.44.113 | SUCCESS => {
"accept_ranges": "bytes",
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"connection": "close",
"content_length": "7",
"content_type": "text/html; charset=UTF-8",
"cookies": {},
"cookies_string": "",
"date": "Tue, 21 Sep 2021 03:33:18 GMT",
"elapsed": 0,
"etag": "\"7-5cc790eac1452\"",
"json": 111111,
"last_modified": "Tue, 21 Sep 2021 03:33:12 GMT",
"msg": "OK (7 bytes)",
"redirected": false,
"server": "Apache/2.4.6 (CentOS)",
"status": 200,
"url": "http://192.168.44.113"
}
13. group模块
用来添加或删除远端主机的用户组
常见参数有:
- name= 被管理的组名
- state=present|absent 是添加还是删除,不指名默认为添加
- gid= 指明GID
- system=yes|no 是否为系统组
[root@master ~]# ansible webservers -m group -a 'name= giodtest gid=2000 system=yes'
[root@master ~]# ansible webservers -m shell -a 'tail -1 /etc/group'
[root@master ~]# ansible webservers -m group -a 'name=test state=absent'
14. user模块
管理远程主机上的用户的账号
常见参数有:
- name= 指明要管理的账号名称
- state=present|absent 指明是创建账号还是删除账号,present表示创建,absent表示删除
- system=yes|no 指明是否为系统账号
- uid= 指明用户UID
- group= 指明用户的基本组
- groups= 指明用户的附加组
- shell= 指明默认的shell
- home= 指明用户的家目录
- move_home=yes|no 当home设定了家目录,如果要创建的家目录已存在,是否将已存在的家目录进行移动
- password= 指明用户的密码,最好使用加密好的字符串
- comment= 指明用户的注释信息
- remove=yes|no 当state=absent时,也就是删除用户时,是否要删除用户的而家目录
[root@master ~]# ansible webservers -m user -a 'name=robin system=no uid=2000 group=test groups=root shell=/sbin/bash home=/home/robin password=123 comment=test'
[root@master ~]# ansible webservers -m shell -a 'id robin'
192.168.10.201 | SUCCESS | rc=0 >>
uid=2000(robin) gid=2000(test) 组=2000(test),0(root)
192.168.10.202 | SUCCESS | rc=0 >>
[root@master ~]# ansible webserver -m user -a 'name=robin remove=yes state=absent' remove 删除家目录
14. script模块
将管理端的某个脚本,移动到远端主机(不需要指明传递到远端主机的哪个路径下,系统会自动移动,然后执行),一般是自动移动到远端主机的/root/.ansible/data目录下,然后自动给予其权限,然后再开个子shell然后运行脚本,运行完成后删除脚本
测试脚本
[root@master ~]# cat test.sh
#!/bin/bash
echo "hello world" >> /tmp/robin.txt
[root@ansible-master ~]# ansible webservers -m script -a '/data/test.sh'
192.168.44.113 | CHANGED => {
"changed": true,
"rc": 0,
"stderr": "Shared connection to 192.168.44.113 closed.\r\n",
"stderr_lines": [
"Shared connection to 192.168.44.113 closed."
],
"stdout": "1111111\r\n",
"stdout_lines": [
"1111111"
]
}
[root@ansible-master ~]# ansible webservers -m shell -a 'ls /data/test.sh'
192.168.44.113 | CHANGED | rc=0 >>
/data/test.sh
[root@ansible-master ~]#
15. setup模块
可收集远程主机的facts变量的信息,相当于收集了目标主机的相关信息(如内核版本、操作系统信息、cpu、…),保存在ansible的内置变量中,之后我们有需要用到时,直接调用变量即可
[root@ansible-master ~]# ansible webservers -m setup
192.168.44.113 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.44.113",
"192.168.122.1"
],
"ansible_all_ipv6_addresses": [
"fe80::20c:29ff:fe4d:41a2"
],
"ansible_apparmor": {
"status": "disabled"
},
"ansible_architecture": "x86_64",
"ansible_bios_date": "07/22/2020",
"ansible_bios_version": "6.00",
16. template模块
基于模板方式,生成一个模板文件,复制到远程主机,让远程主机基于模板,生成符合远程主机自身的文件
注意:此模块不能在命令行使用,只能用在playbook中
常见的参数有:
- src= 指明管理端本地的模板文件的目录
- dest= 指明将模板文件拷贝到远程主机的哪个目录下
- owner= 指明拷贝到远程主机的文件的属主
- group= 指明拷贝到远程主机的文件的属组
- mode= 指明拷贝到远程主机的文件的权限
[root@ansible-master ~]# vim /data/aa.txt
[root@ansible-master ~]# cat /data/aa.txt
aaaaaaaaaaaa
[root@ansible-master ~]#
[root@ansible-master data]# vim aa.yaml
- hosts: 192.168.44.113
remote_user: root
tasks:
- name: test template
template: src=/data/aa.txt dest=/data
[root@ansible-master data]# ansible-playbook aa.yaml
PLAY [192.168.44.113] ************************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [192.168.44.113]
TASK [test template] *************************************************************************************************************************************************************
changed: [192.168.44.113]
PLAY RECAP ***********************************************************************************************************************************************************************
192.168.44.113 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@ansible-master data]#
4. ansible的playbook
1. YAML标记语言介绍
YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言包括XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言另外Ingy dtNet与Oren Ben-Kiki也是这语言的共同设计者。
YAML Ain’t Markup Language即YAML不是XML。不过在开发的这种语言时YAML的意思其实是"Yet Another Markup Language"仍是一种标记语言。其特性
- YAML的可读性好
- YAML和脚本语言的交互性好
- YAML使用实现语言的数据类型
- YAML有一个一致的信息模型
- YAML易于实现
- YAML可以基于流来处理
- YAML表达能力强扩展性好
它的基本语法规则如下。
-
大小写敏感
-
使用缩进表示层级关系
-
缩进时不允许使用Tab键,只允许使用空格。
-
缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
-
表示注释,从这个字符一直到行尾,都会被解析器忽略。
YAML 支持的数据结构有三种。
-
对象:
对象的一组键值对,使用冒号结构表示。
my_key: my_value -
数组:
数据结构的子成员是一个数组,则可以在该项下面缩进一个空格
languages:- python
- perl
- ruby
-
纯量:纯量是最基本的、不可再分的值
- 字符串 var: abc
- 布尔值 var: true
- 整数 var: 123
- 浮点数 var: 12.30
- Null var: ~
- 时间 time: 2001-12-14
- 日期 date 20:10:20
当需要执行的任务有多个时,需要一条一条编辑ansible命令,然后执行,而且当需要重复执行时,又要重新编辑执行,这样效率不高,因此ansible就可以利用playbook来完成将任务写到一个YAML格式的文件中,然后利用ansible-playbook进行调用该文件,从而实现了多条语句,可重复执行的效果,类似shell脚本的效果,ansible的playbook要借助YAML文件来实现,YAML文件扩展名通常为.yaml或.ym
2. playbook的基础组件
- hosts:运行指定任务的目标主机,多个主机用:冒号分隔
- remote_user:在远程主机上执行任务的用户;可以全局指定,也可以单个任务指定
- sudo_user:表示以sudo方式运行任务时,切换为哪个用户身份运行
tasks: 任务列表
如下例子:
[root@master ~]# vim agent.yml
- hosts: 192.168.10.201 主机
remote_user: root 远程用户
tasks: 任务
- name: add group
group: name=new state=present 组资源
- name: add user
user: name=zorro group=new 用户资源
- hosts: 192.168.10.202 主机
remote_user: root 远程用户
tasks: 任务
- name: copy
copy: src=/root/install.log dest=/tmp/ owner=nobody group=nobody 文件资源
[root@ansible-master data]# ansible-playbook agent.yml
PLAY [192.168.44.113] ************************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [192.168.44.113]
TASK [add group] *****************************************************************************************************************************************************************
changed: [192.168.44.113]
TASK [add user] ******************************************************************************************************************************************************************
changed: [192.168.44.113]
PLAY RECAP ***********************************************************************************************************************************************************************
192.168.44.113 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@ansible-master data]#
1. 测试文件语法
[root@ansible-master data]# ansible-playbook --syntax-check agent.yml
playbook: agent.yml
[root@ansible-master data]#
2. 测试执行
[root@ansible-master data]# ansible-playbook --check agent.yml
PLAY [192.168.44.113] ************************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [192.168.44.113]
TASK [add group] *****************************************************************************************************************************************************************
ok: [192.168.44.113]
TASK [add user] ******************************************************************************************************************************************************************
ok: [192.168.44.113]
PLAY RECAP ***********************************************************************************************************************************************************************
192.168.44.113 : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@ansible-master data]#
3. 运行
[root@ansible-master data]# ansible-playbook agent.yml
PLAY [192.168.44.113] ************************************************************************************************************************************************************
TASK [Gathering Facts] ***********************************************************************************************************************************************************
ok: [192.168.44.113]
TASK [add group] *****************************************************************************************************************************************************************
changed: [192.168.44.113]
TASK [add user] ******************************************************************************************************************************************************************
changed: [192.168.44.113]
PLAY RECAP ***********************************************************************************************************************************************************************
192.168.44.113 : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
[root@ansible-master data]#
3. Handlers: 在发生改变时执行的操作
(类似puppet通知机制)
[root@master ~]# cat agent.yml
- hosts: 192.168.44.113
remote_user: root
tasks:
- name: install apache software
yum: name=httpd state=present
- name: copy conf file
copy: src=/tmp/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- start httpd
- log
handlers:
- name: start httpd
service: name=httpd state=started
- name: log
shell: echo "`date` httpd started" >> /tmp/http.log
[root@master ~]# ansible-playbook --check agent.yml
[root@master ~]# ansible-playbook agent.yml
==================================================
部署rpm包的lamp环境
- hosts: webserver
remote_user: root
tasks:
- name: install httpd
yum: name=httpd state=present
- name: install mysql-server
yum: name=mysql-server state=present
- name: install php
yum: name=php state=present
- name: httpd conf
copy: src=/home/ansible/file/httpd.conf dest=/etc/httpd/conf/httpd.conf mode=644
- name: mysql conf
copy: src=/home/ansible/file/my.cnf dest=/etc/my.cnf mode=644
- name: php conf
copy: src=/home/ansible/file/php.ini dest=/etc/php.ini mode=644
notify:
- start mysql
- start httpd
- name: service status
shell: netstat -anplt | grep -E '(mysqld|httpd)' > /tmp/lamp.status
- name: get lamp.status
fetch: src=/tmp/lamp.status dest=/tmp/
- name: test page
copy: src=/home/ansible/file/test.html dest=/var/www/html/test.html
handlers:
- name: start mysql
service: name=mysqld state=started
- name: start httpd
service: name=httpd state=started
4. 变量使用
常用的变量一般就两种
- 一种为用户自己定义的变量
- 一种为facts获取的变量(即ansible webserver -m setup查到的变量)
1. 一自定义变量
1.用户在.yml文件自定义变量
[root@master ansible]# vim /home/ansible/1.yml
- hosts: webserver
remote_user: root
vars:
- var1: "abc"
- var2: 123
tasks:
- name: test vars
shell: echo "{{ var1 }} {{ var2 }}" >> /tmp/var.txt
[root@master ansible]# vim /home/ansible/1.yml
- hosts: webserver
remote_user: root
vars:
- packname: "nmap"
tasks:
- name: install package
yum: name={{ packname }} state=present
2.通过-e参数传递的变量
[root@master ansible]# ansible-playbook 1.yml -e packname=nmap
- hosts: webserver
remote_user: root
tasks:
- name: install package
yum: name={{ packname }} state=present
3.通过主机或者主机组配置文件传递的变量
主机:
[root@master ansible]# cat /etc/ansible/hosts
[webserver]
192.168.10.201 packname=nmap
192.168.10.202 packname=nmap
[root@master ansible]# ansible-playbook 1.yml
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: install package
yum: name={{ packname }} state=present
主机组
[root@master ansible]# cat /etc/ansible/hosts
[webserver]
192.168.10.201
192.168.10.202
[webserver:vars]
packname=nmap
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: install package
yum: name={{ packname }} state=present
2. 通过facts获取的系统的变量
即ansible webserver -m setup查到的变量
1. 获取系统变量
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: hostname ipaddrss
shell: echo "{{ ansible_nodename}} {{ ansible_all_ipv4_addresses }}" > /tmp/facts.txt
- name: fetch file /tmp/facts
fetch: src=/tmp/facts.txt dest=/tmp
[root@master ansible]# cat /tmp/192.168.10.202/tmp/facts.txt
agent202.puppet.com [u'192.168.10.202']
2. 本地facts(facts.d)自定义系统变量
在管控端创建如下目录:
[root@agent202 ~]# mkdir -p /etc/ansible/facts.d
创建文件:
[root@agent202 ~]# vim /etc/ansible/facts.d/test.fact
[general]
test_test1=123
test_test2=abc
[root@master ansible]# ansible webserver -m setup |grep ansible_local -A 5
"ansible_local": {
"test": {
"general": {
"test_test1": "123",
"test_test2": "abc"
}
}
}
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: test
shell: echo "{{ ansible_local.test.general.test_test1 }} {{ ansible_local.test.general.test_test2 }}" > /tmp/facts.txt
- name: fetch file /tmp/facts
fetch: src=/tmp/facts.txt dest=/tmp
[root@master ansible]# cat /tmp/192.168.10.202/tmp/facts.txt
123 abc
3. 变量注册
经常在playbook中,存储某个命令的结果在变量中,以备日后访问是很有用的. 这样使用命令模块可以在许多方面除去写站(site)特异事件,例子 你可以检测某一个特定程序是否存在,这个 ‘register’ 关键词决定了把结果存储在哪个变量中
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: user root
shell: grep ^root /etc/passwd
register: pass_contents
- name: call pass_contents
shell: echo {{ pass_contents.stdout }} > /tmp/call.txt
1. 如想查看那些值可以引用
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: user root
shell: grep ^root /etc/passwd
register: pass_contents
- name: call pass_contents
shell: echo {{ pass_contents }} > /tmp/call.txt
[root@agent202 ~]# cat /tmp/call.txt
{uchanged: True, uend: u2016-11-03 22:31:09.754515, ustdout: uroot:x:0:0:root:/root:/bin/bash, ucmd: ugrep ^root /etc/passwd, ustart: u2016-11-03 22:31:09.750428, udelta: u0:00:00.004087, ustderr: u, urc: 0, stdout_lines: [uroot:x:0:0:root:/root:/bin/bash], uwarnings: []}
2. 剧本中的条件判断
ansible和puppet软件相同 是可以支持条件判断,使用when语句
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: install package nmap
yum: name=nmap state=present
when: ansible_nodename == "agent202.puppet.com"
- name: install package httpd
yum: name=nmap state=present
when: ansible_nodename == "agent201.puppet.com"
3. 使用注册变量
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: package is install
shell: rpm -q httpd|awk -F'-' '{print $1}'
register: httpd_install
- name: test httpd
service: name=httpd state=restarted
when: httpd_install.stdout == 'httpd'
- hosts: webserver
remote_user: root
tasks:
- name: mysql user
shell: grep ^mysql /etc/passwd | awk -F':' '{print $1}'
register: mysql_install
- name: test mysqld service
service: name=mysqld state=restarted
when: mysql_install.rc == 0
4. 剧本中的循环
如:添加 abc1-abc3用户
[root@master ansible]# cat 1.yml
- hosts: webserver
remote_user: root
tasks:
- name: add new users
user: name={{ item }} state=present
with_items:
- abc1
- abc2
- abc3
5. 剧本中的roles
现在已经学过 tasks 和 handlers,那怎样组织 playbook 才是最好的方式呢?简单的回答就是:使用 roles ! Roles 基于一个已知的文件结构,去自动的加载某些 vars_files,tasks 以及 handlers。基于 roles 对内容进行分组,使得我们可以容易地与其他用户分享 roles 。
存放角色的位置:/etc/ansible/roles
如
roles/
common/ # this hierarchy represents a "role" 这里的结构代表了一个 "role"
tasks/ #
main.yml # <-- tasks file can include smaller files if warranted
handlers/ #
main.yml # <-- handlers file
templates/ # <-- files for use with the template resource
ntp.conf.j2 # <------- templates end in .j2
files/ #
bar.txt # <-- files for use with the copy resource
foo.sh # <-- script files for use with the script resource
vars/ #
main.yml # <-- variables associated with this role
defaults/ #
main.yml # <-- default lower priority variables for this role
meta/ #
main.yml # <-- role dependencies
apache/
common/
files/ 存放模块调用的文件(如:copy 和 script)
templates/ 存放模板文件
tasks/ 任务存放的目录,至少包含一个main.yml的文件,该目录下也可以有其他.yml文件,但是需要在main.yml文件中用include指令将其他.yml文件包含进来(类似 puppet)
handlers/ 存放相关触发执行器的目录,至少应该包含一个main.yml的文件,文件中定义了触发器的任务清单,该目录下也可以有其他.yml文件,但是需要在main.yml文件中用include指令将其他.yml文件包含进来
vars/ 变量存放的目录,至少应该包含一个main.yml的文件,文件中定义了相关的变量及其值,该目录下也可以有其他.yml文件,但是需要在main.yml文件中用include指令将其他.yml文件包含进来
defaults/ 默认变量存放的目录,至少应该包含一个main.yml的文件,文件中定义了此角色使用的默认变量,该目录下也可以有其他.yml文件,但是需要在main.yml文件中用include指令将其他.yml文件包含进来
meta/ 用于存放此角色元数据,至少应该包含一个main.yml的文件,文件中定义当前角色的特殊设定及其依赖关系, 该目录下也可以有其他.yml文件,但是需要在main.yml文件中用include指令将其他.yml文件包含进来
6. 如何调用定义的角色?
- hosts: webserver
remote_user: root
roles:
- apache
7. 创建一个测试roles
1. 创建角色的目录结构
mkdir -pv /etc/ansible/roles/apache/{files,templates,tasks,handlers,vars,default,meta}
2. 定义任务:
[root@master ansible]# vim /etc/ansible/roles/apache/tasks/main.yml
[root@master tasks]# cat /etc/ansible/roles/apache/tasks/main.yml
- name: install apache
yum: name=httpd state=present
- name: get main configure file
template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf
- name: get sub configure file
template: src=robin.conf.j2 dest=/etc/httpd/conf.d/robin.conf
- name: create robin dir
file: path=/srv/robin state=directory
- name: get web page
template: src=index.html.j2 dest=/var/www/html/index.html
notify:
- restart apache
3. 触发器任务:
[root@master tasks]# cat /etc/ansible/roles/apache/handlers/main.yml
- name: start apache
service: name=httpd state=present
4. 准备需要的文件
[root@master files]# ls /etc/ansible/roles/apache/templates
httpd.conf.j2 index.html.j2 robin.conf.j2