一、体系结构

ansible是一款基于python开发,揉合了众多自动化运维工具功能的轻量级自动化运维工具,目前实现了除系统安装以外的批量系统配置、批量任务执行及批量程序部署等功能。

  • Inventory:主机库,定义可控制的主机
  • Modules:基于模块化设计,通过模块来实现批量部署
  • playbook:剧本,使用YAML编写的声明性的配置文件
  • plugins: 插件,完成日志记录、邮件等功能

二、 特点

  • 高度模块化,借助模块完成各种任务
  • agentless,无需在被控制端安装agent
  • 默认基于ssh协议向被控制端发送操作指令
    • 基于密钥认证
    • 在inventory文件中指定账号和密码
  • 批量任务执行可写成剧本playbook
  • 幂等性:不会重复执行相同操作

三、简单使用

3.1 ssh免密钥登录

# ssh-keygen -t rsa -P ''
# ssh-copy-id -i /root/.ssh/id_rsa.pub 10.249.6.64
# ssh-copy-id -i /root/.ssh/id_rsa.pub 10.48.156.8

3.2 常用命令

ansible-doc

Options:
-l, --list List available modules //列出所有模块
-s, --snippet Show playbook snippet for specified module(s) //查看指定模块用法
// 更多信息请参考manual手册

ansible

ansible [-f forks][-m module_name] [-a args] [options]

Options:
-a MODULE_ARGS, --args=MODULE_ARGS
module arguments // 传递模块参数
-f FORKS, --forks=FORKS // 指定并发数
specify number of parallel processes to use
(default=5)
-i INVENTORY, --inventory-file=INVENTORY
specify inventory host file
(default=/etc/ansible/hosts)
-m MODULE_NAME, --module-name=MODULE_NAME
module name to execute (default=command)
//更多信息请参考manual手册

ansible-playbook

ansible-playbook … [options]

四、模块

command

命令模块: ansible默认模块,用于在远程执行命令,command模块并不支持shell变量和管道等,若想使用shell来执行,应使用shell模块。

# ansible-doc -l | grep ^command
command Executes a command on a remote node
# ansible 10.249.6.64 -m command -a "date"
10.249.6.64 | success | rc=0 >>
Wed May 11 21:21:35 CST 2016

ping

ping模块:测试指定主机是否能连接

# ansible-doc -l | grep -w ^ping
ping Try to connect to host and return `pong' on success.
# ansible 10.249.6.64 -m ping
10.249.6.64 | success >> {
"changed": false,
"ping": "pong"
}

cron

计划任务模块 :管理计划任务

# ansible-doc -l | grep ^cron
cron Manage cron.d and crontab entries.
# ansible-doc -s cron
- name: Manage cron.d and crontab entries.
action: cron
backup # If set, create a backup of the crontab before it is modified. The location of the backup is returned in the `backup' variable by this module.
cron_file # If specified, uses this file in cron.d instead of an individual user's crontab.
day # Day of the month the job should run ( 1-31, *, */2, etc )
hour # Hour when the job should run ( 0-23, *, */2, etc )
job # The command to execute. Required if state=present.
minute # Minute when the job should run ( 0-59, *, */2, etc )
month # Month of the year the job should run ( 1-12, *, */2, etc )
name # Description of a crontab entry.
reboot # If the job should be run at reboot. This option is deprecated. Users should use special_time.
special_time # Special time specification nickname.
state # Whether to ensure the job is present or absent.
user # The specific user who's crontab should be modified.
weekday # Day of the week that the job should run ( 0-7 for Sunday - Saturday, *, etc )
# ansible 10.249.6.64 -m cron -a 'name="sync time" minute="*/10" \
job="/usr/sbin/ntpdate 0.centos.pool.ntp.org && hwclock -w" '
10.249.6.64 | success >> {
"changed": true,
"jobs": [
"sync time"
]
}

user

用户模块:管理用户账户

# ansible-doc -l | grep ^user
user Manage user accounts
# ansible 10.249.6.64 -m user -a 'name=work shell=/bin/bash home=/home/www' //添加用户
10.249.6.64 | success >> {
"changed": true,
"comment": "",
"createhome": true,
"group": 500,
"home": "/home/www",
"name": "work",
"shell": "/bin/bash",
"state": "present",
"system": false,
"uid": 500
}
# ansible 10.249.6.64 -m user -a 'name=work state=absent' // 删除用户
10.249.6.64 | success >> {
"changed": true,
"force": false,
"name": "work",
"remove": false,
"state": "absent"
}

