企业自动化运维ansible

自动化运维工具ansible

  • 运维自动化发展历程及技术应用

云计算工程师核心职能

  • Linux运维工程师职能划分

自动化动维应用场景

  • 文件传输
  • 命令执行
    • 应用部署
    • 配置管理
    • 任务流编排

企业实际应用场景分析

1 Dev开发环境

使用者:程序员
功能:程序员开发软件,测试BUG的环境
管理者:程序员

2 测试环境

使用者:QA测试工程师
功能:测试经过Dev环境测试通过的软件的功能
管理者:运维
说明:测试环境往往有多套,测试环境满足测试功能即可,不宜过多
(1)测试人员希望测试环境有多套,公司的产品多产品线并发,即多个版本,意味着多个版本同步测试
(2)通常测试环境有多少套和产品线数量保持一样

3 发布环境:代码发布机,有些公司为堡垒机(安全屏障)

使用者:运维
功能:发布代码至生产环境
管理者:运维(有经验)
发布机:往往需要有2台(主备)

4 生产环境

使用者:运维,少数情况开放权限给核心开发人员,极少数公司将权限完全开放给开发人员并其维护
功能:对用户提供公司产品的服务
管理者:只能是运维
生产环境服务器数量:一般比较多,且应用非常重要。往往需要自动工具协助部署配置应用

5 灰度环境(生产环境的一部分)

使用者:运维
功能:在全量发布代码前将代码的功能面向少量精准用户发布的环境,可基于主机或用户执行灰度发布
案例:共100台生产服务器,先发布其中的10台服务器,这10台服务器就是灰度服务器
管理者:运维
灰度环境:往往该版本功能变更较大,为保险起见特意先让一部分用户优化体验该功能,待这部分用户使用没有重大问题的时候,再全量发布至所有服务器

程序发布

  • 预发布验证
    • 新版本的代码先发布到服务器(跟线上环境配置完全相同,只是未接入到调度器)
  • 程序发布:
    • 不能导致系统故障或造成系统完全不可用
    • 不能影响用户体验
  • 灰度发布:
  • 发布路径:
把原来的旧版本保留
/webapp/tuangou-1.1
/webapp/tuangou
/webapp/tuangou-1.2
  • 发布过程:在调度器上下线一批主机(标记为maintanance状态) –> 关闭服务 –> 部署新版本的应用程序 –> 启动服务 –> 在调度器上启用这一批服务器
  • 自动化灰度发布:脚本、发布平台

常用自动化运维工具

  • Ansible:python,Agentless,中小型应用环境(不需要在客户端安装代理程序,基于ssh来管理,300-500台服务器)
  • Saltstack:python,一般需部署agent,执行效率更高
  • Puppet:ruby, 功能强大,配置复杂,重型,适合大型环境
  • Fabric:python,agentless
  • Chef: ruby,国内应用少
  • Cfengine
  • func

企业级自动化运维工具应用实战ansible

  • 公司计划在年底做一次大型市场促销活动,全面冲刺下交易额,为明年的上市做准备。公司要求各业务组对年底大促做准备,运维部要求所有业务容量进行三倍的扩容,并搭建出多套环境可以共开发和测试人员做测试,运维老大为了在年底有所表现,要求运维部门同学尽快实现,当你接到这个任务时,有没有更快的解决方案?

Ansible发展史

  • Ansible
    • 创始人,Michael DeHaan( Cobbler 与 Func 的作者)
    • 2012-03-09,发布0.0.1版,红帽收购
    • 2015-10-17,Red Hat宣布收购
  • 同类自动化工具 GitHub关注程度( 2016- - 07- - 10)

特性

  • 模块化:调用特定的模块,完成特定任务
  • 有Paramiko,PyYAML,Jinja2(模板语言)三个关键模块
  • 支持自定义模块
  • 基于Python语言实现
  • 部署简单,基于python和SSH(默认已安装),agentless
  • 安全,基于OpenSSH
  • 支持playbook编排任务
  • 幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况
  • 无需代理不依赖PKI(无需ssl)
  • 可使用任何编程语言写模块
  • YAML格式,编排任务,支持丰富的数据结构
  • 较强大的多层解决方案

ansible架构

-
- ansible是基于key的,所以需要将主机间实现key验证。当然用密码也可以,只是比较麻烦

Ansible工作原理


    1. 直接使用ansible命令来执行一条命令
    2. 当生产环境稳定后可以用ansible编写playboot脚本来执行
    3. 可以使用公有云或私有云
    4. 可以使用CMDB,配置管理数据库

Ansible主要组成部分

  • ANSIBLE PLAYBOOKS:任务剧本(任务集),编排定义Ansible任务集的配置文件,由Ansible顺序依次执行,通常是JSON格式的YML文件
  • INVENTORY:Ansible管理主机的清单/etc/anaible/hosts
  • MODULES:Ansible执行命令的功能模块,多数为内置的核心模块,也可自定义
  • PLUGINS:模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等,该功能不常用
  • API:供第三方程序调用的应用程序编程接口
  • ANSIBLE:组合INVENTORY、API、MODULES、PLUGINS的绿框,可以理解为是ansible命令工具,其为核心执行工具
  • Ansible命令执行来源:
    • USER,普通用户,即SYSTEM ADMINISTRATOR
    • CMDB(资产管理系统) API 调用
    • PUBLIC/PRIVATE CLOUD API调用
    • USER-> Ansible Playbook -> Ansibile
  • 利用ansible实现管理的方式:
    • Ad-Hoc 即ansible命令,主要用于临时命令使用场景
    • Ansible-playbook 主要用于长期规划好的,大型项目的场景,需要有前提的规划
      -Ansible-playbook(剧本)执行过程:
    • 将已有编排好的任务集写入Ansible-Playbook
    • 通过ansible-playbook命令分拆任务集至逐条ansible命令,按预定规则逐条执行
  • Ansible主要操作对象:
    • HOSTS主机
    • NETWORKING网络设备
  • 注意事项
    • 执行ansible的主机一般称为主控端,中控,master或堡垒机
    • 主控端Python版本需要2.6或以上
    • 被控端Python版本小于2.4需要安装python-simplejson
    • 被控端如开启SELinux需要安装libselinux-python
    • windows不能做为主控端

安装

rpm包安装: EPEL源

yum install ansible
ansible不是一个服务/etc/hosts是最重要的文件
ansible是一个管理端

编译安装

