Ansible基础

概述

  • Ansible首次发布于2012年,作者Michael DeHaan

    于2015年被RedHat收购

  • Ansible是一种自动化运维工具,基于Python开发

    批量系统部署

    批量程序部署

    批量运行命令等功能

Ansible特色

  • 基于SSH架构
  • 模块丰富
  • 社区活跃
  • 支持自定义模块
  • 支持异构IT架构
  • 部署简单,容易上手

Ansible环境部署

配置SSH密钥

  • 所有节点做相同操作
1
2
3
4
5
6
7
8
9
[root@ansible ~]# cat /etc/hosts
······
192.168.1.10 ansible
192.168.1.11 node-1
192.168.1.12 node-2

[root@ansible ~]# ssh-keygen -f /root/.ssh/id_rsa -N ''
[root@ansible ~]# for i in ansible node-1 node-2;do ssh-copy-id root@$i;done

部署软件

  • 控制节点

    要求Python2.6及以上

    Python模块:paramiko、PyYAML、JinJa2······

1
2
3
4
5
6
7
8
[root@ansible ~]# tar xf ansible.tar.gz -C /opt/
[root@ansible ~]# vim /etc/yum.repos.d/local.repo
[ansible]
name=ansible
baseurl=file:///opt/ansible
gpgcheck=0

[root@ansible ~]# yum install ansible -y
  • 被控节点

    Ansible默认通过SSH协议管理机器

    被管理主机要开启SSH服务,并允许控制主机登录

    被管理主机需要安装Python

Ansible基本配置

配置文件

  • 主配置文件ansible.cfg

    参考/etc/ansible/ansible.cfg

  • ansible配置文件查找顺序

    首先检测ANSIBLE_CONFIG变量定义的配置文件

    其次检查当前目录下的ansible.cfg文件

    再次检查当前用户家目录下ansible.cfg文件

    最后检查/etc/ansible/ansible.cfg文件

主配置文件

  • 参考/etc/ansible/ansible.cfg
1
2
3
4
5
6
7
8
[root@ansible ~]# mkdir ansible
[root@ansible ~]# cp /etc/ansible/ansible.cfg ansible/
[root@ansible ~]# cd ansible/
[root@ansible ansible]# vim ansible.cfg
[defaults]
inventory = /root/ansible/hosts #主机清单配置文件
#forks = 5 #ssh并发数量

inventory主机清单文件

  • 将被管理端主机写入一个主机列表文件

    参考/etc/ansible/hosts

1
2
3
4
5
6
7
8
[root@ansible ansible]# vim hosts
[test] #定义主机组(组名称任意)
node-1 #定义组中具体主机
[proxy]
node-2
[webserver]
node-[1:2]

测试

  • 测试Ansible环境与配置是否正常
1
2
3
4
5
6
7
8
[root@ansible ~]# cd ansible/   #ansible命令需要再ansible目录下
[root@ansible ansible]# ansible all --list-hosts

[root@ansible ansible]# ansible node-1 -m ping
[root@ansible ansible]# ansible node-1,node-2 -m ping
[root@ansible ansible]# ansible proxy -m ping
[root@ansible ansible]# ansible webserver -m ping

ad-hoc命令行

命令行语法格式

  • Ansible ad-hoc是一种通过命令行批量管理的方式

    格式:ansible 主机集合 -m 模块名 -a "参数"

    其他选项: -k 使用密码远程、-i 指定主机列表文件

  • 模块就是脚本(多数为Python脚本)

    多数脚本都支持参数

    默认模块为command

1
2
3
4
5
[root@ansible ansible]# ansible node-1 -m command -a "uptime"
[root@ansible ansible]# ansible node-1 -m command -a "uname -r"
[root@ansible ansible]# ansible node-1 -m command -a "ip a s ens33"
[root@ansible ansible]# ansible node-1 -a "ip a s ens33"
[root@ansible ansible]# ansible all -a "date"

获取帮助

  • Ansible总共有哪些模块

  • 每个模块支持哪些参数

1
2
3
[root@ansible ansible]# ansible-doc -l           #列出所有模块
[root@ansible ansible]# ansible-doc -l | grep yum #过滤
[root@ansible ansible]# ansible-doc yum #查看模块帮助

Ansible常用模块

shell模块

command和shell模块的区别

  • command模块的命令不启动shell,直接通过ssh执行命令
  • command不支持bash的特性,如管道和重定向等功能
  • 所有需要调用shell的功能都无法使用
