ansible基础信息
- 特性:
- 有Paramiko,PyYAML,jinja2(模版语言)三个关键模块
- 幂等性:一个任务执行1遍和执行n遍效果一样,不因重复执行带来意外情况(例如复制,不会因为第二次执行文件已存在就报错)
- 无需代理不依赖PKI(无需ssl)
- 可使用任何编程语言写模块
- YAML格式,编排任务,支持丰富的数据结构
- 较强大的多层解决方案
- 安装:
使用epel源:```yum install -y epel-release
yum -y install ansible
```
配置文件:/etc/ansible/ansible.cfg - 常用命令:
ansible-doc:- -a 显示所有模块的文档
- -l,—list 列出可用模块
- -s,—snippet 显示指定模块的playbook模块
示例:
ansible-doc -lansible-doc ping 查看指定模块帮助用法ansible-doc -s ping 查看指定模块帮助用法,以简单模式来显示
host-pattern:
- *:通配符
- 或关系符::
ansible "websrvs:dbsrvs" -m ping - 逻辑与符:
ansible "websrvs:&dbsrvs" -m ping - 逻辑非:
ansible 'websrvs:!dbsrvs' -m ping,在websrvs组,但不在dbsrvs组;此处为单引号 - 综合逻辑:
ansible 'websrvs:dbsrvs:&appsrvs:!ftpsrvs' - 正则:
ansible "websrvs:dbsrvs" -m ping ansible "~(web|db).*\.magedu\.com" -m ping
其他ansible命令
- ansible-galaxy:连接https://galaxy.ansible.com/,下载对应的roles
- 列出所有已安装的galaxy:ansible-galaxy list
- 安装:ansible-galaxy install geerlingguy.redis
- 删除:ansible-galaxy remove geerlingguy.redis
- ansible-pull:推送命令至远程,效率无限提升,对运维要求较高
- ansible-vault: 加密文件
- 加密:ansible-vault encrypt test.yml
- 解密:ansible-vault decrypt test.yml
- 查看:view
- 编辑:edit
- 更改口令:rekey
- 加密创建:create
- ansible-console: 交互执行命令,支持tab
- root@test(2)[f:10]$:执行用户@当前操作的主机组(当前组的主机数量)[f:并发数]$:
- 设置并发数,forks n
- 切换组,cd 组名
- 列出当前组主机列表:list
- 列出所有的内置命令:?或help
常用用法及模块
ansible是通过ssh来完成对远程主机的控制,不需要安装client/agents,所以需要先配置免密登录ssh-copy-id [-i identity_file] [user@]hostname或者在执行时加上 -k 参数,使用交互式输入密码;
ansible是通过主机清单(Inventory)里的主机或主机组,默认的主机清单位置:/etc/ansible/hosts,也可以手动配置环境变量指定路径:export ANSIBLE_INVENTORY=~/ANSIBLE_HOSTS_PATH,或者通过python来调用数据库中的主机组信息;
Inventory文件格式:
- inventory文件遵循INI文件风格,中括号中的字符为组名。可以将同一个主机同时归并到多个不同的组中;此外,当如若目标主机使用了非默认的SSH端口,还可以在主机名称之后使用冒号加端口号来标明:
ntp.com
IP1
[webservers]
www1.com:2222
IP2
IP3
[dbservers]
db1.com
db2.com
- 如果主机名称遵循相似的命名模式,还可以使用列表的方式标识各主机,例如:
[webservers]
www[01:50].example.com
192.168.1.[2:20]
[databases]
db-[a:f].example.com
- 主机变量: 可以在inventory中定义主机时为其添加主机变量以便于在playbook中使用。例如:
[webservers]
IP1 http_port=80 maxRequestsPerChild=808
www2.com http_port=8080 maxRequestsPerChild=909
- 组变量:
[webservers]
www1.com
www2.com
[webservers:vars]
ntp_server=ntp.com
nfs_server=nfs.com
Inventory其他的参数:
| 参数 | 功能 |
|---|---|
| ansible_ssh_host | 远程主机 |
| ansible_ssh_port | 指定远程主机ssh端口 |
| ansible_ssh_user | ssh连接远程主机的用户,默认为root |
| ansible_ssh_pass | 连接远程主机使用的密码,在文件中明文,建议使用-k |
| ansible_sudo_pass | sudo密码,建议使用-K |
| ansible_connection | 指定连接 |
| ansible_ssh_private_key_file | ssh连接使用的私钥 | |
| ansible_shell_type | 指定连接对端的shell类型,默认sh,支持cash,fish |
| ansible_python_interpreter | 指定对端使用的python编译器的路径 |
基于ad-hoc模式使用:ansible
ansible dbsrvs -m command -a 'ls /root' -u test -k -b(--become)
显示所有主机列表,也可以指定某一组主机;
ansible all --list-hosts
ansible all --list
如果文件不存在则执行:ansible all -a "removes=/etc/fstab cat /etc/fstab"
如果文件存在则不执行:ansible all -a "create=/etc/fs cat /etc/fstab"
本地一个脚本运行在远程主机上:
ansible all -m script -a 'SCRIPT_PATH'
ansible all -m script -a f1.sh
也可以指定参数:
--some-arguments 1234
- copy模块,复制文件:
ansible all -m copy -a 'src=FILE_PATH dest=FILE_PATH backup=yes mode= owner=' backup: 做备份 mode: 权限 owner:所有者
直接在客户端创建一个文件,内容自定义:ansible all -m copy -a 'content="something" dest=/data/test' fetch模块,从客户端取文件到服务端,远程为一个文件,本地为一个存在该文件的文件夹:
ansible all -m fetch -a 'scr=FILE_PATH dest=DIC_PATH'archive/unarchive,打包解包模块:```
name: Compress directory /path/to/foo/ into /path/to/foo.tgz archive: path: /path/to/foo dest: /path/to/foo.tgz
name: Create a zip archive of /path/to/foo archive: path: /path/to/foo format: zip
name: Create a tar.gz archive of a single file. archive: path: /path/to/foo/single.file dest: /path/file.tar.gz format: gz force_archive: true
name: Extract foo.tgz into /var/lib/foo unarchive: src: foo.tgz dest: /var/lib/foo
name: Unarchive a file that needs to be downloaded (added in 2.0) unarchive: src: https://example.com/example.zip dest: /usr/local/bin remote_src: yes ```
file,文件模块:
新建:ansible all -m file -a 'name=/data/f1 state=touch' 删除:ansible all -m file -a 'name=/data/f1 state=absent' 新建文件夹:ansible all -m file -a 'name=/data/f1 state=directory' 新建软链接:ansible all -m file -a 'src=/etcf/fstab dest=/data/fstab.link state=link' 删除软链接:ansible all -m file -a 'dest=/data/fstab.link state=absent'hostname,主机名模块:
ansible IP1 -m hostname -a 'name=websrvs'cron,计划任务模块:
创建任务:ansible srv -m cron -a "minute=*/5 job='/usr/bin/ntpdate 172.16.0.1 &>/dev/null' name=Syntime" 删除任务:ansible srv -m cron -a 'state=absent name=Synctime' 禁用任务:ansible srv -m cron -a 'disabled=true job='/usr/bin/ntpdate 172.16.0.1 &>/dev/null' name=Synctime'yum/apt,安装程序包模块:``` 安装:ansible all -m yum -a ‘name=httpd,memcached,… state=latest’ 卸载:ansible all -m yum -a ‘name=httpd state=absent/removed’ 显示所有已经装好的包:ansible all -m yum -a ‘list=installed’
先将本地的rpm包copy到客户端上,在yum模块的name=FILE_PATH来安装该软件,可以带参数: disable_gpg_check=yes update_cache=yes
8. service,服务管理模块:
ansible all -m service -a ‘name=vsftpd state=started/stopped/restarted/reloaded enabled=yes/no’
9. user,用户管理模块:
创建:ansible all -m user -a ‘name=nginx shell=/sbin/nologin system=yes home=/var/nginx groups=root,bin uid=80 comment(描述)=”nginx server”‘ 删除:ansible all -m user -a ‘name=nginx state-absent remove=yes(删除家目录))’
10. group,组管理模块:
创建:ansible all -m group -a ‘name=nginx system=yes gid=80’
11. setup,主机配置模块:
ansible all -m setup //获取所有的变量信息; 显示过滤变量,支持通配符:ansible all -m setup -a ‘filter=…’
12. lineinfile/replace,替换文件内容:
行替换:ansible all -m lineinfile -a “path=/etc/selinux/config regexp=’SELINUX=’ line=SELINUX=enforcing” 字符串替换:ansible all -m replace -a “path=/etc/selinux/config regexp=’enforing’ replace=disabled”
<a name="6a91f5a3"></a>
### ansible剧本(playbook)
playbook的核心元素:
- hosts: 执行的远程主机列表
- tasks: 任务集
- varniables: 内置变量或自定义变量在playbook中调用
- templates:模版,可替换模版文件中的变量并实现一些简单逻辑的文件
- handlers和notify结合使用,由特定条件处罚的操作,满足条件才执行,否则不执行
- tags: 标签,指定某条任务执行,用于选择运行playbook中的部分代码;
ansible具有幂等性,因此会自动跳过没有变化的部分,即便如此,有些代码为测试其确实没有发生变化的时间依然会非常的长;此时,如果确信其没有变化,就可以通过tags跳过这些代码片段:`ansible-playbook -t tagsname user.yml`
如果命令或脚本的退出码不为零,但是依然想继续运行,可以使用如下方式替换:
tasks:
- name: run this command and ignore the result shell: /usr/bin/somecommand || bin/true ```
或者使用ignore_errors来忽略错误信号:
tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: True
ansible-playbook常见选项:
- -C,—check
- —list-hosts: 列出运行任务的主机
- —limit: 主机列表,只针对主机列表中的执行任务
- -v:显示过程 -vv -vvv 更详细
- —list-tasks: 列出剧本中的tasks,ansible-playbook file.yml —list-tasks
回调handlers和notify:
vim httpd.yml
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd service
yum: name=httpd
- name: copy conf file
copy: src=files/httpd.conf dest=/vaer/www/html/conf/ backup=yes
notify: restart httpd service
- name: start httpd service
service: name=httpd state=started
handlers:
- name: restart httpd service
service: name=httpd state=restarted
如果触发多个notify:
notify:
- HANDLERS_NAME1
- HANDLERS_NAME1
tags,标签,给某一个任务标记标签,可以只单独执行该任务:
vim httpd.yml
- hosts: websrvs
remote_user: root
tasks:
- name: install httpd service
yum: name=httpd
tags: inshttpd
- name: copy conf file
copy: src=files/httpd.conf dest=/vaer/www/html/conf/ backup=yes
notify: restart httpd service
- name: start httpd service
service: name=httpd state=started
tags: starhttpd
handlers:
- name: restart httpd service
service: name=httpd state=restarted
ansible-playbook -t starhttpd httpd.yml
注:多个动作可以用同一个tags
模版templates:
一般在/etc/ansible创建templates文件夹,且命名.j2,可以直接使用任何地方的变量:主机清单,变量文件,剧本中的vars定义的变量等
例如:
cp /etc/nginx/nginx.conf /etc/ansible/templates/nginx.conf.j2
在nginx.conf.j2中:
workprocess auto --> worker_processes {{..cpu*2}}
在剧本中使用模版:
...
- name: copy template
template: src=FILE_NAME(可以直接写文件名,会自动寻找到/etc/ansible/templates) dest=
when:在task后添加when子句即可使用条件测试,通过判断某一变量(默认变量:系统版本=ansible_distribution_major_version)是否满足条件,是否继续执行,例如:
- name: copy template for centos7
template: src=nginx.conf7.j2 dest=/etc/nginx/nginx.conf
when: ansible_distribution_major_version == "7"
notify: restart service
with_items,迭代:
如1:
tasks:
- name: create files
file: /data/{{ item }} state=touch
with_items:
- file1
- file2
- file3
如2:
tasks:
- name: create some user
user: name={{ item.name }} group={{item.group }}
with_items:
- { name: 'user1', group: 'g1' }
- { name: 'user2', group: 'g2' }
- { name: 'user3', group: 'g3' }
for循环:
{% for .. in ..%}
{% endfor %}
if:
{% if .. %}
{% endif %}
ansible角色(role)
用于层次性、结构化地组织playbook;roles能够根据层次型结构自动装载变量文件、tasks以及handlers等;要使用roles只需要在playbook中使用include(老版本)指令即可;
复杂场景:建议使用 roles
- 变更指定主机或主机组
- 如命名不规范维护和传承成本大
- 某些功能需多个playbook,通过include即可实现
建议在/etc/ansible下创建roles:
/etc/ansible/
nginx_role.yml
...
roles:
- nginx
roles/
nginx/
tasks/
group.yml
main.yml(定义执行顺序)
- include: group.yml
- include: user.yml
...
restart.yml
start.yml
templ.yml
user.yml
yum.yml
templates/
nginx.conf.j2
在角色中添加标签:
roles:
- { role: httpd, tags: ['web','httpd'] }
- { role: nginx, tags: ['web','nginx'], when ansible_distribution_major_version == "7" }
需要单独执行某个任务,在任务中加上:
tags:
- TAG_NAM
ansible-playbook agent_role.yml --tags "TAG_NAME"
需要从某个任务开始执行:
ansible-playbook playbook.yml --start-at-task="install packages"
docker_container模块:主要是用于ansible-playbook操作docker容器的一个模块;
- docker_container: 部署docker容器;
- docker_image: 用于编译镜像;
- docker_service: 部署docker compose;
通过python动态获取Inventory
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pymysql
import json
import argparse
def execude_sql_hosts(re):
hostlist = {}
return hostlist
def execude_sql_group(re):
hostlist = {}
grouplist = {}
for i in re:
grouplist['jumper'] = {}
grouplist['jumper']['hosts'] = []
hostlist['jumper'] = []
grouplist['_meta'] = {}
grouplist['_meta']['hostvars'] = {}
for i in re:
hostlist['jumper'].append(i[0])
grouplist['jumper']['hosts'] = hostlist['jumper']
grouplist['_meta']['hostvars'][i[0]] = {'ansible_port': i[1], 'ansible_become_password': i[3]}
return grouplist
def group(data):
print json.dumps(data, indent=4)
def host(data, ip):
dict_host = {}
print json.dumps(dict_host,indent=4)
if __name__ == "__main__":
global file,con,cur
conn = pymysql.connect(host='xx', database='xx', user='xx', password='xx')
sql = 'SELECT a.ip_out,a.ssh_port,b.name,b.password from asset as a right join region as b on a.id = b.jumper_id'
cur = conn.cursor()
cur.execute(sql)
re = cur.fetchall()
host_data = execude_sql_hosts(re)
group_data = execude_sql_group(re)
parser = argparse.ArgumentParser()
parser.add_argument('--list',action='store_true',dest='list',help='get all hosts')
parser.add_argument('--host',action='store',dest='host',help='get sigle host')
args = parser.parse_args()
if args.list:
group(group_data)
if args.host:
host(host_data, args.host)