yum -y install python-jinja2 PyYAML python-paramiko
python-babel python-crypto
tar xf ansible-1.5.4.tar.gz
cd ansible-1.5.4
python setup.py build
python setup.py install
mkdir /etc/ansible
cp -r examples/* /etc/ansible

Git方式

git clone git://github.com/ansible/ansible.git --
recursive

cd ./ansible
source ./hacking/env-setup

pip安装: pip是安装Python包的管理器,类似yum

yum install python-pip python-devel
yum install gcc glibc-devel zibl-devel rpm-bulid openssl-devel
pip install --upgrade pip
pip install ansible --upgrade
  • 确认安装: ansible –version

相关文件

配置文件

/etc/ansible/ansible.cfg 主配置文件,配置ansible工作特性
/etc/ansible/hosts 主机清单被管理端的主机存放,如果将主机不写入这个文件则无法管理这个主机
/etc/ansible/roles/ 存放角色的目录
  • 如果要管理的ip没加入到hosts这文件中则会出现这种错误
[root@centos7 yum.repos.d]# ansible 192.168.27.101 -m ping
 [WARNING]: Could not match supplied host pattern, ignoring: all

 [WARNING]: provided hosts list is empty, only localhost is available

 [WARNING]: Could not match supplied host pattern, ignoring: 192.168.27.101

 [WARNING]: No hosts matched, nothing to do
  • 将主机加入到hosts文件中,最简单的方法是将要管理的主机ip写进去,推荐,用分类的方式
    -
  • 分类的方式
[root@centos7 yum.repos.d]# vim /etc/ansible/hosts 
#   - Blank lines are ignored
#   - Groups of hosts are delimited by [header] elements
#   - You can enter hostnames or ip addresses
#   - A hostname/ip can be a member of multiple groups
192.168.27.[101:103]   也可以写成这种格式这表示192.168.27.101103直接的ip


# Ex 1: Ungrouped hosts, specify before any group headers.

## green.example.com
## blue.example.com
## 192.168.100.1
## 192.168.100.10

# Ex 2: A collection of hosts belonging to the 'webservers' group

## [webservers]
## alpha.example.org
## beta.example.org
## 192.168.1.100
## 192.168.1.110

# If you have multiple hosts following a pattern you can specify
# them like this:

## www[001:006].example.com

# Ex 3: A collection of database servers in the 'dbservers' group

## [dbservers]
## 
## db01.intranet.mydomain.net
## db02.intranet.mydomain.net
## 10.25.1.56
## 10.25.1.57

# Here's another example of host ranges, this time there are no
# leading 0s:

## db-[99:101]-node.example.com
  • 然后在次用ping命令来检查主机是否活跃
  • 因为ansible是基于shh的,第一次链接需要输入yes/no,不是很方便,想要去掉这个yes/no需要修改配置文件/etc/ansible/ansible.cfg这个文件
  • 把host_key_checking = False这一行注释去掉
  • 在重新执行就不会提示这样第一次yes或no
  • 但是报错,也没有让我们输入密码,ansible默认是基于key验证的,如果想使用密码在后面加-k
  • 这样就成功了,当然我们也可以多台主机,主机ip之间用逗号隔开
  • 当然我们只用输入一遍密码两个主机都成功了,说明如果主机密码不一样会报错

用分组的方式将被管理主机写如/etc/ansible/ansible.cfg文件中

  • 分组就是将一组主机分成一组,并在中括号里取一个组名,一个主机可以在多个组里。还有一个默认组就是all,这个组表示在这个文件中的所有主机

创建key验证

  1. 用ssh-keygen 生成公钥私钥对
[root@ansible ~]# cd ~
[root@ansible ~]# cd .ssh/
[root@ansible .ssh]# ls
id_rsa  id_rsa.pub  known_hosts
[root@ansible .ssh]# 
  1. 用ssh-copy-id 要连接的ip主机
    • 完成key验证后,就不用加-k选项了

由于ansible默认是没有启用日志的,所以我们要在配置文件中启用配置文件


  • 默认也是没有启用的,把注释去掉启用,当然也可改成其它路径
  • 执行以下ansible命令,然后看一下日志文件
  • 这里面存放着命令的执行结果

程序

/usr/bin/ansible 主程序,临时命令执行工具
/usr/bin/ansible-doc 查看配置文档,模块功能查看工具
/usr/bin/ansible-galaxy 下载/上传优秀代码或Roles模块的官网平台
/usr/bin/ansible-playbook 定制自动化任务,编排剧本工具
/usr/bin/ansible-pull 远程执行命令的工具
/usr/bin/ansible-vault 文件加密工具
/usr/bin/ansible-console 基于Console界面与用户交互的执行工具

主机清单inventory

  • Inventory 主机清单
    • ansible的主要功用在于批量主机操作,为了便捷地使用其中的部分主机,可以在inventory file中将其分组命名
  • 默认的inventory file为/etc/ansible/hosts
  • inventory file可以有多个,且也可以通过DynamicInventory来动态生成
  • /etc/ansible/hosts文件格式
  • inventory文件遵循INI文件风格,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中;此外,当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明
ntp.magedu.com
[webservers]
www1.magedu.com:2222
www2.magedu.com
[dbservers]
db1.magedu.com
db2.magedu.com
db3.magedu.com
  • 如果主机名称遵循相似的命名模式,还可以使用列表的方式标识各主机
    示例
[websrvs]
www[01:100].example.com
[dbsrvs]
db-[a:f].example.com

ansible配置文件

Ansible 配置文件/etc/ansible/ansible.cfg (一般保持默认)
[defaults]
#inventory = /etc/ansible/hosts # 主机列表配置文件
#library = /usr/share/my_modules/ # 库文件存放目录
#remote_tmp = $HOME/.ansible/tmp #临时py命令文件存放在远程主机目录,就是当我们执行命令是相当于把命令复制到远程主机中这个目录下生成一个临时文件,执行完后删除
#local_tmp = $HOME/.ansible/tmp # 本机的临时命令执行目录
#forks = 5 # 默认并发数,如果有100台主机就会5台5台的执行,分批次的,也可以调100,要求机器性能好
#sudo_user = root # 默认sudo 用户,需要在被管理主机上做sudo配置
#ask_sudo_pass = True #每次执行ansible命令是否询问ssh密码
#ask_pass = True
#remote_port = 22  因为是基于ssh,ssh默认是22端口,如果ssh端口号改成其它的,这里也是要改的
#host_key_checking = False # 检查对应服务器的host_key,建议取消注释

ansible系列命令

  • Ansible系列命令
ansible ansible-doc ansible-playbook ansible-vault
ansible-console ansible-galaxy ansible-pull
  • ansible-doc: 显示模块帮助
    ansible-doc [options] [module...]
    -a 显示所有模块的文档
    -l, --list 列出可用模块
    -s, --snippet 显示指定模块的playbook片段

示例

ansible-doc –l 列出所有模块
ansible-doc ping 查看指定模块帮助用法
ansible-doc –s ping 查看指定模块帮助用法简要信息

  • ansible通过ssh实现配置管理、应用部署、任务执行等功能,建议配置ansible端能基于密钥认证的方式联系各被管理节点
  • ansible [-m module_name] [-a args]
--version 显示版本
-m module 指定模块,默认为command模块
-v 详细过程 –vv -vvv更详细
--list-hosts 显示主机列表,可简写—list
-k, --ask-pass 提示连接密码,默认Key验证
-K, --ask-become-pass 提示输入sudo
-C, --check 检查,并不执行,只是模拟的执行一次,并不会在真正的主机上执行
-T, --timeout=TIMEOUT 执行命令的超时时间,默认10s
-u, --user=REMOTE_USER 执行远程执行的用户,指定以哪个用户执行,如果不指定则默认是root
-b, --become 代替旧版的sudo 切换
  • 显示主机列表
  • 指定用户
  • 也可以用sudo,提前是要把对端主机上配置好sudo文件
    1. 配置sudo文件,这里我们直接把用户加入wheel组中,因为wheel组中在sudo是一个管理员组,拥有root的权限
  • 这里用command模块是命令模块,加-a是可以后面跟参数,但是这里报错,让我们输入sudo的密码加上大K参数
  • 这里会让输入两边口令,一个是guo用户ssh的口令,一个sudo的口令,这样会很麻烦,我们改一下sudo配置文件设置成不输入此密码,在/etc/sudoers
  • 这样就只用输入一个ssh口令了

如何将guo用户也设置成key验证呢

  1. 用命令ssh-copy-i [email protected]
    -
    • 在执行以下就不用输入密码了
usermod -aG wheel guo

ansible的Host-pattern

  • ansible的Host-pattern
    • 匹配主机的列表
    • All :表示所有Inventory中的所有主机
      • ansible all –m ping
    • *:通配符
      • ansible “*” -m ping 这个”*”号相当于all全部主机
        “`bash
      • ansible 192.168.1.* -m ping
      • ansible “*srvs” -m ping
    • 或关系
      • ansible “websrvs:appsrvs” -m ping 表示这两个都执行可以加多个
      • ansible “192.168.1.10:192.168.1.20” -m ping
  • 逻辑与
    • ansible ‘websrvs:&dbsrvs’ –m ping 就是取交集
      • 在websrvs组并且在dbsrvs组中的主机
  • 逻辑非
    • ansible ‘websrvs:!dbsrvs’ –m ping
    • 在websrvs组,但不在dbsrvs组中的主机
  • 综合逻辑
    • ansible ‘websrvs:dbsrvs:&appsrvs:!ftpsrvs’ –m ping
  • 正则表达式
    • ansible “websrvs:&dbsrvs” –m ping
    • ansible “~(web|db).*.magedu.com” –m ping

      • 这里的波浪符表示是一个正则表达式,点在正则表示任意字符,所有要用反向单引号转义

ansible命令执行过程

  • ansible命令执行过程可以用-vvv来查看执行过程
  • 可以用-vvv来查看命令执行的详细过程[root@ansible ~]# ansible ‘~(web|db|ag)ser’ -m command -a “ls /root” -vvv
    1. 加载自己的配置文件 默认/etc/ansible/ansible.cfg
    2. 加载自己对应的模块文件,如command
    3. 通过ansible将模块或命令生成对应的临时py文件,并将该 文件传输至远程服务器的对应执行用户$HOME/.ansible/tmp/ansible-tmp-数字/XXX.PY文件
    4. 给文件+x执行
    5. 执行并返回结果
    6. 删除临时py文件,sleep 0退出
  • 执行状态:
    • 颜色的定义在ansible的配置文件里定义的
    • 绿色:执行成功并且不需要做改变的操作
    • 黄色:执行成功并且对目标主机做变更
    • 红色:执行失败

ansible使用示例

  • 以wang用户执行ping存活检测
    • ansible all -m ping -u wang -k
  • 以wang sudo至root执行ping存活检测
    • ansible all -m ping -u wang –b -k
  • 以wang sudo至mage用户执行ping存活检测
    • ansible all -m ping -u wang –b -k –become-user mage
  • 以wang sudo至root用户执行ls
    • ansible all -m command -u wang –become-user=root -a’ls /root’ -b –k -K

ansible常用模块

  • ping :探测对方主机是否在开机,这里的ping模块并不是用ICMP协议的
    “bash
    将主机设置为禁止ping
    [root@localhost ~]# cat /proc/sys/net/ipv4/icmp_echo_ignore_all
    0
    [root@localhost ~]# echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all
    现在在测这个主机
    [root@ansible .ssh]# ansible 192.168.27.101 -m ping
    192.168.27.101 | SUCCESS => {
    “changed”: false,
    “ping”: “pong”
    }
    [root@ansible .ssh]# ping 192.168.27.101
    PING 192.168.27.101 (192.168.27.101) 56(84) bytes of data.
    ^C
    — 192.168.27.101 ping statistics —
    6 packets transmitted, 0 received, 100% packet loss, time 5002ms
    如果把ssh服务停了也是可以执行成功的,只有把网络服务停了才会失败
> Command:在远程主机执行命令,默认模块,可忽略-m选项  
-a是后面要执行的参数  
- 参数
    + chdir 运行command命令前先cd到这个目录
    + creates 如果这个参数对应的文件存在,就不运行command
    + executable 将shell切换为command执行,这里的所有命令需要使用绝对路径
    + removes 如果这个参数对应的文件不存在,就不运行command
```bash  
ansible srvs -m command -a 'service vsftpd start'
ansible srvs -m command -a 'echo magedu |passwd --stdin wang' 不成功
此命令不支持 $VARNAME < > | ; & 等,用shell模块实现
用chdir进入到某个目录来执行参数
[root@ansible ~]# ansible 'dbser' -m command -a 'chdir=/app/ ls'
192.168.27.102 | SUCCESS | rc=0 >>
123

192.168.27.128 | SUCCESS | rc=0 >>
1
aaa
access_log
awk.txt
dir
f1.txt
f2
lost+found
passwd
[root@ansible ~]# ansible 'dbser' -m command -a 'chdir=/app/ creates=123 ls'  
192.168.27.102 | SUCCESS | rc=0 >>
skipped, since 123 exists  这里写着123这个文件存在就跳过,而另一个主机没有对应的文件所有执行ls

192.168.27.128 | SUCCESS | rc=0 >>
1
aaa
access_log
awk.txt
dir
f1.txt
f2
lost+found
passwd




"se-preview-section-delimiter">
  • 如果文件不存在则不执行

    Shell:和command相似,用shell执行命令

  • 常用参数
    • chdir 跟command一样的,运行shell之前cd到某个目录
    • creates 跟command一样的,如果某个文件存在则不运行shell
    • remove 跟command一样的,如果某个文件不存在则不运行shell
ansible srv -m shell -a 'echo magedu |passwd –stdin wang'
调用bash执行命令 类似 cat /tmp/stanley.md | awk -F'|''{print $1,$2}' &> /tmp/example.txt 这些复杂命令,即使使用shell也可能会失败,解决办法:写到脚本时,copy到远程,执行,再把需要的结果拉回执行命令的机器
查看主机名
[root@ansible ~]# ansible 'dbser' -m shell -a  'echo $HOSTNAME'
192.168.27.102 | SUCCESS | rc=0 >>
102

192.168.27.128 | SUCCESS | rc=0 >>
centos6.magedu.com
体验shell和command的区别,先cd到某个需要编译的目录,执行condifgure然后,编译,然后安装。
 ansible -i hosts all -m shell -a "./configure && make && make insatll" chdir=/xxx/yyy/
 shell也支持条件判断&&||
 [root@ansible ~]# ansible 'dbser' -m shell -a 'grep -q root /etc/passwd && ls /app'
192.168.27.102 | SUCCESS | rc=0 >>
123

192.168.27.128 | SUCCESS | rc=0 >>
1
aaa
access_log
awk.txt
dir
f1.txt
f2
lost+found
passwd

[root@ansible ~]# ansible 'dbser' -m shell -a 'grep -q rootsd /etc/passwd && ls /app'
192.168.27.102 | FAILED | rc=1 >>
non-zero return code   如果没有则会出现非0错误码

192.168.27.128 | FAILED | rc=1 >>
non-zero return code




"se-preview-section-delimiter">

Script:运行脚本
- 相当于先把脚本传到远方节点,然后在执行
-

-a "/PATH/TO/SCRIPT_FILE"
snsible websrvs -m script -a f1.sh




"se-preview-section-delimiter">

Copy:从服务器复制文件到客户端
- 常用参数
+ src
+ 用于定位ansible执行的机器上的文件,需要绝对路径。如果拷贝的是文件夹,那么文件夹会整体拷贝,如果结尾是”/”,那么只有文件夹内的东西被考过去。一切的感觉很像rsync,源地址
+ content
+ 用来替代src,用于将指定文件的内容,拷贝到远程文件内
+ dest
+ 用于定位远程节点上的文件,需要绝对路径。如果src指向的是文件夹,这个参数也必须是指向文件夹,目标文件
+ backup
+ 备份远程节点上的原始文件,在拷贝之前。如果发生什么意外,原始文件还能使用。
+ directory_mode
+ 这个参数只能用于拷贝文件夹时候,这个设定后,文件夹内新建的文件会被拷贝。而老旧的不会被拷贝
+ follow
+ 当拷贝的文件夹内有link存在的时候,那么拷贝过去的也会有link
+ force
+ 默认为yes,会覆盖远程的内容不一样的文件(可能文件名一样)。如果是no,就不会拷贝文件,如果远程有这个文件
+ group
+ 设定一个群组拥有拷贝到远程节点的文件权限
+ mode
+ 等同于chmod,参数可以为“u+rwx or u=rw,g=r,o=r”
+ owner
+ 设定一个用户拥有拷贝到远程节点的文件权限
- 把复制selinux配置文件
-
-
- 看一下备份文件
-
- 也可以改权限,所属组,或所有者
-
-

也可以把内容复制到指定的文件中
[root@ansible ~]# ansible cen7 -m copy -a 'content="df-h\nhhhhhhhhhhh\nls\n" dest=/app/f1.sh'
[root@ansible ~]# ansible cen7 -a 'cat /app/f1.sh'
192.168.27.102 | SUCCESS | rc=0 >>
df-h
hhhhhhhhhhh
ls

192.168.27.101 | SUCCESS | rc=0 >>
df-h
hhhhhhhhhhh
ls




"se-preview-section-delimiter">
ansible srv -m copy -a "src=/root/f1.sh dest=/tmp/f2.sh owner=wang mode=600 backup=yes"
如目标存在,默认覆盖,此处指定先备份
ansible srv -m copy -a "content='test content\n' dest=/tmp/f1.txt" 利用内容,直接生成目标文件

Cron:计划任务

支持时间:minute,hour,day,month,weekday
ansible srv -m cron -a "minute=*/5 job='/usr/sbin/ntpdate 172.16.0.1 &>/dev/null'
name=Synctime" 创建任务
ansible srv -m cron -a 'state=absent
name=Synctime' 删除任务
范例1:每五分钟周六周日执行
[root@ansible ~]# ansible cen7 -m cron -a 'minute=*/5 weekday=0,6 job="/usr/bin/wall cront job" name="test"'
name就是给这个计划任务起个名字
job表示要执行的命令或操作但是命令要写绝对路径
[root@ansible ~]# ansible cen7 -a 'crontab -l'
192.168.27.102 | SUCCESS | rc=0 >>
#Ansible: test
*/5 * * * 0,6 /usr/bin/wall cront job
范例2:禁用范例1的计划任务
[root@ansible ~]# ansible cen7 -m cron -a 'disabled=true job="/usr/bin/wall cront job" name=test ' 
必须要job
[root@ansible ~]# ansible cen7 -a 'crontab -l'
192.168.27.101 | SUCCESS | rc=0 >>
#Ansible: test
#* * * * * /usr/bin/wall cront job
开启是disabled=no

