poetry

休言万事转头空。未转头时皆梦。

简介

内容

  • 运维自动化发展历程及技术
  • Ansible命令使用
  • Ansible常用模块详解
  • YAML语法简介
  • Ansible playbook基础
  • Playbook变量、tags、handlers使用
  • Playbook模板templates
  • Playbook条件判断when
  • Playbook字段with_items
  • Ansible Roles

自动化运维应用场景:

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

ansible特性:

  • 模块化,有Paramiko、PyYAML、Jinja2(模板语言)三个关键模块
  • 支持自定义模块
  • 基于Python语言实现
  • 部署简单,基于python和SSH,agentless
  • 安全,基于OpenSSH
  • 支持playbook编排任务
  • 幂等性:一个任务执行1遍和执行n遍的效果是一样的
  • 无需代理,不依赖PKI(无需SSL)
  • 支持其他语言写模块
  • YMAL格式,编排任务,支持丰富的数据结构
  • 较强大的多层解决方案

常用的自动化运维工具:

  • Ansible:python,Agentless,中小型应用环境
  • Saltstack:python,一般需要部署agent,执行效率更高
  • Puppent:ruby,功能强大,配置复杂,重型适合大型环境
  • Fabric:python,agentless
  • Chef:ruby,国内少

ansible架构

ansible_2.webp

  • Ansible Playbooks:任务剧本,编排定义Ansible任务集的配置文件,由ANSI不了顺序依次执行,通常是JSON格式的YML文件
  • Inventory:ANSI不了u案例主机清单/etc/anaible/hosts
  • Modules:Ansible执行命令的功能模块,多数为内置核心模块,可以自定义
  • Plugins:模块功能的补充,如连接类型插件、循环插件、变量插件、过滤插件等,该功能不常用
  • API:供第三方程序调用的应用程序编程接口
  • Ansible:组合Inventory、API、Modules
  • Plugins的绿框,可以理解为是ansible命令工具

安装

  1. yum安装

    1
    2
    yum -y install epel-release
    yum -y install ansible
  2. git安装

    1
    2
    3
    git clone https://github.com/ansible/ansible.git
    cd ./ansible
    source ./hacking/env-setup
  3. pip安装

    1
    2
    3
    4
    yum -y install python-pip python-devel
    yum -y install gcc glibc-devel zibl-devel rpm-bulid openssl-devel
    pip install --upgrade pip
    pip install ansible --upgrade
  4. 确认安装

    1
    ansible --version

配置文件

相关文件

配置文件

  • /etc/ansible/ansible.cfg:主配置文件,配置ansible工作特性
  • /etc/ansible/hosts:主机清单
  • /etc/ansible/roles:存放角色目录

程序

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

ansible命令

1
2
3
4
5
6
7
ansible 
ansible-doc
ansible-playbook
ansible-valut
ansible-console
ansible-galaxy
ansile-pull

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@node01 ~]# vim /etc/ansible/ansible.cfg
[defaults]
#inventory = /etc/ansible/hosts #主机列表配置文件
#library = /usr/share/my_modules/ #库文件存放目录
#remote_tmp = ~/.ansible/tmp #临时py命令文件存放在远程主机目录
#local_tmp = ~/.ansible/tmp #本机的临时命令执行目录
#forks = 5 #默认并发数
#poll_interval = 15
#sudo_user = root #默认sudo用户
#ask_sudo_pass = True #每次执行ansible命令是否询问ssh密码
#ask_pass = True
#remote_port = 22
#log_path = /var/log/ansible.log #日志文件,默认不记录。如果想打开日志记录,可以取消注释
#host_key_checking = False #检查对应服务器的host_key,建议取消注释,取消完ssh可以不敲yes

其中remote_tmplocal_tmp用于执行ansible脚本,当用户在ansible主机上发送命令时,ansible会把命令记录在本地的~/.ansible/tmp/目录,然后发送到受控主机的~/.ansible/tmp目录进行执行,并把执行结果返回给ansible主机。

