一、管理 SECRETS
Ansible Vault
Ansible随附的 Ansible Vault 可以加密任何由Ansible使用的结构化数据文件,包括清单变量、playbook中含有的变量文件、在执行playbook时作为参数传递的变量文件,以及Ansible角色中定义的变量。
#创建加密文件,内容是yaml格式
ansible-vault create user.yml
New Vault password: `123`
Confirm New Vault password: `123`
#写入
user:zhangsan
passwd:1234
#查看
cat user.yml
$ANSIBLE_VAULT;1.1;AES256
37376665376162386437366533663632373966363338373662343361613130383037386235306633
6133373261306564396432363664363939343337653962340a346565333139336530336165343233
61373430326566313663376436646632373030633862656533663161646435353339366165613939
3965313061656232340a373737623033633137343037653361363262313863613532646439323036
37396532383934396534313162376230353366376562306438333931626663653633
#查看加密文件
ansible-vault view user.yml
Vault password: `123`
user:zhangsan
passwd:1234
#编辑加密文件
ansible-vault edit user.yml
Vault password:`123`
#解密文件
ansible-vault decrypt user.yml
Vault password: `123`
Decryption successful
#加密文件
ansible-vault encrypt user.yml
New Vault password: `123`
Confirm New Vault password: `123`
Encryption successful
案例
#将私钥更改名字,这样就不能免密登录node1了
mv ~/.ssh/id_rsa{,.bak}
#验证
ansible node1 -a id
node1 | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password).",
"unreachable": true
}
#查看主机清单
cat inventory
[nodes]
node[1:4]
# group_vars 是 Ansible 的标准目录结构,其中的文件(如 nodes.yml)会自动关联到 Inventory 中同名的主机组(nodes)
vim group_vars/nodes.yml
ansible_ssh_pass: 123
#验证
ansible node1 -a id
node1 | CHANGED | rc=0 >>
uid=0(root) gid=0(root) groups=0(root)
#加密 group_vars/nodes.yml 文件
ansible-vault encrypt group_vars/nodes.yml
New Vault password: `123`
Confirm New Vault password: `123`
Encryption successful
#验证
ansible node1 -a id
ERROR! Attempting to decrypt but no vault secrets found
#把加密密码放入一个文件
echo 123 > password
#使用 --vault-password-file=./password 命令可登录
ansible node1 -a id --vault-password-file=./password
node1 | CHANGED | rc=0 >>
uid=0(root) gid=0(root) groups=0(root)
二、管理 FACTS
FACTS 是 Ansible 在受管主机上自动检测到的变量,默认保存在内容中,只存在于本次playbook执行期间。
FACTS含有主机相关的信息,可以像play中的常规变量一样使用。
受管主机的 facts 包括:
主机名称 • 内核版本 • 网络接口 • IP地址 • 操作系统版本 • 各种环境变量
CPU数量 • 提供的或可用的内存 • 可用磁盘空间
通常,每个play在执行第一个任务之前会先自动收集FACTS。
#收集node1主机的信息
ansible node1 -m debug -a var=ansible_facts
#查看信息,重定向生成一个文件
ansible node1 -m setup > node1.facts
#可以传到windows上查看
sz node1.facts
#也可以是用less命令查看,可以通过/搜索信息
less node1.facts
关闭FACTS收集 即使关闭以后,也可以随时使用setup模块收集facts。
# Ansible 配置文件设置
[defaults]
gathering = explicit
# play 中设置
---
- name: Fact dump
hosts: node1
gather_facts: no
部分FACTS
| FACT | VARIABLE |
|---|---|
| 短主机名 | ansible_facts['hostname'] |
| 完全限定的域名 | ansible_facts['fqdn'] |
| 主要IPv4地址(基于路由) | ansible_facts['default_ipv4'] ['address'] |
| 所有网络接口的名称列表 | ansible_facts['interfaces'] |
| /dev/vdal磁盘分区的大小 | ansible_facts['devices'] ['vda'] ['partitions'] ['vda1] ['size'] |
| DNS服务器列表 | ansible_facts['dns'] ['nameservers'] |
| 当前运行的内核的版本 | ansible_facts['kernel'] |
三、使用JINJA2模板部署文件
Jinja2 模板是功能强大的工具,可用于自定义要在受管节点上部署的配置文件。 创建Jinja2 模板后,可以通过template模块部署到受管节点上, 该模块支持将控制节点中的本地文件转移到受管节点。
#创建模板文件
vim testfile
hello {{ ansible_fqdn }}
#创建使用 template 模块的 play
vim template.yaml
---
- name: show facts
hosts: node1
tasks:
- name: push file
template:
src: /home/zhangzhe/webapp/testfile
dest: /webdev
...
#运行ansible
ansible-playbook template.yaml
#在 node1 里查看 testfile 文件内容
cat testfile
hello node1.zhangzhe.cloud
Jinja2 模板语法
Jinja2 模板由多个元素组成:数据、变量和表达式。在呈现Jinja2模板时, 这些变量和表达式被替换为对应的值。模板中使用的变量可以在playbook的vars部分中指定,也可以使用受管主机FACTS。
变量和逻辑表达式置于分隔符之间:
{{ EXPR }} ,用于装载表达式,比如变量,运算表达式,比较表达式。
{% EXPR %} ,用于装载控制语句,比如if ,for等。
{# #},用于装载注释,模板文件中的注释不会包含在最终生成文件中。
for语句 Jinja2使用for语句来提供循环功能。
#创建for语句文件
vim testfile.j2
{% for user in users %}
{{ user }}
{% endfor %}
#创建使用 template 模块的 play
vim for.yaml
---
- name: test template
hosts: node1
vars:
users:
- tom
- jack
- Snoopy
- lucy
tasks:
- name: test template
template:
src: testfile.j2
dest: /webdev/testfile
...
#运行ansible
ansible-playbook for.yaml
#在 node1 里查看 testfile 文件内容
cat testfile
tom
jack
Snoopy
lucy
四、编写循环任务
Ansible支持使用 loop 关键字对一组项目迭代任务。您可以配置循环以利用列表中的各个项目、列表中各个文件的内容、生成的数字序列或更为复杂的结构来重复任务。
#示例:
vim add.yaml
---
- name: add several users
hosts: node1
gather_facts: no
tasks:
- name: add user jane
user:
name: "jane"
groups: "wheel"
state: present
- name: add user joe
user:
name: "joe"
groups: "wheel"
state: present
...
简单循环对一组项目迭代任务。loop关键字添加到任务中, 将应对其迭代任务的项目列表取为值。循环变量item保存每个迭代过程中使用的值。
#使用loop循环改写:
vim loop1.yaml
---
- name: test loop
hosts: node1
gather_facts: no
tasks:
- name: add users
user:
name: "{{ item }}"
groups: "wheel"
state: present
loop:
- jane
- joe
...
#删除
vim loop2.yaml
---
- name: test loop
hosts: node1
gather_facts: no
tasks:
- name: del users
user:
name: "{{ item }}"
groups: "wheel"
state: absent
remove: yes
loop:
- jane
- joe
...
#新建属于不同组的用户
vim loop3.yaml
---
- name: test loop
hosts: node1
gather_facts: no
tasks:
- name: add users
user:
name: "{{ item.name }}"
groups: "{{ item.groups }}"
state: present
loop:
- name: jane
groups: wheel
- name: joe
groups: root
...
五、编写条件任务
when语句
when 用于有条件地运行任务,取要测试的条件作为值。如果条件满足,则运行任务。若条件不满足,则跳过任务。
#判断node1上是否存在sdb硬盘
vim when1.yaml
---
- name: check device sdb
hosts: node1
tasks:
- name: sdb is exist
debug:
msg: sdb is exist
when: ansible_devices.sdb is defined
- name: sdb is not exist
debug:
msg: sdb is not exist
when: ansible_devices.sdb is not defined
...
#判断受管主机是否具有相应设备
vim when2.yaml
---
- name: create and use lv
hosts: node1
tasks:
- name: Create a logical volume of 4000m
lvol:
vg: research
lv: data
size: 4000
when: ansible_lvm.vgs.research is defined
- debug:
msg: Volume group does not exist
when: ansible_lvm.vgs.research is not defined
...
Ansible block 多个任务作为block子条目,block作为多个任务整体。
#所有受控主机cpu数量大于等于3时安装并运行nginx软件服务
vim deploy.yaml
---
- name: deploy web
hosts: all
vars:
package_name: nginx
service_name: nginx
min_cpu_count: 3
tasks:
- block:
- name: install {{ package_name }}
yum:
name: "{{ package_name }}"
state: present
- name: enable and start {{ service_name }}
service:
name: "{{ service_name }}"
state: started
enabled: yes
when: ansible_processor_vcpus >= min_cpu_count
blocks 还可以与 rescue和always一起使用:
block:定义主要任务。
rescue:block中任务执行失败后,就会执行rescue中定义的任务。
always:无论block和rescue中任务执行是否成功和失败,都会执行的任务。
block配置when指令,同样适用rescue和always。
#如果目录存在直接创建文件,如果目录不存在先创建目录再创建文件
vim create.yaml
---
- name: create file
hosts: node1,node2
vars:
data_path: /usr/share/nginx/html
tasks:
- block:
- name: create file index.html
copy:
content: "Hello World From Nginx"
dest: "{{ data_path }}/index.html"
rescue:
- name: create data path
file:
path: "{{ data_path }}"
state: directory
- name: create file index.html
copy:
content: "Hello World From Nginx"
dest: "{{ data_path }}/index.html"
...
部署myhosts
vim inventory
[controllers]
controller
[dev]
node1
[test]
node2
[prod]
node3
node4
vim myhosts.yaml
---
- name: push my hosts
hosts: all
tasks:
- name: push hosts
template:
src: hosts.j2
dest: /etc/myhosts
when: inventory_hostname in groups.dev
...
vim hosts.j2
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
{% for host in groups.all %}
{{ hostvars[host].ansible_default_ipv4.address }} {{hostvars[host].ansible_fqdn }} {{ hostvars[host].ansible_hostname }}
{% endfor %}
#说明:
#hostvars是magic 变量,通过该变量引用其他受管主机facts。
#[server]不能使用单引号,因为server作为变量处理,替换为all中主机。
#play 必须针对所有主机执行,收集所有主机facts,因为模版需要引用所有主机的facts。
#只部署到dev主机组,使用when语句。groups.dev是magic变量,该变量获取dev主机组中主机清单。
六、Ansible 角色管理
Ansible 角色目录位置
默认role使用以下三个目录:
~/.ansible/roles
/usr/share/ansible/roles
/etc/ansible/roles
优先级从上到下依次降低。
可以在ansible.cfg配置文件[defaults]块中通过变量roles_path定义role位置:
[defaults]
roles_path = ./roles
...
#多个路径使用冒号分隔
roles_path = /etc/ansible/roles:/home/student/web/roles
创建角色
mkdir roles
vim ansible.cfg
[defaults]
inventory = ./inventory
remote_user = zhangzhe
roles_path=./roles
#创建角色
ansible-galaxy role init nginx
mv nginx roles
#查找角色
ansible-galaxy role list
# /home/zhangzhe/webapp/roles
- nginx, (unknown version)
#进入角色查看tree
cd roles/nginx
sudo yum install -y tree
tree
#编辑要执行的任务
vim tasks/main.yaml
---
- name: install package
yum:
name: "{{ package_name }}"
state: present
- name: enable and start {{ service_name }}
service:
name: "{{ service_name }}"
enabled: yes
state: started
- name: push test page
copy:
src: test.html
dest: "{{ web_root_path }}/test.html"
- name: push index page
template:
src: index.j2
dest: "{{ web_root_path }}/index.html"
...
#编辑模板文件
vim templates/index.j2
Welcome to {{ ansible_fqdn }}
#编辑默认变量
vim defaults/main.yaml
package_name: nginx
service_name: nginx
web_root_path: /usr/share/nginx/html
#准备测试页面
echo test page > files/test.html
#改手册
echo ask for zhangzhe > README.md
#准备说明
vim meta/main.yml
---
galaxy_info:
author: zhangzhe
description: zhangzhe web
company: zhangzhe world
license: license (GPLv2, CC-BY, etc)
min_ansible_version: 2.4
platforms:
- name: Fedora
versions:
- all
- 25
- name: SomePlatform
versions:
- all
galaxy_tags: [apache,web]
dependencies: []
#退回webapp目录
cd ../../
#创建play使用nginx角色
vim use_role.yaml
---
- name: deploy nginx with nginx role
hosts: node1
roles:
- nginx
...
#执行paly
ansible-playbook use_role.yaml
#验证
curl http://node1/
Welcome to node1.zhangzhe.cloud
curl http://node1/test.html
test page