Fetch:从客户端取文件至服务器端,copy相反,目录可先tar

ansible srv -m fetch -a 'src=/root/a.sh dest=/data/scripts'
  • 范例2,将远程主机的passwd文件复制到本机上
[root@ansible ~]# ansible cen7 -m fetch -a 'src=/etc/passwd dest=/app/'
这里的src是远程主机的路径,dest是本地路径文件要复制到哪里
在本机会成一个远程主机ip名字的一个目录里面存放着文件
[root@ansible ~]# tree /app/
/app/
├── 192.168.27.101
│   └── etc
│       └── passwd
└── 192.168.27.102
    └── etc
        └── passwd
  • 范例3.将日志文件打包压缩,并复制到本地主机上
[root@ansible ~]# ansible cen7 -m shell -a 'tar Jcf /app/log.tar.xz /var/log/*.log' 
[root@ansible ~]# ansible cen7 -m fetch -a 'src=/app/log.tar.xz dest=/app/' 
  • 有专门的tar模块可以将打包并复制

File:设置文件属性和管理文件
- file模块它包含了文件、文件夹、超级链接类的创立、拷贝、移动、删除操作。
- 常见参数
+ follow
+ 如果原来的文件是link,拷贝后依旧是link
+ force
+ 强制执行,没说的
+ group
+ 设定所属组权限
+ mode
+ 等同于chmod,参数可以为“u+rwx or u=rw,g=r,o=r”
+ owner
+ 设定文件的所有者
+ path
+ 目标路径,也可以用dest,name代替
+ src
+ 待拷贝文件/文件夹的原始位置。
+ state = ile/link/directory/hard/touch/absent
+ file代表拷贝后是文件;link代表最终是个软链接;directory代表文件夹;hard代表硬链接;touch代表生成一个空文件;absent代表删除

ansible srv -m file -a "path=/root/a.sh owner=wang mode=755"
ansible web -m file -a 'src=/app/testfile dest=/app/testfile-link state=link'
  • 范例1:创建一个空文件
[root@ansible ~]# ansible cen7 -m file -a 'path=/app/testfile state=touch'
192.168.27.101 | SUCCESS => {
    "changed": true, 
    "dest": "/app/testfile", 
    "gid": 0,   组id
    "group": "root",  所属组
    "mode": "0644",   文件权限
    "owner": "root",  所有者
    "secontext": "unconfined_u:object_r:default_t:s0", 
    "size": 0,  大小
    "state": "file",   
    "uid": 0  
}
  • 范例2:把范例1创建的文件创建一个软连接
[root@ansible ~]# ansible cen7 -m file -a 'src=/app/testfile path=/app/testlink state=link'
[root@ansible ~]# ansible cen7 -a 'ls -l /app/'   ansible不支持别名命令
lrwxrwxrwx. 1 root root   13 Jan 14 20:18 testlink -> /app/testfile
  • 范例3:创建一个文件夹,推荐用file模块因为比较稳定
方法一[root@ansible ~]# ansible cen7 -a 'mkdir /app/mk1'
方法二[root@ansible ~]# ansible cen7 -m file -a 'path=/app/mk2 state=directory'
  • 范例4:删除文件夹或文件
[root@ansible ~]# ansible cen7 -m file -a 'path=/app/mk1 state=absent'
[root@ansible ~]# ansible cen7 -m file -a 'path=/app/fstab state=absent'
  • 范例5:清空整个文件夹
[root@ansible ~]# ansible cen7 -m shell -a 'rm -rf /app/*'
 ansible cen7 -m file -a 'path=/app state=absent'

Hostname:管理主机名这里改名是永久改,会把配置文件改掉

ansible node1 -m hostname -a "name=websrv"
ansible 192.168.27.101 -m hostname -a 'name=g101.com' 

Yum:管理包
- 常用参数
+ disable_gpg_check
+ 在安装包前检查包,只会影响state参数为present或者latest的时候
+ name
+ 你需要安装的包的名字,也能如此使用name=python=2.7安装python2.7
+ state present/latest/absent
+ 用于描述安装包最终状态,present/latest用于安装包,absent用于remove安装包
+ update_cache
+ 用于安装包前执行更新list,只会影响state参数为present/latest的时候
- 范例1:安装一个包,做这些一定要yum配置好

[root@ansible ~]# ansible cen7 -m yum -a 'name=tree state=present'
name是要安装的包名,
如果是安装的话默认可以不写state=present,默认是安装
当然也可以用命令模块安装或卸载包
  • 范例2:可以安装包最新版
[root@ansible ~]# ansible cen7 -m yum -a 'name=dstat state=latest'
  • 范例3:也可以一次性安装多个包,包之间用逗号隔开
[root@ansible ~]# ansible cen7 -m yum -a 'name=httpd,vsftpd state=present'
ansible srv -m yum -a 'name=httpd state=latest' 安装
ansible srv -m yum -a 'name=httpd state=absent' 删除

Service:管理服务
- 常用参数
+ enabled
+ 启动os后启动对应service的选项。使用service模块的时候,enabled和state至少要有一个被定义,设置成开机启动
+ name
+ 需要进行操作的service名字
+ state stared/stoped/restarted/reloaded
+ service最终操作后的状态。
- 范例1启动服务并设置开机启动

[root@ansible ~]# ansible cen7 -m service -a 'name=httpd state=started enabled=yes'
这相当于先启动服务,然后在把服务设置成开机启动
  • ansible service 模块一条命令只能支持一个服务,name只能写一个
 ansible srv -m service -a 'name=httpd state=stopped'
 ansible srv -m service -a 'name=httpd state=started'
 ansible srv –m service –a 'name=httpd state=reloaded'
 ansible srv -m service -a 'name=httpd state=restarted'

