https://github.com/ansible/ansible
0.简介
0.1 基本概念:
ansible: 是一个基于python开发的轻量级自动化运维管理工具,可以用来批量执行命令,安装程序,支持playbook编排。它通过ssh协议来连接主机,去中心化,相对比puppet和saltstack无需安装客户即可实现文件传输、命令执行、应用部署、配置管理、任务编排等,显得更为简单与轻量。ansible只是提供一种框架,其基于模块工作的,本身没有批量部署。
Playbooks: 是 Ansible的配置,部署,编排语言.他们可以被描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合.
如果 Ansible 模块你是工作室中的工具,那么 playbooks 就是你设置的方案计划.
在基础层面, playbooks 可以被用来管理用于部署到远程主机的配置文件.在更高的层面上,playbooks 可以依次对多层式架构上的服务器执行上线包括滚动更新在内的操作并可以将操作委托给其他主机包括在此过程中发生的与监视服务器,负载均衡服务器的交互操作在内.
0.2 核心组件:
(1)、连接插件connection plugins:负责和被监控端实现通信;
(2)、host inventory:指定操作的主机,是一个配置文件里面定义监控的主机;
(3)、各种模块核心模块、command模块、自定义模块;
(4)、借助于插件完成记录日志邮件等功能;
(5)、playbook:剧本执行多个任务时,非必需可以让节点一次性运行多个任务。
0.3 工具特性:
(1)、no agents:不需要在被管控主机上安装任何客户端;
(2)、no server:无服务器端,使用时直接运行命令即可;
(3)、modules in any languages:基于模块工作,可使用任意语言开发模块;
(4)、yaml,not code:使用yaml语言定制剧本playbook;
(5)、ssh by default:基于SSH工作;
(6)、strong multi-tier solution:可实现多级指挥。
0.4 优缺点:
优点:
轻量级,无需在客户端安装agent,更新时,只需在操作机上进行一次更新即可;
批量任务执行可以写成脚本,而且不用分发到远程就可以执行;
使用python编写,维护更简单;
使用push方式,控制节点向其他节点推方式,可先测试变更,方便控制管理。
缺点: 基于ssh,串行,故超过500台主机效率较低;
1.安装
# cnetos7安装 yum -y install --skip-broken epel-release yum install ansible # debian/ubantu安装 apt-get install software-properties-common apt-add-repository ppa:ansible/ansible apt-get update apt-get install ansible
2.配置
2.1.各主机SSH互信
例如:在A主机执行以下命令,将公钥发送到B主机
ssh-keygen -t rsa #创建公钥与私钥 ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.20.4.11 #将公钥传输给对端服务器
此时A服务器可以免密码登录B服务器
同样方式,可以做A到C主机,如果控制端为B主机,需要B反向将公钥发布到A主机,实现互信。
2.2.修改主机文件inventory:,此文件定义执行命令的主机列表
Ansible 可同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置. 默认的文件路径为 /etc/ansible/hosts:
[webservers] foo.example.com bar.example.com [dbservers] one.example.com two.example.com three.example.com:8080
主机变量:
[atlanta] host1 http_port=80 maxRequestsPerChild=808 host2 http_port=303 maxRequestsPerChild=909
组的变量:
[atlanta] host1 host2 [atlanta:vars] ntp_server=ntp.atlanta.example.com proxy=proxy.atlanta.example.com
2.3 ansible 配置文件
Ansible 配置文件/etc/ansible/ansible.cfg (一般保持默认) [defaults] #inventory = /etc/ansible/hosts # 主机列表配置文件 #library = /usr/share/my_modules/ # 库文件存放目录 #remote_tmp = $HOME/.ansible/tmp #临时py命令文件存放在远程主机目录 #local_tmp = $HOME/.ansible/tmp # 本机的临时命令执行目录 #forks = 5 # 默认并发数 #sudo_user = root # 默认sudo 用户 #ask_sudo_pass = True #每次执行ansible命令是否询问ssh密码 #ask_pass = True #remote_port = 22 #host_key_checking = False # 检查对应服务器的host_key,建议取消注释 #log_path=/var/log/ansible.log #日志文件 启用
2.4 ansible-doc: 显示模块帮助
ansible-doc [options] [module...]
-a 显示所有模块的文档
-l, --list 列出可用模块
-s, --snippet显示指定模块的playbook片段
示例:
ansible-doc –l 列出所有模块
ansible-doc ping 查看指定模块帮助用法
ansible-doc –s ping 查看指定模块帮助用法
3.ansible模块
ansible模块较多,对应可以查看相关文档,此处列出一下日常工作中常用的模块
【copy】模块
ansible agent -m copy -a "src=/root/test.sh dest=/tmp"
【file】
调用-s 参数,需要客户端能够无密码使用sudo命令;
ansible agent -m file -a "dest=/tmp/test.sh mode=755 owner=root group=root" -s
【script】
ansible agent -m script -a "/tmp/test.sh"
【shell】创建用户
ansible agent -m shell -a "/tmp/test.sh"
【group】创建组
ansible agent -m group -a "name=test1 state=present" -s
【user】
ansible agent -m user -a "name=xuel home=/home/xuel state=present" -s
【yum】
可以提供的status:absent,present,installed,removed,latest
ansible agent -m yum -a "name=httpd state=latest" -s
【server】
可以提供的status:running,started,stopped,restarted,reloaded
【cron】
ansible agent -m cron -a 'name="my job" minute=*/1 hour=* day=* month=* weekday=* job="/usr/sbin/ntpdate time1.aliyun.com"'
【get_url】
ansible agent -m get_url -a "url=http://mirrors.sohu.com/fedora-epel/6/x86_64/epel-release-6-8.noarch.rpm dest=/tmp"
【synchronize】需要安装rsync
ansible agent -m synchronize -a "src=/root/test.file dest=/tmp"
模块默认使用的为推送push,如果想使用pull功能需添加mode=pull
ansible agent -m synchronize -a "mode=pull src=/tmp/test.file dest=/root/"
【ini_file】
ansible agent -m ini_file -a "dest=/tmp/test.ini section=Mongo option=Host value=127.0.0.1"
该模块Python需要安装ConfigParser
4.Ansible之playbook的使用
4.1 核心组件
hosts #执行的远程主机列表
tasks #任务集
varniables #内置变量或自定义变量
templates #可替换模版
handlers #触发操作
4.2 命令
Usage: ansible-playbook playbook.yml
ansible-playbook test1.yml #执行剧本
ansible-vault encrypt test1.yml #加密剧本
ansible-vault decrypt test1.yml #加密剧本
ansible-vault view test1.yml #加密剧本
4.3 YAML语法
1.“---”顶行首写 2.#代码注释 3.缩进统一,不可混用空格与tab 4.缩进级别椅子 5.区分大小写 6.k/v值可以同行写也可换行写,同行使用:分割,换行需要-分割 7.一个网址的功能代码需要最少的元素包括name:task 8.一个name只能包括一个task
4.4 安装并启动mysql playbook实例
--- - hosts: agent remote_user: root tasks: - name: install mysql-server yum: name=mysql-server state=present - name: start mysql-server service: name=mysqld state=started - name: check mysql service shell: ps -ef |grep mysqld
执行一次playbook将mysql数据库安装到agent服务分组里:
4.5 playbook基础组件
Hosts:运行执行任务(task)的目标主机
remote_user:在远程主机上执行任务的用户
tasks:任务列表
handlers:任务,与tasks不同的是只有在接受到通知时才会被触发
templates:使用模板语言的文本文件,使用jinja2语法。
variables:变量,变量替换{{ variable_name }}
整个playbook是以task为中心,表明要执行的任务。hosts和remote_user表明在哪些远程主机以何种身份执行。其他组件让其能够更加灵活。
4.6 variable
变量定义在资产(inventory)中:
主机变量: 192.168.200.136 http_port=808 maxRequestsPerChild=808 192.168.200.137 http_port=8080 maxRequestsPerChild=909 主机组变量: [websers] 192.168.200.136 192.168.200.137 [websers:vars] ntp_server=ntp.exampl.com proxy=proxy.exampl.com
变量定义在playbook中
- hosts: webservers vars: http_port: 80
使用facts变量:
facts变量是由setup模块获取远程主机的信息。
用法:
ansible 192.168.200.136 -m setup
在roles中定义变量: 后面介绍
ansible-playbook 命令中传入参数
使用 -e选项传入参数 ansible-playbook 192.168.200.136 -e "httpd_port=808" httpd04.yml
变量的引用
{{ var_name }}4.7 templates
它是一个模块功能,与copy不同的是他的文本文件采用了jinga2语法,
jinga2基本语法如下,
字面量:
字符串:使用单引号或双引号
数字:整型,浮点数
列表:{item1,item2,...}
字典:{key1:value1,key2:value2,...}
布尔型:true/false
算术运算:
+,-,*,/,//,%,**
比较运算:
==,!=,>,>=,<,<=
逻辑运算:
and,or,not注意:template只能在palybook中使用。
4.8 tasks
执行的模块命令
格式: action:模块参数(此种方式只在较新的版本中出现) module:参数(已键值对的形式出现) 每一个task都有一个名称,用于标记此任务。任务示例: name: install httpd yum: name=httpd state=present 注意:shell和command没有参数,可在后面直接跟命令 shell: ss -tnl | grep :80 (1)某任务的运行状态为changed后,可通过相应的notify通知相应的handlers (2)任务可以通过tags打标签,然后通过palybook命令-t选项调用.
5.playbook调用方式
用法: ansible-playbook <filename.yml> ... [options] <filename.yml>:yaml格式的playbook文件路径,必须指明 [options]: 选项 -C, --check:并不在远程主机上执行,只是测试。 -i PATH, --inventory=PATH:资产的文件路径 --flush-cache:清楚fact缓存 --list-hosts:列出匹配的远程主机,并不执行任何动作 -t, TAGS, --tags=TAGS:运行指定的标签任务 --skip-tags:跳过指定的notify,后面详细介绍。
6.palybook示例文件解析
6.1 安装部署httpd服务-version1
资产文件
~] cat /etc/ansible/Hosts [webservers] 192.168.200.136 httpd_port=808 192.168.200.137 httpd_port=8088 [test] 192.168.200.13[6:7]
httpd配置文件
~] grep ^Listen /etc/httpd/conf/httpd.conf Listen 808
palybook文件
~] cat /root/httpd01.yml - hosts: webservers remote_user: root tasks: - name: install httpd yum: name=httpd state=present - name: install configure file copy: src=httpd.conf dest=/etc/httpd/conf/ - name: start httpd service service: name=httpd state=started
测试playbook
ansible-playbook --check httpd01.yml
运行playbook
ansible-playbook httpd01.yml
6.2 安装部署httpd服务-version2
copy命令拷贝配置文件时,无法对配置文件进行修改,不够灵活。接下来我们使用template拷贝文件,并使用主机变量设置httpd端口号
资产文件
~]# cat /etc/ansible/hosts [webservers] 192.168.200.136 httpd_port=8088 [test] 192.168.200.13[6:7]
配置文件
~]# grep ^Listen httpd.conf.j2
Listen {{ httpd_port }}palybook文件
~]# cat httpd02.yml - hosts: 192.168.200.136 remote_user: root tasks: - name: install httpd yum: name=httpd state=present - name: install configure file template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf - name: start httpd service service: name=httpd state=started
运行playbook
ansible-playbook httpd02.yml
playbook中只将文件拷贝过去了,并没有重启服务,所以我们可以看到端口监听的依然是808而不是8088。此时我们需要在配置文件修改时触发一个任务,这就是handlers的用法,重新修改playbook文件。
重新修改YAML文件
~]# cat httpd02.yml - hosts: 192.168.200.136 remote_user: root tasks: - name: install httpd yum: name=httpd state=present - name: install configure file notify: restart httpd service template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf - name: start httpd service service: name=httpd state=started handlers: - name: restart httpd servcie service: name=httpd state=restarted
notify表明此task改变时,它会触发一个事件,此事件会调用name为restart httpd service的handlers task。
再次运行playbook
ansible-playbook httpd02.yml
可以看到,此playbook执行了两个任务,一个是拷贝文件,一个是重启服务。也可以看到此时监听的端口不在是808而是8088
我们修改配置文件并重启服务,这是一个非常常见的操作。由上面我们看到,尽管我们只需执行两个task但我们把所有的任务都执行一遍,这会降低效率,我们可以使用tags来指定执行那个任务。
6.3 指定执行任务
资产文件
注意,修改了端口号
~] cat /etc/ansibele/hosts [webservers] 192.168.200.136 httpd_port=8080 [test] 192.168.200.13[6:7]
修改playbook文件
~]# cat httpd03.yml - hosts: 192.168.200.136 remote_user: root tasks: - name: install httpd yum: name=httpd state=present - name: install configure file notify: restart httpd service tags: reinstall configure file template: src=httpd.conf.j2 dest=/etc/httpd/conf/httpd.conf - name: start httpd service service: name=httpd state=started handlers: - name: restart httpd service service: name=httpd state=restarted
执行playbook
playbook只执行了拷贝文件,以及拷贝文件触发的重启服务事件。也可以看到服务开启的是8080端口。
7 ansible特性--判断和循环
在前面有一个问题没有解决就是centos6和centos7配置文件不兼容的问题,我们需要对其进行判断,不同的版本拷贝同的文件。
我们将lamp放在一台主机上时,我们需要安装多个程序包,写成一个一个的task显得效率不高,写的臃肿。我们可以使用循环来进行安装。
7.1 判断 when
tasks: - name: "shut down Debian flavored systems" command: /sbin/shutdown -t now when: ansible_facts['os_family'] == "Debian"
tasks: # 对条件进行分组 - name: "shut down CentOS 6 and Debian 7 systems" command: /sbin/shutdown -t now when: (ansible_facts['distribution'] == "CentOS" and ansible_facts['distribution_major_version'] == "6") or (ansible_facts['distribution'] == "Debian" and ansible_facts['distribution_major_version'] == "7")
tasks: - name: "shut down CentOS 6 systems" command: /sbin/shutdown -t now when: - ansible_facts['distribution'] == "CentOS" - ansible_facts['distribution_major_version'] == "6"
7.2 循环 {{ item }}
有时候我们写 playbook 的时候发现写了很多的 task 都重复引用某个模块,比如一次想同步10个文件,如果按照以前写 playbook 的思路需要写10个 task,这样写的话发现 playbook 会显得很臃肿。可以用 loops 方式去编写 playbook 减少重复使用某个模块。
7.2.1 标准 Loops:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: yum install package
yum: name={{ item }} state=latest
with_items:
- tree
- openssh-clients
- vim通过下面形式来自定义 yum 包版本
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: yum install package
yum: name={{ item.name }} state={{ item.state }}
with_items:
- {name: "tree", state: "latest"}
- {name: "openssh-clients", state: "present"}
- {name: "vim", state: "latest" }
- {name: "mysql", state: "present" }7.2.2 嵌套 Loops:
嵌套 Loops 也是 playbook 中比较常见一种循环,也主要一对多或者多对多的合并。如下,我需要在客户端创建目录 /tmp/{file1,file2,file3}/{data1,data2,date3}
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: display user info
shell: mkdir -p /tmp/{{ item[0] }}/{{ item[1] }}
with_nested:
- ['file1','file2','file3']
- ['data1','data2','data3']7.2.3 散列 Loops:
散列 loops 相对标准 loops 就是支持更丰富的数据结构,比如标准 loops 的最外层数据必须是 python 的 list 数据类型,而散列 loops 直接支持 YAML 格式的数据变量。
---
- hosts: 10.1.0.51
gather_facts: False
vars:
MYSQL_INFO:
10.1.0.51:
MY_DIR: /usr/local/mysql
MY_DATA_DIR: /usr/local/mysql/data
MY_VER: 5.6.27
MY_HOST: 10.1.0.51
MY_FILE: /etc/my.cnf
MY_USER: root
MY_PASS: pass
10.1.0.52:
MY_DIR: /usr/local/mysql
MY_DATA_DIR: /usr/local/mysql/data
MY_VER: 5.6.27
MY_HOST: 10.1.0.52
MY_FILE: /etc/my/cnf
MY_USER: root
MY_PASS: pass
tasks:
- name: display mysql info
debug: msg="HOST:{{ item.key }},MY_DIR:{{ item.value.MY_DIR }}, MY_DATA_DIR:{{ item.value.MY_DATA_DIR}}, MY_VER:{{ item.value.MY_VER }}, MY_HOST:{{ item.value.MY_HOST }}, MY_FILE:{{ item.value.MY_FILE }}, MY_USER:{{ item.value.MY_USER }}, MY_PASS:{{ item.value.MY_PASS }} ."
with_dict: MYSQL_INFO7.2.4 文件匹配 Loops
文件匹配 Loops 是编写 playbook 的时候需要针对文件进行操作中最常用的一种循环,比如需要针对一个目录下指定格式的文件进行处理,这个时候直接引用 with_fileglob 循环去匹配需要处理的文件即可。
with_fileglob 这个会匹配 /tmp 目录下所有以 yaml 结尾的文件:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: find *.yaml file
debug: msg="files ---> {{ item }} ."
with_fileglob:
- /tmp/*.yaml7.2.5 随机选择 Loops
with_random_choice 就是传入的 list 中随机选择一个值,与使用 python random 实现原理一样。以下 playbook 主要是随机取一个值进行打印:
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: display random value
debug: msg=" random value is a ---> {{ item }} ."
with_random_choice:
- "Ansible-1 : 10.1.0.51"
- "Ansible-2 : 10.1.0.52"
- "Ansible-3 : 10.1.0.53"7.2.6 条件判断 Loops
有时候执行一个 task 之后,我们需要检测这个 task 的结果是否达到了预期效果,如果没有达到该状态时,就需要退出整个 playbook 执行,这个时候就需要对某个结果一直循环检测了。
下面 playbook 匹配 register 中的值是否包含 roo1,如果有则返回,如果无,则持续5次判断,每次间隔2秒,失败退出。
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: find value root
shell: "cat /etc/passwd"
register: user_info
until: user_info.stdout.startswith("roo1")
retries: 5
delay: 27.2.7 register loops
register 是用于 task 直接互相传递数据的,一般我们会把 register 用在单一的 task 中进行变量临时存储,其实 register 还可以同时接受多个 task 结果当作变量临时存储。
下面 playbook 将多个值传递给 register,需要使用 jinja2 的 for 循环才能把所有的结果显示出来
---
- hosts: 10.1.0.51
gather_facts: False
tasks:
- name: debug loops
shell: "{{ item }}"
with_items:
- hostname
- uname
register: system_info
- name: display system_info
debug: msg="{% for i in system_info.results%} {{ i.stdout }} {% endfor %}"参考网址
http://blog.51cto.com/13589448/2068546
https://www.cnblogs.com/wangxiaoqiangs/p/5685239.html