Ansible基础入门

介绍

Ansible是一个IT自动化工具。它能配置系统、部署软件、编排更复杂的IT任务,如连续部署或零停机时间滚动更新。
Ansible用Python编写,尽管市面上已经有很多可供选择的配置管理解决方案(例如Salt,Puppet,Chef等),但它们各有优劣,而Ansible的特点在于它的简洁。让Ansible在主流的配置管理系统中与众不同的一点便是,它并不需要你在想要配置的每个节点上安装自己的组件。同时提供的另一个优点,如果需要的话,你可以在不止一个地方控制你的整个基础架。

一、安装

yum install -y epel-release
yum install -y ansible

二、管理节点(192.168.1.130)与被管理节点(192.168.1.129)建立ssh信任关系

管理节点创建密钥对,敲回车
ssh-keygen -t rsa

三、将本地的公钥传输到被管理节点

每个管理节点都需要被传递
过程需要被管理节点(192.168.1.129)的用户名(root)及密码

[root@ansible ~]# ssh-copy-id root@192.168.1.129
[root@ansible ~]# ssh 'root@192.168.1.129'
Last login: Sun Jul 25 22:31:34 2021 from 192.168.1.10
[root@localhost-2 ~]# 					#已经登录到被管理节点了,exit退出

小插曲:使用ssh传递秘钥之后,使用ssh复制文件

# 从本地复制文件到远程主机,-r表示递归
scp -r /httpd.conf root@远程主机ip:/root
# 从远程主机拉取文件到本地
scp -r root@远程主机ip:/root /httpd.conf 

快速入门

测试场景一

管理节点 192.168.1.130 被管理节点 192.168.1.128 192.168.1.129

管理节点和被管理节点之间的节点已经打通ssh信任关系

在管理节点上,测试与所有被管理节点的网络连通信

[root@ansible ~]# ansible all -i 192.168.1.128,192.168.1.129 -m ping
192.168.1.129 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}
192.168.1.128 | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false, 
    "ping": "pong"
}

注意,-i 参数后面接的是一个列表(list),因此当为一个被管理节点时,我们后面一点要加个英文逗号,告知是list

测试场景二

在管理节点上确保文件/tmp/a.conf 发布到所有被管理节点

touch /tmp/a.conf
ansible all -i 192.168.1.128,192.168.1.129 -m copy -a "src=/tmp/a.conf dest=/tmp/a.conf"

选项参数解析

all 在 ansible中,将其叫做 pattern,即匹配。我通常称它为资产选择器。就是匹配资产(参数指定)中的一部分。这里的是匹配所有指定的所有资产。将在下面资产部分详细阐述。
-i 指定 Ansible的资产,也就是资产清单文件的位置
-m 指定要运行的模块比如这里的ping模块和copy模块
-a 指定模块的参数,这里模块ping没有指定参数。模块copy指定了src和dest参数。

总结:ansible就是用什么模块,让谁去干什么事情

ansible资产

ansible静态资产

自定义资产,默认文件在/etc/ansible/hosts下

## green.example.com		其他组
## 192.168.100.10

## [webservers]				webserver组
## alpha.example.org
## 192.168.1.110

## [dbservers] 				dbserver组
## db01.intranet.mydomain.net
## 10.25.1.57

## [all_servers]			所有组
## [all_servers:children]
[root@ansible ~]# vim /etc/ansible/hosts
[db_servers]
192.168.1.130
192.179.1.131

[web_servers]
192.168.1.128
192.168.1.129

列出所有资产
[root@ansible ~]# ansible all --list-hosts
 hosts (4):
    192.168.1.128
    192.168.1.129
    192.168.1.130
    192.179.1.131
列出选定资产(如web_servers)
[root@ansible ~]# ansible web_servers --list-hosts
  hosts (2):
    192.168.1.128
    192.168.1.129

资产选择器

有时操作者希望只对资产中的一部分服务器进行操作,而不是资产中所有服务器,此时可以使用ansible资产选择器PATTERN

语法格式	ansible PATTERN -i inventory.ini -m module -a argument

选择一台或几台服务器

 ansible 1.1.1.1  --list-hosts
 ansible 1.1.1.1*  --list-hosts
 
 1.1.1.1*可以匹配1.1.1.13等
 ansible 后面可以接ip,域名以及多个ip(逗号隔开),必须根据资产里的服务器写,即使/etc/hosts里有域名解析也不能用ip对应的域名

选择一组

 ansible web_servers  --list-hosts
 ansible all_servers  --list-hosts
在all_servers里有两个组有同一ip,可以去重

使用逻辑匹配

web_servers和db_servers的并集(两个组内所有的主机)
ansible 'web_servers:db_servers'  --list-hosts

web_servers和db_servers的交集(两个组内共有的主机)
ansible 'web_servers:&db_servers'  --list-hosts

在web_servers中,但是不在db_server中
ansible 'web_servers:!db_servers'  --list-hosts

Ansible Ad-Hoc命令

​ Ad-hoc命令是什么呢?这其实是一个概念性的名字,是相对于写Ansibleplaybook来说的.类似于在命令行敲入shell命令和写shell scripts两者之间的关系。可以用于执行一些临时命令。
​ 如果我们敲入一些命令去比较快的完成一些事情,而不需要将这些执行的命令特别保存下来,这样的命令就叫做ad-hoc命令。
​ Ansible提供两种方式去完成任务,一 是ad-hoc命令,一是写Ansibleplaybook。 前者可以解决一些简单的任务,后者解决较复杂的任务,比如做配置管理或部署。

1、命令格式

在快速入门中执行的Ansible命令,类似于批量执行命令。在Ansible中统称Ansible Ad-Hoc

语法格式
ansible pattern [-i inventory] -m module -a argument
pattern 资产选择器,如[web_servers]
-i 指定资产清单文件的位置,如果资产清单不放在默认文件,就要使用-i参数指定文件路径
-m 指定本次Ansible ad-hoc要执行的模块。可以类别与shell中的命令
-a 模块的参数,可以类比成shell中的命令参数

2、模块类型

Ansible模块分三种类型:核心模块(core module)、附加模块(extra module)及用户自定义模块(consume module)。
核心模块是由Ansible的官方团队提供的。
附加模块是由各个社区提供的。例如: OPENSTACK社区、DOCKER社区等等。
当核心模块和附加模块都无法满足你的需求时,用户可以自定义模块。
默认情况下,在安装Ansible的时候, 核心模块和附加模块都已经安装而无需用户干预。

3、联机帮助

Ansible的模块不需要去熟记,掌握。我们可以使用Ansible的帮助文档。

常用帮助参数

列举出所有的核心模块和附加模块
ansible-doc -l

查询某个模块的使用方法
ansible-doc modulename 

查询某个模块的使用方法,比较简洁的信息
ansible-doc -s modulemane

常用模块

环境:被管理主机ip:192.168.1.128 192.168.1.129,资产文件在/root/hosts

资产保存在hosts目录中
[root@ansible ~]# vim /etc/ansible/hosts
[db_servers]
192.168.1.128

[web_servers]
192.168.1.129

command & shell 模块

两个模块都是在远程服务器上去执行命令的

但command模块是ad-hoc的默认 模块,在执行ad-hoc时,若不指定模块的名字则默认使用此模块

[root@ansible ~]# ansible all -a "echo 'hello'"
##								 相对路径
192.168.1.129 | CHANGED | rc=0 >>
hello
192.168.1.128 | CHANGED | rc=0 >>
hello