User:管理用户
- 常用参数
+ home
+ 指定用户的家目录
+ groups
+ 用户的所属组可以指定多个用逗号分隔
+ uid
+ 指定用户uid
+ name
+ 要创建的用户名
+ createhome
+ 是否创建家目录 yes|no
+ system
+ 是否为系统用户
+ remove
+ 当state=absent时,remove=yes则表示连同家目录一起删除,等价于userdel -r
+ state
+ 是创建还是删除,默认是创建
+ shell
+ 指定用户的shell环境
+ password
+ 指定用户的密码
- 范例1:创建一个test1用户,uid=2000,主组是guo,附属组是root,bin.指定家目录为根下 还有描述

[root@ansible ~]# ansible cen7 -m user -a 'name=test1 comment="test is user" uid=2000 home=/test group=guo groups=root,bin'
192.168.27.101 | SUCCESS => {
    "changed": true, 
    "comment": "test is user", 
    "createhome": true, 
    "group": 1000, 
    "groups": "root,bin", 
    "home": "/test", 
    "name": "test1", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": false, 
    "uid": 2000
}
  • 范例2:创建一个系统用户,系统用户是没有家目录的所以要加上createhome=no不创建家目录,如果不加这个选项则是默认创建家目录
[root@ansible ~]# ansible cen7 -m user -a 'name=systemuser system=yes createhome=no' 
192.168.27.102 | SUCCESS => {
    "changed": true, 
    "comment": "", 
    "createhome": false, 
    "group": 996, 
    "home": "/home/systemuser",   虽然这里显示是创建的,但是事实是没有创建
    "name": "systemuser", 
    "shell": "/bin/bash", 
    "state": "present", 
    "system": true, 
    "uid": 998
}
  • 范例3:删除一个普通用户,包括家目录以前删除当state=absent时remove=yes则表示连同家目录一起删除,如果只选state=absent则只删除用户,而不会删除家目录
[root@ansible ~]# ansible cen7 -m user -a 'name=test1 state=absent remove=yes' 
192.168.27.101 | SUCCESS => {
    "changed": true, 
    "force": false, 
    "name": "test1", 
    "remove": true, 
    "state": "absent"
}
ansible srv -m user -a 'name=user1 comment="test user” uid=2048 home=/app/user1 group=root'
ansible srv -m user -a 'name=sysuser1 system=yes home=/app/sysuser1 '
ansible srv -m user -a 'name=user1 state=absent remove=yes'
删除用户及家目录等数据

Group:管理组
+ 和user参数一样
- 范例1:创建组

[root@ansible ~]# ansible cen7 -m group -a 'name=group1'
  • 范例2:删除一个组
[root@ansible ~]# ansible cen7 -m group -a 'name=group1 state=absent'
 ansible srv -m group -a "name=testgroup system=yes"
 ansible srv -m group -a "name=testgroup state=absent"

ansible系列命令

  • ansible-galaxy
    • 连接 https://galaxy.ansible.com 下载相应的roles
      • 这个网站是官方的,国外的一些大神做的比较好的playbook,和一些角色,会传到这里,可以下载使用或者参考
    • 列出所有已安装的galaxy
      • ansible-galaxy list
    • 安装galaxy
      • ansible-galaxy install geerlingguy.redis
      • 就将网站上的角色名写着就可以
        bash
        [root@ansible ~]# ansible-galaxy install geerlingguy.nginx

      • downloading role 'nginx', owned by geerlingguy

      • downloading role from https://github.com/geerlingguy/ansible-role-nginx/archive/2.5.0.tar.gz

      • extracting geerlingguy.nginx to /root/.ansible/roles/geerlingguy.nginx 这里显示了下载的位置

      • geerlingguy.nginx (2.5.0) was installed successfully
      • 下载完后可以用ansible-galaxy list 查看
      • 进入目录看一下里面的内容
        bash
        [root@ansible ~]# ls .ansible/roles/geerlingguy.nginx/
        defaults handlers LICENSE meta README.md tasks templates tests vars
      • 这里都是playbook
      • 最好复制一份并改名,这样修改如果出现重大失误可以有参考
    • 删除galaxy
      • ansible-galaxy remove geerlingguy.redis
  • ansible-pull
    • 推送命令至远程,效率无限提升,对运维要求较高
  • Ansible-playbook
    • ansible-playbook hello.yml 后缀推荐用.yml它是用yml语言写的
  • 语法,在前面要加—个
[root@ansible ansible]# vim hellow.yml
---
- hosts: cen7
  remote_user: root

  tasks:
    - name: test yml
      command: /usr/bin/wall "hellow word"
  • ansible-playbook 运行脚本 要用-C 检查一下
  • 语法要求很严格
  • 创建一个空文件的一个脚本
[root@ansible ansible]# vim hellow.yml 
---
- hosts: cen7
  remote_user: root

  tasks:
    - name: test yml
      file: name=/app/test12 state=touch
  • Ansible-vault
    • 功能:管理加密解密yml文件
    • ansible-vault [create|decrypt|edit|encrypt|rekey|view]
    • ansible-vault encrypt hello.yml 加密
      bash
      [root@ansible ansible]# ansible-vault encrypt hellow.yml
      New Vault password:
      Confirm New Vault password:
      Encryption successful
    • ansible-vault decrypt hello.yml 解密
    • ansible-vault view hello.yml 查看
    • ansible-vault edit hello.yml 编辑加密文件
    • ansible-vault rekey hello.yml 修改口令
    • ansible-vault create new.yml 创建新文件
  • Ansible-console:2.0+新增,可交互执行命令,支持tab
    • root@test (2)[f:10] @()[f:] 执 行 用 户 @ 当 前 操 作 的 主 机 组 ( 当 前 组 的 主 机 数 量 ) [ f : 并 发 数 ]
    • 设置并发数: forks n 例如: forks 10
    • 切换组: cd 主机组 例如: cd web
    • 列出当前组主机列表: list
    • 列出所有的内置命令: ?或help
    • 示例:
      bahs
      root@all (2)[f:5]$ list
      root@all (2)[f:5]$ cd appsrvs
      root@appsrvs (2)[f:5]$ list
      root@appsrvs (2)[f:5]$ yum name=httpd state=present
      root@appsrvs (2)[f:5]$ service name=httpd state=started

playbook

  • playbook是由一个或多个“play”组成的列表
  • play的主要功能在于将事先归并为一组的主机装扮成事先通过ansible中的task定义好的角色。从根本上来讲,所谓task无非是调用ansible的一个module。将多个play组织在一个playbook中,即可以让它们联同起来按事先编排的机制同唱一台大戏
  • Playbook采用YAML语言编写

YAML语法简介

  • List:列表,其所有元素均使用“-”打头
  • 示例
# A list of tasty fruits
- Apple
- Orange
- Strawberry
- Mango

YAML介绍

  • YAML是一个可读性高的用来表达资料序列的格式。YAML参考了其他多种语言,包括:XML、C语言、Python、Perl以及电子邮件格式RFC2822等。Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者
  • YAML Ain’t Markup Language,即YAML不是XML。不过,在开发的这种语言时,YAML的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)
    特性
    YAML的可读性好
    YAML和脚本语言的交互性好
    YAML使用实现语言的数据类型
    YAML有一个一致的信息模型
    YAML易于实现
    YAML可以基于流来处理
    YAML表达能力强,扩展性好
  • 更多的内容及规范参见http://www.yaml.org

YAML语法简介

  • 在单一档案中,可用连续三个连字号(—)区分多个档案。另外,还有选择性的连续三个点号( … )用来表示档案结尾
  • 次行开始正常写Playbook的内容,一般建议写明该Playbook的功能
  • 使用#号注释代码
  • 缩进必须是统一的,不能空格和tab混用
  • 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别是通过缩进结合换行来实现的
  • YAML文件内容和Linux系统大小写判断方式保持一致,是区别大小写的,k/v的值均需大小写敏感
  • k/v的值可同行写也可换行写。同行使用:分隔
  • v可是个字符串,也可是另一个列表
  • 一个完整的代码块功能需最少元素需包括 name: task
  • 一个name只能包括一个task
  • YAML文件扩展名通常为yml或yaml
  • Dictionary:字典,通常由多个key与value构成
    -范例:
---
# An employee record
name: Example Developer
job: Developer
skill: Elite
也可以将key:value放置于{}中进行表示,用,分隔多个key:value
  • 范例2:
---
# An employee record
{name: Example Developer, job: Developer, skill: Elite}
  • YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。其结构(Structure)通过空格来展示,序列(Sequence)里的项用”-“来代表,Map里的键值对用”:”分隔
  • 示列
name: John Smith
age: 41
gender: Male
spouse:
  name: Jane Smith
  age: 37
  gender: Female
children:
- name: Jimmy Smith
  age: 17
  gender: Male
- name: Jenny Smith
  age 13
  gender: Female

Playbook核心元素

  • Hosts 执行的远程主机列表
  • Tasks 任务集
  • Varniables 内置变量或自定义变量在playbook中调用
  • Templates 模板,可替换模板文件中的变量并实现一些简单逻辑的文件
  • Handlers 和notity结合使用,由特定条件触发的操作,满足条件方才执行,否则不执行
  • tags 标签 指定某条任务执行,用于选择运行playbook中的部分代码。ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常地长。此时,如果确信其没有变化,就可以通过tags跳过此些代码片断
ansible-playbook –t tagsname useradd.yml
  • 范例
[root@ansible ansible]# vim test.yml 
---
- hosts: cen7   要执行的主机
  remote_user: root   用哪个用户执行

  tasks:  任务集
    - name: install package   一条任务要有一个名字
      yum: name=httpd       调用的模块,和参数
    - name: start service
      service: name=httpd state=started enabled=yes
  • 查看playbook中被执行的主机列表
[root@ansible ansible]# ansible-playbook test.yml --list-host

playbook: test.yml

  play #1 (cen7): cen7  TAGS: []
    pattern: [u'cen7']
    hosts (2):
      192.168.27.101
      192.168.27.102
  • 查看playbook中有哪些任务
ERROR! You must specify a playbook file to run
[root@ansible ansible]# ansible-playbook test.yml --list-tasks  

playbook: test.yml

  play #1 (cen7): cen7  TAGS: []
    tasks:
      install package   TAGS: []
      start service     TAGS: []
  • 一个playbook推荐只写一个play,当然可以写多个play