ansible配置文件改动不需要用systemctl重启,因为ansible不是以服务的形式长驻内存的,它是用的时候临时调出。

Ad-Hoc

ansible命令执行过程

  1. 加载自己的配置文件,默认/etc/ansible/ansible.cfg
  2. 加载自己对应的模块文件,如command
  3. 通过ansible将模块或命令生成对应的临时py文件,并将该文件传输至远程服务器的对应执行用户$HOME/.ansible/tmp/ansible-tmp-Nbr/XXX.py文件
  4. 给文件+x执行
  5. 执行并返回结果
  6. 删除py文件,sleep 0 退出

可以使用ansible all -m ping -vvv来展示详细过程

颜色说明

  • 红色:执行失败
  • 绿色:成功,对目标系统没有做任何修改
  • 黄色:成功,对目标系统做出变更
  • 在/etc/ansible/ansible.cfg中的[colors]有说明

ansible用法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@node01 ~]# ansible --help
usage: ansible <host-pattern> [-m module_name] [-a args]

optional arguments:
--ask-vault-pass ask for vault password
--list-hosts 列出主机列表,如:ansible webserver --list-all
-a MODULE_ARGS, --args 追加模块参数
-f FORKS 连接并发数,默认是5
-t TREE, --tree TREE log output to this directory
-v, --verbose 显示详细过程,-vv,-vvv更详细
-K, --ask-become-pass 提示输入sudo密码
-b, --become 代替sudo切换
-T TIMEOUT, --timeout 执行命令的超时时间,默认10s
-k, --ask-pass 提示输入ssh连接密码,默认key验证
-u REMOTE_USER, --user 指定连接用户

测试能否ping通另一台主机,这里的Ping不是用ICMP协议,而是用SSH协议

1
2
3
4
5
6
# 首先在主机列表里加入我们的主机
echo "192.168.70.20" >> /etc/ansible/hosts
echo "192.168.70.30" >> /etc/ansible/hosts
echo "192.168.70.40" >> /etc/ansible/hosts

# 然后再使用ping命令,如果能通,对方主机会回复一个pong

ansible_3.webp

1
2
3
4
5
6
7
8
9
10
11
# 这里因为我在`192.168.70.30`上配置了key,所以立刻就能通。下面通过加参数k,来输入密码:
ansible 121.36.70.55 -m ping -k

#多台主机,用逗号隔开
ansible 192.168.70.20,192.168.70.30 -m ping

#执行所有主机
ansible all -m ping

# 通过清单分类ping,清单分类见下文
ansible dbserver -m ping

ansible_4.webp

主机清单分组

1
2
3
4
5
6
7
8
9
10
11
[dbserver]
192.168.70.30
192.168.70.20

[webserver]
192.168.70.10
192.168.70.40

[appserver]
192.168.70.[1:3]
#等价于192.168.70.1, 192.168.70.2, 192.168.70.3

doc

ansible-doc用法

1
2
3
4
5
6
7
8
9
10
11
12
13

# ansible-doc:显示模块帮助
ansible-doc [option] [module...]
-a 显示所有模块的文档
-l,--list 列出可用模块
-s,--$nippet 显示指定模块的playbook片段

# 示例:

ansible-doc -l #列出所有模块
ansible-doc ping #查看ping模块的帮助文档
ansible-doc -s ping #查看ping模块的简略信息

用ansible执行命

  • ansible通过ssh实现配置管理、应用部署、任务执行等功能,建议配置ansible端能基于密钥认证的方式联系各被管理的节点
1
2
3
4
5
6
7
8
# 查看dbserver组里的用户家目录
[root@node01 ~]# ansible dbserver -u root -m command -a 'ls /root'
192.168.70.30 | CHANGED | rc=0 >>
anaconda-ks.cfg

192.168.70.20 | CHANGED | rc=0 >>
anaconda-ks.cfg