[root@ansible ~]# ansible all -m shell -a "echo 'hello'"
192.168.1.128 | CHANGED | rc=0 >>
hello
192.168.1.129 | CHANGED | rc=0 >>
hello

两个模块的差异

默认模块:command模块无法执行SHELL的内置命令和特性
shell 模块可以执行SHELL的内置命令和特性(比如管道符)

script模块

将管理节点上的脚本传递到被管理节点(远程服务器)上进行执行。

管理节点上的一个脚本

创建一个脚本
echo "touch /tmp/testfile" > /root/a.sh		
将管理节点上的脚本传递到被管理节点上进行执行
ansible web_servers -m script -a "/root/a.sh"
查看
ansible web_servers -m shell -a "ls -l /tmp/"

192.168.1.129 | CHANGED | rc=0 >>
总用量 0
-rw-r--r-- 1 root root  0 7月  26 14:13 testfile

copy模块

copy模块主要用于管理节点和被管理节点之间的文件拷贝,经常使用到以下参数:

src		指定拷贝文件的源地址
dest	指定拷贝文件的目标地址
backup	拷贝文件前,若源文件发现变化,则对目标文件进行备份
owner	指定新拷贝文件的所有者
group	指定新拷贝文件的所有组
mod		指定新拷贝文件的权限
ansible web_servers -m copy -a "src=/tmp/testfile dest=/tmp/testfile"
下次再copy时,若被管理节点上的testfile文件被修改,那么testfile修改文件被覆盖
加上参数backup=yes,则被修改文件将备份。
ansible webservers -i hosts -m copy -a "src=/tmp/testfile dest=/tmp/testfile backup=yes"
## copy文件的同时对文件进行用户及用户组的设置
ansible web_servers -m copy -a "src=/tmp/testfile dest=/tmp/testfile ower=nobody group=nobody"
 copy文件的同时对文件权限进行设置,用户及用户组不会被覆盖。
 ansible webservers -m copy -a "src=/tmp/testfile dest=/tmp/testfile mode=0755"

yum_repository模块

添加yum仓库(给被管理节点添加yum仓库),常用参数:

name		仓库名称,就是仓库文件中第一行的中括号的名称,必须的参数。
description	仓库描述信息,添加时必须的参数
baseurl 	yum存储库"repodata"目录所在目录的URL,添加时必须的参数。
			它也可以是多个URL的列表。
file		仓库文件保存到被管理节点的文件名,不包含.repo。默认是name的值。
state 		present确认添加仓库文件, absent确认删除仓库文件。
gpgcheck	是否检查GPG yes|no,没有默认值,使用/etc/yum.conf中的配置。
添加epel源
ansible web_servers -m yum_repository -a "name=epel baseurl='http://http://mirrors.aliyun.com/centos/$releasever/centosplus/$basearch/' description='EPEL YUM repo'"

删除epel源
ansible web_servers -m yum_repository -a "name=epel state=absent"*

yum模块

等同于linux上的YUM命令,对远程服务器的rpm包进行管理。常用参数:

name 	要安装的软件包名,多个软件包以英文逗号(,)隔开
state	对当前指定的软件安装、移除操作(present installed latest absent 				removed)
		支持的参数:
			-present	确认已经安装,但不升级
			-installed	确认已经安装
			-latest 	确保安装,且升级为最新
			-absent和removed	确认已移除
安装一个软件包
ansible webservers -m yum -a "name=nginx state=latest"
安装一个软件包组
ansible webservers -m yum -a "name='@Development tools' state=present"

systemd模块

Centos6之前的版本使用service模块
请使用ansible-doc service命令自行查看帮助信息

管理远程节点上的systemd服务,就是由systemd所管理的服务,常用参数:

daemon_reload	重新载入systemd, 扫描新的或有变动的单元
enabled			是否开机自启动yes|no
name			必选项,服务名称,比如httpd vsftpd
state			对当前服务执行启动、停止、重启、重新加载等操作(started/stopped/restart/reloaded)
重新加载webservers里的firewalld服务
ansible webservers  -m systemd -a "name=firewalld state=reloaded"

group模块

在被管理节点上,对组进行管理,常用参数:

name	组名称,必须的
system	是否为系统组 yes/no,默认是no
state 	创建或删除	present/absent 默认是present
创建普通组db_admin
ansible db_servers  -m group -a "name=db_admin"

user模块

用于被管理节点上对用户进行管理,常用参数:

name			必须的参数,指定用户名
password 		设置用户的密码,这里接受的是一个加密的值,因为会直接存到shadow,默				认不设置密码
update_password	假如设置的密码不同于原密码,则会更新密码.在1.3版本中被加入
home 			指定用户的家目录
shell 			设置用户的shell
# comment 		用户的描述信息
# create-home	在创建用户时,是否创建其家目录。默认创建,假如不创建,设置为                       no,2.5版本之前使用createhome
group 			设置用户的主组
groups 			将用户加入到多个其他组中,多个用逗号隔开。默认会把用户从其他已经				加入的组中删除。
append			yes/no和groups配合使用,yes时不会把用户从其它已经加入的组中删				  除
system			设置为yes时,将会创建一个系统账号
expires			设置用户的过期时间,值为时间戳会转为天数后,放在shadow的最后第8					个字段里
generate_ssh_key	设置为yes将会为用户生成密钥,这不会覆盖原来的密钥
ssh_key_type		指定用户的密钥类型,默认rsa,具体的类型取决于被管理节点
state			删除或添加用户,present为添加, absent为删除;默认值present
remove			当与 state=absent一起使用,删除一个用户及关联的目录,比如家目				 录,邮箱目录。可选的值为:yes/no
创建用户设置密码
先生成密码,openssl是生成密码工具(MD5加密) -1为数字一
[root@ansible ~]# pass=$(echo "123456" | openssl passwd -1 -stdin)
[root@ansible ~]# echo $pass
$1$n6qfuRB4$alPJC8R2UgpmJi7KEKzVg0
执行ansible创建用户foo并设置密码
ansible webservers -m user -a "name=foo password=${pass}"
被管理节点的密文位置
tail -1 /etc/shadow
创建用户goo,并且为其创建密钥对,密钥类型为:ecdsa
ansible dbservers  -m user -a "name=goo generate_ssh_key=yes ssh_ key__type=ecdsa 
创建用户tom,并且设置有效期到2021年7月28日加入到组db_admin,不改变用户原有加入的组
[root@localhost-1 ~]# tail -2  /etc/group	#被管理节点上创建db_admin和db2组
db_admin:x:1003:
db2:x:1005:

ansible dbservers -m user -a "name=tom expires=$(date +%s -d 20210728) groups=db_admin append=yes"

18834是指你哪一天创建的,18835是指到期时间
[root@localhost-1 ~]# tail -1 /etc/shadow
tom:!!:18834:0:99999:7::18835:
[root@localhost-1 ~]# id tom
uid=1003(tom) gid=1004(tom) 组=1004(tom),1003(db_admin)

不使用append参数,加入的db_admin组被覆盖
ansible dbservers  -m user -a "name=tom groups=db2"
[root@localhost-1 ~]# id tom
uid=1003(tom) gid=1004(tom) 组=1004(tom),1005(db2)

使用append参数,指不改变原有的组,追加db_admin组
ansible dbservers  -m user -a "name=tom groups=db_admin append=yes"
[root@localhost-1 ~]# id tom
uid=1003(tom) gid=1004(tom) 组=1004(tom),1003(db_admin),1005(db2)

删除用户,state和remove结合使用,删除用户及家目录
[root@ansible ~]# ansible webservers -m user -a "name=tom state=absent remove=yes "

file模块

file模块主要用于远程主机上的文件操作,常用参数:

owner	定义文件/目录的属主
group	定义文件/目录的属组
mode	定义文件/目录的权限
path	必选项,定义文件/目录的路径
recurse	递归的设置文件的属性,只对目录有效
src		链接(软/硬)文件的源文件的路径,只应用于 state=link的情况
dest	链接文件的路径,只应用于 state=link的情况
state
	-directory	如果目录不存在,创建目录
	-file		文件不存在,则不会被创建,存在则返回文件的信息,常用于检查文件是否				 存在。
	-link		创建软链接
	-hard		创建硬链接
	-touch		如果文件不存在,则会创建一个新的文件,如果文件或目录已存在,则更新				 其最后修改时间
	-absent		删除目录、文件或者取消链接文件
创建一个文件
ansible webservers  -m file -a "path=/tmp/test.java state=touch"

修改文件所有者及权限
ansible webservers  -m file -a "path=/tmp/test.java  owner=nobody group=nobody mode=0644"

创建一个软链接
ansible webservers -m file -a "src=/tmp/test.txt dest=/tmp/link.txt state=link"
## 取消软链接
ansible webservers  -m file -a "path=/tmp/link.txt state=absent"

cron模块

管理远程节点的cron服务,等同于linux中的计划任务。

注意:ansible创建的计划任务,是不能使用本地crontab -e去编辑的,
否则ansible就无法再次操作次此计划任务了

name	指定一个 cron job的名字。一定要指定,便于日之后删除。
minute	指定分钟,可以设置成(0-59,*,*/2等)格式。默认是*,也就是每分钟。
hour	指定小时,可以设置成(0-23,*,*/2等)格式。默认是*,也就是每小时。
day		指定天,可以设置成(1-31,*,*/2等)格式。默认是*,也就是每天
month	指定月份,可以设置成(1-12,*,*/2等)格式。默认是*,也就是每周。
weekday	指定星期,可以设置成(0-6 for Sunday-Saturday,*等)格式。
    	默认是*,也就是每星期。
job		指定要执行的内容,通常可以写个脚本,或者一段内容。
state	指定这个job的状态,可以是新增(present)或者是删除(absent)
		默认为新增(present)

date

// 计算 3 ⼩时之后是⼏点⼏分
date +%T -d '3 hours'
// 任意⽇期的前 N 天,后 N 天的具体⽇期
date +%F -d "20190910 1 day"
date +%F -d "20190910 -1 day"
// 计算两个⽇期相差天数, ⽐如计算⽣⽇距离现在还有多少天
d1=$(date +%s -d 20180728)
d2=$(date +%s -d 20180726)
echo $(((d1-d2)/86400))
新建⼀个 CRON JOB 任务
ansible all  -m cron -a "name='create new job' minute='0' job='ls -alh > /dev/null'"

删除⼀个 CRON JOB 任务,删除时,⼀定要正确指定job 的name参数,以免误删除。   
ansible all  -m cron -a "name='create new job' state=absent"

debug模块

debug 模块主要⽤于调试时使⽤,通常的作⽤是将⼀个变量的值给打印出来。常⽤参数**😗*

var 	直接打印⼀个指定的变量值
msg 	打印⼀段可以格式化的字符串

这⾥引⼊了变量,我们只需了解 debug 模板的使⽤即可。在学习变量、剧本时,我们会对它有更深刻的理解

[root@ansible ~]# ansible webservers  -m debug -a "var=role" -e "role=web"
192.168.1.129 | SUCCESS => {
    "role": "web"
}

[root@ansible ~]# ansible webservers  -m debug -a "msg='role is {{role}} '" -e "role=web"
192.168.1.129 | SUCCESS => {
    "msg": "role is web "
}

template模块

template 模块使⽤了Jinjia2格式作为⽂件模版,可以进⾏⽂档内变量的替换。⽂件以 .j2 结尾。

常⽤参数**😗*

src  	指定 Ansible 控制端的⽂件路径
dest 	指定 Ansible 被控端的⽂件路径
owner 	指定⽂件的属主
group 	指定⽂件的属组
mode 	指定⽂件的权限
backup 	创建⼀个包含时间戳信息的备份⽂件,这样如果您以某种⽅式错误地破坏了原始⽂		件,就可以将其恢复原状。yes/no

⽤法其实和 copy 模块基本⼀样, template 模块的强⼤之处就是使⽤变量替换,就是可以把传递给 Ansible 的变量的值替换到模板⽂件中。

[root@ansible tmp]# cat hello.j2 
hello {{var}}!
[root@ansible tmp]# ansible webservers -m template -a "src=/tmp/hello.j2 dest=/tmp/hello.txt" -e "var=world"

[root@localhost-2 tmp]# cat hello.txt 
hello world!

lineinfile模块

在被管理节点上,⽤正则匹配的⽅式对⽬标⽂件的⼀⾏内容修改删除等操作。如果是在⼀个⽂件中把所有匹配到的多⾏都进⾏统⼀处理,请参考replace 模块。如果想对⼀个⽂件进⾏⼀次性添加/更新/删除多⾏内容等操作,参考blockinfifile模块

path 	被管理节点的⽬标⽂件路径, 必须。
state 	可选值absent 删除 |present 替换(默认值)。
regexp 	在⽂件的每⼀⾏中查找的正则表达式。对于 state=present ,仅找到的最后⼀⾏将		被替换。
line 	要在⽂件中插⼊/替换的⾏。需要state=present。
create 	⽂件不存在时,是否要创建⽂件并添加内容。yes/no
删除被控节点⽂件⾥的某⼀条内容,删除wheel开头的一行内容
ansible dbservers  -m lineinfile -a "path=/etc/sudoers regexp='^%wheel' state=absent"

替换某一行,替换开头为SELINUX的一行内容
ansible dbservers  -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=disabled' state=present"

blockinfile模块

对目标文件进行多行的添加/更新/删除操作,常用参数:

path 	⽬标⽂件路径
block 	⽂件中被操作的块内容
state 	块内容如何处理,absent 删除, present 添加/更新(默认值)
## 向⽂件/etc/ssh/sshd_config的最后添加⼏⾏内容添加的内容是
Match User ansible-agent
PasswordAuthentication no

ansible dbservers  -m blockinfile -a "path=/etc/ssh/sshd_config block='Match User ansible-agent\nPasswordAuthentication no'"

注意:\n 是换⾏符的意思。

更新之前的内容

ansible dbservers -m blockinfile -a
"path=/etc/ssh/sshd_config block='Match User ansible-agent\nPasswordAuthentication yes'"
删除⽂件中的连续出现⼏⾏内容
ansible dbservers -m blockinfile -a
"path=/etc/ssh/sshd_config block='Match User
ansible-agent\nPasswordAuthentication yes' state=absent"

PlayBook

概念

它是ansible自定义的一门语言(可以将playbook比作linux中的shell,而ansible中的module可以比作linux的各种命令。)

YAML学习

playbook遵循yaml的语法格式

1、特点

YAML文件以 # 为注释符;以.yaml或.yml结尾;以—开始,以…结束,但开始和结束的标志都是可选的。

2、基本语法

大小写敏感;
使用缩进表示层次关系;
缩进时是使用Tab键还是使用空格一 定要达到统一,建议使用空格;
相同层级的元素必须左侧对齐即可。

支持的数据结构有三种:字符串,列表,字典

