ansible自动化运维工具,具有以下特性
1、 模块化:调用特定的模块,完成特定任务
2、有Paramiko,PyYAML,Jinja2(模板语言)三个关键模块
3、支持自定义模块
4、基于Python语言实现
5、部署简单,基于python和SSH(默认已安装),agentless
6、安全,基于OpenSSH
7、支持playbook编排任务
8、幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况
9、无需代理不依赖PKI(无需ssl)
11、可使用任何编程语言写模块
12、YAML格式,编排任务,支持丰富的数据结构
13、较强大的多层解决方案
使用
安装
[root@node1 ~]# yum -y install ansible
配置主机
[root@node1 ansible]# vim /etc/ansible/hosts
[frontend]
192.168.1.1.201
192.168.1.1.202
[backend]
192.168.1.1.203
192.168.1.1.210
如何使用ansible
1、 模块查询
[root@node1 ansible]# ansible-doc -l
2、 具体模块帮助
[root@node1 ansible]# ansible-doc -s group
- name: Add or remove groups
group:
gid: # Optional `GID' to set for the group.
name: # (required) Name of the group to manage.
state: # Whether the group should be present or not on the remote host. 创建present 删除absent
system: # If `yes', indicates that the group created is a system group.
3、 检测
[root@node1 ansible]# ansible all -m group -a "gid=3001 name=mygrp1 state=present system=no" -C
192.168.1.210 | SUCCESS => {
"changed": true
}
...
4、执行
[root@node1 ansible]# ansible all -m group -a "gid=3000 name=mygrp state=present system=no"
192.168.1.210 | SUCCESS => {
"changed": true,
"gid": 3000,
"name": "mygrp",
"state": "present",
"system": false
}
.....
4、撤销
[root@node1 ansible]# ansible all -m group -a "gid=3000 name=mygrp state=absent system=no"
192.168.1.210 | SUCCESS => {
"changed": true,
"name": "mygrp",
"state": "absent"
}
....
很多模块都是类似这种操作
user模块
使用查看
[root@node1 ansible]# ansible-doc -s user
添加(absent 删除)
[root@node1 ansible]# ansible all -m user -a 'uid=5000 name=testuser state=present groups=mygrp'
192.168.1.202 | SUCCESS => {
"changed": true,
"comment": "",
"create_home": true,
"group": 5000,
"groups": "mygrp",
"home": "/home/testuser",
"name": "testuser",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 5000
}
验证
[root@node2 ~]# id testuser
uid=5000(testuser) gid=5000(testuser) groups=5000(testuser),3000(mygrp)
copy模块
使用查看
[root@node1 ansible]# ansible-doc -s copy
复制目录
[root@node1 ~]# ansible all -m copy -a 'src=/root/aa dest=/root/ mode=600'
192.168.1.210 | SUCCESS => {
"changed": true,
"dest": "/root/",
"src": "/root/aa"
}
#src 若果没有/ 复制整个目录;如果带/,复制目录中的文件
复制文件
[root@node1 ~]# ansible all -m copy -a 'src=/root/b.exp dest=/root/bb.exp mode=600'
192.168.1.210 | SUCCESS => {
"changed": true,
"checksum": "4e838c8f13d7ca2f3dd9c46383160aded4b75bd9",
"dest": "/root/bb.exp",
"gid": 0,
"group": "root",
"md5sum": "d05c1a3a2690061ef62cc018c2226bd5",
"mode": "0600",
"owner": "root",
"size": 378,
"src": "~None/.ansible/tmp/ansible-tmp-1528591498.22-24846919673848/source",
"state": "file",
"uid": 0
}
[root@node1 ~]# ansible all -m copy -a 'content="hello world\n" dest=/root/hi.txt mode=600'
192.168.1.210 | SUCCESS => {
"changed": true,
"checksum": "22596363b3de40b06f981fb85d82312e8c0ed511",
"dest": "/root/hi.txt",
"gid": 0,
"group": "root",
"md5sum": "6f5902ac237024bdd0c176cb93063dc4",
"mode": "0600",
"owner": "root",
"size": 12,
"src": "~None/.ansible/tmp/ansible-tmp-1528591685.59-213464252719003/source",
"state": "file",
"uid": 0
}
fetch拉取
[root@node1 ~]# ansible-doc -s fetch
[root@node1 ~]# ansible 192.168.1.201 -m fetch -a 'dest=/root/ src=/root/rules.sh'
192.168.1.201 | SUCCESS => {
"changed": true,
"checksum": "68fa058075bcabe9640367e48b934482bb96f64d",
"dest": "/root/192.168.1.201/root/rules.sh",
"md5sum": "af3fbce7c4b620497adf4324f7d92afa",
"remote_checksum": "68fa058075bcabe9640367e48b934482bb96f64d",
"remote_md5sum": null
}
[root@node1 ~]# ls 192.168.1.201/root/rules.sh
command shell模块
command:不做shell解析
shell:更好用[root@node1 ~]# ansible-doc -s command [root@node1 ~]# ansible-doc -s shell
[root@node1 ~]# ansible all -m command -a 'chdir=/root ls'
192.168.1.210 | SUCCESS | rc=0 >>
aa
anaconda-ks.cfg
bb.exp
hi.txt
~None
original-ks.cfg
command不支持管道操作
[root@node1 ~]# ansible all -m command -a 'echo "zander"|passwd testuser --stdin'
192.168.1.210 | SUCCESS | rc=0 >>
zander|passwd testuser --stdin
shell可以解析shell命令
[root@node1 ~]# ansible all -m shell -a 'echo "zander"|passwd testuser --stdin '
192.168.1.210 | SUCCESS | rc=0 >>
Changing password for user testuser.
passwd: all authentication tokens updated successfully.
file模块
[root@node1 ~]# ansible-doc -s file
递归创建
[root@node1 ~]# ansible all -m file -a 'path=/var/tmp/aaa/hello.dir state=directory'
192.168.1.210 | SUCCESS => {
"changed": true,
"gid": 0,
"group": "root",
"mode": "0755",
"owner": "root",
"path": "/var/tmp/aaa/hello.dir",
"size": 6,
"state": "directory",
"uid": 0
}
创建空文件不行,file适合设置文件属性 空文件可以用copy
[root@node1 ~]# ansible all -m file -a 'path=/var/tmp/aaa/hello.txt state=file'
192.168.1.210 | FAILED! => {
"changed": false,
"msg": "file (/var/tmp/aaa/hello.txt) is absent, cannot continue",
"path": "/var/tmp/aaa/hello.txt",
"state": "absent"
}
设置软连接
[root@node1 ~]# ansible all -m file -a 'src=/root/hi.txt path=/var/tmp/aaa/hello.txt state=link'
192.168.1.210 | SUCCESS => {
"changed": true,
"dest": "/var/tmp/aaa/hello.txt",
"gid": 0,
"group": "root",
"mode": "0777",
"owner": "root",
"size": 12,
"src": "/root/hi.txt",
"state": "link",
"uid": 0
}
定时任务模块
[root@node1 ~]# ansible-doc -s cron
添加 name一定要添加,不然删除有问题(名字要唯一)
[root@node1 ~]# ansible all -m cron -a 'minute=*/3 job="/usr/sbin/update 192.168.1.200 &>/dev/null" name=updatetime state=present'
192.168.1.210 | SUCCESS => {
"changed": true,
"envs": [],
"jobs": [
"updatetime"
]
}
[root@node2 ~]# crontab -l
#Ansible: updatetime
*/3 * * * * /usr/sbin/update 192.168.1.200 &>/dev/null
删除 只看name 不要误删
[root@node1 ~]# ansible all -m cron -a 'minute=*/3 job="/usr/sbin/update 192.168.1.200 &>/dev/null" name=updatetime state=absent'
yum模块
[root@node1 ~]# ansible-doc -s yum
[root@node1 ~]# ansible all -m yum -a 'name=zsh state=present'
service模块
[root@node1 ~]# ansible-doc -s service
#`started'/`stopped'
[root@node1 ~]# ansible all -m service -a 'name=mynginx state=reloaded'
远程脚本模块
[root@node1 ~]# ansible-doc -s script
[root@node1 ~]# ansible 192.168.1.203 -m script -a 'script' 本地脚本到远端执行
setup变量模块
[root@node1 playbooks]# ansible-doc -s setup
[root@node1 playbooks]# ansible 192.168.1.201 -m setup
playbook
简单使用
[root@node1 ~]# mkdir playbooks
[root@node1 ~]# cd playbooks/
[root@node1 playbooks]# vim first.yml
- hosts: 192.168.1.201
remote_user: root
tasks:
- name: install vsftpd
yum: name=vsftpd state=latest
- name: config
copy: src=/root/playbooks/vsftpd.conf dest=/etc/vsftpd/vsftpd.conf mode=600
notify: restart vsftpd # 通知下面 handlers name=restart vsftpd的项 如果文件没有修改,不会触发,(比较过文件)
- name: start vsftpd
service: name=vsftpd state=started enabled=false
handlers:
- name: restart vsftpd #接收到通知执行
service: name=vsftpd state=restarted
- hosts: 192.168.1.202
tasks:
- name: ip show
shell: ip a
- hosts: all
tasks:
- name: list
shell: ls
语法检查
[root@node1 playbooks]# ansible-playbook first.yml --syntax-check
playbook: first.yaml
主机任务查看
[root@node1 playbooks]# ansible-playbook --list-hosts --list-tasks first.yml
试运行
[root@node1 playbooks]# ansible-playbook first.yml -C
通知触发notify handlers
- hosts: 192.168.1.201
remote_user: root
tasks:
- name: install vsftpd
yum: name=vsftpd state=latest
- name: config
copy: src=/root/playbooks/vsftpd.conf dest=/etc/vsftpd/vsftpd.conf mode=600
notify: restart vsftpd # 通知下面 handlers name=restart vsftpd的项
- name: start vsftpd
service: name=vsftpd state=started enabled=false
handlers:
- name: restart vsftpd #接收到通知执行
service: name=vsftpd state=restarted
执行指定标签tags
- hosts: 192.168.1.201
remote_user: root
tasks:
- name: install vsftpd
yum: name=vsftpd state=latest
- name: config
copy: src=/root/playbooks/vsftpd.conf dest=/etc/vsftpd/vsftpd.conf mode=600
notify: restart vsftpd
tags: config #指定标签
- name: start vsftpd
service: name=vsftpd state=started enabled=false
handlers:
- name: restart vsftpd
service: name=vsftpd state=restarted
[root@node1 playbooks]# ansible-playbook -t config first.yml #根据标签执行
变量
- hosts: websrvs
remote_user: root
vars:
- pbvar: playbook var
tasks:
- name: command line vars
copy: content={{ cmdvar }} dest=/tmp/cmd.var #来自命令行穿参数
- name: playbook var
copy: content={{ pbvar }} dest=/tmp/pb.var #来自上面的pbvar
- name: host var
copy: content={{ https_port }}{{ http_port }} dest=/tmp/host.var #来自host文件 组和host
host文件
[websrvs:vars]
http_port=8080
[websrvs]
192.168.1.201 https_port=4431 ansible_ssh_port=22 ansible_ssh_user=zander ansible_ssh_pass=zander
192.168.1.202 https_port=4432 ansible_ssh_port=22 ansible_ssh_user=zander ansible_ssh_pass=zander
[root@node1 playbooks]# ansible-playbook sencond.yml -e cmdvar='aaaaaaa'
[root@node2 ~]# cat /tmp/cmd.var
aaaaaaa[root@node2 ~]#
[root@node2 ~]# cat /tmp/pb.var
playbook var[root@node2 ~]#
[root@node2 ~]# cat /tmp/host.var
44318080[root@node2 ~]#
模板
/root/playbooks/nginx.conf.j2: 变量查看setup模块
worker_processes worker_processes {{ ansible_processor_vcpus-1 }};
#listen {{ ansible_ens34.ipv4.address }}
- hosts: websrvs
remote_user: root
vars:
tasks:
- name: command line vars
template: src=/root/playbooks/nginx.conf.j2 dest=/tmp/nginx.conf
when: ansible_distribution_major_version == "7" #加判断
[root@node1 playbooks]# ansible-playbook sencond.yml
每个节点能用对应的变量
[root@node2 ~]# cat /tmp/nginx.conf
worker_processes worker_processes 2;
#listen 192.168.1.201
[root@node3 ~]# cat /tmp/nginx.conf
worker_processes worker_processes 2;
#listen 192.168.1.202
role 简单介绍
roles 定义路径
[root@node1 playbooks]# vim /etc/ansible/ansible.cfg
#roles_path = /etc/ansible/roles
[root@node1 playbooks]# mkdir -pv /etc/ansible/roles/nginx/{files,templates,tasks,vars,handlers,meta,default}
roles/
project/
tasks/ 定义task,role的基本元素,至少应该包含一个名为 main.yml的文件;其它的文件需要在此文件中通过include进行 包含
files/ 存放由copy或script模块等调用的文件
vars/ 不常用 定义变量,至少应该包含一个名为main.yml的文件;其 它的文件需要在此文件中通过include进行包含
default/ 不常用 设定默认变量时使用此目录中的main.yml文件
templates/ template模块查找所需要模板文件的目录
handlers/ 至少应该包含一个名为main.yml的文件;其它的文 件需要在此文件中通过include进行包含
meta/ 不常用 定义当前角色的特殊设定及其依赖关系,至少应该包含一 个名为main.yml的文件,其它文件需在此文件中通过include进 行包含
[root@node1 tasks]# pwd
/etc/ansible/roles/nginx/tasks
[root@node1 tasks]# vim main.yml
- name: install nginx
yum: name=nginx state=latest
- name: install conf
template: src=vhost1.conf.j2 dest=/etc/nginx/conf.d/vhost1.conf #src 可以写相对路径 在role中
[root@node1 playbooks]# vim nginx.yml
- hosts: websrvs
remote_user: root
roles:
- nginx