copy

copy模块:文件复制

# ansible-doc -l | grep ^copy
copy Copies files to remote locations.
# ansible-doc -s copy
- name: Copies files to remote locations.
action: copy
backup # Create a backup file including the timestamp information so you can get the original file back if you somehow clobbered it incorrectly.
content # When used instead of 'src', sets the contents of a file directly to the specified value.
dest= # Remote absolute path where the file should be copied to. If src is a directory, this must be a directory too.
directory_mode # When doing a recursive copy set the mode for the directories. If this is not set we will default the system defaults.
force # the default is `yes', which will replace the remote file when contents are different than the source. If `no', the file will only be transferred if the destination does not exist.
src # Local path to a file to copy to the remote server; can be absolute or relative. If path is a directory, it is copied recursively. In this case, if path ends with "/", only inside contents of that directory are copied to destination. Otherwise, if it does not end with "/", the directory itself with all contents is copied. This behavior is similar to Rsync.
validate # The validation command to run before copying into place. The path to the file to validate is passed in via '%s' which must be present as in the visudo example below. The command is passed securely so shell features like expansion and pipes won't work.
# ansible 10.249.6.64 -m copy -a "src=/root/test.txt dest=/tmp/"
10.249.6.64 | success >> {
"changed": true,
"dest": "/tmp/test.txt",
"gid": 0,
"group": "root",
"md5sum": "d41d8cd98f00b204e9800998ecf8427e",
"mode": "0644",
"owner": "root",
"size": 0,
"src": "/root/.ansible/tmp/ansible-tmp-1463662606.99-26627840524349/source",
"state": "file",
"uid": 0
}
# ansible 10.249.6.64 -m copy -a "content='hello world' dest=/tmp/test.txt"
# ansible 10.249.6.64 -m copy -a 'src=/root/test.txt dest=/tmp/test.txt owner=evans group=evans mode=600 backup=yes'

file

file模块:文件模块,设置文件属性

# ansible-doc -l | grep -w ^file
file Sets attributes of files
# ansible 10.249.6.64 -m file -a "src=/tmp/test.txt path=/tmp/test.link state=link"
# ansible 10.249.6.64 -m file -a "owner=evans group=evans mode=600 path=/tmp/test.txt"

service

service模块: 服务模块,管理系统服务

# ansible-doc -l | grep ^service
service Manage services.
# ansible-doc -s service
- name: Manage services.
action: service
arguments # Additional arguments provided on the command line
enabled # Whether the service should start on boot. *At least one of state and enabled are required.*
name= # Name of the service.
pattern # If the service does not respond to the status command, name a substring to look for as would be found in the output of the `ps' command as a stand-in for a status result. If the string is found, the service will be assumed to be running.
runlevel # For OpenRC init scripts (ex: Gentoo) only. The runlevel that this service belongs to.
sleep # If the service is being `restarted' then sleep this many seconds between the stop and start command. This helps to workaround badly behaving init scripts that exit immediately after signaling a process to stop.
state # `started'/`stopped' are idempotent actions that will not run commands unless necessary. `restarted' will always bounce the service. `reloaded' will always reload. *At least one of state and enabled are required.*
# ansible 10.249.6.64 -m service -a "name=mysqld state=restarted enabled=true" //重启mysql服务并设置开机自启动

shell

shell模块:远程执行命令

# ansible 10.249.6.64 -m shell -a 'date'
10.249.6.64 | success | rc=0 >>
Thu May 19 21:20:51 CST 2016

script

script模块:脚本模块,远程主机运行脚本

# ansible-doc -l | grep ^script
script Runs a local script on a remote node after transferring it..
# ansible 10.249.6.64 -m script -a '/root/test.sh' //在远程主机上运行脚本,并没有拷贝到指定目录
10.249.6.64 | success >> {
"changed": true,
"rc": 0,
"stderr": "",
"stdout": ""
}

yum、apt

yum模块和apt模块:包管理模块

yum Manages packages with the `yum' package manager
apt Manages apt-packages
# ansible 10.249.6.64 -m yum -a "name=tree state=present"
# ansible 10.249.6.43 -m apt -a "name=tree state=present"
# ansible 10.249.6.43 -m apt -a "name=tree state=absent"

state
Whether to install (present',latest’), or remove (`absent’) a package.

setup