YAML中的字符串,可以不使用双引号或单引号
this is a string
若一行写不完你要表述的内容,可以进行折行
列表
可以理解为python中的list,或C语言的数组
定义:
- red
- blue
转换为python是['red','blue']
字典
可以理解为python中的dict
定义:key+冒号(:)+空格+值(value),即:key: value
name: useing ansible
code: 1234
转为python的dict是{'name': 'useing ansible','code': '1234'}
混合结构
---
class: 
  - name: stu1
    num: 001
  - name: stu2
    num: 002
  - name: stu3
    num: 003
...
{'class':[{'name': 'stu1','num': '001'},{'name': 'stu2','num': '002'},{'name': 'stu3','num': '003'},...]}

3、验证yaml语法

将YAML文件,通过Python的YAML模块验证,若不正确则报错.若正确则会输出YAML里的内容。
注意使用时,一定确保安装了yaml软件包。

下载源码包:http : //pyyaml.org/download/libyaml/yaml-0.2.5.tar.gz

要构建和安装 LibYAML,请运行

$ ./configure
$ make && make install
编写一个.yml文件
cat myyaml.yml 
---
- red
- green
- blue
...
校验
python -c 'import yaml,sys; print yaml.safe_load(sys.stdin)' < myyaml.yml
['red', 'green', 'blue']

play定义

playbook是由一个或多个play组成的。

如何定义一个play:
1、每一个play都是有短横杆开始的
2、每一个play都是一个YAML字典格式

---
//一个含有3个playbook的伪playbook构成
- key1: value1
  key2: value2
- key1: value1
  key2: value2
- key1: value1
  key2: value2

play属性

以上一小节中的Pay为基础,Pay中的每一个key,比如key1,key2等;这些key在 Play Book中被定义为Play的属性。
这些属性都具有特殊的意义我们不能随意的自定义Pay的属性。
接下来就来看看 Ansible本身都支持哪些Play属性。

常用属性:

name属性		每个play的名字
hosts属性		每个play涉及的被管理服务器,同 ad-hoc中的资产选择器
tasks属性		每个play中具体要完成的任务,以列表的形式表达
become属性	如果需要提权,则加上 become相关属性
become_user属性	若提权的话,提权到哪个用户上
remote user属性	指定连接到远程节点的用户,就是在远程服务上执行具体操作的用户。若不指定,则默认使用当前执行 ansible Playbook的用户

1、一个完整的剧本

2、tasks属性中任务的多种写法

以启动nginx服务,并增加开机启动为例
一行的形式:
service: name=nginx enable=true state=started
多行的形式
service: 
  name=nginx 
  enable=true 
  state=started
写成字典的形式
service: 
  name: nginx 
  enable: true 
  state: started

3、具有多个play的playbook

4、如何对playbook进行语法校验

vim myplaybook.yml

---
- name: manage web servers
  hosts: webservers
  remote_user: root
  tasks:
    - name: install nginx packge
      yum: name=nginx state=present
    - name: copy nginx.conf to remote server
      copy: src=/root/nginx.conf dest=/etc/nginx/nginx.conf
    - name: start nginx server
      service:
        name: nginx
        enabled: true
        state: started
...
校验playbook是否语法正确,注意:hosts和myplaybook都是用了相对路径
ansible-playbook  myplaybook.yml  --syntax-check
playbook: myplaybook.yml

运行playbook
ansible-playbook  myplaybook.yml

如何单步跟从调试,每运行一个task就会询问一次
ansible-playbook  myplaybook.yml --step

测试运行playbook(模拟是否出错)
ansible-playbook  myplaybook.yml -C

Ansible变量

涉及到变量定义、控制结构的使用等特性

变量命名规则

变量的名字由字母、下划线和数字组成,必须以字母开头;

保留关键字不能作为变量名称。

变量类型

根据变量的作用范围大体的将变量分为:

  • 全局变量;
  • 剧本变量;
  • 资产变量;

1、全局变量

是我们使用ansible或使用ansible-playbook时,手动通过-e参数传递给Ansible的变量,
通过ansible或ansible-playbook的help帮助,可以获取具体格式使用方法

传递一个YAML/JSON的形式(注意不管是YAML还是JSON,它们的最终格式一定要是一个字典)

cat a.json
{"net": "www.baidu.com","person": "zhangsan"}
或者 
---
net: www.baidu.com
person: zhangsan 
...

[root@ansible ~]# ansible webservers  -m debug -a "msg='address is {{net}} the man is {{person}}'" -e @a.json
192.168.1.129 | SUCCESS => {
    "msg": "address is www.baidu.com the man is zhangsan"
}

2、剧本变量

此种变量和playbook有关,定义在playbook中的。它的定义方式有很多,最常用的定义方式如下

通过play属性var定义

---
- name: test play vars
  hosts: all 
  vars:
    user: lilei
    home: /home/lilei

通过play属性var_files定义

当通过var属性定义的变量很多时,这个play就会感觉很臃肿,此时我们可以将变量单独从play中抽离出来,形成单独的yaml文件
---
- name: test play vars
  hosts: all
  vars_files:
    - vars/users.yml
    
# cat var/users.yml
name: lilei
home: /home/lilei 

测试play属性var_files定义

[root@ansible ~]# cat test_vars.yml 
user: lilei
home: /home/lilei

[root@ansible ~]# vim vars_files.yml
---
- name: test play vars
  hosts: all
  vars_files:
    - /root/test_vars.yml
  tasks:
    - name: create the user {{ user }}
      user:
        name: "{{ user }}"
        home: "{{ home }}"
...
[root@ansible ~]# ansible-playbook vars_files.yml -C

# PLAY RECAP ************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

3、资产变量

​ 在之前的课程中学习了资产。资产共分为静态资产和动态资产这一节中学习的资产变量,就是和资产紧密相关的一种变量。
​ 资产变量分为主机变量和主机组变量,分别针对资产中的单个主机和主机组

在/root/下建一个资产清单
主机变量

[root@ansible ~]# cat hostsandhostvars
[webservers]
192.168.1.129 user=lilei port=3309
172.168.1.128

[root@ansible ~]# ansible 192.168.1.129 -i hostsandhostvars -m debug -a "msg='{{user}} {{port}}'"
192.168.1.129 | SUCCESS => {
    "msg": "lilei 3309"
}

主机组变量

以下资产中,定义了一个组变量home,此变量将针对webservers这个主机组中的所有服务器有效

[root@ansible ~]# cat hostsandgroupvars
[webservers]
192.168.1.129 user=lilei
172.168.1.128
[webservers:vars]			# 主机组里的变量会同时资产里生效。
home="/home/lilie"			#1.129和1.128都会创建这个变量

当主机变量和组变量在同一个资产中发生重名的情况
[webservers]
192.168.1.129 user=lilei
172.168.1.128
[webservers:vars]			# 主机组与主机的变量不同时
user=tom
[root@ansible ~]# ansible webservers -i hostsandgroupvars -m debug -a "var=user"
192.168.1.129 | SUCCESS => {
    "user": "lilei"
}
192.168.1.128 | SUCCESS => {
    "user": "tom"
}
得知:主机变量的优先级更高

在/root/下建一个资产清单
变量是否存在变量关系

[root@ansible ~]# vim host_v3

[webservers]
192.168.1.129
[dbservers]
192.168.1.128

[allservers]
[allservers:children]
webservers
dbservers

[allservers:vars]
user=lilei

[root@ansible ~]# ansible allservers -i host_v3 -m debug -a "var=user"
192.168.1.129 | SUCCESS => {
    "user": "lilei"
}
192.168.1.128 | SUCCESS => {
    "user": "lilei"
}
# 得知:变量存在继承关系

内置变量的说明

内置变量几乎都是以ansible_为前缀