1
2
[root@ansible ansible]# ansible test -m command -a "ps | wc -l"       #报错
[root@ansible ansible]# ansible test -m command -a "ls &"

shell模块会启动shell执行命令

  • 不可以使用shell模块执行交互命令,如vim、top等
1
2
3
4
5
[root@ansible ansible]# ansible test -m shell -a "ps | wc -l"
[root@ansible ansible]# ansible test -m shell -a "ls &"
[root@ansible ansible]# ansible test -m shell -a "who"
[root@ansible ansible]# ansible test -m shell -a "touch /tmp/txt"

shell模块支持判断(creates)

  • creates 文件名:文件存在不执行shell命令
1
2
3
4
[root@ansible ansible]# ansible test -m shell -a "ssh-keygen -f ~/.ssh/id_rsa -N '' creates=~/.ssh/id_rsa" 
#如果存在~/.ssh/id_rsa则不创建,跳过
node-1 | SUCCESS | rc=0 >>
skipped, since /root/.ssh/id_rsa exists

script模块

如果命令复杂

  • script允许在本地写脚本,拷贝到被管理主机并执行
  • 脚本不是shell脚本(如python,perl等),可以没有-x
1
2
3
4
5
6
7
8
9
10
11
12
13
[root@ansible ansible]# cat test.sh 
#!/bin/bash
yum install nfs-utils -y
mkdir /opt/nfs
echo "/opt/nfs *(rw)" > /etc/exports
systemctl enable --now nfs
[root@ansible ansible]# ansible test -m script -a "./test.sh"

[root@ansible ansible]# ansible test -a "showmount -e"
node-1 | CHANGED | rc=0 >>
Export list for node-1:
/opt/nfs *

file模块

  • file模块可以创建文件、目录、连接;修改权限与属性等

  • 幂等性:任意执行所产生的影响均与第一次执行的影响相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#新建文件
[root@ansible ansible]# ansible test -m file -a "path=/tmp/file.txt state=touch"

#新建目录
[root@ansible ansible]# ansible test -m file -a "path=/tmp/file state=directory"

#修改文件或目录权限
[root@ansible ansible]# ansible test -m file -a "path=/tmp/file.txt owner=sshd group=adm mode=0777"

#删除文件
[root@ansible ansible]# ansible test -m file -a "path=/tmp/file.txt state=absent"

#删除目录
[root@ansible ansible]# ansible test -m file -a "path=/tmp/file state=absent"

#创建链接文件
[root@ansible ansible]# ansible test -m file -a "src=/etc/hosts path=/tmp/hosts.txt state=link"

copy模块

将文件拷贝到远程主机

  • backup=yes如果目标主机有相同文件,则先备份
1
2
3
4
5
6
[root@ansible ansible]# echo "AAA" > a3.txt
[root@ansible ansible]# ansible test -m copy -a "src=./a3.txt dest=/root/"
[root@ansible ansible]# ansible test -m copy -a "src=./a3.txt dest=/root/a3.txt"

#通过content可以直接提供文件内容,\n代表回车
[root@ansible ansible]# ansible test -m copy -a "content='hello the world\n' dest=/root/new.txt"

fetch模块

与copy类似,但是作用相反

  • 可以将其他主机的文件拷贝到本地
1
2
3
4
[root@ansible ansible]# ansible test -m fetch -a "src=/etc/hostname dest=./"
[root@ansible ansible]# cat ./node-1/etc/hostname
node-1

lineinfile模块

  • 修改单个文件的单行内容时可以使用lineinfile模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#在/etc/issue文件中添加一行内容,默认添加在最后
[root@ansible ansible]# ansible test -m lineinfile -a "path=/etc/issue line='hello world'"

#基于幂等原则,重复执行,不会创建多行
[root@ansible ansible]# ansible test -m lineinfile -a "path=/etc/issue line='hello world'"

# 修改匹配行
[root@ansible ansible]# ansible test -m lineinfile -a "path=/etc/selinux/config regexp='^SELINUX=' line='SELINUX=disabled'"

#匹配行前/后一行添加内容
[root@ansible ansible]# ansible test -m lineinfile -a "dest=/etc/issue insertbefore='^hello' line='www'"

[root@ansible ansible]# ansible test -m lineinfile -a "dest=/etc/issue insertafter='^hello' line='aaa'"

#删除匹配行
[root@ansible ansible]# ansible test -m lineinfile -a "dest=/etc/issue regexp='aaa' state=absent"