setup模块:收集主机信息,playbook运行时,会自动调用setup模块收集远程主机的相关信息(称为facts,如操作系统版本、ip地址、cpu数量等),这些信息保存于变量中,可在playbook中引用

# ansible-doc -l | grep setup
setup Gathers facts about remote hosts
# ansible-doc -s setup
- name: Gathers facts about remote hosts
action: setup
fact_path # path used for local ansible facts (*.fact) - files in this dir will be run (if executable) and their results be added to ansible_local facts if a file is not executable it is read. File/results format can be json or ini-format
filter # if supplied, only return facts that match this shell-style (fnmatch) wildcard.
# ansible 10.249.6.43 -m setup
# ansible 10.249.6.43 -m setup -a 'filter=ansible_eth0' //过滤信息
# ansible 10.249.6.64 -m setup --tree /tmp/test.txt //将收集的信息输出到本地文件

更多模块信息请查看:官方文档

五、playbook

5.1 YAML

YAML是一种可读性高的用来表达资料序列的语言,其语法和其他高阶语言类似,并且可以简单表达清单、散列表、标量等数据结构。

所有的yaml文件都以”—“开头表示开始一个document,所有的列表元素以”-“开头,键值对用”:”,后面必须有空格。YAML文件扩展名通常为.yaml或.yml

5.2 playbook简介

playbook是ansible管理配置、部署应用和编排的文件,可用来描述在远程主机上执行的策略或一组任务。

一个playbook文件由一个或多个play组成,每个play定义了在一个或多个远程主机上执行的一系列的task,其中每个task一般就是调用一个ansible的模块。

playbook使用YAML语言编写,文件名以.yaml或.yml结尾。此外playbook和模板文件(template)还可使用jinja2语法语法实现高级功能。

5.2.1 playbook的基本组成

  • targets:指定要执行playbook的远程主机组
  • variables:定义playbook运行时需要使用的变量
  • tasks:要执行的任务
  • handlers:处理器,在某些条件下被触发的操作

简单playbook示例:

# cat nginx.yml
---
- hosts: 10.249.6.43
user: root
vars:
remote_conffile_path: /etc/nginx/sites-enabled/mirror.conf
tasks:
- name: install nginx
apt: name=nginx state=latest
when: ansible_distribution == 'Ubuntu'
- name: configration file
tags: conf
copy: src=/root/mirror.conf dest={{remote_conffile_path}}
notify: restart nginx
- name: start nginx
service: name=nginx enabled=yes state=started
handlers:
- name: restart nginx
service: name=nginx state=restarted
# ansible-playbook nginx.yml // 执行playbook
  • hosts、user

hosts用于指定要执行指定任务的主机,其可以是一个或多个由逗号分隔主机组;user则用于指定远程主机上的执行任务的用户,还能使用sudo

  • task list、action

task list中的各任务按次序逐个在hosts中指定的所有主机上执行,即在所有主机上完成第一个任务后再开始第二个。如果中途发生错误,所有已执行任务都将回滚,因此,在更正playbook后重新执行一次即可。

task的目的是使用指定的参数执行模块,而在模块参数中可以使用变量。模块执行是幂等的,这意味着多次执行是安全的,因为其结果均一致。

每个task都应该有其name,用于playbook的执行结果输出,建议其内容尽可能清晰地描述任务执行步骤。如果未提供name,则action的结果将用于输出。

定义task的可以使用“action: module options”或“module: options”的格式,推荐使用后者以实现向后兼容例如:

tasks:
- name: make sure apache is running
service: name=httpd state=running

在众多模块中,只有command和shell模块仅需要给定一个列表而无需使用“key=value”格式,例如:

tasks:
- name: disable selinux
command: /sbin/setenforce 0

shell模块执行多条命令

---
- name: update zabbix agent conf
shell: |
...

如果模块执行返回值不为零,即表示执行失败,任务会立即中止,后续任务不再执行。可以使用ignore_errors来忽略错误信息确保后续任务的执行。

tasks:
- name: run this command and ignore the result
shell: /usr/bin/somecommand
ignore_errors: yes
  • handlers

当关注的资源发生变化时触发一定的操作。handler是task列表,这些task与前述的task并没有本质上的不同。

“notify”这个action可用于在每个play的最后被触发,这样可以避免多次有改变发生时每次都执行指定的操作,取而代之,仅在所有的变化发生完成后一次性地执行指定操作。在notify中列出的操作称为handler,也即notify中调用handler中定义的操作。