实验

  • 安装http服务并把它设置为开机启动,并将端口改为8080端口,开启服务,写一个playbook
  • 首先在自己的电脑上装一个http服务,并且修改好配置文件,然后在将配置文件复制到远程主机上,启动服务,这里的所有主机都是centos7的,不同系统版本的http配置文件不一样,所以要同一版本
- hosts: cen7
  remote_user: root

  tasks:
    - name: isntall httpd
      yum: name=httpd
    - name: copy config httpd
      copy: src=/app/httpd.conf dest=/etc/httpd/conf/  backup=yes
    - name: start httpd
      service: name=httpd state=started enabled=yes
  • 当我们在想把配置文件改为80端口,如果在重新执行上面的playbook并不会改过了,因为上面只是定义了启动服务,所以我们要用handlers条件出发
---
- hosts: cen7
  remote_user: root

  tasks:
    - name: isntall httpd
      yum: name=httpd
    - name: copy config httpd
      copy: src=/app/httpd.conf dest=/etc/httpd/conf/  backup=yes
      notify: restart httpd   当copy发生改变时会执行notify所指定的名字任务
    - name: start httpd
      service: name=httpd state=started enabled=yes

  handlers:   handlers是一个特殊的tasks也可以写多个任务
    - name: restart httpd
      service: name=httpd state=restarted 
  • 当第一次执行这个playbook会顺序执行,不会触发handlers,因为第一次包都没有装,但是当第二次执行时开始执行handlers,当copy命令执行结果改变时,会执行对应的handlers的任务

playbook基础组件

  • Hosts
    • playbook中的每一个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务。hosts用于指定要执行指定任务的主机,须事先定义在主机清单中
    • 可以是如下形式
      bash
      one.example.com
      one.example.com:two.example.com
      192.168.1.50
      192.168.1.*
    • Websrvs:dbsrvs 两个组的并集
    • Websrvs:&dbsrvs 两个组的交集
    • webservers:!phoenix 在websrvs组,但不在dbsrvs组
    • 示例: - hosts: websrvs:dbsrvs
  • remote_user: 可用于Host和task中。也可以通过指定其通过sudo的方式在远程主机上执行任务,其可用于play全局或某任务;此外,甚至可以在sudo时使用sudo_user指定sudo时切换的用户
- hosts: websrvs
  remote_user: root
  tasks:
   - name: test connection
   ping:
   remote_user: magedu
   sudo: yes 默认sudo为root
   sudo_user:wang sudo为wang
  • task列表和action
    • play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自下而下某playbook时,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可
    • task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
    • 每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出
  • tasks:任务列表
  • 格式:
    • (1) action: module arguments
    • (2) module: arguments 建议使用
  • 注意:shell和command模块后面跟命令,而非key=value
  • 某任务的状态在运行后为changed时,可通过“notify”通知给相应的handlers
  • 任务可以通过”tags“打标签,而后可在ansible-playbook命令上使用-t指定进行调用
  • 示例
tasks:
- name: disable selinux
  command: /sbin/setenforce 0
  • 如果命令或脚本的退出码不为零,可以使用如下方式替代
tasks:
- name: run this command and ignore the result
  shell: /usr/bin/somecommand || /bin/true
  • 或者使用ignore_errors来忽略错误信息
tasks:
- name: run this command and ignore the result
  shell: /usr/bin/somecommand
  ignore_errors: True

运行playbook

  • 运行playbook的方式
ansible-playbook  ... [options]

常见选项
–check 只检测可能会发生的改变,但不真正执行操作
–list-hosts 列出运行任务的主机
–limit 主机列表 只针对主机列表中的主机执行
-v 显示过程 -vv -vvv 更详细
- 范例

ansible-playbook file.yml --check 只检测
ansible-playbook file.yml
ansible-playbook file.yml --limit websrvs
  • 范例1,限定特定的主机执行,并不是所以的主机执行
[root@ansible app]# ansible-playbook httpd.yml  --limit 192.168.27.101
只针对101主机执行并不是所有主机

Playbook VS ShellScripts

SHELL脚本

#!/bin/bash
# 安装Apache
yum install --quiet -y httpd
# 复制配置文件
cp /path/to/config/httpd.conf
/etc/httpd/conf/httpd.conf
cp/path/to/httpd-vhosts.conf
/etc/httpd/conf/httpd-vhosts.conf
# 启动Apache,并设置开机启动
service httpd start
chkconfig httpd on

Playbook定义

---
- hosts: all
  tasks:
- name: "安装Apache"
    command: yum install -q -y httpd
- name: "复制配置文件"
    command: cp /tmp/httpd.conf  /etc/httpd/conf/httpd.conf
    command: cp /tmp/httpd-vhosts.conf /etc/httpd/conf/httpd-vhosts.conf
- name: "启动Apache,并设置开机启动"
    service: name=httpd state=started enabled=yes
  • 范例
示例:system.yml
---
-hosts: all
 remote_user: root
 tasks:
   - name: create mysql user
   user: name=mysql system=yes uid=36
   - name: create a group
   group: name=httpd system=yes

示例:httpd.yml

- hosts: websrvs
  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/
     - name: start service
       service: name=httpd state=started enabled=yes

handlers和notify结合使用触发条件

  • Handlers
    • 是task列表,这些task与前述的task并没有本质上的不同,用于当关注的资源发生变化时,才会采取一定的操作
  • notify这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作

Playbook中handlers使用

- hosts: websrvs
  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
    - name: ensure apache is running
    service: name=httpd state=started enabled=yes
   handlers:
    - name: restart httpd
    service: name=httpd status=restarted
  • 示例
- hosts: websrvs
  remote_user: root
  tasks:
    - name: add group nginx
    tags: user
    user: name=nginx state=present
    - name: add user nginx
    user: name=nginx state=present group=nginx
    - name: Install Nginx
    yum: name=nginx state=present
    - name: config
    copy: src=/root/config.txt dest=/etc/nginx/nginx.conf
    notify:
        - Restart Nginx
        - Check Nginx Process
  handlers:
    - name: Restart Nginx
    service: name=nginx state=restarted enabled=yes
    - name: Check Nginx process 
    shell: killall -0 nginx > /tmp/nginx.log

Playbook中tags使用,从剧本中挑出tags代表的任务,只执行这个,其它的不执行

  • tage的名字可以相同
示例:httpd.yml
- hosts: websrvs
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/
  tags: conf
  - name: start httpd service
  tags: service
  service: name=httpd state=started enabled=yes
[root@ansible app]# ansible-playbook -t conf,service httpd.yml 
  • 可以一次启动多个tags标签。加-t或者–tags

Playbook中变量使用

  • 变量名:仅能由字母、数字和下划线组成,且只能以字母开头
  • 变量来源:
    • 1 ansible setup facts 远程主机的所有变量都可直接调用
    • 2 在/etc/ansible/hosts中定义
      • 普通变量:主机组中主机单独定义,优先级高于公共变量
      • 公共(组)变量:针对主机组中所有主机定义统一变量
    • 3 通过命令行指定变量,优先级最高
      • ansible-playbook –e varname=value
    • 4 在playbook中定义
      bash
      vars:

    • var1: value1

    • var2: value2
    • 5 在role中定义
  • 用setup模块可以查看机器的所有的内置变量
 ansible cen7 -m setup 
  • 也可以用filter参数搜索变量,支持通配符
[root@ansible app]# ansible cen7 -m setup -a 'filter=*hostname*'
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_hostname": "g102"
    }, 
    "changed": false
}
192.168.27.101 | SUCCESS => {
    "ansible_facts": {
        "ansible_hostname": "g101"
    }, 
    "changed": false
}
[root@ansible app]# ansible cen7 -m setup -a 'filter=*nodename*'
192.168.27.101 | SUCCESS => {
    "ansible_facts": {
        "ansible_nodename": "g101.com"
    }, 
    "changed": false
}
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_nodename": "g102.com"
    }, 
    "changed": false
}
[root@ansible app]# ansible cen7 -m setup -a 'filter=*fqdn*'
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_fqdn": "g102.com"
    }, 
    "changed": false
}
192.168.27.101 | SUCCESS => {
    "ansible_facts": {
        "ansible_fqdn": "g101.com"
    }, 
    "changed": false
}
  • 最好写全名称
  • 可以查ip地址
ct, raw, meta
[root@ansible app]# ansible cen7 -m setup -a 'filter=*addr*'
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.27.102"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fe8b:f0dd"
        ]
    }, 
    "changed": false
}
192.168.27.101 | SUCCESS => {
    "ansible_facts": {
        "ansible_all_ipv4_addresses": [
            "192.168.27.101"
        ], 
        "ansible_all_ipv6_addresses": [
            "fe80::20c:29ff:fec3:887f"
        ]
    }, 
    "changed": false
}

Playbook中变量使用

  • 变量命名
    • 变量名仅能由字母、数字和下划线组成,且只能以字母开头
  • 变量定义:key=value
    • 示例:http_port=80
  • 变量调用方式:
    • 通过{{ variable_name }} 调用变量,且变量名前后必须有空格,有时用“{{ variable_name }}”才生效
ansible-playbook –e 选项指定
ansible-playbook test.yml -e "hosts=www user=mageedu"
  • 范例1:var.yml
- hosts: websrvs
  remote_user: root
  tasks
   - name: install package
     yum: name={{ pkname }} state=present
# ansible-playbook -e pkname=httpd var.yml
  • 范例在playbook中赋值并调用
[root@ansible ansible]# vim var2.yml
---
- hosts: cen7
  remote_user: root
  vars:                    //如果要在play中第一变量要用vars
   - username: user123     //变量名:赋值
   - groupname: group123

  tasks:
   - name: create group
     group: name={{ groupname }}   //调用变量
   - name: create uesr
     user: name={{ username }} group={{ groupname }} home=/app/{{ username }}dir  //可以写变量加其它字段
[root@ansible ansible]# ansible-playbook  var2.yml
[root@ansible ansible]# ansible cen7 -a 'getent passwd user123'
192.168.27.101 | SUCCESS | rc=0 >>
user123:x:1001:1001::/app/user123dir:/bin/bash
  • 范例2:变量
# vim var2.yml
- hosts: websrvs
  remote_user: root
  vars:
   - username: user1
   - groupname: group1

  tasks:
   - name: create group
     group: name={{ groupname }} state=present
   - name: create user
     user: name={{ username }} state=present