replace模块

  • lineinfile会替换一整行,replace可以替换关键字
1
2
3
#将/etc/issue文件全文所有的Kernel替换为Ocean
[root@ansible ansible]# ansible test -m replace -a "path=/etc/issue regexp=Kernel replace=Ocean"

user模块

  • user模块可以实现Linux系统账户系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#远程test组中所有主机并创建系统账户tuser1
[root@ansible ansible]# ansible test -m user -a "name=tuser1"

#创建账户并设置对应的账户属性
[root@ansible ansible]# ansible test -m user -a "name=tuser2 uid=1010 group=adm groups=daemon,root home=/home/tuser2"

#修改密码
[root@ansible ansible]# ansible test -m user -a "name=tuser1 password={{'abc'|password_hash('sha512')}}"

#修改tuser1附加组
[root@ansible ansible]# ansible test -m user -a "name=tuser1 groups=root,daemon"

#删除账户tuser1
[root@ansible ansible]# ansible test -m user -a "name=tuser1 state=absent"

#删除tuser2账户同时删除家目录、邮箱
[root@ansible ansible]# ansible test -m user -a "name=tuser2 state=absent remove=true"

yum_repository模块

  • 使用yum_repository模块可以创建或修改yum源配置文件
1
2
3
4
5
6
7
8
9
10
11
[root@ansible ansible]# ansible test -m yum_repository -a "name=myyum description=hello baseurl=file:///opt/centos gpgcheck=0"

[root@node-1 ~]# cat /etc/yum.repos.d/myyum.repo
[myyum]
baseurl = file:///opt/centos
gpgcheck = 0
name = hello

#删除yum源
[root@ansible ansible]# ansible test -m yum_repository -a "name=myyum state=absent"

yum模块

  • 使用yum模块可以安装、卸载、升级软件包
  • state:present(安装)absent(卸载)latest(升级)
1
2
3
4
5
[root@ansible ansible]# ansible test -m yum -a "name=unzip state=present"

[root@ansible ansible]# ansible test -m yum -a "name=unzip state=latest"

[root@ansible ansible]# ansible test -m yum -a "name=unzip state=absent"

service模块

  • service为服务管理模块(启动、关闭、重启等)
  • state:started(启动)stopped(关闭)restarted(重启)
  • enabled:yes设置开机自启
1
2
3
4
5
6
7
[root@ansible ansible]# ansible test -m yum -a "name=httpd state=present"

[root@ansible ansible]# ansible test -m service -a "name=httpd state=started"

[root@ansible ansible]# ansible test -m service -a "name=httpd state=stopped"

[root@ansible ansible]# ansible test -m service -a "name=httpd state=restarted enabled=yes"

逻辑卷相关模块

lvg模块

  • 创建、删除卷组(VG),修改卷组大小
  • state:present(创建)absent(修改)
1
2
3
4
5
6
7
8
[root@ansible ansible]# ansible test -m yum -a "name=lvm2"

#创建名为myvg卷组,由/dev/sdb组成
[root@ansible ansible]# ansible test -m lvg -a "vg=myvg pvs=/dev/sdb1"

#修改卷组大小
[root@ansible ansible]# ansible test -m lvg -a "vg=myvg pvs=/dev/sdb1,/dev/sdb2"

lvol模块

  • 创建、删除逻辑卷(lv),修改逻辑卷大小
  • state:present(创建)absent(修改)
1
2
3
4
5
6
7
8
9
10
11
#使用myvg卷组创建名为mylv的逻辑卷
[root@ansible ansible]# ansible test -m lvol -a "lv=mylv vg=myvg size=2G"

#修改逻辑卷大小
[root@ansible ansible]# ansible test -m lvol -a "lv=mylv vg=myvg size=4G"

#删除逻辑卷
[root@ansible ansible]# ansible test -m lvol -a "lv=mylv vg=myvg state=absent force=yes"

#删除卷组
[root@ansible ansible]# ansible test -m lvg -a "vg=myvg state=absent"

sudo提权

sudo

  • superuser or another do
  • 以管理员或其他人的身份执行命令

基本流程

  • 管理员需要先授权(修改/etc/sudoers文件)
  • 普通用户以sudo形式执行命令
  • 可以通过sudo -l查看授权情况

sudoers语法

修改/etc/sudoers的方法

  • visudo(带语法检查,默认没有颜色提示)
  • vim /etc/sudoers(不带语法检查,默认有颜色提示)