ansible_ssh_host
	将要连接的远程主机名与你想要设定的主机的别名不同的话,可通过此变量设置
ansible_ssh_port
	ssh端口号.如果不是默认的端囗号,通过此变量设置
ansible_ssh_user
	默认的ssh用户名
ansible_ssh_pass
	ssh 密码(这种方式并不安全,官方强烈建议使用  --ask-pass或SSH密钥)
ansible_sudo_pass
	sudo密码(这种方式并不安全,官方强烈建议使用--ask-sudo-pass)
ansible_sudo_exe (new in version 1.8)
	sudo 命令路径(适用于1.8及以上版本)
ansible_ssh_private_key_ file
	ssh 使用的私钥文件.适用于有多个密钥,而你不想使用SSH代理的情况.
ansible_python_interpreter
	目标主机的python路径.适用于的情况:系统中有多个Python,或者命令路径不是"/usr/bin/python",比如,/usr/local/bin/python3
修改192.168.1.129的ssh的监听端口为2233后,发现不能到达
[root@ansible ~]# ansible allservers -i host_v3 -m shell -a "ls"
192.168.1.129 | UNREACHABLE! => {
    "changed": false, 
    "msg": "Failed to connect to the host via ssh: ssh: connect to host 192.168.1.129 port 22: Connection refused", 
    "unreachable": true
}
# 在host_v3中添加一个参数后可以访问远程节点
[webservers]
192.168.1.129 ansible_ssh_port=2233

playbook中使用变量

使用setup模块中的变量

# 环境:在/root/ansible/inventory.ini

[root@ansible ansible]# vim inventory.ini
[web_servers]
192.168.1.132

[db_servers]
192.168.1.133

[all_servers]
[all_servers:children]
web_servers
db_servers

列举出有关被管理服务器的操作系统版本、服务器iP地址、主机名,磁盘的使用情况、CPU个数、内存大小等等有关被管理服务器的私有信息。
[root@ansible ansible]# ansible db_servers -i inventory.ini -m setup

ansible_nodename是setup模块中的变量 
[root@ansible ansible]# ansible db_servers -i inventory.ini -m setup -a 'filter="ansible_nodename"'

192.168.1.133 | SUCCESS => {
    "ansible_facts": {
        "ansible_nodename": "client-1", 
        "discovered_interpreter_python": "/usr/bin/python"
    }, 
    "changed": false
}

Facts

  Facts变量不包含在前文中介绍的全局变量、剧本变量及资产变量之内。
  Facts变量不需要我们人为去声明变量名及赋值。
  它的声明和赋值完全有 Ansible 中的 setup模块帮我们完成
  它收集了有关被管理服务器的操作系统版本、服务器iP地址、主机名,磁盘的使用情况、CPU个数、内存大小等等有关被管理服务器的私有信息。
  在每次 PlayBook运行的时候都会发现在 PlayBook执行前都会有一个Gathering Facts的过程。这个过程就是收集被管理服务器的 Facts信息过程
手动收集Facts变量
ansible all -i localhost, -c local -m setup
过滤获取指定信息(内存信息)
ansible all -i localhost, -c local -m setup -a "filter=*memory*"

要求在playbook中使用,不要用ansible命令调用

使用playbook创建一个以ansible_nodename变量作为名字的空文件夹
[root@ansible ansible]# vim var.yml
---
- hosts: db_servers
  remote_user: root

  task:
    - name: create log file
      file: name=/data/{{ ansible_nodename }}.log state=touch owner=admin mode=600

[root@ansible ansible]# ansible db_servers -i inventory.ini -a "ls -l /data"
192.168.1.133 | CHANGED | rc=0 >>
总用量 0
-rw------- 1 admin root 0 8月  15 17:15 client-1.log

使用Facts变量

默认情况下,在执行playbook的时候,它会自动地获取每台被管理服务器的Facts信息

可以像使用其他变量一样去使用Facts变量

[root@ansible ~]# vim facts.yml 
---
- name: print facts variable
  hosts: all
  tasks: 
   - name: print facts variable
     debug: 
       msg:"The default IPV4 address is {{ ansible_default_ipv4.address }}"
...
[root@ansible ~]# ansible-playbook -i inventory.ini facts.yml 

关闭 facts 变量的收集功能(在剧本中添加)

gather_facts: no 关闭后不能使用系统变量
remote_user: root 指定playbook的用户

命令行定义变量

[root@ansible ansible]# vim var2.yml
---
- hosts: db_servers
  remote_user: root
  tasks:
    - name: install package
      yum: name={{ pkname }} state=present
# 使用-e来传递变量
[root@ansible ansible]# ansible-playbook -i inventory.ini -e pkname=memcached var2.yml
[root@ansible ansible]# vim var3.yml  
---
- hosts: db_servers
  remote_user: root
  vars:
    - username: user1
    - groupname: group1

  tasks:
    - name: create group
      group: name={{ groupname }} state=present

    - name: create user
      user: name={{ username }} group={{ groupname }} state=present
      
[root@ansible ansible]# ansible-playbook -i inventory.ini  var3.yml

注册变量

往往用于保存一个task任务的执行结果,以便于 debug时使用。
或者将此次task任务的结果作为条件,去判断是否去执行其他task任务。
注册变量在 PlayBook中通过 register关键字去实现。

[root@ansible ~]# vim myplaybook3.yml
---
- name: install a package and print the result
  hosts: web_servers
  remote_user: root
  tasks:
    - name: install nginx package
      yum: name=nginx state=present
      register: install_result		#注册变量
    - name: print result
      debug: var=install_result		#打印变量的值
...
[root@ansible ~]# ansible-playbook -i hosts myplaybook3.yml
PLAY [install a package and print the result] *****************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.1.132]

TASK [install nginx package] **********************************************************
changed: [192.168.1.132]

TASK [print result] *******************************************************************
ok: [192.168.1.132] => {
    "install_result": {
        "changed": true, 
        "changes": {
            "installed": [
                "nginx"
            ]
        }, 
        "failed": false, 
        "results": []
    }
}

PLAY RECAP ****************************************************************************
192.168.1.132              : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

变量优先级

思考:我们同时在全局变量、剧本变量及资产变量声明了同一个变量名,那么哪一个优先级最高?

环境准备:

[root@ansible ~]# cat host_v3 
[webservers]
192.168.1.129

[dbservers]
192.168.1.128

[allservers]
[allservers:children]
webservers
dbservers

[allservers:vars]
user=tomcat

[root@ansible ~]# cat playbook3.yml 
---
- name: test variable priority
  hosts: all
  remote_user: root
  vars:
    user: mysql
  tasks:
    - name: print the user value
      debug: msg='the user value is {{ user }}'
...
## www为全局
[root@ansible ~]# ansible-playbook -i host_v3 playbook3.yml  -e "user=www"
PLAY [test variable priority] *********************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.1.132]

TASK [print the user value] ***********************************************************
ok: [192.168.1.132] => {
    "msg": "the user value is www"
}

PLAY RECAP ****************************************************************************
192.168.1.132              : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

输出结果为www
得出结论:优先级最高的是全局变量,其次剧本变量,最后是资产变量

Ansible任务控制

循环控制

[root@ansible ~]# vim loop.yml
---
- name: loop test
  hosts: webservers
  remote_user: root
  tasks:
    - name: nginx firewalld started
      service:
        name: "{{ item }}"
        state: started
      with_items:
          - nginx
          - firewalld
...
[root@ansible ~]# cat inventory.ini 
[dbservers]
192.168.1.128

[webservers]
192.168.1.129

[root@ansible ~]# ansible-playbook -i inventory.ini loop.yml 