# ansible-playbook var2.yml
# ansible-playbook -e "username=user2 groupname=group2” var2.yml
  • 范例3

[root@ansible ansible]# vim var1.yml

  • hosts: cen7
    remote_user: root

    tasks:

    • name: install package
      yum: name={{ pkname }} //定义一个变量名为pkname

[root@ansible ansible]# ansible-playbook -e pkname=vsftpd var1.yml 用-e 写要指定定的变量名然后赋值

- 范例4:使用多个变量名
```bash  




[root@ansible ansible]# vim var1.yml --- - hosts: cen7 remote_user: root tasks: - name: install package yum: name={{ pkname }} //第一个变量 - name: copy file copy: src=/app/{{ filename }} dest=/app/ //第二个变量 [root@ansible ansible]# ansible-playbook -e "pkname=dstat filename=httpd.conf" var1.yml 要赋值多个变量要用双引号引起来,
  • 用yum模块可以一次安装或卸载多个包
[root@ansible ansible]# ansible cen7 -m yum -a 'name=dstat,httpd state=absent'




"se-preview-section-delimiter">
[root@ansible ansible]# vim var2.yml
---
- hosts: cen7
  remote_user: root
  vars:
   - username: user123
   - groupname: group123

  tasks:
   - name: create group
     group: name={{ groupname }}
   - name: create uesr
     user: name={{ username }} group={{ groupname }} home=/app/{{ username }}dir

也可在palybook中调用setup模块中的变量,setup模块是系统内所有的变量可以用filter参数搜索对应的变量名

  • 在setup是有定义主机名的变量,分别是ansible_nodename这个变量是主机名全名,还有一个ansible_hostnam是只有前面一般,可以根据下面的例子看出很明显。
[root@ansible ansible]# ansible cen7 -m setup -a 'filter="*nodename*"'
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_nodename": "g102.com"
    }, 
    "changed": false
}
[root@ansible ansible]# ansible cen7 -m setup -a 'filter="*hostname*"'
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_hostname": "g102"
    }, 
    "changed": false
}
  • 在playbook中调用setup系统中的变量
[root@ansible ansible]# vim var3.yml
---
- hosts: cen7
  remote_user: root

  tasks:
    - name: create file
      file: name=/app/{{ ansible_hostname }}.txt  state=touch    //ansible_hostnam 是setup中的变量,我们可以直接调用
[root@ansible ansible]# ansible-playbook var3.yml 

在主机清单hosts中定义变量


- 这里101主机定义的http_port是85,102直接定义的是86,虽然变量名都是同一个,针对不同的主机可以设置不同的值
- 然后我们可以调用用命令行

[root@ansible ~]# ansible webser -m hostname -a 'name=web{{ http_port  }}'  //修改主机名,会针对不同的主机的变量值不一样
[root@ansible ~]# ansible webser -a 'hostname'
192.168.27.101 | SUCCESS | rc=0 >>
web85   这是针对某个主机设置不同的值

192.168.27.102 | SUCCESS | rc=0 >>
web86
  • 也可定义多个变量,并在playbook中使用
[root@ansible ansible]# vim var4.yml
---
- hosts: webser
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name={{ hostname }}-{{ http_port}}
[root@ansible ansible]# ansible webser -a 'hostname'
192.168.27.101 | SUCCESS | rc=0 >>
web1-88

192.168.27.102 | SUCCESS | rc=0 >>
web2-86
  • 如果我们人为的用-e指定变量值,则会以-e定义的参数为准,不会调用hosts中的变量
[root@ansible ansible]# ansible-playbook -e "http_port=9090 hostname=abc.com" var4.yml
[root@ansible ansible]# ansible webser -a 'hostname'
192.168.27.101 | SUCCESS | rc=0 >>
abc.com-9090

192.168.27.102 | SUCCESS | rc=0 >>
abc.com-9090

变量

  • 主机变量
    • 可以在inventory中定义主机时为其添加主机变量以便于在playbook中使用
[websrvs]
www1.magedu.com http_port=80 maxRequestsPerChild=808
www2.magedu.com http_port=8080 maxRequestsPerChild=909
  • 组变量
    • 组变量是指赋予给指定组内所有主机上的在playbook中可用的变量
  • 范例
[websrvs]
www1.magedu.com
www2.magedu.com

[websrvs:vars]     这个相当于是在websrvs组中的所有主机公用的,叫做组公共变量
ntp_server=ntp.magedu.com
nfs_server=nfs.magedu.com

[root@ansible ansible]# vim var4.yml
---
- hosts: webser
  remote_user: root

  tasks:
    - name: set hostname
      hostname: name={{ hname }}-{{ http_port}}
[root@ansible ansible]# ansible-playbook  var4.yml 
[root@ansible ansible]# ansible webser -a 'hostname'
192.168.27.102 | SUCCESS | rc=0 >>
ansible-91

192.168.27.101 | SUCCESS | rc=0 >>
ansible-90
  • 普通变量
[websrvs]
192.168.99.101 http_port=8080 hname=www1
192.168.99.102 http_port=80 hname=www2
  • 公共变量
[websvrs:vars]
http_port=808
mark=“_”
[websrvs]
192.168.99.101 http_port=8080 hname=www1
192.168.99.102 http_port=80 hname=www2
# ansible websvrs –m hostname –a ‘name={{ hname }}{{ mark }}{{http_port }}’
  • 命令行指定变量
ansible websvrs –e http_port=8000 –m hostname –a ‘name={{hname }}{{ mark }}{{ http_port }}’

也可以把变量专门写到一个文件中,可以让别的playbook调用此变量

  • 名字必须是.yml后缀的
[root@ansible ~]# vim add.yml
host: 123  在文件中定义变量是这中格式 host是变量名 :后面是值 这是和上面定义变量是用区别的 
filen: 456
[root@ansible ansible]# vim var5.yml
---
- hosts: cen7
  remote_user: root
  vars_files:   一定要写vars_files 
    - /root/add.yml   这里是定义变量的文件路径,然后就可以调用里面的变量了

  tasks:
   - name: create file
     file: name=/app/{{host}}-{{filen}}.log  state=touch  mode=600 owner=guo  
[root@ansible ansible]# ansible cen7 -a 'ls /app/'
192.168.27.102 | SUCCESS | rc=0 >>
123-456.log

192.168.27.101 | SUCCESS | rc=0 >>
123-456.log
f2
f3

模板templates

  • templates 是一个模块
    • 模板存放着将来要实现的配置文件的公用设置放在模板里,必须是特殊格式的,可以根据一些变量来根据主机生成配置文件
  • 文本文件,嵌套有脚本(使用模板编程语言编写)
  • Jinja2语言,使用字面量,有下面形式
字符串:使用单引号或双引号
数字:整数,浮点数
列表:[item1, item2, ...]
元组:(item1, item2, ...)
字典:{key1:value1, key2:value2, ...}
布尔型:true/false
  • 算术运算:+, -, , /, //, %, *
  • 比较操作:==, !=, >, >=, <, <=
  • 逻辑运算:and, or, not
  • 流表达式:For If When
  • templates功能:根据模块文件动态生成对应的配置文件,并复制到远端主机上
    • templates文件必须存放于templates目录下,且命名为 .j2 结尾
    • yaml/yml 文件需和templates目录平级,目录结构如下
./
├── temnginx.yml
└── templates
    └── nginx.conf.j2   

范例

  • 利用templates 同步nginx配置文件
    • 准备templates/nginx.conf.j2文件
# vim temnginx.yml
---
- hosts: websrvs
  remote_user: root
  tasks:
   - name: template config to remote hosts
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
# ansible-playbook temnginx.yml
首先要在本机上安装相同的服务,然后把配置文件复制一份到template目录下改后缀为j2
1. [root@ansible ansible]# cp /etc/nginx/nginx.conf templates/nginx.conf.j2   首先把要准备一份模板文件,后缀名必须是j2
[root@ansible ansible]# vim temnginx.yml
---
- hosts: cen7
  remote_user: root

  tasks:
   - name: install nginx
     yum: name=nginx
   - name: template
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf    //src=这个可以写绝对路径,也可以这样写,因为我的template目录和这playbook文件是在同一目录,默认play会到template目录下找。
     tags: instconf
   - name: start service
     service: name=nginx state=started
[root@ansible ansible]# ansible-playbook temnginx.yml  运行这个playbook,安装服务,并生成模板文件
  1. 修改模板文件
  2. 查看一下默认是几个进程号发现是一个进程
  3. 我们修改一下CPU的数量在看一下结果,只修改一个主机上的CPU个数
  4. 在setup模块中有一个变量是显示CPU个数的变量
[root@ansible ansible]# ansible cen7 -m setup -a 'filter="*cpu*"'
192.168.27.101 | SUCCESS => {
    "ansible_facts": {
        "ansible_processor_vcpus": 1   这个就是显示CPU变量的
    }, 
    "changed": false
}
192.168.27.102 | SUCCESS => {
    "ansible_facts": {
        "ansible_processor_vcpus": 4
    }, 
    "changed": false
}
  1. 修改模板文件

    • 这样就会变成,模板是一样的,但是到各个主机上回吧ansible_processor_vcpus这个变量会变成CPU个数,机器有是几个CPU就替换成几,这样生成的配置文件也不同。
    • 刚刚上面的playbook已经将服务启动了,所以我们在修改一下playbook,在template下加一个notify,当template这个任务执行时会触发指示的任务
[root@ansible ansible]# vim temnginx.yml 
---
- hosts: cen7
  remote_user: root

  tasks:
   - name: install nginx
     yum: name=nginx
   - name: template
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
     notify: restart nginx
     tags: instconf
   - name: start service
     service: name=nginx state=started

  handlers:
   - name: restart nginx
     service: name=nginx state=restarted
  • 现在执行一下playbook,执行完后我们可以看一下是不是按CPU数来创建进程的
  • 确实4颗CPU就是4个进程数,1个CPU就是一个,这就是template模板的强大之处
  • 看一下各自的配置文件

  • 还可以做运算

  • 我们*2把CPUx2,执行一下playbook

Playbook中template变更替换

  • 修改文件nginx.conf.j2 下面行为
worker_processes {{ ansible_processor_vcpus }};
# cat temnginx2.yml
---
- hosts: websrvs
remote_user: root
tasks:
  - name: template config to remote hosts
    template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
# ansible-playbook temnginx2.yml

Playbook中template算术运算

  • 算法运算
  • 范例
# vim nginx.conf.j2
worker_processes {{ ansible_processor_vcpus*2 }};
worker_processes {{ ansible_processor_vcpus+2 }};

template也支持在主机清单中变量


- 修改模板文件

- 执行playbook
- 查看监听端口是否改变

- 我们也可以在playbook中定义变量让模板调用

[root@ansible ansible]# vim temnginx.yml 
---
- hosts: cen7
  remote_user: root
  vars:
    - http_port: 6060    在playbook中定义http_port变量值为6060

  tasks:
   - name: install nginx
     yum: name=nginx
   - name: template
     template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
     notify: restart nginx
     tags: instconf
   - name: start service
     service: name=nginx state=started

  handlers:
   - name: restart nginx
     service: name=nginx state=restarted
  • 现在有一个问题是,我们在主机清单中也定义了同样名字的变量,这种情况会以playbook中的变量会生效,如果在命令行中用-e也定义了同样名字的变量则命令行的优先

变量参数的优先级

命令行定义变量> playbook中定义的> hosts主机清单中定义的变量> 模板文件中

when

  • 条件测试:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提时要用到条件测试,通过when语句实现,在task中使用,jinja2的语法格式
  • when语句
  • 在task后添加when子句即可使用条件测试;when语句支持Jinja2表达式语法
  • 示例
tasks:
  - name: "shutdown RedHat flavored systems"
    command: /sbin/shutdown -h now
    when: ansible_os_family == "RedHat"
  • 示例:when条件判断
---

- hosts: websrvs
  remote_user: root
  tasks:
    - name: add group nginx
      tags: user
      user: name=nginx state=present
    - name: add user nginx
      user: name=nginx state=present group=nginx
    - name: Install Nginx
      yum: name=nginx state=present
    - name: restart Nginx
      service: name=nginx state=restarted
      when: ansible_distribution_major_version == "6"
  • 示例
tasks:
  - name: install conf file to centos7
    template: src=nginx.conf.c7.j2
    when: ansible_distribution_major_version == "7"
  - name: install conf file to centos6
    template: src=nginx.conf.c6.j2
    when: ansible_distribution_major_version == "6"
  • 范例
  • 当系统版本不同,服务版本也会不同,就httpd服务,7版本和6版本不同,如果在管理主机中有6的机器,也有centos7的机器,我们在用模板文件创建配置文件时就需要判断一下系统是几的就用对应的配置模板
  • 首先把centos7 版本和6版本的httpd配置文件复制一份到template文件夹中当模板文件
  • 现在已经找到了版本号我们在playbook中用when判断
[root@ansible ansible]# vim when.yml                 
---
- hosts: all
  remote_user: root

  tasks:
   - name: install httpd
     yum: name=httpd

   - name: template
▽    template: src=httpd-6.conf.j2 dest=/etc/httpd/conf/httpd.conf
     when: ansible_distribution_major_version=="6"    当版本==6时会执行,不然就不执行

   - name: template
     template: src=httpd-7.conf.j2 dest=/etc/httpd/conf/httpd.conf
     when: ansible_distribution_major_version=="7"

   - name: start service
     service: name=httpd state=started

迭代:with_items(类似于循环)

  • 迭代:当有需要重复性执行的任务时,可以使用迭代机制
    • 对迭代项的引用,固定变量名为”item“
    • 要在task中使用with_items给定要迭代的元素列表
    • 列表格式:
      • 字符串
      • 字典
  • 示例,在系统中创建若干用户
- name: add several users
  user: name={{ item }} state=present groups=wheel   //item是个关键字,必须这样写
  with_items:
    - testuser1
    - testuser2
  • 上面语句的功能等同于下面的语句
- name: add user testuser1
  user: name=testuser1 state=present groups=wheel
- name: add user testuser2
  user: name=testuser2 state=present groups=wheel
  • 创建多个用户
[root@ansible ansible]# vim with_items.yml
---
- hosts: cen7
  remote_user: root

  tasks:
   - name: create servel users
     user: name={{ item }} group=root groups=guo   
     with_items:
       - xiaoming
       - xiaowang
       - xiaoguo
````
- 示例:将多个文件进行copy到被控端




"se-preview-section-delimiter">
```bash --- - hosts: testsrv remote_user: root tasks - name: Create rsyncd config copy: src={{ item }} dest=/etc/{{ item }} with_items: - rsyncd.secrets - rsyncd.conf
- hosts: websrvs
  remote_user: root
  tasks:
    - name: copy file
      copy: src={{ item }} dest=/tmp/{{ item }}
      with_items:
        - file1
        - file2
        - file3

    - name: yum install httpd
      yum: name={{ item }} state=present
      with_items:
        - apr
        - apr-util
        - httpd