- name: template configuration file
template: src=/root/template.conf dest=/etc/template.conf
notify:
- restart memcached
- restart apache
handlers:
- name: restart memcached
service: name=memcached state=restarted
- name: restart apache
service: name=apache state=restarted
  • vars

变量名仅能由字母、数字和下划线组成,且只能以字母开头

  • when

条件判断:如果需要根据变量、facts或此前任务的执行结果来做为某task执行与否的前提,这时就要用到条件判断。

when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
when: ansible_distribution == 'CentOS' or ansible_distribution == 'RedHat' and ansible_distribution_version|int >=6

忽略此前某语句的错误并基于其结果(failed或者sucess)运行后面指定的语句:

---
- hosts: 10.249.6.64
user: root
tasks:
- name: false test
command: /bin/false
register: result
ignore_errors: yes
- name: when false to do
command: touch /tmp/1.txt
when: result | failed
- name: when success to do
command: touch /tmp/2.txt
when: result | success
- name: when skip to do
command: touch /tmp/3.txt
when: result | skipped
  • item

item 迭代:当有需要重复性执行的任务时,可以使用迭代机制。其使用格式为将需要迭代的内容定义为item变量引用,并通过with_items语句来指明迭代的元素列表即可

- name: install base software
apt: pkg={{ item }} state=present force=yes
with_items:
- gcc
- g++
- mysql-client-5.5
- libmcrypt-dev
- libmysqlclient-dev
- libgmp10
- vim
- openssh-client
- ethtool
when: ansible_distribution == 'Debian' or ansible_distribution == 'Ubuntu'
  • tag

tag标签:让用户选择运行playbook中的某个或某些任务。虽然ansible具有幂等性,会跳过没有变化的部分,有些代码为测试其确实没有发生变化,也会耗费很长时间。我们将playbook中的指定任务打上标签,在运行playbook时指定标签名称,这样就不用运行全部代码了。

---
- hosts: 10.249.6.43
user: root
vars:
remote_conffile_path: /etc/nginx/sites-enabled/mirror.conf
tasks:
- name: install nginx
apt: name=nginx state=latest
when: ansible_distribution == 'Ubuntu'
- name: configration file
tags: conf
copy: src=/root/mirror.conf dest={{remote_conffile_path}}
notify: restart nginx
- name: start nginx
service: name=nginx enabled=yes state=started
handlers:
- name: restart nginx
service: name=nginx state=restarted
# ansible-playbook nginx.yml -t conf // 只执行tags部分

六、roles

roles 用于层次性、结构化地组织playbook。

roles能够根据层次型结构自动装载变量文件、tasks以及handlers等。roles就是通过分别将变量、文件、任务、模块及处理器放置于单独的目录中,并可以便捷地include它们的一种机制。角色一般用于基于主机构建服务的场景中,但也可以是用于构建守护进程等场景中。

# ls
deploy_hosts deploy.yml roles run.sh
# cat deploy_hosts //主机或主机组列表
[web]
10.126.83.30
10.126.93.83
[db]
10.126.87.150
10.126.92.89
# cat deploy.yml // 总的playbook 调用roles
---
- name: init for os
hosts: web
user: root
gather_facts: True
roles:
- init
- raid
- name: init for os
hosts: db
user: root
gather_facts: True
roles:
- init
- raid
- db_init
# cat run.sh // 运行playbook脚本
#!/bin/bash
/usr/bin/ansible-playbook -i ./deploy_hosts deploy.yml
# ls roles/
db_init init raid

在每个角色命名的目录中分别创建files、handlers、meta、tasks、templates和vars目录,用不到的目录可以创建为空目录,也可以不创建。

role内各目录中可用的文件:

  • tasks目录:至少应该包含一个名为main.yml的文件,其定义了此角色的任务列表;此文件可以使用include包含其它的位于此目录中的task文件

  • files目录:存放由copy或script等模块调用的静态文件

  • templates目录:template模块会自动在此目录中寻找Jinja2模板文件

  • handlers目录:此目录中应当包含一个main.yml文件,用于定义此角色用到的各handler;此文件可以使用include包含其它的位于此目录中的handler文件

  • vars目录:至少有一个main.yml文件,用于定义此角色用到的变量

  • meta目录:至少有一个main.yml文件,用于定义此角色的特殊设定及其依赖关系;ansible 1.3及其以后的版本才支持

  • default目录:为当前角色设定默认变量时使用此目录;应当包含一个main.yml文件

参考文档

轻量级自动化运维工具ansible

ansible doc