循环散列

[root@ansible ~]# cat loop2.yml
---
- name: create user loop
  hosts: web_servers
  remote_user: root
  tasks:
    - name: create user
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        state: present
      with_items:
        - name: stu1
          groups: root
        - name: stu2
          groups: foo

      register: create_result
    - name: show result
      debug:
        var=create_result
执行以上任务会得到结果,将结果赋值给注册变量,若没有任何变量则结果不显示。
[root@ansible ~]# ansible-playbook -i inventory.ini loop1.yml 
PLAY [create user loop] ***************************************************************

TASK [Gathering Facts] ****************************************************************
ok: [192.168.1.132]

TASK [create user] ********************************************************************
changed: [192.168.1.132] => (item={u'name': u'stu1', u'groups': u'root'})
changed: [192.168.1.132] => (item={u'name': u'stu2', u'groups': u'foo'})

TASK [show result] ********************************************************************
ok: [192.168.1.132] => {
    "create_result": {
        "changed": true, 
        "msg": "All items completed", 
        "results": [
            {
                "ansible_loop_var": "item", 
                "changed": true, 
                "failed": false, 
                "invocation": {
                    "module_args": {
                        "append": false, 
                        "authorization": null, 
                        "comment": null, 
                        "create_home": true, 
                        "expires": null, 
                        "force": false, 
                        "generate_ssh_key": null, 
                        "group": null, 
                        "groups": [
                            "root"
                        ], 
                        "hidden": null, 
                        "home": null, 
                        "local": null, 
                        "login_class": null, 
                        "move_home": false, 
                        "name": "stu1", 
                        "non_unique": false, 
                        "password": null, 
                        "password_lock": null, 
                        "profile": null, 
                        "remove": false, 
                        "role": null, 
                        "seuser": null, 
                        "shell": null, 
                        "skeleton": null, 
                        "ssh_key_bits": 0, 
                        "ssh_key_comment": "ansible-generated on client", 
                        "ssh_key_file": null, 
                        "ssh_key_passphrase": null, 
                        "ssh_key_type": "rsa", 
                        "state": "present", 
                        "system": false, 
                        "uid": null, 
                        "update_password": "always"
                    }
                }, 
                "item": {
                    "groups": "root", 
                    "name": "stu1"
                }
            }, 
            {
                "ansible_loop_var": "item", 
                "changed": true, 
                "failed": false, 
                "invocation": {
                    "module_args": {
                        "append": false, 
                        "authorization": null, 
                        "comment": null, 
                        "create_home": true, 
                        "expires": null, 
                        "force": false, 
                        "generate_ssh_key": null, 
                        "group": null, 
                        "groups": [
                            "foo"
                        ], 
                        "hidden": null, 
                        "home": null, 
                        "local": null, 
                        "login_class": null, 
                        "move_home": false, 
                        "name": "stu2", 
                        "non_unique": false, 
                        "password": null, 
                        "password_lock": null, 
                        "profile": null, 
                        "remove": false, 
                        "role": null, 
                        "seuser": null, 
                        "shell": null, 
                        "skeleton": null, 
                        "ssh_key_bits": 0, 
                        "ssh_key_comment": "ansible-generated on client", 
                        "ssh_key_file": null, 
                        "ssh_key_passphrase": null, 
                        "ssh_key_type": "rsa", 
                        "state": "present", 
                        "system": false, 
                        "uid": null, 
                        "update_password": "always"
                    }
                }, 
                "item": {
                    "groups": "foo", 
                    "name": "stu2"
                }
            }
        ]
    }
}

PLAY RECAP ****************************************************************************
192.168.1.132              : ok=3    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

列表循环

[root@ansible ~]# vim loop.yml
---
- name: loop test
  hosts: webservers
  remote_user: root
  vars:
    myservice:			# 使用列表
      - nginx
      - firewalld
  tasks:
      - name: nginx firewalld stopped
        service:
          name: "{{ item }}"		# 使用item,指向with_items
          state: stopped
        with_items: "{{ myservice }}"		# 指向列表变量
...
[root@ansible ~]# ansible-playbook -i inventory.ini loop.yml 

when条件判断

nginx启动逻辑欠缺考虑
若 Nginx的配置文件语法错误则会导致启动 Nginx失败,以至于 Play Book执行失败。
如果我们能够在启动之前去对Nginx的配置文件语法做正确性的校验,只有当校验通过的时候我们才去启动或者重启 Nginx;否则则跳过启动 Nginx的过程。这样就会避免 Nginx配置文件语法问题而导致的无法启动Nginx的风险。

nginx语法校验
- name: check nginx syntax
  shell: /usr/sbin/nginx -t

那如何将Nginx语法检查的TASK同Nginx启动的TASK关联起来呢?

如果我们能够获得语法检查的TASK的结果,根据这个结果去判断“启动NGINX的TASK”是否执⾏,这将是⼀个很好的⽅案。 如何和获取到语法检查TASK的结果呢? 此时就可以使⽤之前学到的 Ansible中的注册变量。

获取Task任务结果
- name: check nginx syntax
  shell: /usr/sbin/nginx -t
  register: nginxsyntax

此时有可能还有疑问,我获取到任务结果,但是结果⾥⾯的内容是个什么样⼦, 我如何根据内容在后续的PlayBook中使⽤呢?

通过debug模块去确认返回结果的数据结构
- name: print nginx syntax result
  debug: var=nginxsyntax

通过debug 模块,打印出来的返回结果。 当nginxsyntax.rc 为 0时语法校验正确。

[root@ansible ~]# cat inventory.ini 		#资产管理文件
[dbservers]
192.168.1.128

[webservers]
192.168.1.129

[root@ansible ~]# vim nginxsyntax.yml
---
- name: check nginx syntax
  hosts: webservers
  gather_facts: no			#不需要收集facts信息
  remote_user: root
  tasks:
   - name: check nginx syntax
     shell: /usr/sbin/nginx -t
     register: nginxsyntax
     
   - name: print nginx syntax result
     debug: var=nginxsyntax
     when: nginxsyntax.rc == 0
     
[root@ansible ~]# ansible-playbook -i inventory.ini nginxsyntax.yml 

Handlers属性

观察当前的 Playbook,不能发现,当我的配置⽂件没有发⽣变化时,每次依然都会去触发TASK “reload nginx server”。

如何能做到只有配置⽂件发⽣变化的时候才去触发TASK “reload nginx server”,这样的处理才是最完美的实现。此时可以使⽤handlers 属性。

在tasks下添加一个通知notif,如
tasks:
  - name: update nginx main config
    copy: src=nginx.conf dest=/etc/nginx/nginx.conf
    notify: reload nginx service
    tags: updateconfig
    
handlers:
  - name: reload nginx service
    service: name=nginx state=reloaded
    when: 
      - nginxsyntax.rc == 0
      - nginxrunning.stat.exists == true
[root@ansible ~]# ansible-playbook -i inventory.ini tags.yml -t updateconfig 

环境准备:一个修改后的http.conf文件,拷贝到/root/ansible/files/

[root@ansible ~]# tree ansible/
ansible/
├── files
│   └── httpd.conf
├── http.yml
└── inventory.ini

[root@ansible ansible]# cat http.yml 
---
- hosts: db_servers
  gather_facts: no
  remote_user: root

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
# notify作用:当上一条文件发生变化时,就重启httpd,类似标签,可写多个值
      notify: 
        - restart httpd
    - name: ensure apache running
      service: name=httpd state=started enabled=yes
      