授权格式

  • 用户或组 主机列表=(提权身份) [NOPASSWD]:命令列表
  • 命令需要写绝对路径

配置sudo提权

  • 为所有被管理主机创建系统账户
1
[root@ansible ansible]# ansible all -m user -a "name=alice password={{'000000'|password_hash('sha512')}}"
  • 在所有被管理主机配置sudo,让alice可以执行任何命令
  • 使用NOPASSWD开启无密码验证
1
2
3
4
5
[root@ansible ansible]# ansible all -m lineinfile -a "path=/etc/sudoers line='alice ALL=(ALL) NOPASSWD:ALL'"

[root@ansible ansible]# ssh alice@node-1
[alice@node-1 ~]$ sudo -l
[alice@node-1 ~]$ sudo systemctl restart httpd

Ansible进阶配置

主配置文件

  • 修改~/ansible/ansible.cfg

  • 修改sudo相关配置(参考/etc/ansible/ansible.cfg)

1
2
3
4
5
6
7
8
[defaults]
inventory=/root/ansible/hosts
remote_user=alice #以什么用户远程被管理主机
[privilege_escalation]
become=True #是否需要切换用户
become_method=sudo #如何切换用户
become_user=root #切换什么用户
become_ask_pass=False #sudo是否需要输入密码

配置SSH密钥

  • 使用alice远程被管理主机,需要提前配置SSH密钥
1
2
3
4
5
[root@ansible ansible]# for i in  node-1 node-2
do
ssh-copy-id alice@$i
done
[root@ansible ansible]# ansible all -a "who" #测试

主机清单文件

inventory配置

  • 如果个别主机账户不同该如何处理
  • 如果有些主机需要使用密码该如何
  • 有些主机的SSH端口不是22该如何
  • 修改主机清单文件,添加变量(多个变量空格分开)
1
2
3
4
5
6
7
8
[test]
node-1 ansible_ssh_port=220 #自定义远程SSH端口
[proxy]
node-2 ansible_ssh_user=alice #自定义远程连接用户
[master]
ansible ansible_ssh_pass=密码 #自定义远程连接的密码
[webserver]
node-[1:2] ansible_ssh_private_key_file=密钥文件 #自定义密钥

Ansible Playbook

Playbook概述

  • Ansible ad-hoc可以通过命令行形式远程管理其他主机

    适合执行一些临时性简单任务

  • Ansible playbook中文名称叫剧本

    将经常需要执行的任务写入一个文件(剧本)

    剧本中可以包含多个任务

    剧本写后,我们随时调用剧本,执行相关的任务命令

YAML简介

  • 是一个可读性高、用来表达数据序列的格式语言
  • YAML:YAML Aint't a Markup Language
  • YAML以数据为中心,重点描述数据的关系和结构

YAML格式

  • “#”代表注释,一般第一行为三个横杠
  • 键值对使用":"表示,数组使用"-"表示
  • 缩进必须由两个或以上空格组成
  • 相同层级的缩进必须对齐
  • 全文不可以使用tab键
  • 区分大小写、扩展名为yaml或yml
  • 跨行数据需要使用>或者|(|会保留换行符)

YAML格式的键值对数据

  • key和value之间使用”:”分隔
  • “:”后面必须有空格
  • 缩进代表层级关系
1
2
3
4
"key": "value"
或者
"key":
"value"

YAML格式的数组数据

  • 使用短横杠和空格表示一行表示数据
1
2
3
4
"key":
- "value1"
- "value2"
- "value3"

Playbook语法格式

  • playbook采用YAML格式编写

  • playbook文件中由一个或多个play组成

  • 每个play中可以包含:

    hosts(主机)、tasks(任务)

    variables(变量)、roles(角色)、handlers等元素

  • 使用ansible-playbook命令运行playbook剧本

1
2
3
4
5
6
[root@ansible ansible]# vim test.yml
- hosts: all
tasks:
- name: first playbook
ping:
[root@ansible ansible]# ansible-playbook test.yml
  • hosts由一个或多个组或主机组成,逗号分割
  • tasks由一个或多个任务组成,多个任务按顺序执行
  • 可以使用-f选项自定义并发量
1
2
3
4
5
6
7
8
[root@ansible ansible]# vim test.yml
- hosts: all
tasks:
- name: first playbook
ping:
- name: Run command
shell: touch ~/shell.txt
[root@ansible ansible]# ansible-playbook test.yml -f 5
  • 一个playbook中可以有多个play