普通用户在ansible执行自己主机root权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#首先在node02上添加普通用户dzq,密码000000,然后追加扩展组到wheel便于执行sudo权限
[root@node02 ~]# useradd dzq
[root@node02 ~]# passwd dzq
更改用户 dzq 的密码
新的 密码:000000
无效的密码: 密码是一个回文
重新输入新的 密码:000000
passwd:所有的身份验证令牌已经成功更新。
[root@node02 ~]# visudo
[root@node02 ~]# usermod -aG wheel dzq

#然后到node01上执行
[root@node01 ~]# ansible 192.168.70.20 -u dzq -m command -a 'ls /root' -k -K -b
SSH password: 000000
BECOME password[defaults to SSH password]: 000000
192.168.70.20 | CHANGED | rc=0 >>
anaconda-ks.cfg

# visudo带颜色
[root@node02 ~]# echo export EDITOR=vim >> /etc/profile.d/env.sh
[root@node02 ~]# source /etc/profile.d/env.sh

hosts

主机清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 匹配主机的列表
1、All:表示有Inventory中的所有主机
2、通配符 *
3、或关系 :
4、与关系
5、逻辑非
6、综合逻辑
7、正则表达式

演示:
1、ansible all -m ping
2、ansible 192.168.70.* -m ping
3、ansible "webserver:appserver" -m ping
4、ansible 'webserver:&appserver' -m ping
5、ansible 'webserver:!appserver' -m ping
6、ansible 'webserver:appserver:&dbserver:!ftpserver' -m ping
7、ansible '~(web|db).*\.server' -m ping