- hosts:websrvs
  remote_user: root
  tasks
    - name: install some packages
      yum: name={{ item }} state=present
      with_items:
        - nginx
        - memcached
        - php-fpm

示例:迭代嵌套子变量

  • 当有多个变量时可以用子变量
- hosts:websrvs
  remote_user: root
  tasks:
    - name: add some groups
      group: name={{ item }} state=present
      with_items:
        - group1
        - group2
        - group3
    - name: add some users
      user: name={{ item.name }} group={{ item.group }} state=present
      with_items:
        - { name: 'user1', group: 'group1' }
        - { name: 'user2', group: 'group2' }
        - { name: 'user3', group: 'group3' }
  • 范例2
[root@ansible ~]# vim qiantaoitem.yml                 
---
- hosts: cen7
  remote_user: root

  tasks:
   - name: create group
     group: name={{ item }}
     with_items:
      - igroup1
      - igroup2
      - igroup3

   - name: create user
     user: name={{item.username }} group={{item.groupname}}
     with_items:
      - {username: 't1', groupname: 'igroup1' }   一行有两个变量,用逗号隔开冒号后面有空格
      - {username: 't2', groupname: 'igroup2' }
      - {username: 't3', groupname: 'igroup3' }

Playbook中template for if

  • for语句
  • 格式
{%for 变量 in 列表%}
    循环体   
{%endfor}
这种是用于template模板中,在playbook中定义列表
  • 范例
[root@ansible ansible]# vim for.yml
---
- hosts: cen7
  remote_user: root
  vars:        
▽   ports:        //这里定义列表名,里面有几个值for就循环几遍
      - 81
      - 82
      - 83

  tasks:
    - name: test for1
      template: src=for1.conf.j2 dest=/app/for1.conf


[root@ansible ansible]# vim templates/for1.conf.j2   在模板文件里写for循环
{%for port in ports %}     port类似shell,for循环中的i可以随便写,ports必须写列表名
server {                         这里是循环体
        listen {{port}};         这里调用{{port}}这里的值就是列表里的值
}
{%endfor%}     结束循环体

运行playbook文件
[root@ansible-90 ~]# cat /app/for1.conf 
server {
        listen 81;
}
server {
        listen 82;
}
server {
        listen 83;
}
  • 第二种for写法
[root@ansible ansible]# vim for2.yml
---
- hosts: cen7
  remote_user: root
  vars:
    ports:  这是列表名
       - http_port: 81      写成这样相当于子变量
       - http_port: 82
       - http_port: 83

  tasks:
▽  - name: template file
     template: src=for2.conf.j2 dest=/app/for2.conf
模板文件
[root@ansible ansible]# vim templates/for2.conf.j2
{%for port in ports %}
server {
        listen {{port.http_port}};   这种类似于item的写法port相当于http_port,然后把http_port的变量名替换成值
}
{%endfor%} 
---------------------------------------------------------------------------------------
[root@ansible-90 ~]# cat /app/for2.conf 
server {
        listen 81;
}
server {
        listen 82;
}
server {
        listen 83;
}

重点第三种for写法

[root@ansible ansible]# vim for3.yml                 
---
- hosts: cen7
  remote_user: root
  vars:
    hhh:   一个列表中可以有对个元素
      - web1:
        port: 88
        name: web1.server
        root: /app/web1.server
      - web2:
        port: 89
        name: web2.server
        root: /app/web2.server
      - web3:
        port: 90
        name: web3.server
        root: /app/web3.server

  tasks:
    - name: test for3
      template: src=for3.conf.j2  dest=/app/for3.con
模板文件
{%for i in hhh%}    
server {
        listen {{i.prot}}
        servername {{i.name}}
        rootdir {{i.root}}
}
{%endfor%}
生成结果
[root@ansible-90 ~]# cat /app/for3.conf 
server {
        listen 88
        servername web1.server
        rootdir /app/web1.server 
}
server {
        listen 89
        servername web2.server
        rootdir /app/web2.server 
}
server {
        listen 90
        servername web3.server
        rootdir /app/web3.server 
}
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost.listen | default('80 default_server') }};

{% if vhost.server_name is defined %}   //这表示如果vhost.server_name被定义了有值了则就用这里定义的值,如果没有定义就不用
server_name {{ vhost.server_name }};
{% endif %}

{% if vhost.root is defined %}
root {{ vhost.root }};
{% endif %}

-范例

[root@ansible ansible]# vim for4-if.yml 
---
- hosts: cen7
  remote_user: root
  vars:
    hhh:
      - web1:
▽       port: 88
        #name: web1.server   我们把web1中name注释
        root: /app/web1.server
      - web2:
        port: 89
        name: web2.server
        root: /app/web2.server
      - web3:
        port: 90
        #name: web3.server  web#中name注释   这样用if时就不会定义这个name
        root: /app/web3.server

  tasks:
    - name: test for3
      template: src=for4-if.conf.j2  dest=/app/for4-if.conf
模板文件
[root@ansible ansible]# vim templates/for4-if.conf.j2
{%for i in hhh %}
service {
        listen {{i.port}}
{%if i.name is defined %}            一点要顶头写这些,当i.name被定义就调用,如果没有被定义则不调用
        servername {{i.name}}
{%endif%}                            if这种格式一点要顶头写
        rootdir {{i.root}}
}
{%endfor%}
结果
[root@ansible-90 ~]# cat /app/for4-if.conf 
service {
        listen 88
        rootdir /app/web1.server
}
service {
        listen 89
        servername web2.server     这里只有web2的name有名字
        rootdir /app/web2.server
}
service {
        listen 90
        rootdir /app/web3.server
}
  • 示列1