# notify就会查找handlers对应它的名字,执行相应的模块,可写多个值	
  handlers:
    - name: restart httpd
      service: name=httpd state=restarted
      
[root@ansible ansible]# ansible-playbook -i inventory.ini -C http.yml 

测试,查看被管理节点的监听端口

[root@ansible ansible]# ansible 192.168.1.133 -i inventory.ini -a "ss -ntl"
192.168.1.133 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      100    127.0.0.1:25                     *:*                  
LISTEN     0      128          *:22                       *:*                  
LISTEN     0      100        [::1]:25                    [::]:*                  
LISTEN     0      128       [::]:8080 (默认端口已变为8080)      [::]:*                  
LISTEN     0      128       [::]:22                    [::]:* 

当修改files/httpd.conf的端口时,重复以上操作,发现端口已更改。

tags属性

我们可以通过Play中的tags 属性,去解决⽬前PlayBook变更⽽导致的扩⼤变更范围和变更⻛险的问题。

在改进的PlayBook中,针对⽂件发布TASK 任务"update nginx main config" 和 “add virtualhost config"新增了属性 tags ,属性值为updateconfig。另外我们新增"reload nginx server” TASK任务。当配置⽂件更新后,去reload Nginx 服务。

那重新加载需要依赖于 Nginx 服务是已经启动状态。所以,还需要进⼀步通过判断 Nginx 的 pid ⽂件存在,才证明 Nginx 服务本身是启动中,启动中才可以 reload Nginx 服务。判断⼀个⽂件是否存在使⽤ stat 模块。

在任务下加一个标签
tags: updateconfig
当执行ansible-playbook -i inventory.ini http.yml -t updateconfig 的时候
它会自动执行有标签tags的任务,而不是全部去执行任务,减少了变更范围和变更风险。
[root@ansible ansible]# cat http.yml 
---
- hosts: db_servers
  gather_facts: no
  remote_user: root

  tasks:
    - name: Install httpd
      yum: name=httpd state=present
      
    - name: Install configure file
      copy: src=files/httpd.conf dest=/etc/httpd/conf/
      notify: restart httpd
      tags: updateconfig
      
    - name: ensure apache running
      service: name=httpd state=started enabled=yes

  handlers:
    - name: restart httpd
      service: name=httpd state=restarted
   
[root@ansible ansible]# ansible-playbook -i inventory.ini http.yml -t updateconfig

JinJa2模板

背景介绍

⽬前Nginx的配置⽂件在所有的服务器上都是相同的,但我希望能根据每⼀台服务器的性能去定制服务的启动进程。 同时定制每⼀台Nginx服务的响应头,以便于当某台服务出现问题时能快速定位到具体的服务器。 要做这样的定制势必会导致⼀个问题,Nginx 在每台物理服务器上的配置⽂件都不⼀样,这样的配置⽂件如何管理呢? 再使⽤copy 模块去做管理显然已经不合适。此时使⽤Ansible 提供的另⼀个==模板(template)==功能,它可以帮助我们完美的解决问题。

jinja2语言使用字面量,有下面形式

字符串:使用单引号或双引号
数字:整数、浮点型
列表:[item1,item2,…]
元组:(item1,item2,…)
字典:{key1:value1,key2:value2,…}
布尔型:true/false
算术运算:+ - * / // % **
比较操作:== != > < >= <=
逻辑运算:and or not
流表达式:For If When