1
2
3
4
5
6
7
8
9
10
[root@ansible ansible]# vim test.yml
- hosts: test
tasks:
- name: first playbook
ping:
- hosts: proxy
tasks:
- name: second command
ping:
[root@ansible ansible]# ansible-playbook test.yml

Ansible Playbook应用案例

用户

  • 编写playbook创建系统账户、账户属性、设置密码
  • 花括号外面必须有双引号
1
2
3
4
5
6
7
8
9
10
11
[root@ansible ansible]# vim user_john.yml
- hosts: all
tasks:
- name: create user john
user:
name: john
uid: 1040
shell: /bin/bash
group: daemon
password: "{{'000000'|password_hash('sha512')}}"
[root@ansible ansible]# ansible-playbook user_john.yml
  • 删除用户
1
2
3
4
5
6
7
8
[root@ansible ansible]# vim user_john.yml
- hosts: all
tasks:
- name: create user john
user:
name: john
state: absent
[root@ansible ansible]# ansible-playbook user_john.yml

逻辑卷

  • 使用vdb创建卷组和逻辑卷
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
[root@ansible ansible]# vim lvm.yml
- hosts: test
tasks:
- name: 分区
parted:
device: /dev/sdb
number: 1
state: present
part_end: 1GiB
- name: 分区
parted:
device: /dev/sdb
number: 2
state: present
part_start: 1GiB
part_end: 3GiB
- name: 创建卷组
lvg:
vg: my_vg
pvs: /dev/sdb1,/dev/sdb2
- name: 创建逻辑卷
lvol:
vg: my_vg
lv: my_lv
size: 512m
[root@ansible ansible]# ansible-playbook lvm.yml

软件管理

  • 安装软件、升级软件、安装组包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@ansible ansible]# vim pacakege.yml
- hosts: test
tasks:
- name: install packages
yum:
name:
- httpd
- mariadb
- mariadb-server
state: present
- name: install package group
yum:
name: "@Development Tools" #通过yum groups list显示组包
- name: update software
yum:
name: '*'
state: latest
[root@ansible ansible]# ansible-playbook pacakege.yml

特殊模块

setup模块

  • ansible_facts用于采集被管理设备的系统信息
  • 所有收集的信息都被保存在变量中
  • 每次执行playbook默认第一个任务就是Gathering Facts
  • 使用setup模块可以查看收集到的facts信息
1
2
3
4
5
6
7
8
9
[root@ansible ansible]# ansible test -m setup
node-1 | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.1.11"
······
]
}
}
  • 找出相应facts信息(父子关系时使用.分割)
1
2
3
4
5
6
7
8
9
10
[root@ansible ansible]# ansible test -m setup -a "filter=ansible_mem*"

[root@ansible ansible]# ansible test -m setup -a "filter=ansible_all_ipv4_addresses"

[root@ansible ansible]# ansible test -m setup -a "filter=ansible_bios_version"

[root@ansible ansible]# ansible test -m setup -a "filter=ansible_memtotal_mb"

[root@ansible ansible]# ansible test -m setup -a "filter=ansible_hostname"

debug模块

  • debug模块可以显示变量的值,可以辅助排错
  • debug模块有两个参数,mas(引用变量需要双大括号)var(引用不需要双大括号)
1
2
3
4
5
6
7
8
[root@ansible ansible]# vim debug.yml
- hosts: test
tasks:
- debug:
msg: "{{ansible_hostname}}"
- debug:
var: ansible_hostname
[root@ansible ansible]# ansible-playbook debug.yml

firewalld模块

  • 使用firewalld模块可以配置防火墙策略
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@ansible ansible]# vim firewalld.yml
- hosts: test
tasks:
- name: install firewalld
yum:
name: firewalld
state: present
- name: run firewalld
service:
name: firewalld
state: started
enabled: yes
- name: set firewalld rule
firewalld:
port: 80/tcp
permanent: yes
state: enabled
[root@ansible ansible]# ansible-playbook firewalld.yml

template模块

  • copy模块可以将一个文件拷贝给远程主机

  • 但是如果希望每个拷贝的文件内容都不一样

  • 如何给所有web主机拷贝index.html内容是各自的IP地址

  • Ansible可以利用Jinja2模板引擎读取变量

    之前在playbook中调用变量,也是Jinja2的功能

    Jinja2模块的表达式包含在分隔符双大括号内