// temnginx.yml
---
- hosts: testweb
  remote_user: root
  vars:
    nginx_vhosts:
        - listen: 8080
//templates/nginx.conf.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost.listen }}
}
{% endfor %}

生成的结果
server {
listen 8080
}
  • 示例2

// temnginx.yml

  • hosts: mageduweb
    remote_user: root
    vars:
    nginx_vhosts:
    • web1
    • web2
    • web3
      tasks:
    • name: template config
      template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
```bash 
 // templates/nginx.conf.j2
{% for vhost in nginx_vhosts %}
server {
listen {{ vhost }}
}
{% endfor %}



生成的结果:
server {
listen web1
}
server {
listen web2
}
server {
listen web3
}


roles

  • roles
    • ansilbe自1.2版本引入的新特性,用于层次性、结构化地组织playbook。roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可。简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中
  • 复杂场景:建议使用roles,代码复用度高
    • 变更指定主机或主机组
    • 如命名不规范维护和传承成本大
    • 某些功能需多个Playbook,通过Includes即可实现
  • 角色(roles):角色集合
roles/    这个文件夹名比较小是roles是固定的
  mysql/
  httpd/
  nginx/
  memcached/

Ansible Roles目录编排


- git是一个角色
+ 下面有一个tasks目录,里面放着所有的任务 这人文件必须要有
+ 而tasks文件夹里必要要有个文件是main.yml这个playbook。当我们调用git这个服务角色时,它会自动去找tasks下的main.yml这是整个任务的入口,相当于主联系人。这个文件必须存在
+ file则是放着要用的文件
+ vars放着所有的变量
- 当roles写好后要有人调用,则userconf.yml就相当于调用这些服务

roles目录结构

  • 每个角色,以特定的层级目录结构进行组织
  • roles目录结构
playbook.yml   这是调用的剧本是和roles是在同一目录是平级的
roles/
 project/
  tasks/
  files/
  vars/ 不常用
  default/ 不常用
  templates/
  handlers/
  meta/ 不常用
- roles这个文件夹在哪里创建都可以,但是官方有一个推荐位置/etc/ansible/目录下有一个roles
- 而roles目录创建完里面的目录可以根据需求来创建,但是tasks必须要有
    -

Roles各目录作用

  • /roles/project/ :项目名称,有以下子目录
    • files/ :存放由copy或script模块等调用的文件
    • templates/:template模块查找所需要模板文件的目录
    • tasks/:定义task,role的基本元素,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
    • handlers/:至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
    • vars/:定义变量,至少应该包含一个名为main.yml的文件;其它的文件需要在此文件中通过include进行包含
    • meta/:定义当前角色的特殊设定及其依赖关系,至少应该包含一个名为main.yml的文件,其它文件需在此文件中通过include进行包含
    • default/:设定默认变量时使用此目录中的main.yml文件

创建role

  • 创建role的步骤
    1. 创建以roles命名的目录
    2. 在roles目录中分别创建以各角色名称命名的目录,如webservers等
    3. 在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录;用不到的目录可以创建为空目录,也可以不创建
    4. 在playbook文件中,调用各角色

针对大型项目使用Roles进行编排


- 范例

site.yml
webservers.yml
dbservers.yml
roles/
    common/
        files/
        templates/
        tasks/
        handlers/
        vars/
        meta/
    webservers/
        files/
        templates/
        tasks/
        handlers/
        vars/
        meta/

playbook调用角色

  • 调用角色方法1
- hosts: websrvs
  remote_user: root
  roles:
    - mysql   这是调用roles,就是roles目录中所创建的
    - memcached
    - nginx
  • 调用角色方法2
传递变量给角色
- hosts:
  remote_user:
  roles:
    - mysql
    - { role: nginx, username: nginx }
    键role用于指定角色名称
    后续的k/v用于传递变量给角色
  • 调用角色方法3:还可基于条件测试实现角色调用
roles:
- { role: nginx, username: nginx, when:ansible_distribution_major_version== '7' }

完整的roles架构完整的roles架构

roles playbook tags使用

  • roles playbook tags使用
# ansible-playbook --tags="nginx,httpd,mysql" nginx-role.yml
// nginx-role.yml
---
- hosts: testweb
  remote_user: root
  roles:
    - { role: nginx ,tags: [ 'nginx', 'web' ] ,when:ansible_distribution_major_version == "6“ }
    - { role: httpd ,tags: [ 'httpd', 'web' ] }
    - { role: mysql ,tags: [ 'mysql', 'db' ] }
    - { role: marridb ,tags: [ 'mysql', 'db' ] }
    - { role: php }

创建一个简单的roles

  1. 首先创建一个roles目录
  2. 在roles目录中创建服务目录,这里创建一个NGINX服务的安装和启动,和复制文件的roles
  3. 那就那nginx服务来示列,进去入到nginx目录中,根据自己的需求创建目录,tasks目录必须创建这里存放playbook,其它如files目录如果不涉及文件的传输可以不创建
  4. 进入tasks目录创建添加组,添加用户,安装服务,复制模本,启动服务
[root@ansible tasks]# ls
groupadd.yml  install.yml   start.yml  useradd.yml 
  1. 当然这里写的playbook和我们之前写的单个playbook是有区别的,这里更简单了 ,不用写hosts和remote_user和tasks ,只用写对应的任务

[root@ansible tasks]# cat groupadd.yml 
- name: add group
  group: name=nginx

[root@ansible tasks]# cat useradd.yml 
- name: create user
  user: name=nginx group=nginx  system=yes shell=/sbin/nologin
[root@ansible tasks]# cat install.yml 
- name: install package
  yum: name=nginx

[root@ansible tasks]# cat start.yml 
- name: start service
  service: name=nginx state=started enabled=yes
  1. 当然我们知道每个目录中都有一个main.yml这个文件,这个文件相当于这些任务的总入口,当我们从外面调用这些模块化的服务时,它会自动到tasks目录中找main.yml,所有我们在创建一个main.yml

    1. 编写main.yml文件,这里是有先后顺序的一定要注意,自上而下的执行。用- import_tasks: playbookname
[root@ansible tasks]# cat main.yml 
- import_tasks: groupadd.yml
- import_tasks: useradd.yml
- import_tasks: install.yml
- import_tasks: start.yml
  1. 这样就创建好了,现在回到和roles同级的目录,创建一个调用这个角色符playbook,一定要和roles这目录在同级目录中
[root@ansible ansible]# ls
1  nginx-roles.yml  roles这个是目录
  1. nginx-roles.yml的编写
[root@ansible ansible]# cat nginx-roles.yml 
- hosts: cen7
  remote_user: root

  roles:
    - role: nginx   这是调用角色
  1. 这样就是一个简单roles
    • 当然如果有多个角色也可以调用
[root@ansible ansible]# cat nginx-roles.yml 
- hosts: cen7
  remote_user: root

  roles:
    - role: nginx   这是调用角色
    - role: filecopy  也可以调用复制文件的角色
  • 当也可以单独调用某一个角色中在tasks目录下某个剧本,比如在filecopy角色中调用安装nginx服务当然,首先我们把刚刚安装的nginx服务卸载了。
    • 首先要filecopy目录下中的tasks目录中的main.yml剧本中添加一行
- name: file copy
  copy: src=fstab dest=/app/

- name: file create
  file: name=/app/testfile state=touch mode=600

- import_tasks: roles/nginx/tasks/install.yml      //这是调用另一个角色中的一个剧本 ,一般不推荐这样使用               

角色中加标签tags

  • 在创建一个角色用于安装和创建httpd服务的
[root@ansible roles]# ls
filecopy  httpd  memcached  nginx
  • 这里有4个服务角色,在roles的父目录中写一个调用角色的脚本叫tags-roles.yml
  • 用大括号引起来
[root@ansible ansible]# vim tags-roles.yml
---
- hosts: cen7
  remote_user: root

  roles:
    - { role: nginx, tags: ['nginx','web' ] }  一个服务可以有多个标签,也可以和其它标签名相同tags:后一定要有空格,标签用单引号,多个标签用中括号分隔
    - { role: httpd, tags: ['httpd','web' ] }
    - { role: filecopy, tags: 'copyfile' }
  • 然后就可以根据标签来执行,调用时和任务标签语法一样。

创建一个大型的角色示列

  1. 创建roles目录和目录里的nginx角色
  2. 在nginx中创建要用的目录
mkdir {tasks,templates,handlers,vars}
  1. 想tasks目录中创建main.yml剧本
[root@ansible ~]# cat /root/ansible/roles/nginx/tasks/main.yml 
- name: install package    安装nginx包
  yum: name=nginx

- name: template
  template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
  tags: 123  这里也可以定义标签
  notify: restart service   当template任务发生变化时则执行restart service

- name: start service
  service: name=nginx state=started
  1. 在handlers目录中创建main.yml并编辑
[root@ansible ~]# cat /root/ansible/roles/nginx/handlers/main.yml 
- name: restart service    这个名字必须和notify所表示的名字一样,当template任务发生变化时执行这里的任务
  service: name=nginx state=restarted
  1. 复制一份nginx文件到template当模板文件
cp /etc/nginx/nginx.conf templates/nginx.conf.j2
  1. 修改模板文件

    • 这样就会不同主机会生成不同的配置文件
  2. 在vars目录中创建一个main.yml文件来设置自定义变量
[root@ansible ~]# cat /root/ansible/roles/nginx/vars/main.yml 
lport: 8080
  1. 在模板文件中调用这个配置文件
  2. 在roles同级目录中写调用角色的playbook文件
[root@ansible ~]# cat ansible/testnginx.yml 
- hosts: all
  remote_user: root

  roles:
    - { role: nginx, when: ansible_distribution_major_version=='7' }  判断如果系统版本号为7就执行这个角色
      lport: 9090   在这里也定义了变量,这里的变量优先级比vars目录中的变量优先级高这里会生效
    - role: filecopy  还可以调用多个角色
      when: ansible_nodename=='ansible-90'  也是判断,当主机名为ansible-90就执行这个角色
  1. 用-C检查然后执行

推荐资料

  • http://galaxy.ansible.com
  • https://galaxy.ansible.com/explore#/
  • http://github.com/
  • http://ansible.com.cn/
  • https://github.com/ansible/ansible
  • https://github.com/ansible/ansible-examples

你可能感兴趣的:(企业自动化运维ansible)