ansible安装与连接
实验准备: 三台机器,一台管理,两台被管理
- 静态ip
- 主机名及主机名互相绑定
- 关闭防火墙, selinux
- 时间同步
- 确认和配置yum源(需要epel源)
实验过程:
第1步: 管理机上安装ansible,被管理节点必须打开ssh服务.
# yum install epel-release
# yum install ansible
# ansible --version
ansible 2.8.4
config file = /etc/ansible/ansible.cfg
configured module search path = [u'/root/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
ansible python module location = /usr/lib/python2.7/site-packages/ansible
executable location = /usr/bin/ansible
python version = 2.7.5 (default, Oct 30 2018, 23:45:53) [GCC 4.8.5 20150623 (Red Hat 4.8.5-36)]
第2步: 实现master对agent的免密登录
只需要实现master对被管理机的免密即可
如果这一步不做, 则在后面操作agent时都要加-k参数传密码;或者在主机清单里传密码
master# ssh-keygen
master# ssh-copy-id 10.1.1.12
master# ssh-copy-id 10.1.1.13
第3步: 在master上定义主机组,并测试连接性
master# vim /etc/ansible/hosts
[group1]
10.1.1.12
10.1.1.13
master# ansible -m ping group1
10.1.1.13 | SUCCESS => {
"changed": false,
"ping": "pong"
}
10.1.1.12 | SUCCESS => {
"changed": false,
"ping": "pong"
}
master# ansible -m ping all
10.1.1.13 | SUCCESS => {
"changed": false,
"ping": "pong"
}
10.1.1.12 | SUCCESS => {
"changed": false,
"ping": "pong"
}
服务器分组
ansible通过一个主机清单功能来实现服务器分组。
Ansible的默认主机清单配置文件为/etc/ansible/hosts.
示例:
[nginx] 组名
apache[1:10].aaa.com 表示apache1.aaa.com到apache10.aaa.com这10台机器
nginx[a:z].aaa.com 表示nginxa.aaa.com到nginxz.aaa.com共26台机器
10.1.1.[11:15] 表示10.1.1.11到10.1.1.15这5台机器
示例:
[nginx]
10.1.1.13:2222 表示10.1.1.13这台,但ssh端口为2222
示例: 定义10.1.1.13:2222这台服务器的别名为nginx1
nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222
示例: 没有做免密登录的服务器可以指定用户名与密码
nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 ansible_ssh_user=root ansible_ssh_pass="123456"
示例: 利用别名来分组
nginx1 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 ansible_ssh_user=root ansible_ssh_pass="123456"
nginx2 ansible_ssh_host=10.1.1.12
[nginx]
nginx1
nginx2
小结:
主机清单的作用: 服务器分组。
主机清单的常见功能:
-
可以通过IP范围来分, 主机名名字的范围来分
-
如果ssh端口不是22的,可以传入新的端口。
-
没有做免密登录,可以传密码。
拓展: 使用加密的主机清单
主机清单内有明文密码,如果公司对安全性要求高,可对主机清单加密
master# ansible-vault encrypt /etc/ansible/hosts
New Vault password:
Confirm New Vault password: # 设置一个密码2次(密码要记住)
Encryption successful
设置完后/etc/ansible/hosts就变成了密文
批量管理时,要指定密码
master# ansible -m ping group1 --ask-vault
Vault password: # 批量操作时,需要指定上面步骤设置的密码了
如果要编辑加密的主机清单,则使用下面命令
master# ansible-vault edit /etc/ansible/hosts --ask-vault-pass
Vault password:
还原的做法:
master# rm /etc/ansible/hosts -rf # 删除密文主机清单
master# vim /etc/ansible/hosts # 再编辑明文主机清单(按需求自定义)
10.1.1.13 ansible_ssh_host=10.1.1.13 ansible_ssh_port=2222 ansible_ssh_user=root ansible_ssh_pass="123456"
[group1]
10.1.1.12
10.1.1.13
master# chmod 600 /etc/ansible/hosts # 改为只有root可读可写
三、ansible模块
ansible是基于模块工作的。
ansible本身没有批量管理的能力, 真正具有批量管理的是ansible所运行的模块。
ansible支持的模块非常的多,目前版本3000多个。
学习建议: 熟悉一些常见的模块,其它模块需要用到时就去查询,主要学习思路。
查看所有支持的模块
# ansible-doc -l
a10_server Manage A10 Networks AX/SoftAX...
a10_server_axapi3 Manage A10 Networks AX/SoftAX...
a10_service_group Manage A10 Networks AX/SoftAX...
a10_virtual_server Manage A10 Networks AX/SoftAX...
aci_aaa_user Manage AAA users (aaa:User)
。。。。。。
如果要查看ping模块的用法,使用下面命令(其它模块以此类推)
# ansible-doc ping
官网模块文档地址: https://docs.ansible.com/ansible/latest/modules/list_of_all_modules.html
hostname模块
hostname模块用于修改主机名(注意: 它不能修改/etc/hosts文件)
https://docs.ansible.com/ansible/latest/modules/hostname_module.html#hostname-module
将其中一远程机器主机名修改为agent1.cluster.com
master# ansible 10.1.1.12 -m hostname -a 'name=agent1.cluster.com'
基本格式: ansible 被管理机名称或组名 -m 模块名 -a "参数1=值1 参数2=值2"
file模块(重点)
file模块用于对文件相关的操作(创建, 删除, 软硬链接等)
https://docs.ansible.com/ansible/latest/modules/file_module.html#file-module
创建一个目录
master# ansible group1 -m file -a 'path=/test state=directory'
创建一个文件
master# ansible group1 -m file -a 'path=/test/111 state=touch'
递归修改owner,group,mode
master# ansible group1 -m file -a 'path=/test recurse=yes owner=bin group=daemon mode=1777'
ansible nginx -m file -a 'path=/usr/local/nginx recurse=yes owner=www group=www mode=1777'
删除目录(连同目录里的所有文件)
master# ansible group1 -m file -a 'path=/test state=absent'
创建文件并指定owner,group,mode等
master# ansible group1 -m file -a 'path=/tmp/111 state=touch owner=bin group=daemon mode=1777'
删除文件
master# ansible group1 -m file -a 'path=/tmp/111 state=absent'
创建软链接文件
master# ansible group1 -m file -a 'src=/etc/fstab path=/tmp/fstab state=link'
创建硬链接文件
master# ansible group1 -m file -a 'src=/etc/fstab path=/tmp/fstab2 state=hard'
copy模块(重点)
copy模块用于对文件的远程拷贝操作(如把本地的文件拷贝到远程的机器上)
https://docs.ansible.com/ansible/latest/modules/copy_module.html#copy-module
在master上准备一个文件,拷贝此文件到group1的所有机器上
master# echo master > /tmp/222
master# ansible group1 -m copy -a 'src=/tmp/222 dest=/tmp/333'
使用content参数直接往远程文件里写内容(会覆盖原内容)
master# ansible group1 -m copy -a 'content="ha ha\n" dest=/tmp/333'
注意:ansible中-a后面的参数里也有引号时,记得要单引双引交叉使用,如果都为双引会出现问题
使用force参数控制是否强制覆盖
如果目标文件已经存在,则不操作;目标文件不存在,则会拷贝过去
master# ansible group1 -m copy -a 'src=/tmp/222 dest=/tmp/333 force=no'
如果目标文件已经存在,则会强制覆盖
master# ansible group1 -m copy -a 'src=/tmp/222 dest=/tmp/333 force=yes'
使用backup参数控制是否备份文件
backup=yes表示如果拷贝的文件内容与原内容不一样,则会备份一份
group1的机器上会将/tmp/333备份一份(备份文件命名加上时间),再远程拷贝新的文件为/tmp/333
master# ansible group1 -m copy -a 'src=/etc/fstab dest=/tmp/333 backup=yes owner=daemon group=daemon mode=1777'
copy模块拷贝时要注意拷贝目录后面是否带"/"符号
/etc/yum.repos.d后面不带/符号,则表示把/etc/yum.repos.d整个目录拷贝到/tmp/目录下
master# ansible group1 -m copy -a 'src=/etc/yum.repos.d dest=/tmp/'
/etc/yum.repos.d/后面带/符号,则表示把/etc/yum.repos.d/目录里的所有文件拷贝到/tmp/目录下
master# ansible group1 -m copy -a 'src=/etc/yum.repos.d/ dest=/tmp/'
练习: 在master上配置好所有的yum源,然后拷贝到group1的远程机器上(要求目录内的内容完全一致)
练习: 使用hostname模块修改主机名后.在master上修改/etc/hosts文件,并拷贝到group1的远程机器上
关于DNS的补充:
-
域名为公网的唯一名字,主机名为内网的名字(可以重名,但最好不要这么做)
-
目前自建DNS做域名解析已经很少了, 但可以通过DNS解析主机名来实现内网多台服务器的解析
-
现在学了ansible的hostname与copy模块,轻松实现N多台服务器的主机名管理,DNS也不需要再搭建了
fetch模块
fetch模块与copy模块类似,但作用相反。用于把远程机器的文件拷贝到本地。
https://docs.ansible.com/ansible/latest/modules/fetch_module.html#fetch-module
第1步: 在两台被管理机上分别创建一个同名文件(但内容不同)
agent1# echo agent1 > /tmp/1.txt
agent2# echo agent2 > /tmp/1.txt
第2步: 从master上fecth文件(因为group1里有2台机器,为了避免同名文件文件冲突,它使用了不同的目录)
master# ansible group1 -m fetch -a 'src=/tmp/1.txt dest=/tmp/'
10.1.1.12 | CHANGED => {
"changed": true,
"checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"dest": "/tmp/10.1.1.12/tmp/1.txt", 10.1.1.12的在这里
"md5sum": "0d59da0b2723eb03ecfbb0d779e6eca5",
"remote_checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"remote_md5sum": null
}
10.1.1.13 | CHANGED => {
"changed": true,
"checksum": "b27fb3c4285612643593d53045035bd8d972c995",
"dest": "/tmp/10.1.1.13/tmp/1.txt", 10.1.1.13的在这里
"md5sum": "cd0bd22f33d6324908dbadf6bc128f52",
"remote_checksum": "b27fb3c4285612643593d53045035bd8d972c995",
"remote_md5sum": null
}
第3步: 先删除上面fetch过来的, 然后尝试只fetch其中一台机器的,也会使用名称来做子目录区分
master# rm /tmp/10.1.1.* -rf
master# ansible 10.1.1.12 -m fetch -a 'src=/tmp/1.txt dest=/tmp/'
10.1.1.12 | CHANGED => {
"changed": true,
"checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"dest": "/tmp/10.1.1.12/tmp/1.txt", 只fetch一个,也会这样命名
"md5sum": "0d59da0b2723eb03ecfbb0d779e6eca5",
"remote_checksum": "d2911a028d3fcdf775a4e26c0b9c9d981551ae41",
"remote_md5sum": null
}
注意: fetch模块不能从远程拷贝目录到本地
user模块
user模块用于管理用户账号和用户属性。
https://docs.ansible.com/ansible/latest/modules/user_module.html#user-module
创建aaa用户,默认为普通用户,创建家目录
master# ansible group1 -m user -a 'name=aaa state=present'
创建bbb系统用户,并且登录shell环境为/sbin/nologin
master# ansible group1 -m user -a 'name=bbb state=present system=yes shell="/sbin/nologin"'
创建ccc用户, 使用uid参数指定uid, 使用password参数传密码
master# echo 123456 | openssl passwd -1 -stdin
$1$DpcyhW2G$Kb/y1f.lyLI4MpRlHU9oq0
下一句命令注意一下格式,密码要用双引号引起来,单引号的话验证时会密码不正确
master# ansible group1 -m user -a 'name=ccc uid=2000 state=present password="$1$DpcyhW2G$Kb/y1f.lyLI4MpRlHU9oq0"'
创建一个普通用户叫hadoop,并产生空密码密钥对
master# ansible group1 -m user -a 'name=hadoop generate_ssh_key=yes'
删除aaa用户,但家目录默认没有删除
master# ansible group1 -m user -a 'name=aaa state=absent'
删除bbb用户,使用remove=yes参数让其删除用户的同时也删除家目录
master# ansible group1 -m user -a 'name=bbb state=absent remove=yes'
group模块
group模块用于管理用户组和用户组属性。
https://docs.ansible.com/ansible/latest/modules/group_module.html#group-module
创建组
master# ansible group1 -m group -a 'name=groupa gid=3000 state=present'
删除组(如果有用户的gid为此组,则删除不了)
master# ansible group1 -m group -a 'name=groupa state=absent'
cron模块
cron模块用于管理周期性时间任务
https://docs.ansible.com/ansible/latest/modules/cron_module.html#cron-module
创建一个cron任务,不指定user的话,默认就是root(因为我这里是用root操作的)。
如果minute,hour,day,month,week不指定的话,默认都为*
master# ansible group1 -m cron -a 'name="test cron1" user=root job="touch /tmp/111" minute=*/2'
删除cron任务
master# ansible group1 -m cron -a 'name="test cron1" state=absent'
yum_repository模块
yum_repository模块用于配置yum仓库。
https://docs.ansible.com/ansible/latest/modules/yum_repository_module.html
增加一个/etc/yum.repos.d/local.repo配置文件
master# ansible group1 -m yum_repository -a "name=local description=localyum baseurl=file:///mnt/ enabled=yes gpgcheck=no"
注意:此模块只帮助配置yum仓库,但如果仓库里没有软件包,安装一样会失败。所以可以手动去挂载光驱到/mnt目录
# mount /dev/cdrom /mnt
删除/etc/yum.repos.d/local.repo配置文件
master# ansible group1 -m yum_repository -a "name=local state=absent"
yum模块(重点)
yum模块用于使用yum命令来实现软件包的安装与卸载。
https://docs.ansible.com/ansible/latest/modules/yum_module.html#yum-module
使用yum安装一个软件(前提:group1的机器上的yum配置都已经OK)
master# ansible group1 -m yum -a 'name=vsftpd state=present'
使用yum安装httpd,httpd-devel软件,state=latest表示安装最新版本
master# ansible group1 -m yum -a 'name=httpd,httpd-devel state=latest'
使用yum卸载httpd,httpd-devel软件
master# ansible group1 -m yum -a 'name=httpd,httpd-devel state=absent'
service模块(重点)
service模块用于控制服务的启动,关闭,开机自启动等。
https://docs.ansible.com/ansible/latest/modules/service_module.html#service-module
启动vsftpd服务,并设为开机自动启动
master# ansible group1 -m service -a 'name=vsftpd state=started enabled=on'
关闭vsftpd服务,并设为开机不自动启动
master# ansible group1 -m service -a 'name=vsftpd state=stopped enabled=false'
command与shell模块(重点)
两个模块都是用于执行linux命令的,这对于命令熟悉的工程师来说,用起来非常high。
shell模块与command模块差不多(command模块不能执行一些类似$HOME,>,<,|等符号,但shell可以)
https://docs.ansible.com/ansible/latest/modules/command_module.html
https://docs.ansible.com/ansible/latest/modules/shell_module.html
master# ansible -m command group1 -a "useradd user2"
master# ansible -m command group1 -a "id user2"
master# ansible -m command group1 -a "cat /etc/passwd |wc -l" --报错
master# ansible -m shell group1 -a "cat /etc/passwd |wc -l" --成功
master# ansible -m command group1 -a "cd $HOME;pwd" --报错
master# ansible -m shell group1 -a "cd $HOME;pwd" --成功
注意: shell模块并不是百分之百任何命令都可以,比如vim或ll别名就不可以。
script模块(重点)
script模块用于在远程机器上执行==本地==脚本(不需要把脚本文件拷贝到远程)。
https://docs.ansible.com/ansible/latest/modules/script_module.html#script-module
在master上准备一个脚本
master# vim /tmp/1.sh
#!/bin/bash
mkdir /tmp/haha
touch /tmp/haha/{1..10}
在group1的远程机器里都执行master上的/tmp/1.sh脚本(此脚本不用给执行权限)
master# ansible group1 -m script -a '/tmp/1.sh'
扩展: 使用shell脚本实现在group1的被管理机里的mariadb里创建一个abc库
#!/bin/bash
yum install mariadb-server -y &> /dev/null
systemctl start mariadb
systemctl enable mariadb
mysql << EOF
create database abc;
quit
EOF
把上面的脚本使用script模块在group1被管理机里执行即可
四、playbook
playbook(剧本): 是ansible用于配置,部署,和管理被控节点的剧本,用于ansible操作的编排。
参考:https://docs.ansible.com/ansible/latest/user_guide/playbooks_intro.html
写playbook的格式为yaml格式,此格式对书写有一定要求, 用到yaml格式的工具还比较多, 如:
- saltstack
- elk配置文件
- prometheus配置文件
- docker-compose编排文件
- kubernetes编排文件
YAML格式
- 以.yaml或.yml结尾
- 文件的第一行以 "—"开始,表明YMAL文件的开始(可选)
- 以#号开头为注释
- 列表中的所有成员都开始于相同的缩进级别, 并且使用一个
"- "
作为开头(一个横杠和一个空格) - 一个字典是由一个简单的
键: 值
的形式组成(这个冒号后面必须是一个空格) - 注意: 写这种文件不要使用tab键,都使用空格
参考: https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html#yaml-syntax
下面看一个官方的示例感受一下
---
# 一位职工记录
name: Example Developer
job: Developer
skill: Elite
employed: True
foods:
- Apple
- Orange
- Strawberry
- Mango
languages:
ruby: Elite
python: Elite
dotnet: Lame
playbook实例
先直接来看一个实例
第1步: 创建一个存放playbook的目录(路径自定义)
master# mkdir /etc/ansible/playbook
第2步: 准备httpd配置文件,并修改成你想要的配置
master# yum install httpd -y
按需要修改你想要的配置(为了测试可以随意改动标记一下)
master# vim /etc/httpd/conf/httpd.conf
第3步: 写一个playbook文件(后缀为.yml或.yaml)
master# vim /etc/ansible/playbook/example.yml
---
- hosts: group1
remote_user: root
tasks:
- name: ensure apache is at the latest version
yum: name=httpd,httpd-devel state=latest
- name: write the apache config file
copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify:
- restart apache
- name: ensure apache is running (and enable it at boot)
service: name=httpd state=started enabled=yes
handlers:
- name: restart apache
service: name=httpd state=restarted
第4步: 执行写好的palybook
- 会显示出执行的过程,并且执行的每一步都有ok,changed,failed等标识
- 执行如果有错误(failed)会回滚,解决问题后,直接再执行这条命令即可,并会把failed改为changed(幂等性)
master# ansible-playbook /etc/ansible/playbook/example.yml
Playbook常见语法
hosts: 用于指定要执行任务的主机,其可以是一个或多个由冒号分隔主机组.
remote_user: 用于指定远程主机上的执行任务的用户.
- hosts: group1
remote_user: root
tasks: 任务列表, 按顺序执行任务.
- 如果一个host执行task失败, 整个tasks都会回滚, 修正playbook 中的错误, 然后重新执行即可.
tasks:
- name: ensure apache is at the latest version
yum: name=httpd,httpd-devel state=latest
- name: write the apache config file
copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/httpd.conf
handlers: 类似task,但需要使用notify通知调用。
- 不管有多少个通知者进行了notify,等到play中的所有task执行完成之后,handlers也只会被执行一次.
- handlers最佳的应用场景是用来重启服务,或者触发系统重启操作.除此以外很少用到了.
notify:
- restart apache
- name: ensure apache is running (and enable it at boot)
service: name=httpd state=started enabled=yes
handlers:
- name: restart apache
service: name=httpd state=restarted
练习: 修改httpd的端口为8080,再执行playbook测试
variables: 变量
- 定义变量可以被多次方便调用
master# vim /etc/ansible/playbook/example2.yml
---
- hosts: group1
remote_user: root
vars:
- user: test1
tasks:
- name: create user
user: name={{user}} state=present
master# ansible-playbook /etc/ansible/playbook/example2.yaml
案例: playbook编排vsftpd
写一个playbook实现
- 配置yum
- 安装vsftpd包
- 修改配置文件(要求拒绝匿名用户登录)
- 启动服务并实现vsftpd服务开机自动启动
---
- hosts: group1
remote_user: root
tasks:
- name: rm yum repository
file: path=/etc/yum.repos.d/ state=absent
- name: 同步master上的yum源到group1
copy: src=/etc/yum.repos.d dest=/etc/
- name: ensure vsftpd is at the latest version
yum: name=vsftpd state=latest
- name: write the apache config file
copy: src=/etc/vsftpd/vsftpd.conf dest=/etc/vsftpd/vsftpd.conf
notify:
- restart vsftpd
- name: ensure vsftpd is running (and enable it at boot)
service: name=vsftpd state=started enabled=yes
handlers:
- name: restart vsftpd
service: name=vsftpd state=restarted
playbook编排多个hosts任务
--- # ---代表开始(可选项,不写也可以)
- hosts: 10.1.1.12
remote_user: root
tasks:
- name: 创建/test1/目录
file: path=/test1/ state=directory
# 这里不能用---分隔,会报语法错误(后面课程玩k8s编排也写YAML文件,是可以用---来分隔段落的)
- hosts: 10.1.1.13
remote_user: root
tasks:
- name: 创建/test2/目录
file: path=/test2/ state=directory
... # ...代表结束(可选项,不写也可以)
案例: 编排nfs搭建与客户端挂载
1, 在master上准备nfs配置文件
master# vim /etc/exports
/share *(ro)
2, 编写yaml编排文件
master# vim /etc/ansible/playbook/nfs.yml
---
- hosts: 10.1.1.12
remote_user: root
tasks:
- name: 安装nfs服务相关软件包
yum: name=nfs-utils,rpcbind,setup state=latest
- name: 创建共享目录
file: path=/share/ state=directory
- name: 同步nfs配置文件
copy: src=/etc/exports dest=/etc/exports
notify: restart nfs
- name: 启动rpcbind服务,并设置为开机自启动
service: name=rpcbind state=started enabled=on
- name: 启动nfs服务,并设置为开机自启动
service: name=nfs state=started enabled=on
handlers:
- name: restart nfs
service: name=nfs state=restarted
- hosts: 10.1.1.13
remote_user: root
tasks:
- name: 安装nfs客户端软件包
yum: name=nfs-utils state=latest
- name: 挂载nfs服务器的共享
shell: mount 10.1.1.12:/share /mnt
3, 执行playbook
master# ansible-playbook /etc/ansible/playbook/nfs.yaml
幂等性解决
上面的shell: mount 10.1.1.12:/share /mnt
会有幂等性问题
- 解决方法1: 改成如下
shell: umount /mnt/ &> /dev/null && mount 10.1.1.12:/share /mnt || mount 10.1.1.12:/share /mnt
这样第2次挂载不会出现问题,但是每次都是changed
- 解决方法2: 改成如下
mount: src=10.1.1.12:/share path=/share fstype=nfs state=mounted
这样就是完全的幂等性了,并且挂载点/share不存在会自动创建
handlers重启多个服务(拓展)
有时候可能修改一个配置文件需要重启多个服务,可参考下面的格式(注意: 此例只是为了说明格式,实际上同步vsftpd.conf后并不需要重启httpd服务)
- hosts: group1
remote_user: root
tasks:
- name: 安装vsftpd
yum: name=vsftpd state=present
- name: 同步vsftpd.conf配置文件
copy: src=/etc/vsftpd/vsftpd.conf dest=/etc/vsftpd/vsftpd.conf
notify:
- restart vsftpd
- restart httpd
- name: 启动vsftpd服务并设置开机自启动
service: name=vsftpd state=started enabled=on
handlers:
- name: restart vsftpd
service: name=vsftpd state=restarted
- name: restart httpd
service: name=httpd state=restarted
五、roles(难点)
roles介绍
假设我们要写一个playbook来安装管理lamp环境,那么这个playbook就会写很长。
所以我们希望把这个很大的文件分成多个功能拆分, 分成apache管理,php管理,mysql管理这3个roles.
然后在需要使用的时候直接调用写好的roles就可以了, 以实现代码复用。
问题: 什么是roles?使用roles有什么好处?
答: 相当于编程里的函数,类,模块化的概念,把需要重复调用的代码封装,调用方便,减少代码量。
roles的目录结构
使用roles需要先创建其目录结构,然后将playbook拆分写到不同的目录里。
每一个角色一个目录,角色目录下又需要包含以下几个子目录:
目录名称 | 描述 |
---|---|
files | 用来存放由copy模块或script模块调用的文件 |
tasks | 目录里需要一个main.yml文件,用来定义tasks |
handlers | 目录里需要一个main.yml文件,用来定义handlers |
templates | 用来存放jinjia2模板(python相关,这里可以先忽略) |
vars | 目录里需要一个main.yml文件,用来定义变量 |
meta | 目录里需要一个main.yml文件,定义此角色的特殊设定及其依赖关系 |
说明: 我们主要使用的目录为files,tasks,handlers, 用不到的目录可以按结构创建出来不使用即可.
案例: roles实现lamp
需定制三个角色: httpd,mysql,php (roles名可以自定义)
1, 创建roles目录结构
master# cd /etc/ansible/roles/
master# mkdir -p {httpd,mysql,php}/{files,tasks,handlers,templates,vars,meta}
master# touch {httpd,mysql,php}/{tasks,handlers,vars,meta}/main.yml
master# yum install tree -y
master# tree /etc/ansible/roles/
/etc/ansible/roles/
├── httpd
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ └── vars
│ └── main.yml
├── mysql
│ ├── files
│ ├── handlers
│ │ └── main.yml
│ ├── meta
│ │ └── main.yml
│ ├── tasks
│ │ └── main.yml
│ ├── templates
│ └── vars
│ └── main.yml
└── php
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
└── vars
└── main.yml
2, 准备相关配置文件
主要准备httpd服务器的主页文件,php测试页和配置文件等
master# echo "test main page" > /etc/ansible/roles/httpd/files/index.html
master# cat > /etc/ansible/roles/httpd/files/test.php <<EOF
<?php
phpinfo();
?>
EOF
master# yum install httpd -y
拷贝到httpd角色目录里的files子目录,可以按需要求自行修改配置文件
master# cp /etc/httpd/conf/httpd.conf /etc/ansible/roles/httpd/files/
3, 编排httpd角色的tasks
master# vim /etc/ansible/roles/httpd/tasks/main.yml
---
- name: 安装httpd
yum: name=httpd,httpd-devel state=present
- name: 同步httpd配置文件
copy: src=/etc/ansible/roles/httpd/files/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd
- name: 同步主页文件
copy: src=/etc/ansible/roles/httpd/files/index.html dest=/var/www/html/index.html
- name: 同步php测试页
copy: src=/etc/ansible/roles/httpd/files/test.php dest=/var/www/html/test.php
- name: 启动httpd并开机自启动
service: name=httpd state=started enabled=yes
4, 编排httpd角色的handlers
master# vim /etc/ansible/roles/httpd/handlers/main.yml
---
- name: restart httpd
service: name=httpd state=restarted
5, 编排mysql角色的tasks
master# vim /etc/ansible/roles/mysql/tasks/main.yml
---
- name: 安装mysql
yum: name=mariadb,mariadb-server,mariadb-devel state=present
- name: 启动mysql并开机自启动
service: name=mariadb state=started enabled=yes
6, 编排php角色的tasks
master# vim /etc/ansible/roles/php/tasks/main.yml
---
- name: 安装php及依赖包
yum: name=php,php-gd,php-ldap,php-odbc,php-pear,php-xml,php-xmlrpc,php-mbstring,php-snmp,php-soap,curl,curl-devel,php-bcmath,php-mysql state=present
notify: restart httpd
7, 编排playbook调用roles
master# vim /etc/ansible/playbook/lamp.yml
---
- hosts: group1
remote_user: root
roles:
- httpd
- mysql
- php
8, 执行playbook
master# ansible-playbook /etc/ansible/playbook/lamp.yml
案例拓展: roles实现lamp+discuz
1, 创建roles目录结构
master# cd /etc/ansible/roles/
master# mkdir -p {httpd,mysql,php}/{files,tasks,handlers,templates,vars,meta}
master# touch {httpd,mysql,php}/{tasks,handlers,vars,meta}/main.yml
2, 准备httpd相关文件
master# ls /etc/ansible/roles/httpd/files/
Discuz_X3.2_SC_UTF8.zip Discuz相关软件包
httpd.conf 配置好的httpd.conf配置文件(配置主页为index.php)
3, 编排httpd角色的tasks
master# vim /etc/ansible/roles/httpd/tasks/main.yml
- name: 安装httpd相关软件包
yum: name=httpd,httpd-devel state=latest
- name: 同步配置文件
copy: src=/etc/ansible/roles/httpd/files/httpd.conf dest=/etc/httpd/conf/httpd.conf
notify: restart httpd
- name: 拷贝discuz压缩包
copy: src=/etc/ansible/roles/httpd/files/Discuz_X3.2_SC_UTF8.zip dest=/tmp/
- name: 解压并mv网站文件到httpd家目录
shell: rm -rf /var/www/html/* && rm -rf /test/ && mkdir -p /test/ && unzip /tmp/Discuz_X3.2_SC_UTF8.zip -d /test/ &> /dev/null && mv /test/upload/* /var/www/html/ && chown -R apache.apache /var/www/html/
# 上面的命令有点多,可以写成脚本,然后使用script模块来调用执行
- name: 启动httpd并开机自启动
service: name=httpd state=started enabled=on
4, 编排httpd角色的handlers
master# vim /etc/ansible/roles/httpd/handlers/main.yml
---
- name: restart httpd
service: name=httpd state=restarted
5, 编排mysql角色的tasks
master# vim /etc/ansible/roles/mysql/tasks/main.yml
---
- name: 安装mariadb相关软件包
yum: name=mariadb-server,mariadb-devel state=latest
- name: 启动mariadb服务并设置开机自启动
service: name=mariadb state=started enabled=on
- name: 执行建库脚本
script: /etc/ansible/roles/mysql/files/create.sh
6, 编写mysql的建库脚本
master# vim /etc/ansible/roles/mysql/files/create.sh
#!/bin/bash
mysql << EOF
create database if not exists discuz default charset=utf8;
grant all on discuz.* to 'discuz'@'localhost' identified by '123';
flush privileges;
EOF
7, 编排php角色的tasks
master# vim /etc/ansible/roles/php/tasks/main.yml
---
- name: 安装php相关软件包
yum: name=php,php-gd,php-ldap,php-odbc,php-pear,php-xml,php-xmlrpc,php-mbstring,php-snmp,php-soap,curl,curl-devel,php-bcmath,php-mysql state=present
notify: restart httpd
8, 编排playbook调用roles
master# vim /etc/ansible/playbook/lamp.yml
---
- hosts: group1
remote_user: root
roles:
- httpd
- mysql
- php
9, 执行playbook
master# ansible-playbook /etc/ansible/playbook/lamp.yml
注意: shell
模块与script
模块,它不像其它模块一样有幂等性。也就是说每次执行playbook,都会执行这两个模块定义的命令与脚本。
所以如果只是为了修改httpd配置文件,就不要直接用上面编排的playbook。
可以采用把安装与配置分开的做法,做成2个playbook,一个只负责安装。另一个负责修改配置后重启服务。
六、课后练习
使用role来实现rpm版lnmp
1,创建三个role(自定义为nginx,mariadb,php7)
[root@master ~]# cd /etc/ansible/roles/
[root@master roles]# mkdir {nginx,mariadb,php7}/{files,tasks,handlers,templates,vars,meta} -p
[root@master roles]# touch {nginx,mariadb,php7}/{tasks,handlers,vars,meta}/main.yml
2, 准备nginx相关文件
[root@master roles]# cd nginx/files/
[root@master files]# pwd
/etc/ansible/roles/nginx/files
[root@master files]# echo "nginx测试主页" > index.html
[root@master files]# cat > test.php << EOF
> <?php
> phpinfo();
> ?>
> EOF
[root@master files]# vim nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
index index.php index.html;
include /etc/nginx/default.d/*.conf;
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
}
3, 编排nginx角色的tasks
[root@master ~]# vim /etc/ansible/roles/nginx/tasks/main.yml
---
- name: 安装epel
yum: name=epel-release state=present
- name: 安装nginx
yum: name=nginx state=present
- name: 拷贝测试主页文件
copy: src=/etc/ansible/roles/nginx/files/index.html dest=/usr/share/nginx/html/index.html
- name: 拷贝php测试页
copy: src=/etc/ansible/roles/nginx/files/test.php dest=/usr/share/nginx/html/test.php
- name: 拷贝nginx.conf配置文件
copy: src=/etc/ansible/roles/nginx/files/nginx.conf dest=/etc/nginx/nginx.conf
notify: restart nginx
- name: 启动nginx服务并设置开机自动启动
service: name=nginx state=started enabled=on
4, 编排nginx角色的handlers
[root@master ~]# vim /etc/ansible/roles/nginx/handlers/main.yml
---
- name: restart nginx
service: name=nginx state=restarted
5, 编排mariadb角色的tasks
[root@master ~]# vim /etc/ansible/roles/mariadb/tasks/main.yml
---
- name: 安装mariadb
yum: name=mariadb,mariadb-server state=present
- name: 启动mariadb并设置为开机自动启动
service: name=mariadb state=started enabled=on
6, 准备php-fpm的配置文件
[root@master ~]# vim /etc/ansible/roles/php7/files/www.conf
[www]
user = nginx
group = nginx
listen = 127.0.0.1:9000
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
slowlog = /var/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
7, 编排php7角色的tasks
[root@master ~]# vim /etc/ansible/roles/php7/tasks/main.yml
---
- name: 安装php7.x的yum源配置
script: /etc/ansible/roles/php7/files/install_php7yum.sh
- name: 安装php7.x相关软件包
yum: name=php72w,php72w-mysql,php72w-gd,libjpeg,php72w-ldap,php72w-odbc,php72w-pear,php72w-xml,php72w-xmlrpc,php72w-mbstring,php72w-bcmath,php72w-common,php72w-fpm,php72w-pecl-zendopcache state=latest
- name: 拷贝php-fpm配置文件
copy: src=/etc/ansible/roles/php7/files/www.conf dest=/etc/php-fpm.d/www.conf
notify: restart php-fpm
- name: 启动php-fpm并设置开机自动启动
service: name=php-fpm state=started enabled=on
因为shell
模块用rpm安装幂等性会有问题,所以使用脚本做判断,选择是否安装
[root@master ~]# vim /etc/ansible/roles/php7/files/install_php7yum.sh
#!/bin/bash
is_install=`rpm -qa |grep webtatic-release |wc -l`
if [ $is_install -eq 0 ];then
rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm &> /dev/null
fi
8,编排php7角色的handlers
[root@master ~]# vim /etc/ansible/roles/php7/handlers/main.yml
---
- name: restart php-fpm
service: name=php-fpm state=restarted
9, 编排playbook调用roles
[root@master ~]# vim /etc/ansible/playbook/lnmp.yml
---
- hosts: group1
remote_user: root
roles:
- mariadb
- nginx
- php7
10, 执行playbook
[root@master ~]# ansible-playbook /etc/ansible/playbook/lnmp.yml
如果要实现lnmp+discuz,则在以上基础上做如下操作:
1, 准备discuz包(注意:经测试zabbix3.2版本与我们现在的php7.2不兼容)
[root@master ~]# cd /etc/ansible/roles/nginx/files
[root@master files]# git clone https://gitee.com/ComsenzDiscuz/DiscuzX.git
[root@master files]# tar cf Discuz_X3.4.tar DiscuzX/
说明:
- discuz官方不再提供zip包下载,而是直接用git下载(此命令以后课程会学到)
- 下载后就是一个目录,需要打包
[root@master files]# ls
Discuz_X3.4.tar nginx.conf
只留下httpd.conf配置文件和打包的discuz3.4软件包。其他都可以删除
2,修改nginx角色的tasks
[root@master files]# vim /etc/ansible/roles/nginx/tasks/main.yml
---
- name: 安装epel
yum: name=epel-release state=present
- name: 安装nginx
yum: name=nginx state=present
- name: 拷贝nginx.conf配置文件
copy: src=/etc/ansible/roles/nginx/files/nginx.conf dest=/etc/nginx/nginx.conf
notify: restart nginx
- name: 拷贝discuz压缩包
copy: src=/etc/ansible/roles/nginx/files/Discuz_X3.4.tar dest=/tmp/
- name: 解压并mv网站文件到httpd家目录
shell: rm -rf /usr/share/nginx/html/* && rm -rf /test/ && mkdir -p /test/ && tar xf /tmp/Discuz_X3.4.tar -C /test/ && mv /test/DiscuzX/upload/* /usr/share/nginx/html/ && chown -R nginx.nginx /usr/share/nginx/html/
- name: 启动nginx服务并设置开机自动启动
service: name=nginx state=started enabled=on
3, 修改mariadb角色的tasks
---
- name: 安装mariadb
yum: name=mariadb,mariadb-server state=present
- name: 启动mariadb并设置为开机自动启动
service: name=mariadb state=started enabled=on
- name: 执行建库脚本
script: /etc/ansible/roles/mariadb/files/create.sh
4, 创建建库脚本
[root@master files]# vim /etc/ansible/roles/mariadb/files/create.sh
#!/bin/bash
mysql << EOF
create database if not exists discuz default charset=utf8;
grant all on discuz.* to 'discuz'@'localhost' identified by '123';
flush privileges;
EOF
5, 一键编排(其它步骤都不变)
[root@master files]# ansible-playbook /etc/ansible/playbook/lnmp.yml