ansible 自动化运维工具
ansible的软件可以去登录ansible官网去下载;也可以去epel源去下载。
命令 ansible saltstack
文件 ansible puppet func
运维工具的分类: 是否具有客户端代理
agent: puppet, func 带有客户端代理
agentless: ansible, fabric, saltstack 无客户端代理
ssh
运维工作:系统安装(物理机、虚拟机)--> 程序包安装、配置、服务启动 --> 批量操作 --> 程序发布(svn,git) --> 监控
ansible:
模块化,调用特定的模块,完成特定的任务;
基于Python语言实现,由Paramiko、PyYAML和Jinja2三个关键模块;
部署简单,agentless;只需要布置服务器端;客户端必须预装ssh和python
主从模式
支持自定义模块
支持Playbook: 以文件形式自动化管理
幂等性:多次执行同一个操作,结果都是一致的
特点: 高度模块化、无客户端代理、支持playbook、幂等性
工作原理: 管理端靠paramiko模块连接,发送功能模块,在被管理端运行功能模块;运行后模块会自动删除
playbook的核心元素:
tasks: 任务,即调用模块完成的某操作
variables: 变量 -------yaml语言中变量需要事先定义
templates: 模板
handlers: 触发器,由某时间触发执行的操作
roles: 角色
一、ansible如何以命令形式管理:
1)、准备工作
时间同步、实验环境->双机互信(真实只需将管理端公钥拷贝给被管理端)----->>可以靠expect编写脚本--->实现秘钥自动分发
----->>pxe装机可以实现安装后脚本-->wget 下载公钥
客户端还需:Python环境
2)、使用格式:
#man ansible
ansible <host-pattern> [options]
-m: 指定调用哪些模块
-a: 向模块传递的参数
3)、配置文件解读:
#vim /etc/ansible/ansible.cfg
[defaults]
inventory = /etc/ansible/hosts 指定主机清单文件
roles_path = /etc/ansible/roles 指定角色的根目录
timeout = 10 连接超时
remote_user = root 默认连接远程的用户
[inventory] 设定动态清单文件
[privilege_escalation] 定义切换用户身份(执行模块的身份)
#become=True
#become_method=sudo
#become_user=root
#become_ask_pass=False
[paramiko_connection] 定义远程连接
[colors] 定义显示的颜色
4)、定义主机清单文件:
#vim /etc/ansible/hosts
192.168.0.248 单独定义一个主机
[node1] 定义第一个主机组,名称为node1,成员为247
192.168.0.247
[node2]
192.168.0.248
[allnodes]
192.168.0.247
192.168.0.248
查看主机组:
#ansible 主机组名(host-pattern) --list-hosts
查看ansible都安装哪些模块:
#ansible-doc -l
获取模块参数帮助文档:
#ansible-doc 模块名
ansible命令管理:
#ansible 主机组名 -m 模块名 -a '向模块传递的参数'
常用模块:
ping(验证连通性) cron (时间同步) command shell(执行命令) user、group(用户与组管理)
copy (拷贝) lineinfile(主文件内容管理) file(文件权限管理) archive unarchive (归档与展开归档)
yum(rpm包管理) yum_repository(配置yum源文件) mount(挂载)
service(服务管理) iptables firewalld (防火墙管理)
二、ansible中Playbook编写和使用: 了解列表和字典
playbook: YAML语言(借助了python两种数据结构:列表、字典)
YAML语言的格式:
python中:
列表: 多个有序元素的集合
(1).定义列表
列表名(变量名)=['元素1', '元素2','元素3', ...]
name=['tom', 'jerry','lisi']
(2).引用列表元素:有序的 *****
靠列表索引去引用元素: 索引类似数组的下标
print name[0]
print name[1]
...
注释:索引从0开始
(3). 列表的嵌套: *****
list1=['tom','jerry','lisi']
list2=['a1','b2',list1,'c3','d4','e5']
list2第3个元素,其值又是一个列表
print list2[2]
['tom','jerry','lisi']
print list2[2][1]
jerry
字典: 多个键值对的集合
python中:
(1).定义字典:
dict1={'name':'loring', 'age': 40 , 'uid': 99}
(2).引用字典元素: 是无序的集合 *****
print dict1['name']
loring
(3).字典嵌套:
dict2={'class': 1 , 'alltype': dict1 , 'score': 80}
print dict2['alltype']
{'age': 40, 'name': 'loring', 'uid': 99}
print dict2['alltype']['uid']
99
列表中嵌套字典:
dict1={'name':'loring', 'age': 40 , 'uid': 99}
list3=['hello','welcome',dict1,'haha']
字典嵌套列表:
dict3={'passwd': 'redhat', 'username': 'root', 'test1': list1 , 'shell': 'bash'}
YAML语言中:
列表:
[tom , jerry , lisi]
yaml:
- tom
- jerry
- lisi
[a1, b2, [tom, jerry ,lisi] , c3, d4]
- a1
- b2
- tom
- jerry
- lisi
- c3
- d4
字典:
{name: loring , age:40 , uid:99}
name: loring
age: 40
uid: 99
{'class': 1 , 'alltype': dict1 , 'score': 80}
class: 1
alltype:
name: loring
age: 40
uid: 99
score: 80
列表嵌套字典:
dict1={name: loring , age:40 , uid:99}
list3=['hello','welcome',dict1,'haha']
- hello
- welcome
- name: loring
age: 40
uid: 99
- haha
字典嵌套列表:
list1=['tom', 'jerry', 'lisi']
dict3={'passwd': 'redhat', 'username': 'root', 'test1': list1 , 'shell': 'bash'}
passwd: redhat
username: root
test1:
- tom
- jerry
- lisi
shell: bash
格式:
playbook编写:
#vim a.yml (或者使用a.yaml)
---
- hosts: 主机组名 #指定管理的主机组
remote_user: 用户名 #连接远程主机的用户
tasks: #任务、指定调用的模块
- 模块1: 参数名1=值1 参数名2=值2 ...
- 模块2:
参数名1:值1
参数名2:值2
...
...
注释:在playbook文件中,最外层列表,每一个元素值,代表了一个play(主机、用户和任务)
检测:
运行playbook: ansible-playbook命令
格式:
ansible-playbook [options] playbook.yml
--syntax-check: 语法检测
-C, --check: dry-run 测试运行,但不会真正执行
编写多play剧本:对不同主机组操作
#vim multipaly.yml
---
- hosts: node2
remote_user: root
tasks:
- user:
name: jerry66
state: present
- hosts: allnodes
remote_user: root
tasks:
- user:
name: jerry77
state: present
三、ansible中变量使用:playbook中
1、变量命名规则:naming variables:the same to the variables in shell
只能包含字母、数值、下划线、;不能以字母开头;不能使用关键字
2、定义变量:
全局:Global scope: Variables set from the command line or Ansible configuration
在play中:Play scope: Variables set in the play and related structures
主机:Host scope: Variables set on host groups and individual hosts by the inventory, fact gathering,or registered tasks
变量优先级判断:1·引用变量先后顺序,后大于前;2·作用范围大小,小的优先级高
变量优先级:
主机组变量 < 主机变量 < fact变量 < in-play变量 < register变量 < globle变量
变量类别:
1、主机组变量:
定义: 在主机清单中定义
#vim /etc/ansible/hosts
[主机组:vars]
变量名=变量值
引用主机组变量: 引用变量用 {{ 变量名 }} ;括号和名之间必须有空格
---
- hosts: allnodes
remote_user: root
tasks:
- debug: #可以打印信息
msg: "{{ testvar }}"
注意1:变量引用前面,如果没有其它字符串,变量要加引号
注意2:如果多个主机组中都去定义变量,变量名尽量不要一样
2、主机变量: host 在清单文件中定义
定义:
#vim /etc/ansible/hosts
[allnodes]
192.168.0.247 testvar="from 247"
192.168.0.248 testvar="from 248"
3、fact变量: 靠setup模块实现 *****
setup 模块获取:在连接之后;默认是执行的第一个模块
只对本次剧本运行有效
---
- hosts: allnodes
remote_user: root
gather_facts: false #关闭模拟的fact变量获取
tasks:
- debug:
msg: "{{ ansible_fqdn }}"
利用setup模块,打印所有fact变量(关于node2主机组的常用变量)
#ansible node2 -m setup | more
{ "ansible_facts": {键值对(变量)} }
靠嵌套字典中的键,去引用变量值
迭代引用变量
---
- hosts: allnodes
remote_user: root
gather_facts: false
tasks:
- setup:
- debug:
msg: "{{ ansible_date_time['date'] }}"
注意: ansible_date_time['date'] 等同于 ansible_date_time.date
其中最后的date都代表前ansible_date_time值中的一个叫data的键
4、 register变量: 变量名自定义;其值来自上某个模块运行的结果
位置与对应模块对齐
对register变量运行之后的模块有效
---
- hosts: node2
remote_user: root
tasks:
- yum:
name: vsftpd
state: installed
register: haha
- debug:
msg: "{{ haha }}"
5、in play 变量:
在playbook的play中用
vars: vars_files:
变量: “值” - 文件名
例1:
---
- hosts: node2
remote_user: root
vars:
testvar1: "hello loring"
testvar2: "welcmoe tom"
tasks:
- debug:
msg: "{{ testvar1 }} and {{ testvar2 }}"
例2:
---
- hosts: node2
remote_user: root
vars_files:
- /etc/ansible/playbooks/varfiles.yml
tasks:
- debug:
msg: "{{ testvar1 }} and {{ testvar2 }}"
6、全局变量: 命令行传递
playbook中引用变量
#ansible-playbook var10.yml -e "变量名='变量值'"
#ansible-playbook var10.yml -e "@/etc/ansible/playbooks/varfiles.yml"
#ansible-playbook var10.yml -e "@变量所在文件"
vim /etc/ansible/playbooks/varfiles.yml
testvar1: "test from file1"
testvar2: "haha to file2"
7、交互式输入变量: vars_prompt
---
- hosts: node2
remote_user: root
vars_prompt:
- name: "testvar1"
prompt: "input a num"
- name: "testvar2"
prompt: "input your name"
tasks:
- debug:
msg: "{{ testvar1 }} and {{ testvar2 }}"
四、ansible中实现控制:
一)、循环
1·with_items: 类似for循环
值为列表循环
---
- hosts: node2
remote_user: root
tasks:
- yum:
name: "{{ item }}"
state: installed
with_items:
- vsftpd
- dhcp
- httpd
------------------------
值为列表嵌套字典循环
- debug:
msg: "{{ item.name }} and {{ item['pass'] }}"
with_items:
- {name: 'tom' , pass: 'redhat'}
- {name: 'jerry' , pass: 'fedora'}
2·with_nested: 类似双重for循环
---
- hosts: node2
remote_user: root
tasks:
- debug:
msg: "{{ item[0] }} and {{ item[1] }}"
with_nested:
- ['tom','jerry']
- ['redhat','fedora','suse']
二)、判断: when
例1:
#vim when1.yml
---
- hosts: node2
remote_user: root
tasks:
- debug:
msg: "{{ loringvar }}"
when: loringvar is defined
注释:当when给定的条件为真,则会运行所在的模块;如果为假,则不运行; loringvar is difined代表判断loringvar变量是否被定义
when: 变量1 in 变量2
注释:当变量1的值被包含在变量2的值中时为真;否则为假
with_items: "{{ ansible_mounts }}"
when: item.mount == "/" and item.size_available > 20199577600
注释:在when中,支持比较运算和逻辑运算
三)、忽略错误: ignore_errors: (建议忽略与之后程序无关的error)
可忽略错误继续运行playbook
---
- hosts: node2
remote_user: root
tasks:
- yum:
name: httpdfs
state: installed
ignore_errors: yes
- debug:
msg: "the test for ignore_errors"
四)、标签:tags:
可将打标签的模块动作 一个或几个标签 单独运行;但不能改变模块在playbook中的运行顺序
注意: 多个模块,可以共用一个名称的标签;将整个任务分组(过滤)
#vim tag1.yml
---
- hosts: node2
remote_user: root
tasks:
- name: 安装dhcp服务
yum:
name: dhcp
state: installed
tags: dhcptag
- name: 安装httpd服务
yum:
name: httpd
state: installed
tags: httpdtag
- name: 安装vsftpd服务
yum:
name: vsftpd
state: installed
tags: vsftpdtag
tags应用:
#ansible-playbook tag1.yml --tags='dhcptag,vsftpdtag'
五)、handlers: 触发器
触发器:当满足某个特定条件时自动触发执行哪些操作
触发器属于某个play;在play中定义触发器(每个play中只能有一个handlers)
注意:触发器要在所有tasks任务运行结束之后,去运行
handlers定义:
一般在playbook中末尾定义handlers ;且handlers与hosts平级
handlers:
- name: loringhand #触发器名
service:
name: httpd
state: restarted
handlers应用 : notify--> 通知
- name: 下载页面文件
get_url:
url: http://192.168.0.247/loringdir/test.html
dest: /var/www/virtual/index.html
mode: 0777
force: yes
notify: loringhand #通知loringhand触发器
注释:当该模块运行状态为changed时,回调用触发器
六)、block: 类似if多分支语句
- block:
rescue:
always:
一般都tasks的最后编写
运行顺序:张氏定义([block || rescue] 为真 ;运行-->之后的模块 else 不运行---> 之后的模块 {always常运行} )
注释:
先运行block下的模块,一旦运行成功,则不会运行rescue下的模块,但是always下模块会运行,而且整个状态为真
如果block下模块运行失败,则运行rescue下的模块,always下会运行,整个状态为真
如果block下模块运行失败,去运行rescue下的模块也失败,也会运行always下模块,但是整个状态为假;在下面所有模块不再运行
#vim block1.yml
---
- hosts: node2
remote_user: root
tasks:
- block:
- name: 安装httpd
yum:
name: httpd
state: installed
rescue:
- name: 安装dhcp服务
yum:
name: dhcp
state: installed
always:
- debug:
msg: "message from always"
- debug:
msg: "the end for testing..."
七)、ansible中使用jinja2模板: ---->(一般为拷贝大文件“配置文件”修改某个变量值实现不同功能)
在拷贝文件中,文件中支持变量引用
jinja2模版文件,必须与.j2结尾
注释:调用template模块,去拷贝j2文件;在拷贝时支持变量引用
例1:基本使用
#vim /tmp/haha.j2
hello from ansible
welcome to {{ ansible_fqdn }}
#vim jinja.yml
---
- hosts: node2
remote_user: root
tasks:
- template:
src: /tmp/haha.j2
dest: /var/www/virtual/c.txt
例2:jianji2中使用控制语句:
格式;for语句:
{% for 变量名 in 列表 %}
内容(可引用变量)
{% endif %}
{# 注释信息 #} ---->> (对模版的注释;不输出显示)
格式: if语句:
{% if 条件判断 %}
内容(可引用变量)
{% elseif %}
内容
{% else %}
内容
{% endif %}
#vim if.j2
{% if ansible_fqdn=="c1.loring.com" %}
TYPE master
proity 100
{% else %}
TYPE backup
proity 90
{% endif %}
device eth0
address 192.168.0.100
注释: 以上jianja2模版文件定义好后,需在playbook中被调用才有意义
vim jinja4.yml
---
- hosts: allnodes
remote_user: root
tasks:
- file:
path: /loringdir
state: directory
- template:
src: /etc/ansible/playbooks/if.j2
dest: /loringdir/if.conf
八)、ansible中使用角色(role): *****
#vim /etc/ansible/ansible.cfg
roles_path = /etc/ansible/roles 指定角色根目录
在根目录中,建立的所有子目录,都是角色目录(角色名)
角色目录中的关键文件:角色目录是自定义
defaults(目录): 在该目录中建立的main.yml中,包含了角色的默认值(变量默认值)
files(目录): 该目录中,包含了角色需要使用的静态文件,可以被copy模块所调用,调用只需使用相对路径;不包括jinja2模板
handlers(目录): 该目录中,main.yml文件,是角色需要用到的触发器
meta(目录): 该目录中的main.yml包含了源数据,无需自己给定源数据
tasks(目录***): 该目录中的main.yml文件,就是角色的任务;必须要有
templates(目录): 该目录中,保存jinja2模板
tests(目录): 测试剧本的目录,一般不用给定
vars(目录): 该目录中main.yml是定义角色的变量,可以覆盖defaults中设定的变量
九)、ansible中使用vault(文件加密):
ansible-vault
创建:
#ansible-vault create a.yml
查看:
#ansible-vault view a.yml
修改:
#ansible-vault edit a.yml
加密文件: 前提b.yml文件必须存在
#ansible-vault encrypt b.yml
解密:
#ansible-vault decrypt b.yml
修改秘钥(密码):
#ansible-vault rekey a.yml
使用文件保存密码:
#vim pass.txt 存放vault密码的文件
redhat
#ansible-vault view a.yml --vault-password-file=pass.txt
#ansible-vault encrypt vault.yml 将整个剧本加密
#ansible-playbook vault.yml --ask-vault-pass 加密剧本运行
#ansible-vault encrypt varfile.yml 对变量文件加密
#ansible-playbook vault.yml --ask-vault-pass
注释:只有剧本使用到的文件被加密,那么在运行剧本时,必须加--ask-vault-pass选项
或者:
#vim pass.txt
redhat 存放vault密码的文件
#ansible-playbook vault.yml --vault-password-file=pass.txt
为了保证数据的安全(来剧本所需的文件;如账号密码等);也可以防止其它人随意修改
十)、配置文件、普通用户管理:
一、设定主机清单文件
1)、默认清单文件修改主配置文件
#vim /etc/ansible/ansible.cfg
[defaults]
inventory = /etc/ansible/hosts ----默认清单文件(可为目录;目录下所有文件皆为清单文件)指定(文件名随意)
#vim /etc/ansible/hosts ------- 清单文件内容编辑
[node1]
192.168.0.247
[node2]
192.168.0.248
[allnodes]
192.168.0.247
192.168.0.248
手动指定主机清单文件
-i: 指定主机清单文件
#vim a.txt
[node1]
192.168.2.10
[node2]
192.168.2.20
#ansible node2 --list-hosts -i a.txt
2)、设定配置文件:
默认主配置文件: /etc/ansible/ansible.cfg
自定义:
在家目录中定义: ~/.ansible.cfg 或: $HOME/.ansible.cfg
在运行剧本的当前目录: ./ansible.cfg
优先级: 当前位置配置文件 > 家目录中配置文件 > 默认配置文件
、
二、普通用户
普通用户管理需sudo提权
所有主机节点: 设定sudo条目
#vim /etc/sudoers
loring ALL=(root) NOPASSWD: ALL ----- (不一定为ALL)
1)、命令方式管理:
$ ansible node2 -m user -a 'name=tom666 state=present' --become --become-method "sudo" --become-user "root"
--become: 在执行模块时,要切换身份
--become-user: 切换到哪个用户身份,一般是root
--become-method: 以哪种方式切换,一般为sudo
--ask-sudo-pass: 是否需要sudo密码
2)、剧本方式管理:
执行剧本模块运行sudo: 在剧本中
$vim user2.yml
---
- hosts: node2
remote_user: loring
become: yes #切换用户
become_method: sudo
become_user: root
tasks:
- name: 创建tom999
user:
name: tom999
state: present
3)、全局sudo:
$vim ~/.ansible.cfg
[defaults]
inventory = /etc/ansible/hosts
[privilege_escalation]
become=True
become_method=sudo
become_user=root
become_ask_pass=False