Ansible(官方文档,中文文档)是一款为类Unix系统开发的自由开源的配置和自动化工具。它用Python写成,类似于saltstack和Puppet,但是有一个不同和优点是我们不需要在节点中安装任何客户端。它使用SSH来和节点进行通信。Ansible基于 Python paramiko 开发,分布式,无需客户端,轻量级,配置语法使用 YMAL 及 Jinja2模板语言,更强的远程命令执行操作
ansiblle具有如下特点:
IP | System | Hostname | Role |
---|---|---|---|
172.17.2.245 | CentOS7.4 | node245.ginvip.com | master |
172.17.2.246 | CentOS7.4 | node246.ginvip.com | slaver |
172.17.2.247 | CentOS7.4 | node247.ginvip.com | slaver |
172.17.2.248 | CentOS7.4 | node248.ginvip.com | slaver |
Ansible仓库默认不在yum仓库中,因此我们需要使用下面的命令安装epel仓库。
# master端安装即可
yum install epel-release -y
yum install ansible -y
ansible --version
pip方式安装:
yum install python-pip python-devel
yum install gcc glibc-devel zibl-devel rpm-build openssl-devl
pip install --upgrade pip
pip install ansible --upgrade
配置文件:
/etc/ansible/ansible.cfg # 主配置文件,配置ansible工作特性
/etc/ansible/hosts # 主机清单
/etc/ansible/roles/ # 存放角色的目录
程序文件:
/usr/bin/ansible # 主程序,临时命令执行工具
/usr/bin/ansible-doc # 查看配置文档,模块功能查看工具
/usr/bin/ansible-galaxy # 下载/上传优秀代码或Roles模块的官网平台
/usr/bin/ansible-playbook # 定制自动化任务,编排剧本工具/usr/bin/ansible-pull远程执行命令的工具
/usr/bin/ansible-vault # 文件加密工具
/usr/bin/ansible-console # 基于Console界面与用户交互的执行工具
Ansible默认安装好后有一个配置文件/etc/ansible/ansible.cfg,该配置文件中定义了ansible的主机的默认配置部分,如默认是否需要输入密码、是否开启sudo认证、action_plugins插件的位置、hosts主机组的位置、是否开启log功能、默认端口、key文件位置等等。
[defaults]
# some basic default values...
# hostfile = /etc/ansible/hosts # 指定默认hosts配置的位置
# library_path = /usr/share/my_modules/
# remote_tmp = $HOME/.ansible/tmp
# pattern = *
# forks = 5 # 默认并发数
# poll_interval = 15
# sudo_user = root # 远程sudo用户
# ask_sudo_pass = True # 每次执行ansible命令是否询问ssh密码
# ask_pass = True # 每次执行ansible命令时是否询问sudo密码
# transport = smart
# remote_port = 22
# module_lang = C
# gathering = implicit
host_key_checking = False # 检查对应服务器的host_key,建议取消注释
log_path = /var/log/ansible.log # 需要时可以自行添加。chown -R root:root ansible.log
# 一般只修改上面两项配置,其他的无需修改
ansible-doc:显示模块帮助
ansible-doc [options] [module...]
-a:显示所有模块的文档
-l, --list:列表可用模块
-s, --snippet:显示指定模块的playbook片段
ansible-doc ping # 查看ping模块的使用说明
ansible-doc -s ping # 简洁版ping模块的使用说明
anisble命令语法: ansible [-i 主机文件] [-f 批次] [组名] [-m 模块名称] [-a 模块参数]
ansible详细参数:
-v,–verbose # 详细模式,如果命令执行成功,输出详细的结果 (-vv –vvv -vvvv)
-i PATH, -inventory=PATH # 指定 host 文件的路径,默认是在 /etc/ansible/hosts ,inventory [ˈɪnvəntri] 库存
-f NUM,-forks=NUM # NUM 是指定一个整数,默认是 5 ,指定 fork 开启同步进程的个数。
-m NAME,-module-name=NAME # 指定使用的 module 名称,默认使用 command模块
-a,MODULE_ARGS # 指定 module 模块的参数
-k,-ask-pass # 提示输入 ssh 的密码,而不是使用基于 ssh 的密钥认证
-sudo # 指定使用 sudo 获得 root 权限
-K,-ask-sudo-pass # 提示输入 sudo 密码,与 -sudo 一起使用
-u USERNAME,-user=USERNAME # 指定移动端的执行用户
-C,–check # 测试此命令执行会改变什么内容,不会真正的去执行
-b,--become # 代替旧版的sudo切换
ansible dbservers -m command -a 'ls -l /root' -u ubuntu -k -b -K # 以root身份执行
ansible all --list # 查看inventory列表
ansible webservers --list # 查看某个分组下inventory
在/etc/ansible/hosts文件中加入要管理的主机:
[root@node245 ~]# tail -1 /etc/ansible/hosts
172.17.2.24[6:8]
使用ping模块检测远程主机是否存活:
[root@node245 ~]# ansible 172.17.2.247,172.17.2.248 -m ping -k
SSH password:
172.17.2.248 | SUCCESS => {
"changed": false,
"ping": "pong"
}
# ansible只会输入一次密码,即这一个密码会自动去匹配所有的主机
# -k 使用密码登录远程主机
# -m 指定使用哪个ansible模块
# 也可以将上面的IP地址换成 all 代表检测清单中的所有主机
在/etc/ansible/hosts文件中配置远程主机密码:
[root@node245 ~]# tail -3 /etc/ansible/hosts
172.17.2.246 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=root
172.17.2.247 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=root
172.17.2.248 ansible_ssh_port=22 ansible_ssh_user=root ansible_ssh_pass=root
ansible all -m ping # 检测清单中所有主机,因为清单文件中配置了SSH登录信息,所以这里就不再提示要输入密码
在/etc/ansible/hosts文件中配置主机分组:
[root@node245 ~]# tail -10 /etc/ansible/hosts
[webservers]
172.17.2.246
172.17.2.247
[dbservers]
172.17.2.247
172.17.2.248
[appservers]
172.17.2.24[6:8]
这样在执行ansible命令时就可以指定分组:
ansible appservers -m ping -k
一般来说,使用明文密码不安全,所以增加主机无密码访问。在Ansible服务端生成密钥,并且复制公钥到节点中。
ssh-keygen -t rsa -P "" -f ~/.ssh/id_rsa
for i in {0..2}; do ssh-copy-id -i ~/.ssh/id_rsa.pub root@172.17.2.24${i}; done
测试所有主机连通性:
ansible appservers -m ping # 此时就不再需要输入密码
ansible all -m ping # 所有
ansible 172.17.2.* -m ping # 通配符
ansible webservers:dbservers -m ping # 或关系
ansible 'webservers:&dbservers' -m ping # 且关系。注意清单列表要加上引号(单双都可),因为&符在linux下表示后台执行
ansible 'webservers:!dbservers' -m ping # 非关系。在webservers但不在dbservers中。(因为有!号,所有只参使用单引号)
ansible '~(web|db)servers' -m ping # 正则表达式,~表示包含
ansible命令执行过程:
执行状态:
ansible all -m ping -vvv # 加-v或-vv或-vvv参数查看命令执行过程
command模块执行shell命令,command作为ansible的默认模块,可以运行远程权限范围内的所有shell命令
ansible webservers -m ping # 默认模块为command,可省略
ansible webservers -a 'removes=/etc/fs cat /etc/fstab'
# removes表示后面的文件不存在,则不执行后面的命令
ansible webservers -a 'creates=/etc/fs cat /etc/fstab'
# creates表示后面的文件存在,则不执行后面的命令
ansible webservers -a 'chdir=/boot cat /etc/fstab' # chdir切换到指定目录下执行后面的命令
comand模块比较简单,常见的命令都可以使用,但其命令的执行不是通过shell执行的,所以,像这些 “<”, “>”, “|”, and "&"操作都不可以,当然,也就不支持管道; 缺点:不支持管道,没法批量执行命令
使用shell模块,在远程命令通过/bin/sh来执行;所以,我们在终端输入的各种命令方式,都可以使用。
ansible webservers -m shell -a "useradd test"
ansible webservers -m shell -a "echo test|passwd --stdin test"
ansible webservers -m shell -a "source ~/.bash_profile && df -h | grep sda"
对shell模块的使用可以分成两块:
使用scripts模块可以在本地写一个脚本,在远程服务器上执行,(不需要手动上传脚本到指定服务器)
ansible all -m script -a '/root/ansible/host.sh'
实现主控端向目标主机拷贝文件,类似scp功能
ansible all -m copy -a "src=/root/ansible/selinux dest=/etc/selinux/config backup=yes"
# 如果目标存在,默认覆盖;backup=yes 覆盖前先备份
ansible webservers -m copy -a "content='test content\n' dest=/tmp/f1.txt"
# 利用内容,直接生成目标文件
Fetch files from remote nodes(不能拉到目录)
ansible all -m fetch -a "src=/var/log/messages dest=/backup"
ansible all -m shell -a "tar Jcf /root/tools.tar.xz /root/tools" # 打包文件
设置文件属性
ansible all -m file -a 'name=/root/f3 state=touch' # 创建文件
ansible all -m file -a 'name=/root/f3 state=absent' # 删除文件或目录,软链接
ansible all -m file -a 'name=/root/dir1 state=directory' # 创建目录
ansible all -m file -a 'src=/etc/fstab path=/root/fstab.link state=link' # 创建软链接
ansible all -m file -a 'path=/root/f3 owner=ginvip mode=755' # 修改文件权限
hostname模块执行后会修改/etc/hostname文件,且立即生效。但不会修改/etc/hosts文件
ansible 172.17.2.241 -m hostname -a "name=node08.ginvip.com"
远程主机crontab配置。
ansible webservers -m cron -a "minute=* weekday=1,3,4 job='/usr/bin/wall warning' name=warningcron"
ansible webservers -m cron -a "disabled=true job='/usr/bin/wall warning' name=warningcron"
# 禁用定时任务,job,name都需要指定(本质上是在定时任务文件中注释了该条命令)
ansible webservers -m cron -a "disabled=false job='/usr/bin/wall warning' name=warningcron"
# 重新启动定时任务
ansible webservers -m cron -a "job='/usr/bin/wall warning' name=warningcron state=absent"
# 删除计划任务
linux平台软件包管理。yum模块可以提供的status状态: latest ,present,installed #这3个代表安装;removed, absent #这2个是卸载
ansible webservers -m yum -a "name=httpd state=latest" # 安装
ansible webservers -m yum -a "name=httpd,httpd,memcached state=latest" # 安装多个
ansible webservers -m yum -a "name=httpd state=absent" # 卸载
获取远程文件信息
ansible -i /etc/ansible/hosts webservers -m stat -a "path=/tmp/hosts"
实现远程主机下载指定url到本地,支持sha256sum文件校验。
例如:下载epel-release-latest-7.noarch.rpm到主机清单中的/tmp/目录下
ansible -i /etc/ansible/hosts webservers -m get_url -a "url=https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm dest=/tmp/ mode=0440 force=yes"
注:url=https://xxx 的等号=前后不能有空格
扩展:查看force=yes的作用
ansible-doc -s get_url #在弹出的信息中找到force
如果force=yes,当下载文件时,如果所下的内容和原目录下的文件内容不一样,则替换原文件,如果一样,就不下载了。
如果为“否”,则仅在目标不存在时才下载文件。 一般来说,只有小型本地文件才应该为“是”。 在0.6之前,该模块表现为默认为“是”。
查看下载的文件:
ll /tmp/epel-release-latest-7.noarch.rpm
-r--r----- 1 root root 15080 8月 24 16:20 /tmp/epel-release-latest-7.noarch.rpm
测试:下载文件时,当文件不一样时,会替换原来的文件
[root@node245 ~]# cp /etc/passwd /tmp/epel-release-latest-7.noarch.rpm
[root@node246 ~]# ansible -i /etc/ansible/hosts webservers -m get_url -a "url=https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm dest=/tmp/ mode=0440 force=yes"
172.17.2.245 | SUCCESS => {
"changed": false, # 原来的文件和当前的文件一样,就没有改变。执行成功,但没有发生改变,那么显示绿色
。。。
}
172.17.2.246 | SUCCESS => {
"changed": true, # 文件名字一样,但是内容变,就会重新下载。执行成功,且发生改变,那么显示黄色
远程主机系统服务管理。
service模块常用参数:
(1)、name参数:此参数用于指定需要操作的服务名称,比如 nginx,httpd。
(2)、state参数:此参数用于指定服务的状态,比如,我们想要启动远程主机中的httpd,则可以将 state 的值设置为 started;如果想要停止远程主机中的服务,则可以将 state 的值设置为 stopped。此参数的可用值有 started、stopped、restarted(重启)、reloaded。
enabled参数:此参数用于指定是否将服务设置为开机 启动项,设置为 yes 表示将对应服务设置为开机启动,设置为 no 表示不会开机启动。
注:想使用service模块启动服务,被启动的服务,必须可以使用service 命令启动或关闭
[root@node245 ~]# ansible webservers -m service -a "name=httpd state=restarted"
远程主机sysctl配置。
例:开启路由转发功能
[root@node245 ~]# ansible -i /etc/ansible/hosts webservers -m sysctl -a "name=net.ipv4.ip_forward value=1 reload=yes"
验证:
[root@node245 ~]# cat /proc/sys/net/ipv4/ip_forward
1
远程主机用户管理
[root@node245 ~]# ansible -i /etc/ansible/hosts webservers -m user -a "name=xuegod6 state=present"
连接到https://galaxy.ansible.com下载相应的roles
[root@node245 ansible]# ansible-galaxy install geerlingguy.nginx
- downloading role 'nginx', owned by geerlingguy
- downloading role from https://github.com/geerlingguy/ansible-role-nginx/archive/2.7.0.tar.gz
- extracting geerlingguy.nginx to /root/.ansible/roles/geerlingguy.nginx
- geerlingguy.nginx (2.7.0) was installed successfully
[root@node245 ansible]# ansible-galaxy list geerlingguy.nginx
# /root/.ansible/roles
- geerlingguy.nginx, 2.7.0
删除galaxy:
ansible-galaxy remove geerlingguy.nginx # 直接删除目录也可
列出所有已安装的galaxy:
ansible-galaxy list
playbook实例:安装tomcat
[root@node245 ansible]# cat tomcat.yml
--- # 表明下面要写playbook。不写也可以
- hosts: tomcat
remote_user: root # 以什么身份在远程主机上执行命令
gather_facts: no
vars:
tomcat_version: 8.5.53
tomcat_install_dir: /usr/local
tasks:
- name: install jdk1.8
yum: name=java-1.8.0-openjdk state=present
- name: download tomcat
get_url: url=https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v{{ tomcat_version }}/bin/apache-tomcat-{{ tomcat_version }}.tar.gz dest=/tmp validate_certs=no
- name: unarchive tomcat-{{ tomcat_version }}.tar.gz
unarchive:
src: /tmp/apache-tomcat-{{ tomcat_version }}.tar.gz
dest: "{{ tomcat_install_dir }}"
copy: no
- name: start tomcat
shell: cd {{ tomcat_install_dir }} && mv apache-tomcat-{{ tomcat_version }} tomcat && cd tomcat/bin/ && nohup ./startup.sh &
执行playbook:
[root@node245 ansible]# ansible-playbook tomcat.yml
ansible-vault encrypt hello.yml # 对文件进行加密(会要求输入密码)
ansible-vault view hello.yml # 查看加密后的文件内容(真实内容)
ansible-vault edit hello.yml # 编辑
ansible-vault rekey hello.yml # 修改密码
ansible-vault decrypt hello.yml # 解密
2.0+新增,可交互执行命令,支持tab
[root@node245 ansible]# ansible-console
Welcome to the ansible console.
Type help or ? to list commands.
# 执行用户@当前操作的主机组 (当前组的主机数量)[f:并发数]
root@all (3)[f:5]$ cd webservers # 进入webservers清单
root@webservers (2)[f:5]$ forks 10 # 修改为10个并发
root@webservers (2)[f:10]$ command hostname # 调用command模块执行hostname命令
root@webservers (2)[f:10]$ cd 172.17.2.241
root@172.17.2.241 (1)[f:10]$ hostname name=node03.ginvip.com
中文文档见:http://www.ansible.com.cn/
一个完整的代码块功能需最少元素包括 name: task
一个name只能包括一个task
YAML文件扩展名通常为yml或yaml
[root@node245 ansible]# cat file.yml
---
- hosts: webservers
remote_user: root
tasks:
- name: create new file
file: name=/data/newfile state=touch
- name: create new user
user: name=test2 system=yes shell=/sbin/nologin
- name: install packge
yum: name=httpd
- name: copy html
copy: src=/var/www/html/index.html dest=/var/www/html
- name: start service
service: name=httpd state=started enabled=yes
执行playbook:
ansible-playbook -C file.yml # -C check测试运行
ansible-playbook file.yml
ansible-playbook file.yml --limit 172.17.2.240 # 限制只在后面的IP主机上运行playbook
ansible-playbook file.yml --list-tasks # 查看playbook中有几个任务
ansible-playbook file.yml --list-host # 查看playbook中的主机
如果命令或脚本的即出码不为零(即playbook中有一行代码报错了)但又不想退出而是继续执行下面的操作,可以使用如下方式替代:
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
handlers:是task列表,这些task与前述的task并没有本质上的区别,用于当关注的资源发生变化时才会采取一定的操作
notify:此action可用于在每个play的最后触发,这样可避免多次有改变发生时每次都执行指定的操作,仅在所有的变化发生完成后一次性地执行指定操作。在nofity中列出的操作称为handler,也即notify中调用handler中定义的操作
[root@node245 ansible]# cat httpd.yml
---
- hosts: webservers
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd
- name: copy conf file
copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart service # 当配置文件发生改变触发下面的handlers,重启httpd程序
- name: copy html file
copy: src=files/index.html dest=/var/www/html/
- name: start service
service: name=httpd state=started enabled=yes
handlers:
- name: restart service
service: name=httpd state=restarted
[root@node245 ansible]# cat httpd.yml
---
- hosts: webservers
remote_user: root
tasks:
- name: install httpd package
yum: name=httpd
tags: ihttpd
- name: copy conf file
copy: src=files/httpd.conf dest=/etc/httpd/conf/ backup=yes
notify: restart service
- name: copy html file
copy: src=files/index.html dest=/var/www/html/
- name: start service
service: name=httpd state=started enabled=yes
tags: shttpd # 定义tags
handlers:
- name: restart service
service: name=httpd state=restarted
定义完tags后,在执行playbook时可以指定tags执行(只会执行指定的tags的task,其它的task不会被执行)
ansible-playbook -t shttpd httpd.yml
也可以指定多个tags执行:
ansible webservers -m yum -a 'name=httpd state=absent' # 卸载
ansible webservers -m shell -a 'rpm -q httpd'
ansible-playbook -t ihttpd,shttpd httpd.yml
ansible webservers -m shell -a "ss -tnl|grep :80"
# 也可以多个动作使用相同的tags
查看tags列表:
ansible-playbook httpd.yml --list-tags
变量名:仅由字母,数字和下划线组成,且只能以字母开头
变量来源:
ansible webservers -m setup # ansible收集的远程主机信息
ansible webservers -m setup -a "filter=ansible_nodename" # 过滤,只看nodename
ansible-playbook -e varname=value
[root@node01 ansible]# cat app.yml
---
- hosts: appservers
remote_user: root
tasks:
- name: install package
yum: name={{ pkname }}
- name: start service
service: name={{ pkname }} state=started enabled=yes
yml文件中定义了变量,在命令行中对变量进行赋值
ansible-playbook -e 'pkname=vsftpd' app.yml
ansible appservers -m shell -a 'ss -tnl|grep :21'
ansible-playbook -e 'pkname1=httpd pkname2=memcached' app.yml # 多个变量赋值
ansible appservers -m shell -a 'rpm -q httpd memcached'
在playbook中定义变量且赋值变量:
[root@node01 ansible]# cat app.yml
---
- hosts: appservers
remote_user: root
vars:
- pkname1: httpd
- pkname2: vsftpd
tasks:
- name: install package
yum: name={{ pkname1 }}
- name: install package
yum: name={{ pkname2 }}
在hosts中定义变量:
[webservers]
172.17.2.240 http_port=81 # 只针对该主机有效
172.17.2.241 http_port=82
[webservers:vars] # 公共变量,对整个webservers组都有效
nodename=www
domainname=ginvip.com
[root@node245 ansible]# cat hostname.yml
- hosts: webservers
remote_user: root
tasks:
- name: set hostname
hostname: name={{ nodename }}{{ http_port }}.{{ domainname }}
Jinja2语言,使用字面量
[root@node245 ansible]# cat templates/nginx.conf.j2
worker_processes {{ ansible_processor_vcpus**2 }};
# nginx.conf.j2文件是复制nginx.conf文件而来
# ansible_processor_vcpus变量是通过执行ansible webservers -m setup|grep cpu获取
[root@node01 ansible]# cat test.yml
---
- hosts: webservers
remote_user: root
tasks:
- name: install nginx
yum: name=nginx
- name: copy template
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
notify: restart service
- name: start service
service: name=nginx state=started enabled=yes
handlers:
- name: restart service
service: name=nginx state=restarted
因为模板中使用了worker_processes {{ ansible_processor_vcpus**2 }},当playbook执行后,进程数会是远程主机cpu核数的两倍
[root@node245 ansible]# cat templates/for1.conf.j2
{% for port in ports %}
server {
listen {{ port }}
}
{% endfor %}
[root@node245 ansible]# cat for.yml
---
- hosts: webservers
remote_user: root
vars:
ports:
- 81
- 82
- 83
tasks:
- name: copy conf
template: src=for1.conf.j2 dest=/tmp/for1.conf
条件测试:如果需要根据变量,facts或此前任务的执行结果来为某task执行与否的前提时要用条件测试,通过when语句实现
.................
- name: copy template for centos7
template: src=nginx.conf.j2 dest=/etc/nginx/nginx.conf
notify: restart service
when: ansible_distribution_major_version=="7" # Centos7执行该任务
- name: copy template for centos6
template: src=nginx.conf6.j2 dest=/etc/nginx/nginx.conf
notify: restart service
when: ansible_distribution_major_version=="6" # Centos6执行该任务
...................
迭代:当有需要重复性执行的任务时,可以使用迭代机制
[root@node245 ansible]# cat testitem.yml
---
- hosts: webservers
remote_user: root
tasks:
- name: create some files
file: name=/tmp/{{ item }} state=touch
with_items:
- file1
- file2
- file3
- name: install some packages
yum: name={{ item }}
with_items:
- htop
- sl
- hping3
[root@node01 ansible]# cat user.yml
---
- hosts: webservers
remote_user: root
tasks:
- name: create some groups
group: name={{ item }}
with_items:
- g1
- g2
- g3
- name: create some users
user: name={{ item.name }} group={{ item.group }}
with_items:
- { name: 'user1', group: 'g1' }
- { name: 'user2', group: 'g2' }
- { name: 'user3', group: 'g3' }
Roles思想就是模块化,不同的文件放到不同的目录中
ansible roles目录编排:
roles目录结构(roles目录在哪都可以,官方推荐/etc/ansible/roles)
[root@node01 ansible]# tree
.
├── nginx_roles.yml
└── roles
├── mysql
├── nginx
│ ├── tasks
│ │ ├── group.yml
│ │ ├── main.yml
│ │ ├── restart.yml
│ │ ├── start.yml
│ │ ├── templ.yml
│ │ ├── user.yml
│ │ └── yum.yml
│ └── templates
│ └── nginx.conf.j2
└── redis
# tasks目录中一个任务一个yml文件
# 执行顺序在main.yml中使用include按先后顺序将文件引入
执行playbook:
ansible-playbook nginx_roles.yml