1
2
3
4
5
6
7
8
9
10
11
12
13
#给所有主机拷贝首页,每个主机内容不同
[root@ansible ansible]# mkdir template
[root@ansible ansible]# vim template/index.html
welcome to {{ansible_hostname}} on {{ansible_ens33.ipv4.address}}.

[root@ansible ansible]# vim template.yml
- hosts: all
tasks:
- template:
src: ~/ansible/template/index.html
dest: /var/www/html/index.html

[root@ansible ansible]# ansible-playbook template.yml
  • 也可自定义变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@ansible ansible]# vim template/source.j2
{{welcome}} {{iname}}

[root@ansible ansible]# vim template.yml
- hosts: all
vars:
welcome: 'hello'
iname: 'jack'
tasks:
- template:
src: ~/ansible/template/source.j2
dest: /tmp/

[root@ansible ansible]# ansible-playbook template.yml

Ansible定义变量

变量定义优先级排序

  • inventory变量
  • Host Facts变量
  • Playbook变量
  • 变量文件
  • 当以双大括号开头时要用引号

定义变量

inventory变量

  • 在主机清单配置文件中定义变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@ansible ansible]# vim hosts 
[test]
node-1 myvar1="hello world" myvar2="content"
[proxy]
node-2
yourname="jacob"
[webserver]
node-[1:2]

[root@ansible ansible]# vim inventory_var.yml
- hosts: test
tasks:
- name: create a file
shell: echo {{myvar1}} > /tmp/{{myvar2}}
- hosts: proxy
tasks:
- name: create a user
user:
name: "{{yourname}}"
[root@ansible ansible]# ansible-playbook inventory_var.yml

Host Facts变量

  • 可以直接调用ansible收集的系统信息
1
2
3
4
5
6
7
8
[root@ansible ansible]# vim facts_var.yml 
- hosts: test
tasks:
- name: use facts info
copy:
content: "{{ansible_hostname}}:{{ansible_bios_version}}"
dest: /tmp/facts.txt
[root@ansible ansible]# ansible-playbook facts_var.yml

Playbook变量

  • 使用vars关键词可以在playbook内定义变量
1
2
3
4
5
6
7
8
9
10
11
[root@ansible ansible]# vim playbook_var.yml 
- hosts: test
vars:
iname: heal
ipass: '123456' #密码必须是字符串,需要引号
tasks:
- name: creat user
user:
name: "{{iname}}"
password: "{{ipass|password_hash('sha512')}}"
[root@ansible ansible]# ansible-playbook playbook_var.yml

用vars_files调用变量文件

  • 单独定义个变量文件,在playbook中用vars_files调用文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@ansible ansible]# vim variables.yml
iname: cloud
ipass: '123456'

[root@ansible ansible]# vim file_var.yml
- hosts: test
vars_files: ./variables.yml
tasks:
- name: creat user
user:
name: "{{iname}}"
password: "{{ipass|password_hash('sha512')}}"
[root@ansible ansible]# ansible-playbook file_var.yml

使用-e参数定义变量

  • 执行ansible-playbook命令时使用-e参数定义变量
1
2
3
4
5
6
7
8
9
[root@ansible ansible]# vim command_var.yml 
- hosts: test
tasks:
- name: creat user
user:
name: "{{iname}}"
password: "{{ipass|password_hash('sha512')}}"
[root@ansible ansible]# ansible-playbook command_var.yml -e iname='beth' -e ipass='123456'

Ansible高级语法

error处理机制

  • 默认ansible在遇到error会立刻停止playbook
  • 使用ignore_errors可以忽略错误,继续后续的任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@ansible ansible]# vim error.yml
- hosts: all
# ignore_errors: true #针对playbook全局忽略错误
tasks:
- name: start a service
service:
name: hehehehe
state: started
ignore_errors: true #针对某一任务忽略错误
- name: touch a file
file:
path: /tmp/service.txt
state: touch
[root@ansible ansible]# ansible-playbook error.yml

handlers

当某个任务需要依赖其他任务怎么办

  • 可以通过handlers定义一组任务
  • 仅当某个任务触发(notify)handlers时才执行相应的任务
  • 如果有多个notify触发执行handlers任务,也仅执行一次
  • 仅当任务的执行状态为changed时handlers任务才执行
  • handlers任务在所有其他任务都执行后才执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