jinja2 中存在 三种定界符
注释: {# 注释内容 #}
变量引⽤: {{ var }}
逻辑表达: {% %}

使用template模块

# 在/root/ansible/inventory.ini下

[root@ansible ansible]# vim inventory.ini 
[web_servers]
192.168.1.132

[db_servers]
192.168.1.133

[all_servers]
[all_servers:children]
web_servers
db_servers
[root@ansible ansible]# ls
inventory.ini  temnginx.yml  templates
--------------------------------------------
[root@ansible ansible]# vim templates/nginx.conf.j2
#user  nobody;
worker_processes  {{ ansible_processor_vcpus }};
...
-------------------------------------------
[root@ansible ansible]# vim temnginx.yml
---
- hosts: web_servers
  remote_user: root
# 必须打开数据收集,才能使用变量值,或者不写这个默认开启
  gather_facts: yes

  tasks:
    - name: install epel
      yum: name=epel-release
      
    - name: install nginx
      yum: name=nginx
# 源文件路径会根据template模块查找执行目录的templates目录下查找,templates文件夹名不可改为其他名
    - name: template config  to remote hosts
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf

    - name: start service
      service: name=nginx state=started enabled=yes
-------------------------------------------------------
[root@ansible ansible]# ansible-playbook -i inventory.ini temnginx.yml 

验证

[root@ansible ansible]# ssh root@192.168.1.133
Last login: Sun Aug 15 23:11:26 2021 from 192.168.1.130

[root@client-1 ~]# head -3 /etc/nginx/nginx.conf

#user  nobody;
worker_processes  2;
# 根据内核数得到进程数

小插曲:nginx的配置文件在/etc/nginx/nginx.conf还是在/usr/local/nginx/conf/nginx.conf?

/usr/local下的才是你正在使用的配置文件,/etc/nginx那个只是默认的配置, nginx -t 如果没有加-c指定配置文件,则测试的是默认配置文件

是安装方式不同导致的,使用 wget 安装的配置文件在 /usr/local/nginx/conf,使用 yum 安装的配置文件在 /etc/nginx下。

使用流程控制for和if

template中也可以使用流程控制for循环和if条件判断,实现动态生成文件功能

案例一

[root@ansible ansible]# vim nginx.conf1.j2
{% for vhost in nginx_vhosts %}
server {
   {{ vhost }}
}
{% endfor %}

[root@ansible ansible]# vim temnginx1.yml 
---
- hosts: db_servers
  remote_user: root
  vars:
    nginx_vhosts:
     - 81
     - 82
     - 83

  tasks:
     - name: template config
       template: src=nginx.conf1.j2 dest=/data/nginx.conf

[root@ansible ansible]# ansible-playbook -i inventory.ini  temnginx1 .yml 

验证

[root@client-1 data]# cat nginx.conf 
server {
   81
}
server {
   82
}
server {
   83
}

案例二

[root@ansible ansible]# vim templates/nginx.conf2.j2
{% for vhost in nginx_vhosts %}
server {
   listen {{ vhost.listen }}
}
{% endfor %}

{% for num in worker_processor%}
worker_processor {{ num.worker }}
{% endfor %}
--------------------------------------------------------
[root@ansible ansible]# vim temnginx2.yml 
---
- hosts: db_servers
  remote_user: root
  vars:
    nginx_vhosts:
      - listen: 8080
       #server_name: "web1.123.com"(可添加字典)
       #root: "/var/www/nginx/web1"
      - listen: 8081
       #server_name: "web2.123.com"
       #root: "/var/www/nginx/web2"
    worker_processor:
      - worker: 2
      - worker: 3

  tasks:
    - name: config file
      template: src=nginx.conf2.j2 dest=/data/nginx.conf2
-------------------------------------------------------------
[root@ansible ansible]# ansible-playbook -i inventory.ini  temnginx2.yml 

验证

[root@client-1 data]# cat nginx.conf2
server {
   listen 8080
   #server_name web1.123.com
   #root /var/www/nginx/web1
}
server {
   listen 8081
   #server_name web1.123.com
   #root /var/www/nginx/web1
}

worker_processor 2
worker_processor 3

案例三

[root@ansible ansible]# vim templates/nginx.conf4.j2
{% for vhost in nginx_vhost %}
server {
  listen {{ vhost.listen }}
  {% if vhost.server_name is defined %}
server_name {{ vhost.server_name }}
  {% endif %}
root {{ vhost.root }}
}
{% endfor %}
-------------------------------------------------------------
[root@ansible ansible]# vim temnginx4.yml
---
- hosts: db_servers
  remote_user: root
  vars:
    nginx_vhost:
      - web1:
        listen: 8080
        root: "/var/www/nginx/web1"
      - web2:
        listen: 8080
        server_name: "web2.123.com"
        root: "/var/www/nginx/web2"
      - web3:
        listen: 8080
        server_name: "web3.123.com"
        root: "/var/www/nginx/web3"
        
  tasks:
    - name: template config to
      template: src=nginx.conf4.j2 dest=/data/nginx4.conf
----------------------------------------------------------
[root@ansible ansible]# ansible-playbook -i inventory.ini  temnginx4.yml 

验证

[root@client-1 data]# cat nginx4.conf 
server {
  listen 8080
  root /var/www/nginx/web1
}
server {
  listen 8080
  server_name web2.123.com
  root /var/www/nginx/web2
}
server {
  listen 8080
  server_name web3.123.com
  root /var/www/nginx/web3
}

使用迭代 with_items

迭代:当有需要重复执行的任务时,可以使用迭代机制,对迭代项的引用,固定变量名为“item”
要在task中使用with_item给定要迭代的元素列表

[root@ansible ansible]# vim with_item.yml
---
- hosts: db_servers
  remote_user: root

  tasks:
    - name: add several users
      user: name={{ item }} state=present groups=wheel
      with_items:
        - testuser1
        - testuser2
[root@ansible ansible]# ansible-playbook -i inventory.ini  with_item.yml 

验证
[root@ansible ansible]# ansible db_servers -i inventory.ini  -a 'getent passwd'
testuser1:x:1002:1002::/home/testuser1:/bin/bash
testuser2:x:1003:1003::/home/testuser2:/bin/bash

迭代嵌套子变量

在迭代中还可以嵌套子变量,关联多个变量一起使用

---
- hosts: db_servers
  remote_user: root

  tasks:
    - name: add some group
     group: name={{ item }} state=present 
      with_items:
        - nginx
        - mysql
        -apache
    - name: add some users
      user: name={{ item.name }} group={{ item.group }} home={{ item.home }} create_home=yes state=present
      with_items:
        -{ name: 'nginx', group: 'nginx', home='/data/nginx' }
        -{ name: 'mysql', group: 'mysql' home='/data/mysql' }
        -{ name: 'apache', group: 'apache' home='/data/apache' }

Ansible Roles

介绍

  • 一个数据中心有可能存在好多类型的服务器。比如WEB类型、DB类型、开发人员使用的开发类型、QA使用的测试类型等等。
  • 实际生产中,基本每个类型的服务器的初始化行为都不一致。
  • 那要在一个PlayBook中将这些动作完成,这个PlayBook将变s臃肿、庞大,且难以后续维护和更新。
  • 如果能够针对每个类型的服务器单独编写PlayBook,最后通过某种方式整合这PlayBook, 在管理方式上就又会变得简单。
  • Ansible中提供了类似的概念,也就是Role,它允许管理员将他们复杂的PlayBook分解成一个个小的逻辑单元,以便于维护和管理。
  • roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。
  • 运维复杂的场景建议使用roles,代码复用度高

roles: 多个角色的集合,可以将多个的role,分别放在roles目录下的独立子目录中

roles/
mysql/
httpd/
nginx/
redis/

结构

使用roles安装httpd服务

实验要求: 安装httpd服务,更换配置文件和index.html首页

1、启动入口,编排任务执行顺序,属于tasks任务文件
vim roles/httpd/tasks/main.yml
- include: install.yml
- include: config.yml
- include: index.yml
- include: service.yml
2、安装httpd,属于tasks任务文件
vim roles/httpd/tasks/install.yml
- name: install httpd package
  yum: name=httpd
3、拷贝文件,属于tasks任务文件,将修改的httpd.conf替换默认的配置文件
使用notify,当配置文件发生改变,执行playbook时重启服务,需添加handlers属性文件
vim roles/httpd/tasks/config.yml
- name: config file
  copy: src=httpd.conf dest=/etc/httpd/conf/ backup=yes
  notify: restart
4、拷贝文件,属于tasks任务文件
vim roles/httpd/tasks/index.yml
- name: index.html
  copy: src=index.html dest=/var/www/html
5、启动服务,属于tasks任务文件
vim roles/httpd/tasks/service.yml
- name: start service
  service: name=httpd state=started enabled=yes
6、因为添加了notify,需要添加handlers文件
vim roles/httpd/handlers/main.yml
- name: restart
  service: name=httpd state=restarted
 7、ansible-playbook执行文件,执行httpd的roles角色,后续可以添加其他的角色,如mysql,nginx
 vim roles_httpd.yml
- hosts: web_servers
  remote_user: root

  roles:
    - httpd
[root@ansible ansible]# tree roles/
roles/
└── httpd
    ├── files
    │   ├── httpd.conf#修改过的配置文件
    │   └── index.html#更换首页html
    ├── handlers
    │   └── main.yml
    └── tasks
        ├── config.yml
        ├── index.yml
        ├── install.yml
        ├── main.yml
        └── service.yml

4 directories, 8 files
[root@ansible ansible]# ansible-playbook -i inventory.ini roles_httpd.yml 

验证

[root@ansible ansible]# ansible web_servers -i inventory.ini -a 'ss -ntl'
192.168.1.132 | CHANGED | rc=0 >>
State      Recv-Q Send-Q Local Address:Port               Peer Address:Port              
LISTEN     0      128          *:22                       *:*         
LISTEN     0      100    127.0.0.1:25                     *:*         
LISTEN     0      128       [::]:6666                  [::]:*         
LISTEN     0      128       [::]:22                    [::]:*    
LISTEN     0      100      [::1]:25                    [::]:* 
# 6666端口是修改后httpd的监听端口,是默认配置文件被替换的

使用memcached

准备工作

创建文件夹,写好template模板
mkdir roles/memcached/{tasks,templates} -pv
cp /root/memcached roles/memcached/templates/memcached.j2

vim roles/memcached/templates/memcached.j2
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="{{ ansible_memtotal_mb//4 }}"#指memcached缓存为服务器内存整除4
OPTIONS=""
vim roles/memcached/tasks/main.yml
- include: install.yml
- include: config.yml
- include: service.yml

vim roles/memcached/tasks/install.yml
- name: install
  yum: name=memcached

vim roles/memcached/tasks/config.yml
- name: config file
  template: src=memcached.j2 dest=/etc/sysconfig/memcached

vim roles/memcached/tasks/service.yml
- name: service
  service: name=memcached state=started enabled=yes
 
 vim roles_memcached.yml
 ---
- hosts: web_servers
  roles: 
    - memcached

[root@ansible ansible]# tree roles/memcached/
roles/memcached/
├── tasks
│   ├── config.yml
│   ├── install.yml
│   ├── main.yml
│   └── service.yml
└── templates
    └── memcached.j2
    
[root@ansible ansible]# ansible-playbook -i inventory.ini roles_memcached.yml 

验证:

默认值为64
[root@ansible ansible]# ansible web_servers -i inventory.ini -a 'cat /etc/sysconfig/memcached'
192.168.1.132 | CHANGED | rc=0 >>
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="243"
OPTIONS=""

作者:月光染衣袂
转发请加上该地址 : https://editor.csdn.net/md/?articleId=119152387
如果笔记对您有用,请帮忙点个赞!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值