Ansible
文中具体主机组与ip在上一篇中已解析
[webgrp1]
ansible-web1
[webgrp2]
ansible-web2
核心元素:
variables #变量元素,可传递给tasks/templates使用。
tasks #任务元素,由模块定义的操作的列表,即调用模块完成任务。
templates #模板元素,使用了模板语法的文本文件。
handlers #处理器元素,通常指在某事件满足时触发的操作。
roles #角色元素。
ansible剧本(playbook)
简单的剧本
playbook格式:playbook由YMAL语言编写,YMAL格式是类似于JSON的文件格式,便于人理解和阅读,同时便于书写。
一个剧本里面可以有多个play,每个play只能有一个tasks,每个tasks可以有多个name
playbool的基础组件
- name:定义playbook或者task的名称(描述信息),每一个play都可以完成一个任务。
- hosts:hosts用于指定要执行指定任务的主机。
- user:remote_user则用于指定远程主机上的执行任务的用户。
- tasks:任务列表play的主体部分是task list,task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。
- vars:定义变量(如果不使用内部变量需要提前定义)。
- vars_files:调用定义变量文件。
- notify:任务执行结果如果是发生更改了的则触发定义在handler的任务执行。
- handlers:用于当前关注的资源发生变化时采取一定指定的操作。
先创建一个YMAL格式的文件,结尾以 .yml 或 .ymal 都可以
[root@ansible-server] cd /etc/ansible/
[root@ansible-server] vim test.yml
在文件中按格式写入想要执行的操作
---
- hosts: webgrp1
user: root
tasks:
- name: playbook_test
file: state=touch path=/tmp/playbook.txt
#参数解释
hosts: 参数指定了对哪些主机进行操作
user: 参数指定了使用什么用户登录远程主机操作
tasks: 指定了一个任务
name: 参数同样是对任务的描述,在执行过程中会打印出来
写好后检测语法
[root@ansible-server] ansible-playbook --syntax-check test.yml
playbook: test.yml #这个反馈就是没有问题
#在当前目录下敲的是相对路径
[root@ansible-server] ansible-playbook test.yml #执行该剧本
'PLAY [webgrp1] *****************************************************************
......
ansible-web1 : ok=2 changed=1 unreachable=0 failed=0 '
#我们到web1机器上查看一下/tmp/下是否有playbook.txt文件
[root@ansible-web1] ll /tmp/playbook.txt
-rw-r--r--. 1 root root 0 Mar 25 17:30 /tmp/playbook.txt
触发器
上面是一个简单的剧本,下面我们来了解一下触发器
#参数详解
handlers:由特定条件触发的tasks
handlers:处理器
notify:触发器
[root@ansible-server] cd /etc/ansible/
[root@ansible-server] vim handlers.yml
触发器可以实现多级操作,剧本中开头的—也可以不写
- hosts: webgrp2
user: root
tasks:
- name: test copy
copy: src=/root/b.txt dest=/mnt
notify: test handlers
handlers:
- name: test handlers
shell: echo "abcd" >> /mnt/b.txt
#注意notify里的名字一定要和handlers里的name一样
#只有 copy 模块真正执行后,才会去调用下面的 handlers 相关的操作,追加内容。所以这种比较适合配置文件发生更改后,需要重启服务的操作。
保存并退出测试一下剧本
[root@ansible-server] ansible-playbook --syntax-check handlers.yml
playbook: handlers.yml
[root@ansible-server] ansible-playbook handlers.yml
ok: [ansible-web2]
TASK [test copy] ********************************************************************
An exception occurred during task execution. To see the full traceback, use -vvv. The error was: AnsibleFileNotFound: Could not find or access '/root/b.txt'
fatal: [ansible-web2]: FAILED! => {"changed": false, "msg": "Could not find or access '/root/b.txt'"}
to retry, use: --limit @/etc/ansible/handlers.retry
PLAY RECAP **************************************************************************
ansible-web2 : ok=1 changed=0 unreachable=0 failed=1
#这次反馈不是绿色的了,星号中间的是报错信息,我们来看一下
执行剧本之前一定要确定有我们要发的文件,没有的话就创建一个
[root@ansible-server] touch /root/b.txt
[root@ansible-server] ansible-playbook handlers.yml
'PLAY [webgrp2] **********************************************************************
......
ansible-web2 : ok=3 changed=2 unreachable=0 failed=0 '
#执行成功,我们到web2上查看
[root@ansible-web2] ll /mnt/b.txt
-rw-r--r--. 1 root root 5 Mar 25 17:49 /mnt/b.txt
[root@ansible-web2] cat /mnt/b.txt
abcd
#我们可以看到不仅文件拷贝了过去,还触发了我们的第二个指令,将abcd重定向到b.txt里
循环迭代
对迭代项的引用,固定变量名为”item”,使用with_item属性给定要迭代的元素,需要重复执行的任务。
#我们在web2上确定一下有没有安装httpd php php-mysql php-mbstring php-gd软件
[root@ansible-web2] rpm -q httpd php php-mysql php-mbstring php-gd
package httpd is not installed
package php is not installed
package php-mysql is not installed
package php-mbstring is not installed
package php-gd is not installed
#确定都没有后回到server端,有就卸载
[root@ansible-server] cd /etc/ansible/
[root@ansible-server] vim list.yml
接下来我们就用with_item属性
item相当于for循环里面的i
with_items取值,但不支持通配符
- hosts: webgrp2
remote_user: root
tasks:
- name: install packages
yum: name={{ item }} state=latest
with_items:
- httpd
- php
- php-mysql
- php-mbstring
- php-gd
检测
[root@ansible-server] ansible-playbook --syntax-check list.yml
playbook: list.yml
[root@ansible-server] ansible-playbook list.yml #这次时间会长一点
'PLAY [webgrp2] **********************************************************************
......
ansible-web2 : ok=2 changed=1 unreachable=0 failed=0 '
#我们到web2主机上
[root@ansible-web2] rpm -q httpd php php-mysql php-mbstring php-gd
httpd-2.4.6-90.el7.centos.x86_64
php-5.4.16-46.1.el7_7.x86_64
php-mysql-5.4.16-46.1.el7_7.x86_64
php-mbstring-5.4.16-46.1.el7_7.x86_64
php-gd-5.4.16-46.1.el7_7.x86_64
#可以看到都下载好了
自定义vars_files变量
变量调用语法: {{ var_name }}
我们要先创建变量目录
[root@ansible-server] mkdir /etc/ansible/vars
[root@ansible-server] cd /etc/ansible/vars/
[root@ansible-server] vim file.yml
在这个变量文件中写入
src_path: /root/test/b.txt
dest_path: /opt/test/
在server端创建/root/test/目录和/root/test/b.txt文件
[root@ansible-server] cd /etc/ansible/
[root@ansible-server] vim vars.yml
在文件中写入
- hosts: ansible-web1
user: root
vars_files:
- /etc/ansible/vars/file.yml
tasks:
- name: create directory
file: path={{ dest_path }} mode=755 state=directory
- name: copy file
copy: src={{ src_path }} dest={{ dest_path }}
#ansible-web1就是webgrp1主机组内的主机,不要忘记哦
检测
[root@ansible-server] ansible-playbook --syntax-check vars.yml
playbook: vars.yml
[root@ansible-server] ansible-playbook vars.yml
'PLAY [ansible-web1] *****************************************************************
......
ansible-web1 : ok=3 changed=1 unreachable=0 failed=0 '
#我们换到web1上查看
[root@ansible-web1] ll -d /opt/test/
drwxr-xr-x. 2 root root 19 Mar 25 18:30 /opt/test/
[root@ansible-web1] ls /opt/test/
b.txt
#我们可以看到不仅创建好了目录,文件也拷贝了过去,所以引用变量也是可以的
剧本多个play
在web1上创建一个名为mygrp的系统组,gid为2003,并且创建一个bai用户为系统用户,用户组为mygrp
在web2上下载apache并启动apache
#web1主机
[root@ansible-web1] id bai
id: bai: no such user
[root@ansible-web1] cat /etc/group |grep mygrp
#web2主机
[root@ansible-web2] rpm -q httpd
httpd-2.4.6-90.el7.centos.x86_64
[root@ansible-web2] yum -y remove httpd
#server主机
[root@ansible-server] cd /etc/ansible/
[root@ansible-server] vim play.yml
创建多个play
- hosts: webgrp1
user: root
tasks:
- name: create a group
group: name=mygrp gid=2003 system=true
- name: create a user
user: name=bai group=mygrp system=true
- hosts: webgrp2
user: root
tasks:
- name: install apache
yum: name=httpd state=latest
- name: start httpd service
service: name=httpd state=started
#group模块参数
#name参数:必须参数,用于指定组名称。
#state参数:用于指定组的状态,两个值可选,present,absent,默认为 present,设置为absent 表示删除组。
#gid参数:用于指定组的gid,不指定为随机。
#system参数:true为系统组,可选。
测试
[root@ansible-server] ansible-playbook --syntax-check play.yml
playbook: play.yml
[root@ansible-server] ansible-playbook play.yml
'PLAY [webgrp1] **********************************************************************
......
ansible-web1 : ok=3 changed=2 unreachable=0 failed=0
ansible-web2 : ok=3 changed=2 unreachable=0 failed=0 '
#web1主机
[root@ansible-web1] id bai
uid=998(bai) gid=2003(mygrp) groups=2003(mygrp)
[root@ansible-web1] cat /etc/group |grep mygrp
mygrp:x:2003:
#web2主机
[root@ansible-web2] rpm -q httpd
httpd-2.4.6-90.el7.centos.x86_64
[root@ansible-web2] systemctl status httpd
● httpd.service - The Apache HTTP Server
Loaded: loaded (/usr/lib/systemd/system/httpd.service; disabled; vendor preset: disabled)
Active: 'active (running)' since Thu 2020-03-26 03:20:26 CST; 13min ago
when模块
先判断when条件是否成立,如果成立则继续执行当前模块的命令
[root@ansible-server] cd /etc/ansible/
[root@ansible-server] vim when.yml
- hosts: webgrp1
user: root
tasks:
- name: use when
file: state=touch path=/tmp/when.txt
- name: insert data
shell: echo 123 >> /tmp/when.txt
when: ansible_hostname == "ansible-web1" #when可以在shell上也可以在shell下
注意:when判断中的ansible-web1指的是被控节点上真正的主机名称,不是解析的名,如果被控节点的主机名不是ansible-web1则不会执行insert data的命令
测试
[root@ansible-server] ansible-playbook --syntax-check when.yml
playbook: when.yml
[root@ansible-server] ansible-playbook when.yml
'PLAY [webgrp1] **********************************************************************
......
ansible-web1 : ok=3 changed=2 unreachable=0 failed=0 '
#web1主机
[root@ansible-web1] ll /tmp/when.txt
-rw-r--r--. 1 root root 4 Mar 26 03:45 /tmp/when.txt
[root@ansible-web1] cat /tmp/when.txt
123
#可以看到我们执行成功了
剧本中定义变量
使用变量并不显示搜集主机相关信息
gather_facts参数:指定了在任务部分执行前,是否先执行setup模块获取主机相关信息,默认值为true,改成false之后在执行过程中不会搜集主机相关信息。
在文件中
roles角色
roles是在ansible中playbooks的目录组织结构,模块化之后,成为roles的组织结构,易读,代码可重用,层次清晰
目录顺序
- role_name/:角色名称=目录。
- files/:存储一些可以用copy调用的静态文件。
- tasks/: 存储任务的目录,此目录中至少应该有一个名为main.yml的文件,用于定义各task。其它的文件需要由main.yml进行“包含”调用。
- handlers/:此目录中至少应该有一个名为main.yml的文件,用于定义各handler。其它的文件需要由(与notify:名字相同,方便notify通知执行下一条命令)通过main.yml进行“包含”调用。
- vars/:此目录中至少应该有一个名为main.yml的文件,用于定义各variable;其它的文件需要由main.yml进行“包含”调用。
- templates/:存储由template模块调用的模板文本。(也可以调用变量)
- site.yml:定义哪个主机应用哪个角色。
编写内容
[root@ansible-server] cd /etc/ansible/roles/ #role为自带目录,如果不存在可以创建
[root@ansible-server] mkdir nginx/{files,handlers,tasks,templates,vars} -p #递归创建
[root@ansible-server] touch site.yml nginx/{handlers,tasks,vars}/main.yml
[root@ansible-server] yum install -y tree #下载tree命令,结构树
[root@ansible-server] tree /etc/ansible/roles/nginx/
/etc/ansible/roles/nginx/
├── files
├── handlers
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
#files目录和templates目录下还没有创建文件,先不着急
#创建nginx的测试文件
[root@ansible-server] echo 1234 > nginx/files/index.html
#安装nginx并配置模板
[root@ansible-server] yum install -y nginx && cp /etc/nginx/nginx.conf nginx/templates/nginx.conf.j2
#一定要将nginx的主配置文件结尾改为.j2结尾
#编写任务
[root@ansible-server] vim nginx/tasks/main.yml
任务中写入
---
- name: install epel
yum: name=epel-release state=latest
- name: install nginx
yum: name=nginx state=latest
- name: copy nginx.conf templte
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
- name: copy index.html
copy: /etc/ansible/roles/nginx/files/index.html dest=/usr/share/nginx/html/index.html
notify: start nginx
#注意这里的notify一定和nginx/handlers/main.yml的name一样
保存并退出,继续
[root@ansible-server] vim nginx/handlers/main.yml
文件中写入
---
- name: start nginx
service: name=nginx state=started
#这里的name一定要和nginx/tasks/main.yml的notify的名字一样
保存并退出,继续
vim nginx/templates/nginx.conf.j2
#以.j2结尾可以引用变量,
修改成如下内容,自定义变量
把worker_processes 后改为变量名,变量还没有定义,继续
[root@ansible-server] vim nginx/vars/main.yml #这个文件中写入变量的值
在文件中定义变量
worker_connections: 2
编写剧本
[root@ansible-server] vim site.yml
文件中写入
---
- hosts: webgrp2
user: root
roles:
- nginx
保存并退出,检测
[root@ansible-server] ansible-playbook --syntax-check site.yml
playbook: site.yml
[root@ansible-server] ansible-playbook site.yml
#web2主机
[root@ansible-web2] netstat -lntp #或者ss -lntp
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 3102/nginx: master
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 926/sshd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 1007/master
tcp6 0 0 :::80 :::* LISTEN 3102/nginx: master
tcp6 0 0 :::22 :::* LISTEN 926/sshd
tcp6 0 0 ::1:25 :::* LISTEN 1007/master
[root@ansible-web2] cat /etc/nginx/nginx.conf | grep worker_processes #查看一下我们的变量
#worker_processes auto;
worker_processes 2;
#这里看到已经为2了