[root@ansible ansible]# vim handlers.yml
- hosts: test
tasks:
- name: create directory
file: #多次执行playbook该任务状态不再是changed
path: /tmp/handlers
state: directory
notify: touch #notify后面名称必须和handlers中一致
handlers:
- name: touch
file:
path: /tmp/handlers/new.txt
state: touch
[root@ansible ansible]# ansible-playbook handlers.yml

when条件判断

  • when可以定义判断条件,条件为真时才执行某个任务

  • 常见条件操作入下:

    ==、!=、>、>=、<、<=

  • 多个条件可以使用and或or分割

  • when表达式调用变量不要使用双大括号

远程主机剩余内存不足700M则关闭NetworkManager服务

1
2
3
4
5
6
7
8
9
[root@ansible ansible]# vim when1.yml 
- hosts: test
tasks:
- name: check
service:
name: NetworkManager
state: stopped
when: ansible_memfree_mb < 700
[root@ansible ansible]# ansible-playbook when1.yml

判断操作系统是Centos7则创建测试文件

1
2
3
4
5
6
7
8
9
10
11
12
[root@ansible ansible]# vim when2.yml 
- hosts: test
tasks:
- name: touch a file
file:
path: /tmp/when.txt
state: touch
when: >
ansible_distribution == "CentOS"
and
ansible_distribution_major_version == "7"
[root@ansible ansible]# ansible-playbook when2.yml

block认为块

  • 使用block可以将多个任务合并为一个组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@ansible ansible]# vim block1.yml 
- hosts: test
tasks:
- name: define g group
block:
- name: install httpd
yum:
name: httpd
state: present
- name: start httpd
service:
name: httpd
state: started
[root@ansible ansible]# ansible-playbook block1.yml

  • rescue定义block任务执行失败时要执行的其他任务
  • always定义无论block任务是否成功,都要执行的任务
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@ansible ansible]# vim block2.yml 
- hosts: test
tasks:
- block:
- name: touch
file:
path: /tmp/hhhhh/test #创建失败
state: touch
rescue:
- name: touch2
file:
path: /tmp/test2.txt
state: touch
always:
- name: touch3
file:
path: /tmp/test3.txt
state: touch

[root@ansible ansible]# ansible-playbook block2.ym

loop循环

  • 很多任何在使用相同的模块,使用loop避免重复
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@ansible ansible]# vim loop.yml 
- hosts: test
tasks:
- name: mkdir
file:
path: /tmp/{{item}} #items是关键字
state: directory
loop:
- school
- legend
- life
[root@ansible ansible]# ansible-playbook loop.yml

[root@ansible ansible]# vim loop.yml
- hosts: test
tasks:
- name: create user
user:
name: "{{item.iname}}"
password: "{{item.ipass | password_hash('sha512')}}"
loop:
- {iname: 'term',ipass: '000000'}
- {iname: 'amy',ipass: '123456'}
[root@ansible ansible]# ansible-playbook loop.yml
  • 把多个变量写入文件,也可以用loop调用
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
nginx:
- gcc
- pcre-devel
- openssl-devel
php:
- php
- php-fpm
- php-mysql
mysql:
- mariadb
- mariadb-server
- mariadb-devel

- name: yum nginx
yum:
name: "{{item}}"
state: present
loop: "{{nginx}}"
- name: yum php
yum:
name: "{{item}}"
state: present
loop: "{{php}}"
- name: yum mariadb
yum:
name: "{{item}}"
state: present
loop: "{{mysql}}"

Ansible Vault

加密敏感数据

  • ansible有时需要访问一些敏感数据,如密码、key等

  • 使用ansible-vault可以加密和解密数据

    encrypt(加密)、decrypt(解密)、view(查看)

1
2
3
4
5
6
7
8
[root@ansible ansible]# echo 123456 > data.txt

[root@ansible ansible]# ansible-vault encrypt data.txt

[root@ansible ansible]# cat data.txt
# 显示乱码
[root@ansible ansible]# ansible-vault view data.txt

修改密码

  • ansible-vault rekey可以修改加密的密码
1
2
3
4
5
6
7
8
9
10
11
[root@ansible ansible]# ansible-vault encrypt data.txt 
New Vault password: 000000
Confirm New Vault password: 000000
Encryption successful

[root@ansible ansible]# ansible-vault rekey data.txt
Vault password: 000000
New Vault password: 123456
Confirm New Vault password: 123456
Rekey successful

密码文件

  • 加密、解密每次都输入密码很麻烦,可以将密码写入文件