ansible常用模块

  • Command:在远程主机上执行命令,默认模块,可忽略-m选项

    ansible node24 -m command -a ‘systemctl restart sshd’

    ansible node24 -m command -a ‘echo 000 | passwd –stdin dzq’ 【不成功】

    此模块不支持 $VARNAME < > | ; $ $ 等,应该用shell模块实现

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

    ansible node24 -m shell -a ‘echo 000 | passwd –stdin dzq’

    调用bash执行命令,类似`cat /etc/passwd | awk -F ‘:’ ‘{print $1,$2}’ &> /tmp/example.txt这些复杂的命令,即使使用shell也可能会失败,解决办法:写到脚本里,copy到远程执行,再把所需要的结果拉回执行命令的机器

  • Script:运行脚本呢

    -a “/PATH/TO/SCRIPT_FILE”

    ansible node24 -m script -a f1.sh

  • Copy:把服务器上的文件复制到受控端

    如果目标存在,默认覆盖,此处指定先备份

    ansible all -m copy -a ‘src=/etc/selinux/config dest=/etc/selinux/config backup=yes owner=dzq mode=000’

    利用内容直接生成目标文件

    ansible all -m copy -a ‘content=’hello world’ dest/tmp/1.txt’

  • Fetch:从客户端取文件至服务器端

    ansible node23 -m file -a ‘src=/root/a.sh dest=/tmp/‘

  • File:设置文件属性

    ansible node23 -m file -a ‘path=/root/a.sh owner=dzq mode=755’

    ansible node23 -m file -a ‘src=/app/testfile dest=/app/testfile-link state=link’

    创建文件

    ansible node23 -m file -a ‘path=’/root/f1.txt’ state=touch’

    删除文件

    ansible node23 -m file -a ‘path=’/root/f1.txt’ state=absent’

    创建软连接

    ansible node23 -m file -a ‘src=/etc/ssh/sshd_config dest=/root/ssh_link state=link’

  • Hostname:管理主机名

    更改主机名

    ansible 192.168.70.20 -m hostname -a ‘name=node02’

  • Cron:计划任务

    支持时间:minute、hour、day、month、weekday

    创建任务:

    ansible node24 -m cron -a ‘minute=* weekday=1,3,5 job=”/usr/bin/wall FBI warning” name=warnning’

    禁用任务:

    ansible node24 -m cron -a ‘disabled=trued job=”/usr/bin/wall FBI warning” name=warnning’

    删除任务:

    ansible node24 -m cron -a ‘job=”/usr/bin/wall FBI warning” name=warnning state=absent’

  • Yum:管理包

    安装

    ansible node24 -m yum -a ‘name=httpd state=latest’

    删除

    ansible node24 -m yum -a ‘name=httpd state=absent’

  • Service:管理服务

    安装FTP服务启动,并设置开启自启

    ansible node24 -m service -a ‘name=vsftpd state=started enabled=yes’

    ansible srv -m service -a ‘name=httpd state=stoped’

    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=restart’

  • User:管理用户

    1. ansible srv -m user -a ‘name=user1 commment=”test user” uid=2048 home=/app/user1” group=root’
    2. ansible srv -m user -a ‘name=sysuser1 system=yes home=/app/sysuser1’
    3. ansible srv -m user -a ‘name=user1 state=absent remove=yes’ #删除用户家目录
  • Group:管理组

    ansible srv -m group -a ‘name=testgroup system=yes’

    ansible srv -m group -a ‘name=testgroup state=absent’

ansible-galaxy

ansible-vault

加密解密playbook

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#解密
ansible-vault encrypt hello.yml

#加密
ansible-vault decrypt hello.yml

#加密后查看文件
ansible-vault view hello.yml

#加密后编辑文件
ansible-vault eidtor hello.yml

#重新设置口令
ansible-vault rekey hello.yml

ansible-console

ansible交互式控制台

1
2
3
4
5
6
7
8
9
10
11
12
13
ansible-console
root@node24 (2)[f:5]$ forks 3
root@node24 (2)[f:3]$ cd node23
root@node23 (2)[f:3]$ ? #查看有哪些命令
root@node23 (2)[f:3]$ command hostname #执行命令:模块名/命令
192.168.70.20 | CHANGED | rc=0 >>
node02

192.168.70.30 | CHANGED | rc=0 >>
node03

root@node23 (2)[f:3]$ service name=httpd state=start

ansible-playbook

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

ansible_5.webp

Playbook核心元素

  • Hosts:执行的远程主机列表
  • Tasks:任务集
  • Varniables:内置变量或自定义变量在playbook中调用
  • Templates:模板,可替换模板文件的变量并实现一些简单逻辑文件
  • Handlers和Notity结合使用,由特定条件出发的操作,满足条件方才执行,否则不执行
  • tags:标签,指定某条任务执行,用于选择运行playbook中的部分代码,ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有变化的时间依然会非常长,此时如果确信没有其他变化,可以就通过tags跳过此代码片段

playbook常见选项

1
2
3
4
ansible-playbook file.yml --list-hosts	#列出playbook中的主机
ansible-playbook file.yml --list-tasks #列出playbook中的任务
ansible-playbook file.yml --limit node23#限制任务执行的主机
ansible-playbook -C file.yml #检查yaml语法

hosts

playbook中每个play的目的都是为了让某个或某些主机以某个指定的用户身份执行任务,hosts用于指定要执行任务的主机,须事先定义在主机清单中。

可以是如下形式

one.example.com

one.example.com:two.example.com

192.168.1.2

192.168.1.*

webserver:dbserver:&appserver

示例:

  • host: webserver

检查playbook语法:ansbile-playbook -C file.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- hosts: websrvs
remote_user: root

tasks:
- name: create new file
file: name=/data/newfile state=touch
- name: create new user
user: name=test3 state=present system=yes
- name: install package
yum: name=httpd state=installed
- name: copy html
copy: src=/var/www/html/index.html dest=/var/www/html/
- name: start service
service: name=httpd state=started enabled=yes

tasks

task列表和action

  • play的主体部分是task list。task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。在运行自上而下某playbook,如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可
  • task的目的是使用指定参数执行模块,而在模块参数中可以使用变量,模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致
  • 每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤,如果未提供name,则action的结果将用于输出

action格式:

1
2
(1) action: module arguments
(2) module: arguments

脚本错误继续执行

1
2
3
4
5
6
7
8
9
10
(1)
task:
-name: run this command and ignore the result
shell: /usr/bin/comnands || /bin/true

(2)
tasks
-name: run this command and ignore the result
shell: /usr/bin/commands
ignore_errors: True

handlers & notify

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

一旦配置文件发生变化,服务重启

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
---
#httpd服务

- host: webserver
remote_user: root

tasks:
- name: install httpd package
yum: name=httpd
- name: copy config file
copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart httpd service
- name: start service
service: name=httpd state=started enabled=yes

handlers:
- name: restart httpd service
service: name=httpd state=restarted

# 如果copy config file改动,则触发handlers中的restart httpd service

notify & handler 多个的写法,用列表的形式呈现

1
2
3
4
5
6
7
8
9
10
11
12
- name: config
copy: src=file/nginx.conf dest=/etc/nginx/nginx.conf
notify:
- restart nginx
- check nginx process

handlers:
- name: restart nginx
service: name=nginx state=restarted enable=yes
- name: check nginx process
shell: killall -0 nginx > /tmp/nginx.log

tags

只执行标签内容,或者不执行标签内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#以下是不同的动作不同的标签,也可以多个动作公用一个标签
- host: webserver
remote_user: root

tasks:
- name: install httpd package
yum: name=httpd
tags: install_httpd
- name: copy config file
copy: src=/etc/httpd/conf/httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart httpd service
- name: start service
service: name=httpd state=started enabled=yes
tags: restart_httpd

handlers:
- name: restart httpd service
service: name=httpd state=restarted

# ansible-playbook调用
# ansible-playbook -t install_httpd,restart_httpd httpd.yml

Varniables

变量名:仅能由字母、下划线、数字组成,且只能以字母开头

变量来源:

  1. ansible setup facts 远程主机的所有变量都可以直接调用

  2. 在/etc/ansible/hosts中定义

    普通变量:主机组中主机单独定义,优先级高于公共变量

    公共变量:针对主机中所有主机定义的统一变量

  3. 通过命令行指定变量,优先级最高

    ansible-playbook -e ‘varname=value’ file.yml

  4. 在playbook中定义

    1
    2
    3
    vars:
    - var1: value1
    - var2: value2
  5. 在role中定义

优先级:命令行>Playbook>主机清单

命令行的变量赋值

1
2
3
4
5
6
7
8
9
10
11
- host: webserver
remote_user: root

tasks:
- name: install httpd package
yum: name={{ pkgname }}
- name: start service
service: name={{ pkgname }} state=started enabled=yes

# 执行
# ansible-playbook -e 'pkgname=vsftpd' app.yml

定义playbook中的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
---
- host: webserver
remote_user: root

vars:
- pkgname1: httpd
- pkgname2: vsftpd

tasks:
- name: install httpd package
yum: name={{ pkgname1 }}
- name: start service
service: name={{ pkgname2 }} state=started enabled=yes

在主机清单中定义变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 定义主机清单中的变量
$ vim /etc/ansible/hosts
[node23]
192.168.70.30 Nbr=300
192.168.70.20 Nbr=200

# 在playbook中调用

- hosts: node23
remote_user: root

tasks:
- name: set hostanem
hostname: name=www.{{ Nbr }}ymlog.cn

清单统一变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ vim /etc/ansible/hosts
[node23]
192.168.70.30 Nbr=300
192.168.70.20 Nbr=200

[node23:vars]
nodename=www
domainname=ymlog.cn

- hosts: node23
remote_user: root

tasks:
- name: set hostanem
hostname: name={{ nodename }}{{ Nbr }}{{ domainname }}

templates

模板

  • 文本文件,嵌套有脚本

  • Jinja2语言,使用字面量,有下面形式:

    字符串:使用单引号或双引号

    数字:整数、浮点数

    列表:[item1,item2,…]

    元组:{item1,item2,…}

    字典:{key1:value1,key2:value2,….}

    布尔型:true/false

  • 算术运算:+ ,- , * ,/ ,// , % ,**

  • 比较操作:==, != ,< , <= ,> ,>=

  • 逻辑运算:and、or、not

  • 流表达式:For If When

1
2
3
4
5
6
7
8
9
10
11
12
13
---
- hosts: webserver
remote_user: root
vars_files:
- vars.yml

tasks:
- name: install package
yum: name=nginx
- name: copy template
template: src=/root/.ansible/templates/nginx.conf.j2 dest=/etc/nginx/nginx.conf
- name: start service
service: name=nginx state=started enabled=yes

1、将Nginx的工作进程改为CPU核心数的二次方倍:修改templates的文件,原来templates/nginx.conf.j2的文件就是nginx.conf的配置文件。

先查看setup中的cpu变量用什么标识:

1
2
3
[root@node01 ~]# ansible node24 -m setup | grep cpu
"ansible_processor_vcpus": 1,
"ansible_processor_vcpus": 1,
1
2
3
user  nginx;
worker_processes {{ ansible_processor_vcpus**2 }};

2、将node2的端口改为200,node3的端口改为300

1
2
192.168.70.30 Nbr=300
192.168.70.20 Nbr=200
1
2
3
4
5

server {
listen {{ Nbr }} default_server;
listen [::]:{{ Nbr }} default_server;

3、 when

  • 条件测试:如果需要根据变量、facts或此前任务执行结果来作为某task执行与否的前提时要用到条件测试,通过when语句实现,在task中使用,jinja2的语法格式

  • when语句

  • 在task后添加when子句即可使用条件测试;when语句支持jinja2表达语法

  • 示例:

    1
    2
    3
    4
    tasks:
    - name: "shutdown RedHat Flavored Systems"
    command: /sbin/shutdown -h now
    when: ansible_os_family == "Redhat"

当为7版的时候才复制文件

1
2
3
4
5
6
7
8
9
10
11
12
---
- hosts: webserver
remote_user: root

tasks:
- name: install package
yum: name=nginx
- name: copy template
template: src=/root/.ansible/templates/nginx.conf.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version == "7"
- name: start service
service: name=nginx state=started enabled=yes

with_item

迭代

1、新建多个文件

1
2
3
4
5
6
7
8
9
10
11
---
- hosts: webserver
remote_user: root

tasks:
- name: create some files
file: name=/data/{{ item }} state=touch
with_items:
- file1
- file2
- file3

2、迭代嵌套子变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
---
- hosts: webserver
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:'user',group: 'group3'}

for循环

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ vim templates.yml
- hosts: all
remote_user: root
vars:
ports:
- 81
- 82
- 83

tasks:
- name: copy conf
template: src=for1.conf.j2 dest/data/for1.conf


$ vim for1.conf.j2
{% for port in ports %}
server{
listen {{ port }}
}

{% endfor %}

$ 执行
ansible-playbook all templates.yml

把列表换成字典

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ vim templates.yml
- hosts: all
remote_user: root
vars:
ports:
- web1:
port: 81
name: www.web1.com
rootdir: /root/website1
- web2:
port: 82
name: www.web2.com
rootdir: /root/website2
- web3:
port: 83
name: www.web3.com
rootdir: /root/website3

tasks:
- name: copy conf
template: src=for1.conf.j2 dest/data/for1.conf


$ vim for1.conf.j2
{% for p in ports %}
server{
listen {{ p.port }}
servername {{ p.name }}
documentroot {{ p.rootdir}}
}

{% endfor %}

$ 执行
ansible-playbook all templates.yml

if条件判断

1
2
3
4
5
# 如果p.name被定义了
{% if p.name is defined %}
do...
{% endif %}
do...

YAML介绍

  • YAML是一个可读性高的用来表达资料序列的格式,YAML参考了其他多种语言,包括XML、C、Python、Perl以及电子邮件格式RFC2822等,Clark Evans在2001年首次发表了这种语言。

  • YAML特性

    • 可读性好
    • 和脚本语言的交互性好
    • 使用实现语言的数据类型
    • 有一个一致的信息模型
    • 易于实现
    • 可以基于流来处理
    • 表达能力强,扩展性好

    http://www.yaml.org

YAML语法简介

  1. 在单一文件中,可以连续用三个连字号-区分多个档案,另外,还有选择性的连续三个点号(…)用来表示档案结尾
  2. 次行开始正常写playbook的内容,一般建议写明该Playbook的功能
  3. 使用#注释代码
  4. 缩进必须是统一的,不能空格和tab混用
  5. 缩进的级别也必须是一致的,同样的缩进代表同样的级别,程序判别配置的级别就是通过缩进 结合换行来实现的
  6. YAML文件内容和Linux系统大小写判断方式保持一致,区分大小写,key/value值均需大小写敏感
  7. key/value值可同行写也可以换行写,同行使用:分隔
  8. value可以是字符串,也可以是另一个列表
  9. 一个完整的代码块功能需要最少元素包括name:task
  10. 一个name只能包含一个task
  11. YAML文件扩展名通常为yml或yaml
  • LIST:列表

    1
    2
    3
    4
    5
    6
    # A List
    - blue
    - grenn
    - black
    - white
    - yellow
  • DICTIONARY:字典

    1
    2
    3
    4
    5
    6
    # A Dictionay
    name: A
    jobs: B
    skil: C

    {name: A,Job: B,Skil: C}

基础写法:在node24上创建文件

1
2
3
4
5
6
7
---
- hosts: node24 #指定受控端
remote_user: root #以什么身份登录受控端

tasks:
- name: hello #任务名,没什么意思
command: touch /root/pl.test #使用command模块执行命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 把变量定义到一个文件中
$ cat vars.yml
var1: vsftpd
var2: httpd

# 调用变量

---
- hosts: webserver
remote_user: root
vars_files:
- vars.yml

tasks:
- name: install package
yum: name={{ var1 }}

roles

  • roles

    ansible自1.2版本引入的新特性,用于层次性、结构化组织playbook。roles能够根据层次型结构自动装载变量、tasks以及handlers等。要使用roles只需要在playbook中使用include指令即可,简单来讲,roles就是通过分别将变量、文件、任务、模板及处理器放置于单独的目录中,并且可以便捷的inlcude它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以用于构建 守护进程等场景中。

  • 复杂场景建议使用roles,代码复用性高

    某些功能需要多个playbook,通过includes即可实现

1
2
3
4
5
6
7
8
9
10
11
12
13
roles目录结构
----------------------------------------------
# 每个角色,以特定的层级目录结构进行组织
playbook.yml
roles/
project/
tasks/
files/
vars/
default/
templates/
handlers/
meta/

各目录的作用:

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

Ansible搭建httpd

安装httpd

1、目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
├── httpd_role.yml
├──*roles
│   └──*httpd
│   ├──*tasks
│   │   ├── group.yml
│   │   ├── install.yml
│   │   ├── main.yml
│   │   ├── restart.yml
│   │   ├── start.yml
│   │   ├── templ.yml
│   │   └── user.yml
│   └──*templates
│   └── httpd.conf.j2

dir:roles,httpd,task,templates


2、文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
[root@node01 .ansible]# cat httpd_role.yml 
- hosts: all
remote_user: root
roles:
- role: httpd


[root@node01 tasks]# cat group.yml
- name: add group
group: name=httpd gid=110 system=yes

[root@node01 tasks]# cat user.yml
- name: add user
user: name=httpd uid=110 system=yes

[root@node01 tasks]# cat install.yml
- name: install httpd
yum: name=httpd

[root@node01 tasks]# cat templ.yml
- name: copy config
template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf

[root@node01 tasks]# cat start.yml
- name: start service
service: name=httpd state=started


templates下的文件就是复制的/etc/httpd/conf/httpd.conf,并改名为httpd.conf.j2

调用别的角色的任务

1
2
3
4
main.yml

- include: install package
- include: roles/httpd/tasks/copyfile.yml

调用多个角色、启用标签、判断语句

1
2
3
4
5
6
7
[root@node01 .ansible]# cat some.yml 
- hosts: all
remote_user: root
roles:
- { role: httpd,tags:['web','httpd'],when: ansible_distribution_major_version == "7" } #加判断
- role: nginx #这里可以调用多个角色
- { role: vsftpd,tags: ['web','vsftpd' ]} #加标签

标签执行

1
ansible-play -t vsftpd some.yml

Ansible搭建Haproxy

主机 IP 功能
node01 192.168.70.10 ansible
node02 192.168.70.20 haproxy
node03 192.168.70.30 httpd
node04 192.168.70.40 httpd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123

#!/bin/bash
yum -y install ansible
IP2="192.168.70.20"
IP3="192.168.70.30"
IP4="192.168.70.40"

rm -rf /etc/ansible/hosts
echo "[webs]" >> /etc/ansible/hosts
echo "$IP3 SITE=web1" >> /etc/ansible/hosts
echo "$IP4 SITE=web2" >> /etc/ansible/hosts
echo "[lb]" >> /etc/ansible/hosts
echo "$IP2" >> /etc/ansible/hosts

rm -rf /root/.ansible/*
mkdir -p /root/.ansible/roles/{httpd,haproxy}/{tasks,templates}
touch /root/.ansible/roles/{httpd,haproxy}/tasks/install.yml
touch /root/.ansible/roles/{httpd,haproxy}/tasks/templa.yml
touch /root/.ansible/roles/{httpd,haproxy}/tasks/start.yml
touch /root/.ansible/roles/{httpd,haproxy}/tasks/main.yml

cat >> /root/.ansible/roles/httpd/tasks/install.yml << 'EOF'
- name: install httpd
yum: name=httpd
EOF

echo "this is {{ SITE }}" >> /root/.ansible/roles/httpd/templates/index.html.j2

cat >> /root/.ansible/roles/httpd/tasks/templa.yml << 'EOF'
- name: copy index.html
template: src=/root/.ansible/roles/httpd/templates/index.html.j2 dest=/var/www/html/index.html
EOF

cat >> /root/.ansible/roles/httpd/tasks/start.yml << 'EOF'
- name: start service
service: name=httpd state=started
EOF

cat >> /root/.ansible/roles/httpd/tasks/main.yml << 'EOF'
- include: install.yml
- include: templa.yml
- include: start.yml
EOF

cat >> /root/.ansible/roles/haproxy/tasks/install.yml << 'EOF'
- name: install haproxy
yum: name=haproxy
EOF

cat >> /root/.ansible/roles/haproxy/templates/haproxy.cfg.j2 << 'EOF'
global
log 127.0.0.1 local2
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 3
timeout http-request 10s
timeout queue 1m
timeout connect 10s
timeout client 1m
timeout server 1m
timeout http-keep-alive 10s
timeout check 10s
maxconn 3000
frontend main *:8080
acl url_static path_beg -i /static /images /javascript /stylesheets
acl url_static path_end -i .webp .gif .webp .css .js
use_backend static if url_static
default_backend app
backend static
balance roundrobin
server static 127.0.0.1:4331 check
backend app
balance roundrobin
EOF
echo -e " server web1 $IP3:80 check" >> /root/.ansible/roles/haproxy/templates/haproxy.cfg.j2
echo -e " server web2 $IP4:80 check" >> /root/.ansible/roles/haproxy/templates/haproxy.cfg.j2

cat >> /root/.ansible/roles/haproxy/tasks/templa.yml << 'EOF'
- name: copy index.html
template: src=/root/.ansible/roles/haproxy/templates/haproxy.cfg.j2 dest=/var/www/html/index.html dest=/etc/haproxy/haproxy.cfg
EOF


cat >> /root/.ansible/roles/haproxy/tasks/start.yml << 'EOF'
- name: start service
service: name=haproxy state=started
EOF

cat >> /root/.ansible/roles/haproxy/tasks/main.yml << 'EOF'
- include: install.yml
- include: templa.yml
- include: start.yml
EOF

cat >> /root/.ansible/lb_role.yml << 'EOF'
---
- hosts: webs
remote_user: root
roles:
- role: httpd

- hosts: lb
remote_user: root
roles:
- role: haproxy
EOF

cd /root/.ansible/
ansible-playbook -$1 lb_role.yml
echo "Please Curl $IP2:8080"