1
2
3
4
5
6
7
8
9
10
[root@ansible ansible]# echo "hello world" > data.txt 
[root@ansible ansible]# echo 123456 > pass.txt #密码
[root@ansible ansible]# ansible-vault encrypt --vault-id=pass.txt data.txt

[root@ansible ansible]# cat data.txt

[root@ansible ansible]# ansible-vault decrypt --vault-id=pass.txt data.txt

[root@ansible ansible]# cat data.txt

Ansible Roles

Ansible Roles基础

基本概念

  • 在实际生产环境中,为了实现不同的功能,我们会编写大量的playbook文件

  • 而且,每个playbook还可能会调用其他文件(如变量文件)

  • 对于海量的、无规律的文件,管理起来很痛苦

  • Roles是管理ansible文件的一种规范(目录结构)

    Roles会按照标准的规范,自动到特定的目录和文件中读取数据

Roles规范的目录结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
roles/user.example
├── defaults
│   └── main.yml
├── files
├── handlers
│   └── main.yml
├── meta
│   └── main.yml
├── README.md
├── tasks
│   └── main.yml
├── templates
├── tests
│   ├── inventory
│   └── test.yml
└── vars
└── main.yml

  • default/main.yml:定义变量的缺省值,优先级较低
  • files目录:存储静态文件的目录
  • handlers/main.yml:定义handlers
  • meta/main.yml:写作者、版本等描述信息
  • REASME.md:整个角色(role)的描述信息
  • tasks/main.yml:定义任务的地方
  • template目录:存放动态数据文件的地方(模板文件)
  • vars/main.yml:定义变量,优先级高

Ansible Roles应用

创建Role

  • ansible-galaxy命令可以创建、管理自己的roles
1
2
3
4
5
[root@ansible ansible]# mkdir roles

[root@ansible ansible]# ansible-galaxy init roles/user.example

[root@ansible ansible]# tree roles/user.example

修改Role

  • 定义模板文件
1
2
3
4
5
[root@ansible ansible]# vim roles/user.example/templates/issue.j2 
This is the system {{ansible_hostname}}
Today's date is {{ansible_date_time.date}}
Contact to {{admin}}

  • 定义变量文件
1
2
3
[root@ansible ansible]# vim roles/user.example/vars/main.yml 
# vars file for roles/user.example
admin: yoyo@123.com
  • 修改任务文件,任务文件中不需要tasks关键词

    role的各个文件之间相互之间不需要写路径

1
2
3
4
5
6
7
8
[root@ansible ansible]# vim roles/user.example/tasks/main.yml 

# tasks file for roles/user.example
- name: delever issue file
template:
src: issue.j2
dest: /etc/issue

在Playbook中调用Role

  • 方法一:在role相同的目录下创建一个playbook调用
  • 方法二:在ansible.cfg设置roles_path=路径
1
2
3
4
5
6
7
8
9
10
[root@ansible ansible]# vim ansible.cfg
[defaults]
inventory=/root/ansible/hosts
remote_user=alice
roles_path=./roles
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False
  • 编写playbook文件,通过roles关键词调用role
1
2
3
4
5
6
[root@ansible ansible]# vim issue.yml 
- hosts: test
roles:
- user.example
# - role2 #执行加载多个role
[root@ansible ansible]# ansible-playbook issue.yml

Ansible Galaxy

1
2
3
4
5
6
7
8
9
10
11
#联网搜索roles
[root@ansible ansible]# ansible-galaxy search 'httpd'

#查看roles基本信息
[root@ansible ansible]# ansible-galaxy info acandid.httpd

#下载roles到特定的目录
[root@ansible ansible]# ansible-galaxy install acandid.httpd -p ~/ansible/roles/

#列出本地有哪些roles
[root@ansible ansible]# ansible-galaxy list -p roles
  • 下载Roles方法

    使用ansible-galaxy install 或者编写requirements.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[root@ansible ansible]# vim roles/requirements.yml 
#格式一:直接从Ansible Galaxy官网下载
- src: acandid.httpd
#格式二:从某个git服务器下载
- src: http://gitlab.com/xxx/xxx.git
scm: git
version: 56e00a54
name: nginx-acme
#格式三:下载tar包,支持http、https、file
- src: http://example.com/myrole.tar
name: myrole

[root@ansible ansible]# ansible-galaxy install -r roles/requirements.yml -p roles

-r 读取 -p 下载路径