Welcome to ansible-cn’s documentation!

Contents:

总体介绍

首先,我们要学一些Ansible的安装和一些基本概念,然后我们会开始研究一些真正有意思的东西 – playbook,配置管理,部署以及语法编排.我们将会学习如何使用/usr/bin/ansible执行ad-hoc并行命令,我们还会学习ansoble的核心有什么样的模块可供使用.当然以后你也可以写你自己的模块,我们会在后期讲到.

Installation

从Github获取Ansible

如果你有一个github账户,可以跟进Ansible在Github的项目: Github project 我们在这里保持对bugs和feature ideas的跟踪.

需要安装些什么

Ansible默认通过 SSH 协议管理机器.

安装Ansible之后,不需要启动或运行一个后台进程,或是添加一个数据库.只要在一台电脑(可以是一台笔记本)上安装好,就可以通过这台电脑管理一组远程的机器.在远程被管理的机器上,不需要安装运行任何软件,因此升级Ansible版本不会有太多问题.

选择哪一个版本?

因为Ansible可以很简单的从源码运行,且不必在远程被管理机器上安装任何软件,很多Ansible用户会跟进使用开发版本.

Ansible一般每两个月出一个发行版本.小bugs一般在下一个发行版本中修复,并在稳定分支中做backports.大bugs会在必要时出一个维护版本,虽然这不是很频繁.

若你希望使用Ansible的最新版本,并且你使用的操作系统是 Red Hat Enterprise Linux (TM), CentOS, Fedora, Debian, Ubuntu,我们建议使用系统的软件包管理器.

另有一种选择是通过”pip”工具安装,”pip”是一个安装和管理Python包的工具.

若你希望跟进开发版本,想使用和测试最新的功能特性,我们会分享如何从源码运行Ansible的方法.从源码运行程序不需要进行软件安装.

对管理主机的要求

目前,只要机器上安装了 Python 2.6 (windows系统不可以做控制主机),都可以运行Ansible.

主机的系统可以是 Red Hat, Debian, CentOS, OS X, BSD的各种版本,等等.

对托管节点的要求

On the managed nodes, you only need Python 2.4 or later, but if you are running less than Python 2.5 on the remotes, you will also need:

托管节点上需要安装 Python 2.4 及以上的版本.但如果版本低于 Python 2.5 ,则需要额外安装一个模块:

  • python-simplejson

Note

没安装python-simplejson,也可以使用Ansible的”raw”模块和script模块,因此从技术上讲,你可以通过Ansible的”raw”模块安装python-simplejson,之后就可以使用Ansible的所有功能了.

Note

如果托管节点上开启了SElinux,你需要安装libselinux-python,这样才可使用Ansible中与copy/file/template相关的函数.你可以通过Ansible的yum模块在需要的托管节点上安装libselinux-python.

Note

Python 3 与 Python 2 是稍有不同的语言,而大多数Python程序还不能在 Python 3 中正确运行.而一些Linux发行版(Gentoo, Arch)没有默认安装 Python 2.X 解释器.在这些系统上,你需要安装一个 Python 2.X 解释器,并在 inventory (详见 Inventory文件) 中设置 ‘ansible_python_interpreter’ 变量指向你的 2.X Python.你可以使用 ‘raw’ 模块在托管节点上远程安装Python 2.X.

Red Hat Enterprise Linux, CentOS, Fedora, and Ubuntu 等发行版都默认安装了 2.X 的解释器,包括几乎所有的Unix系统也是如此.

安装管理主机

从源码运行

从项目的checkout中可以很容易运行Ansible,Ansible的运行不要求root权限,也不依赖于其他软件,不要求运行后台进程,也不需要设置数据库.因此我们社区的许多用户一直使用Ansible的开发版本,这样可以利用最新的功能特性,也方便对项目做贡献.因为不需要安装任何东西,跟进Ansible的开发版相对于其他开源项目要容易很多.

从源码安装的步骤

$ git clone git://github.com/ansible/ansible.git --recursive
$ cd ./ansible
$ source ./hacking/env-setup

如果没有安装pip, 请先安装对应于你的Python版本的pip:

$ sudo easy_install pip

以下的Python模块也需要安装:

$ sudo pip install paramiko PyYAML Jinja2 httplib2

注意,当更新ansible版本时,不只要更新git的源码树,也要更新git中指向Ansible自身模块的 “submodules” (不是同一种模块)

$ git pull --rebase
$ git submodule update --init --recursive

一旦运行env-setup脚本,就意味着Ansible从源码中运行起来了.默认的inventory文件是 /etc/ansible/hosts.inventory文件也可以另行指定 (详见 Inventory文件)

.. code-block:: bash
$ echo “127.0.0.1” > ~/ansible_hosts $ export ANSIBLE_HOSTS=~/ansible_hosts

你可以在手册的后续章节阅读更多关于 inventory 文件的使用,现在让我们测试一条ping命令:

$ ansible all -m ping --ask-pass

你也可以使用命令 “sudo make install”

通过Yum安装最新发布版本

通过Yum安装RPMs适用于 EPEL 6, 7, 以及仍在支持中的Fedora发行版.

托管节点的操作系统版本可以是更早的版本(如 EL5), 但必须安装 Python 2.4 或更高版本的Python.

Fedora 用户可直接安装Ansible, 但RHEL或CentOS用户,需要 配置 EPEL

# install the epel-release RPM if needed on CentOS, RHEL, or Scientific Linux
$ sudo yum install ansible

你也可以自己创建RPM软件包.在Ansible项目的checkout的根目录下,或是在一个tarball中,使用 make rpm 命令创建RPM软件包. 然后可分发这个软件包或是使用它来安装Ansible.在创建之前,先确定你已安装了 rpm-build, make, and python2-devel .

$ git clone git://github.com/ansible/ansible.git
$ cd ./ansible
$ make rpm
$ sudo rpm -Uvh ~/rpmbuild/ansible-*.noarch.rpm
通过Apt (Ubuntu)安装最新发布版本

Ubuntu 编译版可在PPA中获得: ` <https://launchpad.net/~ansible/+archive/ansible>`_.

配置PPA及安装ansible,执行如下命令:

$ sudo apt-get install software-properties-common
$ sudo apt-add-repository ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install ansible

Note

在早期Ubuntu发行版中, “software-properties-common” 名为 “python-software-properties”.

也可从源码checkout中创建 Debian/Ubuntu 软件包,执行:

$ make deb

你或许也想从源码中运行最新发行版本,可看前面的说明.

通过 Portage (Gentoo)安装最新发布版本
$ emerge -av app-admin/ansible

要安装最新版本,你或许需要...

$ echo 'app-admin/ansible' >> /etc/portage/package.accept_keywords

Note

若在Gentoo托管节点中,Python 3 默认作为 Python slot(这也是默认设置),则你必须在你的 group 或 inventory 变量中设置 ansible_python_interpreter = /usr/bin/python2

通过 pkg (FreeBSD)安装最新发布版本
$ sudo pkg install ansible

你或许想从ports中安装:

$ sudo make -C /usr/ports/sysutils/ansible install
通过 Homebrew (Mac OSX)安装最新发布版本

在Mac中安装,确定你已安装 Homebrew:

$ brew update
$ brew install ansible
通过 Pip 安装最新发布版本

Ansible可通过 “pip” 安装(安装和管理Python包的工具),若你还没有安装 pip,可执行如下命令安装:

$ sudo easy_install pip

然后安装Ansible:

$ sudo pip install ansible

如果你是在 OS X Mavericks 上安装,编译器可能或告警或报错,可通过如下设置避免这种情况:

$ sudo CFLAGS=-Qunused-arguments CPPFLAGS=-Qunused-arguments pip install ansible

使用 virtualenv 的读者可通过 virtualenv 安装 Ansible, 然而我们建议不用这样做,直接在全局安装 Ansible.不要使用 easy_install 直接安装 ansible.

发行版的Tarball

不想通过git checkout 创建Ansible的软件包?在这里可获取Tarball Ansible downloads

各种版本的Ansible在这里做了版本标注 git repository

See also

Introduction To Ad-Hoc Commands
Examples of basic commands
Playbooks
Learning ansible’s configuration management language
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

新手上路

前言

现在你已经阅读了 Installation 安装指南并安装了Ansible.是时候通过一些命令开始深入了解Ansible了.

我们最先展示的并非那强大的集配置,部署,自动化于一身的playbook. Playbooks 相关内容将在另一章节中讲述.

本章节讲述如何进行初始化.一旦你有了这些概念,请去阅读 Introduction To Ad-Hoc Commands 以获取更多细节,然后你就能去深入playbook并探索它最有趣的部分.

远程连接概述


在我们开始前要先理解Ansible是如何通过SSH与远程服务器连接是很重要的.

Ansible 1.3及之后的版本默认会在本地的 OpenSSH可用时会尝试用其进行远程通讯.这会启用ControlPersist(一个性能特性),Kerberos,和在~/.ssh/config中的配置选项如 Jump Host setup.然而,当你使用Linux企业版6作为主控机(红帽企业版及其衍生版如CentOS),其OpenSSH版本可能过于老旧无法支持ControlPersist. 在这些操作系统中,Ansible将会退回并采用 paramiko (由Python实现的高质量OpenSSH库). 如果你希望能够使用像是Kerberized SSH之类的特性,烦请考虑使用Fedora, OS X, 或 Ubuntu 作为你的主控机直到相关平台上有更新版本的OpenSSH可供使用,或者启用Ansible的“accelerated mode”.参见 Accelerated Mode.

在Ansible 1.2 及之前的版本,默认将会使用 paramiko. 本地OpenSSH必须通过-c ssh 或者 在配置文件中设定.

你偶尔会遇到不支持SFTP的设备.虽然这很少见,但你会有概率中奖.你可以通过在配置文件(Ansible的配置文件)中切换至 SCP模式来与之链接.

说起远程设备,Ansible会默认假定你使用 SSH Key(我们推荐这种)但是密码也一样可以.通过在需要的地方添加 –ask-pass选项 来启用密码验证.如果使用了sudo 特性,当sudo需要密码时,也同样适当的提供了–ask-sudo-pass选项.

也许这是常识,但也值得分享:任何管理系统受益于被管理的机器在主控机附近运行.如果在云中运行,可以考虑在使用云中的一台机器来运行Ansible.

作为一个进阶话题,Ansible不止支持SSH来远程连接.连接方式是插件化的而且还有许多本地化管理的选项诸如管理 chroot, lxc, 和 jail containers.一个叫做‘ansible-pull’的模式能够反转主控关系并使远程系统通过定期从中央git目录检出 并 拉取 配置指令来实现背景连接通信.

你的第一条命令

现在你已经安装了Ansible,是时候从一些基本知识开始了. 编辑(或创建)/etc/ansible/hosts 并在其中加入一个或多个远程系统.你的public SSH key必须在这些系统的``authorized_keys``中:

192.168.1.50
aserver.example.org
bserver.example.org

这里有个节点设置文件(inventory file)将会在 Inventory文件 中得到深入说明. 我们假定你使用SSH Key来授权.为了避免在建立SSH连接时,重复输入密码你可以这么 做:

$ ssh-agent bash
$ ssh-add ~/.ssh/id_rsa

(根据你的建立方式,你也许希望使用Ansible的 --private-key 选项,通过指定pem文件来代替SSH Key来授权) 现在ping 你的所有节点:

$ ansible all -m ping

Ansible会像SSH那样试图用你的当前用户名来连接你的远程机器.要覆写远程用户名,只需使用’-u’参数. 如果你想访问 sudo模式,这里也有标识(flags)来实现:

# as bruce
$ ansible all -m ping -u bruce
# as bruce, sudoing to root
$ ansible all -m ping -u bruce --sudo
# as bruce, sudoing to batman
$ ansible all -m ping -u bruce --sudo --sudo-user batman

(如果你碰巧想要使用其他sudo的实现方式,你可以通过修改Ansible的配置文件来实现.也可以通过传递标识给sudo(如-H)来设置.) 现在对你的所有节点运行一个命令:

$ ansible all -a "/bin/echo hello"

恭喜你!你刚刚通过Ansible连接了你的所有节点.很快你就会阅读更多的关于现实案例 Introduction To Ad-Hoc Commands 并探索可以通过不同的模块做什么以及研究Ansible的playbook语言

Playbooks .Ansible不只是能运行命令,它同样也拥有强大的配置管理和部署特性.虽然还有更多内容等待你的探索,但你基础设施已经能完全工作了!

公钥认证

Ansible1.2.1及其之后的版本都会默认启用公钥认证.

如果有个主机重新安装并在“known_hosts”中有了不同的key,这会提示一个错误信息直到被纠正为止.在使用Ansible时,你可能不想遇到这样的情况:如果有个主机没有在“known_hosts”中被初始化将会导致在交互使用Ansible或定时执行Ansible时对key信息的确认提示.

如果你想禁用此项行为并明白其含义,你能够通过编辑 /etc/ansible/ansible.cfg or ~/.ansible.cfg来实现:

[defaults]
host_key_checking = False

或者你也可以通过设置环境变量来实现:

$ export ANSIBLE_HOST_KEY_CHECKING=False

同样注意在paramiko 模式中 公钥认证 相当的慢.因此,当使用这项特性时,切换至’SSH’是推荐做法.

Ansible将会对远程系统模块参数记录在远程的syslog中,除非一个任务或者play被标记了“no_log: True”属性,稍后解释. 在主控机上启用基本的日志功能参见 Ansible的配置文件 文档 并 在配置文件中设置’log_path’.企业用户可能也对 Ansible Tower 感兴趣.

塔提供了非常实用数据库日志.它使一次次向下钻取并查看基于主机,项目,和特定的结果集成为可能———— 同时提供了图形和 RESTful API.

See also

Inventory文件
More information about inventory
Introduction To Ad-Hoc Commands
Examples of basic commands
Playbooks
Learning Ansible’s configuration management language
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

Inventory文件

Ansible 可同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置. 默认的文件路径为 /etc/ansible/hosts

除默认文件外,你还可以同时使用多个 inventory 文件(后面会讲到),也可以从动态源,或云上拉取 inventory 配置信息.详见 动态 Inventory.

主机与组

/etc/ansible/hosts 文件的格式与windows的ini配置文件类似:

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

方括号[]中是组名,用于对系统进行分类,便于对不同系统进行个别的管理.

一个系统可以属于不同的组,比如一台服务器可以同时属于 webserver组 和 dbserver组.这时属于两个组的变量都可以为这台主机所用,至于变量的优先级关系将于以后的章节中讨论.

如果有主机的SSH端口不是标准的22端口,可在主机名之后加上端口号,用冒号分隔.SSH 配置文件中列出的端口号不会在 paramiko 连接中使用,会在 openssh 连接中使用.

端口号不是默认设置时,可明确的表示为:

badwolf.example.com:5309

假设你有一些静态IP地址,希望设置一些别名,但不是在系统的 host 文件中设置,又或者你是通过隧道在连接,那么可以设置如下:

jumper ansible_ssh_port=5555 ansible_ssh_host=192.168.1.50

在这个例子中,通过 “jumper” 别名,会连接 192.168.1.50:5555.记住,这是通过 inventory 文件的特性功能设置的变量. 一般而言,这不是设置变量(描述你的系统策略的变量)的最好方式.后面会说到这个问题.

一组相似的 hostname , 可简写如下:

[webservers]
www[01:50].example.com

数字的简写模式中,01:50 也可写为 1:50,意义相同.你还可以定义字母范围的简写模式:

[databases]
db-[a:f].example.com

对于每一个 host,你还可以选择连接类型和连接用户名:

[targets]

localhost              ansible_connection=local
other1.example.com     ansible_connection=ssh        ansible_ssh_user=mpdehaan
other2.example.com     ansible_connection=ssh        ansible_ssh_user=mdehaan

所有以上讨论的对于 inventory 文件的设置是一种速记法,后面我们会讨论如何将这些设置保存为 ‘host_vars’ 目录中的独立的文件.

主机变量

前面已经提到过,分配变量给主机很容易做到,这些变量定义后可在 playbooks 中使用:

[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

把一个组作为另一个组的子成员

可以把一个组作为另一个组的子成员,以及分配变量给整个组使用. 这些变量可以给 /usr/bin/ansible-playbook 使用,但不能给 /usr/bin/ansible 使用:

[atlanta]
host1
host2

[raleigh]
host2
host3

[southeast:children]
atlanta
raleigh

[southeast:vars]
some_server=foo.southeast.example.com
halon_system_timeout=30
self_destruct_countdown=60
escape_pods=2

[usa:children]
southeast
northeast
southwest
northwest

如果你需要存储一个列表或hash值,或者更喜欢把 host 和 group 的变量分开配置,请看下一节的说明.

分文件定义 Host 和 Group 变量

在 inventory 主文件中保存所有的变量并不是最佳的方式.还可以保存在独立的文件中,这些独立文件与 inventory 文件保持关联. 不同于 inventory 文件(INI 格式),这些独立文件的格式为 YAML.详见 YAML 语法 .

假设 inventory 文件的路径为:

/etc/ansible/hosts

假设有一个主机名为 ‘foosball’, 主机同时属于两个组,一个是 ‘raleigh’, 另一个是 ‘webservers’. 那么以下配置文件(YAML 格式)中的变量可以为 ‘foosball’ 主机所用.依次为 ‘raleigh’ 的组变量,’webservers’ 的组变量,’foosball’ 的主机变量:

/etc/ansible/group_vars/raleigh
/etc/ansible/group_vars/webservers
/etc/ansible/host_vars/foosball

举例来说,假设你有一些主机,属于不同的数据中心,并依次进行划分.每一个数据中心使用一些不同的服务器.比如 ntp 服务器, database 服务器等等. 那么 ‘raleigh’ 这个组的组变量定义在文件 ‘/etc/ansible/group_vars/raleigh’ 之中,可能类似这样:

---
ntp_server: acme.example.org
database_server: storage.example.org

这些定义变量的文件不是一定要存在,因为这是可选的特性.

还有更进一步的运用,你可以为一个主机,或一个组,创建一个目录,目录名就是主机名或组名.目录中的可以创建多个文件, 文件中的变量都会被读取为主机或组的变量.如下 ‘raleigh’ 组对应于 /etc/ansible/group_vars/raleigh/ 目录,其下有两个文件 db_settings 和 cluster_settings, 其中分别设置不同的变量:

/etc/ansible/group_vars/raleigh/db_settings
/etc/ansible/group_vars/raleigh/cluster_settings

‘raleigh’ 组下的所有主机,都可以使用 ‘raleigh’ 组的变量.当变量变得太多时,分文件定义变量更方便我们进行管理和组织. 还有一个方式也可参考,详见 Ansible Vault 关于组变量的部分. 注意,分文件定义变量的方式只适用于 Ansible 1.4 及以上版本.

Tip: Ansible 1.2 及以上的版本中,group_vars/ 和 host_vars/ 目录可放在 inventory 目录下,或是 playbook 目录下. 如果两个目录下都存在,那么 playbook 目录下的配置会覆盖 inventory 目录的配置.

Tip: 把你的 inventory 文件 和 变量 放入 git repo 中,以便跟踪他们的更新,这是一种非常推荐的方式.

Inventory 参数的说明

如同前面提到的,通过设置下面的参数,可以控制 ansible 与远程主机的交互方式,其中一些我们已经讲到过:

ansible_ssh_host
      将要连接的远程主机名.与你想要设定的主机的别名不同的话,可通过此变量设置.

ansible_ssh_port
      ssh端口号.如果不是默认的端口号,通过此变量设置.

ansible_ssh_user
      默认的 ssh 用户名

ansible_ssh_pass
      ssh 密码(这种方式并不安全,我们强烈建议使用 --ask-pass 或 SSH 密钥)

ansible_sudo_pass
      sudo 密码(这种方式并不安全,我们强烈建议使用 --ask-sudo-pass)

ansible_sudo_exe (new in version 1.8)
      sudo 命令路径(适用于1.8及以上版本)

ansible_connection
      与主机的连接类型.比如:local, ssh 或者 paramiko. Ansible 1.2 以前默认使用 paramiko.1.2 以后默认使用 'smart','smart' 方式会根据是否支持 ControlPersist, 来判断'ssh' 方式是否可行.

ansible_ssh_private_key_file
      ssh 使用的私钥文件.适用于有多个密钥,而你不想使用 SSH 代理的情况.

ansible_shell_type
      目标系统的shell类型.默认情况下,命令的执行使用 'sh' 语法,可设置为 'csh' 或 'fish'.

ansible_python_interpreter
      目标主机的 python 路径.适用于的情况: 系统中有多个 Python, 或者命令路径不是"/usr/bin/python",比如  \*BSD, 或者 /usr/bin/python
      不是 2.X 版本的 Python.我们不使用 "/usr/bin/env" 机制,因为这要求远程用户的路径设置正确,且要求 "python" 可执行程序名不可为 python以外的名字(实际有可能名为python26).

      与 ansible_python_interpreter 的工作方式相同,可设定如 ruby 或 perl 的路径....

一个主机文件的例子:

some_host         ansible_ssh_port=2222     ansible_ssh_user=manager
aws_host          ansible_ssh_private_key_file=/home/example/.ssh/aws.pem
freebsd_host      ansible_python_interpreter=/usr/local/bin/python
ruby_module_host  ansible_ruby_interpreter=/usr/bin/ruby.1.9.3

See also

动态 Inventory
Pulling inventory from dynamic sources, such as cloud providers
Introduction To Ad-Hoc Commands
Examples of basic commands
Playbooks
Learning ansible’s configuration management language
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

动态 Inventory

使用配置管理系统经常有一种需求,可能要在其他的软件系统中保存自己的 inventory 配置信息.

Ansible 本身通过基于文本的方式来记录 inventory 配置信息,这在前面已介绍过(详见 Inventory文件 ).

除此之外,Ansible 也支持用其他方式保存配置信息.

在其他软件系统保存配置信息的例子有:

1, 从云端拉取 inventory
2, LDAP(Lightweight Directory Access Protocol,轻量级目录访问协议)
3, `Cobbler <http://cobbler.github.com>`_
4, 或者是一份昂贵的企业版的 CMDB(配置管理数据库) 软件.

对于这些需求,Ansible 可通过一个外部 inventory 系统来支持.在 ansible 的 “/plugins” 插件目录下已经含有一些选项 – 包括 EC2/Eucalyptus, Rackspace Cloud,and OpenStack,我们稍后会详细介绍它们.

Ansible Ansible Tower 提供了一个数据库来存储 inventory 配置信息, 这个数据库可以通过 web 访问,或通过 REST 访问. Tower 与所有你使用的 Ansible 动态 inventory 源保持同步,并提供了一个图形化的 inventory 编辑器. 有了这个数据库,便可以很容易的关联过去的事件历史,可以看到在上一次 playbook 运行时,哪里出现了运行失败的情况.

关于如何编写你自己的动态 inventory 源,请参见 开发动态的Inventory数据源.

Cobbler 外部 Inventory 脚本

当管理的物理机器到达了一定数量的时,很多使用 Ansible 的用户可能同时也会使用到 Cobbler . (注: Cobbler 最初由 Michael DeHaan 编写,现在项目主导人是 James Cammarata, 他目前在 Ansible 公司工作).

Cobbler 主要用于操作系统的 kickoff 安装,以及管理 DHCP 和 DNS,除此之外,它有一个通用层,可为多种配置管理系统(甚至是同时的)提供数据. 所以 Cobbler 也被一些管理员称为是轻量级的 CMDB.

如何将 Ansible 的 inventory 与 Cobbler 联系起来呢?方法是: 将脚本 script 拷贝到 /etc/ansible,通过 chmod +x 赋予可执行权限.

在使用 Ansible 之前,先启动 cobblerd 进程.

现在使用 Ansible 要加上 -i 选项 ( 例如:-i /etc/ansible/cobbler.py).cobbler.py这个脚本使用 Cobbler 的 XMLRPC API 与 Cobbler 通信.

执行脚本 /etc/ansible/cobbler.py ,应该能看到一些 JSON 格式的数据输出(也许还没有具体的内容).

在 cobbler 中,假设有一个如下的场景:

cobbler profile add --name=webserver --distro=CentOS6-x86_64
cobbler profile edit --name=webserver --mgmt-classes="webserver" --ksmeta="a=2 b=3"
cobbler system edit --name=foo --dns-name="foo.example.com" --mgmt-classes="atlanta" --ksmeta="c=4"
cobbler system edit --name=bar --dns-name="bar.example.com" --mgmt-classes="atlanta" --ksmeta="c=5"

‘foo.example.com’ 是一个域名,Ansible 可以通过这个域名寻址找到对应的主机foo,对其进行操作.也可以通过组名 ‘webserver’ 或者 ‘atlanta’ 寻址找到这个主机,只要这个主机是属于这两个组的.直接使用 foo 是不行的.例如执行命令 “ansible foo” ,无法找到该主机,但使用 “ansible ‘foo*’” 却可以,因为域名 ‘foo.example.com’ 以foo开头.

这个脚本不仅提供主机和组的信息.如果运行了 ‘setup’ 模块(只要使用 playbooks,’setup’ 模块会自动运行),变量 a, b, c 可按照以下模板自动填充:

# file: /srv/motd.j2
Welcome, I am templated with a value of a={{ a }}, b={{ b }}, and c={{ c }}

模板的使用如下:

ansible webserver -m setup
ansible webserver -m template -a "src=/tmp/motd.j2 dest=/etc/motd"

Note

组名 ‘webserver’ 是 cobbler 中定义的.你仍然可以在 Ansible 的配置文件中定义变量. 但要注意,变量名相同时,外部 inventory 脚本中定义的变量会覆盖 Ansible 中的变量.

执行上面命令后,主机 foo 的/etc/motd文件被写入如下的内容:

Welcome, I am templated with a value of a=2, b=3, and c=4

主机 ‘bar’ (bar.example.com)的 /etc/motd 中写入如下内容:

Welcome, I am templated with a value of a=2, b=3, and c=5

你也可以通过下面这个命令测试变量的替换:

ansible webserver -m shell -a "echo {{ a }}"

也就是说,你可以在参数或命令操作中使用变量的替换.

AWS EC2 外部 inventory 脚本

使用 AWC EC2时,维护一份 inventory 文件有时不是最好的方法.因为主机的数量有可能发生变动,或者主机是由外部的应用管理的,或者使用了 AWS autoscaling.这时,使用 EC2 external inventory 脚本是更好的选择.

脚本的使用方式有两种,最简单的是直接使用 Ansible 的命令行选项 -i ,指定脚本的路径(脚本要有可执行权限):

ansible -i ec2.py -u ubuntu us-east-1d -m ping

第二种方式,把脚本拷贝为 /etc/ansible/hosts ,并赋予可执行权限.还需把 ec2.ini 文件拷贝到 /etc/ansible/ec2.ini,然后运行 ansible.

要成功的调用 API 访问 AWS,需要配置 Boto (Boto 是 AWS 的 Python 接口).可用的方法有多种,请参见: methods .

最简单的方法是定义两个环境变量:

export AWS_ACCESS_KEY_ID='AK123'
export AWS_SECRET_ACCESS_KEY='abc123'

如何知道配置是否正确,执行脚本来测试:

cd plugins/inventory
./ec2.py --list

你可以看到以 JSON 格式表示的覆盖所有 regions 的 inventory 信息.

因为每一个 region 需要自己的 API 调用,如果你仅使用了所有 regions 中的一个子集,可以编辑 ec2.ini ,使之仅显示你所感兴趣的那些 regions. 在配置文件 ec2.ini 中,包含了其他配置选项,包括缓存控制和目的地址变量.

inventory 文件的核心部分,是一些名字到目的地址的映射.默认的 ec2.ini 设置适用于在 EC2 之外运行 Ansible(比如一台笔记本电脑),但这不是最有效的方式.

在 EC2 内部运行 Ansible 时,内部的 DNS 名和 IP 地址比公共 DNS 名更容易理解.你可以在 ec2.ini 文件中修改 destination_variable 变量, 改为一个实例的私有 DNS 名.对于在私有子网的 VPC 上运行 Ansible ,这种设置很重要,使得我们可以使用内部IP地址之外的方式访问到一个VPC.在 ec2.ini 文件中, vpc_destination_variable 可以命名为任意一个 boto.ec2.instance 变量.

EC2 外部 inventory 提供了一种从多个组到实例的映射:

全局 实例都属于 ec2 这个组.

实例ID
例如: i-00112233 i-a1b1c1d1
Region
属于一个 AWS region 的所有实例构成的一个组. 例如: us-east-1 us-west-2
可用性区域
所有属于 availability zone 的实例构成一个组. 例如: us-east-1a us-east-1b
安全组

实例可属于一个或多个安全组.每一个组的前缀都是 security_group_ ,符号(-) 已被转换为(_). with all characters except alphanumerics (这句没明白)

例如: security_group_default security_group_webservers security_group_Pete_s_Fancy_Group

标签
每一个实例可有多个不同的 key/value 键值对,这些键值对被称为标签.标签名可以随意定义,最常见的标签是 ‘Name’.每一个键值对是这个实例自己的组. 特殊字符已转换为下划线,格式为 tag_KEY_VALUE 例如: tag_Name_Web tag_Name_redis-master-001 tag_aws_cloudformation_logical-id_WebServerGroup

使用 Ansible 与指定的服务器进行交互时,EC2 inventory 脚本被再次调用(调用时加上了命令行选项 --host HOST ),这个调用会在索引缓存中进行查找,获取实例 ID,然后调用 API 访问 AWS,获取指定实例的所有信息.这些信息被转换为 playbooks 中的变量,可以进行访问.每一个变量的前缀为 ec2_,下面是一些变量的示例:

  • ec2_architecture
  • ec2_description
  • ec2_dns_name
  • ec2_id
  • ec2_image_id
  • ec2_instance_type
  • ec2_ip_address
  • ec2_kernel
  • ec2_key_name
  • ec2_launch_time
  • ec2_monitored
  • ec2_ownerId
  • ec2_placement
  • ec2_platform
  • ec2_previous_state
  • ec2_private_dns_name
  • ec2_private_ip_address
  • ec2_public_dns_name
  • ec2_ramdisk
  • ec2_region
  • ec2_root_device_name
  • ec2_root_device_type
  • ec2_security_group_ids
  • ec2_security_group_names
  • ec2_spot_instance_request_id
  • ec2_state
  • ec2_state_code
  • ec2_state_reason
  • ec2_status
  • ec2_subnet_id
  • ec2_tag_Name
  • ec2_tenancy
  • ec2_virtualization_type
  • ec2_vpc_id

其中 ec2_security_group_idsec2_security_group_names 变量的值为所有安全组的列表,使用逗号分隔.每一个 EC2 标签是一个格式为 ec2_tag_KEY 的变量.

要查看一个实例的完整的可用变量的列表,执行脚本:

cd plugins/inventory
./ec2.py --host ec2-12-12-12-12.compute-1.amazonaws.com

注意,AWS inventory 脚本会将结果进行缓存,以避免重复的 API 调用,这个缓存的设置可在 ec2.ini 文件中配置.要显式地清空缓存,你可以加上 --refresh-cache 选项,执行脚本如下:

# ./ec2.py --refresh-cache

其它 inventory 脚本

除了 Cobbler 和 EC2 之外,还有以下的系统可以使用 inventory 脚本:

BSD Jails
DigitalOcean
Google Compute Engine
Linode
OpenShift
OpenStack Nova
Red Hat's SpaceWalk
Vagrant (not to be confused with the provisioner in vagrant, which is preferred)
Zabbix

关于这些系统还没有专门的章节讲述如何操作,但步骤与上面所讲述的 AWS 一样,具体可看看Ansible checkout 的 “plugins/” 目录.

如果你开发了一个通用的 inventory 脚本,请提交一个 pull request,我们可能会把它放入项目中.

使用多个 inventory 源

如果 -i 选项后给出的地址是一个目录 (or as so configured in ansible.cfg),Ansible 可以同一时间使用多个 inventory 源.这样在同一个 ansible 运行操作中,可混合的使用动态和静态的 inventory 源.

动态组作为静态组的子组

在静态 inventory 文件中,如果定义一个由一些组作为子成员的组,这些子组也需要定义(译者注:即包含具体的 host),否则执行时 ansible 会返回一个错误. 如果定义一些动态组作为一个静态组的子组,也需在静态 inventory 文件中定义动态组,但是动态组定义为一个空的组即可:

[tag_Name_staging_foo]

[tag_Name_staging_bar]

[staging:children]
tag_Name_staging_foo
tag_Name_staging_bar

See also

Inventory文件
All about static inventory files
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

Patterns

Topics

在Ansible中,Patterns 是指我们怎样确定由哪一台主机来管理. 意思就是与哪台主机进行交互. 但是在:doc:playbooks 中它指的是对应主机应用特定的配置或执行特定进程.

我们再来复习下:doc:intro_adhoc 章节中介绍的命令用法,命令格式如下:

ansible <pattern_goes_here> -m <module_name> -a <arguments>

示例如下:

ansible webservers -m service -a "name=httpd state=restarted"

一个pattern通常关联到一系列组(主机的集合) –如上示例中,所有的主机均在 “webservers” 组中.

不管怎么样,在使用Ansible前,我们需事先告诉Ansible哪台机器将被执行. 能这样做的前提是需要预先定义唯一的 host names 或者 主机组.

如下的patterns等同于目标为仓库(inventory)中的所有机器:

all
*

也可以写IP地址或系列主机名:

one.example.com
one.example.com:two.example.com
192.168.1.50
192.168.1.*

如下patterns分别表示一个或多个groups.多组之间以冒号分隔表示或的关系.这意味着一个主机可以同时存在多个组:

webservers
webservers:dbservers

你也可以排队一个特定组,如下实例中,所有执行命令的机器必须隶属 webservers 组但同时不在 phoenix组:

webservers:!phoenix

你也可以指定两个组的交集,如下实例表示,执行命令有机器需要同时隶属于 webservers 和 staging 组.

webservers:&staging

你也可以组合更复杂的条件:

webservers:dbservers:&staging:!phoenix

上面这个例子表示“‘webservers’ 和 ‘dbservers’ 两个组中隶属于 ‘staging’ 组并且不属于 ‘phoenix’ 组的机器才执行命令” ... 哟!唷! 好烧脑的说!

你也可以使用变量如果你希望通过传参指定group,ansible-playbook通过 “-e” 参数可以实现,但这种用法不常用:

webservers:!{{excluded}}:&{{required}}

你也可以不必严格定义groups,单个的host names, IPs , groups都支持通配符:

*.example.com
*.com

Ansible同时也支持通配和groups的混合使用:

one*.com:dbservers

在高级语法中,你也可以在group中选择对应编号的server:

webservers[0]

或者一个group中的一部分servers:

webservers[0-25]

大部分人都在patterns应用正则表达式,但你可以.只需要以 ‘~’ 开头即可:

~(web|db).*\.example\.com

同时让我们提前了解一些技能,除了如上,你也可以通过 --limit 标记来添加排除条件,/usr/bin/ansible or /usr/bin/ansible-playbook都支持:

ansible-playbook site.yml --limit datacenter2

如果你想从文件读取hosts,文件名以@为前缀即可.从Ansible 1.2开始支持该功能:

ansible-playbook site.yml --limit @retry_hosts.txt

够简单吧. 为了更好的掌握该章节内容,可以先了解 Introduction To Ad-Hoc CommandsPlaybooks

See also

Introduction To Ad-Hoc Commands
Examples of basic commands
Playbooks
Learning ansible’s configuration management language
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

Introduction To Ad-Hoc Commands

在下面的例子中,我们将演示如何使用 /usr/bin/ansible 运行 ad hoc 任务.

所谓 ad-hoc 命令是什么呢?

(这其实是一个概念性的名字,是相对于写 Ansible playbook 来说的.类似于在命令行敲入shell命令和 写shell scripts两者之间的关系)...

如果我们敲入一些命令去比较快的完成一些事情,而不需要将这些执行的命令特别保存下来, 这样的命令就叫做 ad-hoc 命令.

Ansible提供两种方式去完成任务,一是 ad-hoc 命令,一是写 Ansible playbook.前者可以解决一些简单的任务, 后者解决较复杂的任务.

一般而言,在学习了 playbooks 之后,你才能体会到 Ansible 真正的强大之处在哪里.

那我们会在什么情境下去使用ad-hoc 命令呢?

比如说因为圣诞节要来了,想要把所有实验室的电源关闭,我们只需要执行一行命令 就可以达成这个任务,而不需要写 playbook 来做这个任务.

至于说做配置管理或部署这种事,还是要借助 playbook 来完成,即使用 ‘/usr/bin/ansible-playbook’ 这个命令.

(关于 playbook 的使用,请参考 Playbooks )

如果你还没有阅读 Inventory文件 ,最好先看一看,然后我们继续往下.

Parallelism and Shell Commands

举一个例子

这里我们要使用 Ansible 的命令行工具来重启 Atlanta 组中所有的 web 服务器,每次重启10个.

我们先设置 SSH-agent,将私钥纳入其管理:

$ ssh-agent bash
$ ssh-add ~/.ssh/id_rsa

如果不想使用 ssh-agent, 想通过密码验证的方式使用 SSH,可以在执行ansible命令时使用 --ask-pass (-k)选项, 但这里建议使用 ssh-agent.

现在执行如下命令,这个命令中,atlanta是一个组,这个组里面有很多服务器,”/sbin/reboot”命令会在atlanta组下 的所有机器上执行.这里ssh-agent会fork出10个子进程(bash),以并行的方式执行reboot命令.如前所说“每次重启10个” 即是以这种方式实现:

$ ansible atlanta -a "/sbin/reboot" -f 10

在执行 /usr/bin/ansible 时,默认是以当前用户的身份去执行这个命令.如果想以指定的用户执行 /usr/bin/ansible, 请添加 “-u username”选项,如下:

$ ansible atlanta -a "/usr/bin/foo" -u username

如果想通过 sudo 去执行命令,如下:

$ ansible atlanta -a "/usr/bin/foo" -u username --sudo [--ask-sudo-pass]

如果你不是以 passwordless 的模式执行 sudo,应加上 --ask-sudo-pass (-K)选项,加上之后会提示你输入 密码.使用 passwordless 模式的 sudo, 更容易实现自动化,但不要求一定要使用 passwordless sudo.

也可以通过``–sudo-user`` (-U)选项,使用 sudo 切换到其它用户身份,而不是 root(译者注:下面命令中好像写掉了–sudo):

$ ansible atlanta -a "/usr/bin/foo" -u username -U otheruser [--ask-sudo-pass]

Note

在有些比较罕见的情况下,一些用户会受到安全规则的限制,使用 sudo 切换时只能运行指定的命令.这与 ansible的 no-bootstrapping 思想相悖,而且 ansible 有几百个模块,在这种限制下无法进行正常的工作. 所以执行 ansible 命令时,应使用一个没有受到这种限制的账号来执行.One way of doing this without sharing access to unauthorized users would be gating Ansible with Ansible Tower, which can hold on to an SSH credential and let members of certain organizations use it on their behalf without having direct access.

以上是关于 ansible 的基础.如果你还没阅读过 patterns 和 groups,应先阅读 Patterns .

在前面写出的命令中, -f 10 选项表示使用10个并行的进程.这个选项也可以在 Ansible的配置文件 中设置, 在配置文件中指定的话,就不用在命令行中写出了.这个选项的默认值是 5,是比较小的.如果同时操作的主机数比较多的话, 可以调整到一个更大的值,只要不超出你系统的承受范围就没问题.如果主机数大于设置的并发进程数,Ansible会自行协调, 花得时间会更长一点.

ansible有许多模块,默认是 ‘command’,也就是命令模块,我们可以通过 -m 选项来指定不同的模块.在前面所示的例子中, 因为我们是要在 Atlanta 组下的服务器中执行 reboot 命令,所以就不需要显示的用这个选项指定 ‘command’ 模块,使用 默认设定就OK了.一会在其他例子中,我们会使用 -m 运行其他的模块,详情参见 模块相关 .

Note

command 模块不支持 shell 变量,也不支持管道等 shell 相关的东西.如果你想使用 shell相关的这些东西, 请使用’shell’ 模块.两个模块之前的差别请参考 模块相关 .

使用 shell 模块的示例如下:

$ ansible raleigh -m shell -a 'echo $TERM'

使用 Ansible ad hoc 命令行接口时(与使用 Playbooks 的情况相反),尤其注意 shell 引号的规则. 比如在上面的例子中,如果使用双引号”echo $TERM”,会求出TERM变量在当前系统的值,而我们实际希望的是把这个命令传递 到其它机器执行.

在此我们已经演示了一些简单命令如何去执行,但通常来讲大多数 Ansible 模块的工作方式与简单的脚本不同.They make the remote system look like you state, and run the commands necessary to get it there.这一般被称为 ‘idempotence’, 是 Ansible 设计的核心目标.但我们也认识到,能运行任意命令也是重要的,所以 Ansible 对这两者都做支持.

File Transfer

这是 /usr/bin/ansible 的另一种用法.Ansible 能够以并行的方式同时 SCP 大量的文件到多台机器. 命令如下:

$ ansible atlanta -m copy -a "src=/etc/hosts dest=/tmp/hosts"

若你使用 playbooks, 则可以利用 template 模块来做到更进一步的事情.(请参见 module 和 playbook 的文档)

使用 file 模块可以做到修改文件的属主和权限,(在这里可替换为 copy 模块,是等效的):

$ ansible webservers -m file -a "dest=/srv/foo/a.txt mode=600"
$ ansible webservers -m file -a "dest=/srv/foo/b.txt mode=600 owner=mdehaan group=mdehaan"

使用 file 模块也可以创建目录,与执行 mkdir -p 效果类似:

$ ansible webservers -m file -a "dest=/path/to/c mode=755 owner=mdehaan group=mdehaan state=directory"

删除目录(递归的删除)和删除文件:

$ ansible webservers -m file -a "dest=/path/to/c state=absent"

Managing Packages

Ansible 提供对 yum 和 apt 的支持.这里是关于 yum 的示例.

确认一个软件包已经安装,但不去升级它:

$ ansible webservers -m yum -a "name=acme state=present"

确认一个软件包的安装版本:

$ ansible webservers -m yum -a "name=acme-1.5 state=present"

确认一个软件包还没有安装:

$ ansible webservers -m yum -a "name=acme state=absent"

对于不同平台的软件包管理工具,Ansible都有对应的模块.如果没有,你也可以使用 command 模块去安装软件. 或者最好是来为那个软件包管理工具贡献一个相应的模块.请在 mailing list 中查看相关的信息和详情.

Users and Groups

使用 ‘user’ 模块可以方便的创建账户,删除账户,或是管理现有的账户:

$ ansible all -m user -a "name=foo password=<crypted password here>"

$ ansible all -m user -a "name=foo state=absent"

更多可用的选项请参考 模块相关 ,包括对组和组成员关系的操作.

Deploying From Source Control

直接使用 git 部署 webapp:

$ ansible webservers -m git -a "repo=git://foo.example.org/repo.git dest=/srv/myapp version=HEAD"

因为Ansible 模块可通知到 change handlers ,所以当源码被更新时,我们可以告知 Ansible 这个信息,并执行指定的任务, 比如直接通过 git 部署 Perl/Python/PHP/Ruby, 部署完成后重启 apache.

Managing Services

确认某个服务在所有的webservers上都已经启动:

$ ansible webservers -m service -a "name=httpd state=started"

或是在所有的webservers上重启某个服务(译者注:可能是确认已重启的状态?):

$ ansible webservers -m service -a "name=httpd state=restarted"

确认某个服务已经停止:

$ ansible webservers -m service -a "name=httpd state=stopped"

Time Limited Background Operations

需要长时间运行的命令可以放到后台去,在命令开始运行后我们也可以检查运行的状态.如果运行命令后,不想获取返回的信息, 可执行如下命令:

$ ansible all -B 3600 -P 0 -a "/usr/bin/long_running_operation --do-stuff"

如果你确定要在命令运行后检查运行的状态,可以使用 async_status 模块.前面执行后台命令后会返回一个 job id, 将这个 id 传给 async_status 模块:

$ ansible web1.example.com -m async_status -a "jid=488359678239.2844"

获取状态的命令如下:

$ ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"

其中 -B 1800 表示最多运行30分钟, -P 60 表示每隔60秒获取一次状态信息.

Polling 获取状态信息的操作会在后台工作任务启动之后开始.若你希望所有的工作任务快速启动, --forks 这个选项的值 要设置得足够大,这是前面讲过的并发进程的个数.在运行指定的时间(由``-B``选项所指定)后,远程节点上的任务进程便会被终止.

一般你只能在把需要长时间运行的命令或是软件升级这样的任务放到后台去执行.对于 copy 模块来说,即使按照前面的示例想放到 后台执行文件传输,实际上并不会如你所愿.

Gathering Facts

在 playbooks 中有对于 Facts 做描述,它代表的是一个系统中已发现的变量.These can be used to implement conditional execution of tasks but also just to get ad-hoc information about your system. 可通过如下方式查看所有的 facts:

$ ansible all -m setup

我们也可以对这个命令的输出做过滤,只输出特定的一些 facts,详情请参考 “setup” 模块的文档.

如果你已准备好仔细研究 Playbooks ,可以继续读读 Variables ,会对 facts有更多了解.

See also

Ansible的配置文件
All about the Ansible config file
模块相关
A list of available modules
Playbooks
Using Ansible for configuration management & deployment
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

Ansible的配置文件

Ansible的一些的设置可以通过配置文件完成.在大多数场景下默认的配置就能满足大多数用户的需求,在一些特殊场景下,用户还是需要自行修改这些配置文件

用户可以修改一下配置文件来修改设置,他们的被读取的顺序如下:

* ANSIBLE_CONFIG (一个环境变量)
* ansible.cfg (位于当前目录中)
* .ansible.cfg (位于家目录中)
* /etc/ansible/ansible.cfg

版本1.5之前的读取顺序如下:

* ansible.cfg (位于当前目录)
* ANSIBLE_CONFIG (一个环境变量)
* .ansible.cfg (位于家目录下)
* /etc/ansible/ansible.cfg

Ansible 将会按以上顺序逐个查询这些文件,直到找到一个为止,并且使用第一个寻找到个配置文件的配置,这些配置将不会被叠加.

获取最新配置文件

如果使用程序包管理器安装ansible,最新的 ansible.cfg 配置文件有可能出现在 /etc/ansible 下并且命名为 ”.rpmnew”, 也可能根据不同的更新命名为其它名称

如果你是通过 pip 或者其他方式安装,则可能需要自行创建这个文件,以免原配置文件被覆盖.Ansible 的默认设置将会将其覆盖

配置文件的详细参数以及取值范围请查看`ansible.cfg <https://raw.github.com/ansible/ansible/devel/examples/ansible.cfg>`_

环境配置

Ansible 通过环境变量的形式来进行配置.这些设置后的环境变量将会覆盖掉所有配置文件读取的配置.为了节省篇幅,这些变量没有被列在这里,详情请见源代码目录中的 ‘constants.py’. 相对于配置文件它门会比当作遗产系统(legacy system) 来被使用,但是仍然有效

配置文件不同段详解

配置文件被切割成了不同段.多数配置选项位于“general”段, 也有一些属于特定的链接类型(connection type)

通用默认段

在 [defaults] 段中,一下选项是可以调节的:

action_plugins

“行为”是 ansible中的一段代码,用来激活一些事件,例如执行一个模块,一个模版,等等

这是一个以开发者为中心的特性,使得一些底层模块可以从外部不同地方加载:

action_plugins = ~/.ansible/plugins/action_plugins/:/usr/share/ansible_plugins/action_plugins

大多数用户都会使用这一特性,详情请见 Developing Plugins .

ansible_managed

Ansible-managed 是一个字符串.可以插入到Ansible配置模版系统生成的文件中.如果你使用以下的自字符:

{{ ansible_managed }}

默认设置可以哪个用户修改和修改时间:

ansible_managed = Ansible managed: {file} modified on %Y-%m-%d %H:%M:%S by {uid} on {host}

这个设置可以告知用户,Ansible修改了一个文件,并且手动写入的内容可能已经被覆盖.

需要注意的是,如果使用这一特性,这个字符串中将包含一个日期注释,如果日期更新,模版系统将会在每一次报告文件修改.

ask_pass
这个可以控制,Ansible 剧本playbook 是否会自动默认弹出弹出密码.默认为no::
ask_pass=True

如果使用SSH 密钥匙做身份认证.可能需要修改这一参数

ask_sudo_pass

类似 ask_pass,用来控制Ansible playbook 在执行sudo之前是否询问sudo密码.默认为no:

ask_sudo_pass=True

如果用户使用的系统平台开启了sudo 密码的话,应该开绿这一参数

bin_ansible_callbacks

New in version 1.8.

用来控制callback插件是否在运行 /usr/bin/ansible 的时候被加载. 这个模块将用于命令行的日志系统,发出通知等特性. Callback插件如果存在将会永久性的被 /usr/bin/ansible-playbook 加载,不能被禁用:

bin_ansible_callbacks=False

1.8 版本之前,callbacks 插件不可以被 /usr/bin/ansible加载. .. _callback_plugins:

callback_plugins

Callbacks 在ansible中是一段代码,在特殊事件时将被调用.并且允许出发通知. 这是一个以开发者为中心的特性,可以实现对Ansible的底层拓展,并且拓展模块可以位于任何位置:

callback_plugins = ~/.ansible/plugins/callback_plugins/:/usr/share/ansible_plugins/callback_plugins

大多数的用户将会用到这一特性,详见 Developing Plugins.

command_warnings

New in version 1.8.

从Ansible 1.8 开始,当shell和命令行模块被默认模块简化的时,Ansible 将默认发出警告. 这个包含提醒使用’git’但不是通过命令行执行.使用模块调用比冒然使用命令行调用可以使playbook工作更具有一致性也更加可靠同时也更加便于维护:

command_warnings = False

我们可以通过在命令行末尾添加 warn=yes 或者 warn=no选项来控制是否开启警告提示:

- name: usage of git that could be replaced with the git module
  shell: git update foo warn=yes
connection_plugins

连接插件允许拓展ansible拓展通讯信道,用来传输命令或者文件. 这是一个开发者中心特性,拓展插件可以从任何不同地方加载:

connection_plugins = ~/.ansible/plugins/connection_plugins/:/usr/share/ansible_plugins/connection_plugins

大多数用户会用到这一特性, 详见:Developing Plugins .. _deprecation_warnings:

deprecation_warnings

New in version 1.3.

允许在ansible-playbook输出结果中禁用“不建议使用”警告:

deprecation_warnings = True

“不建议警告”指的是使用一些在新版本中可能会被淘汰的遗留特性.

display_skipped_hosts
如果设置为`False`,ansible 将不会显示任何跳过任务的状态.默认选项是现实跳过任务的状态::
display_skipped_hosts=True

注意Ansible 总是会显示任何任务的头文件, 不管这个任务被跳过与否.

error_on_undefined_vars
从Ansible 1.3开始,这个选项将为默认,如果所引用的变量名称错误的话, 将会导致ansible在执行步骤上失败::
error_on_undefined_vars=True

If set to False, any ‘{{ template_expression }}’ that contains undefined variables will be rendered in a template or ansible action line exactly as written.

executable
这个选项可以在sudo环境下产生一个shell交互接口. 用户只在/bin/bash的或者sudo限制的一些场景中需要修改.大部分情况下不需要修改::
executable = /bin/bash
filter_plugins

过滤器是一种特殊的函数,用来拓展模版系统 .

这是一个开发者核心的特性,允许Ansible从任何地方载入底层拓展模块:

filter_plugins = ~/.ansible/plugins/filter_plugins/:/usr/share/ansible_plugins/filter_plugins

Most users will not need to use this feature. See Developing Plugins for more details 大部分用户不会用到这个特性,详见:doc:developing_plugins.

force_color
到没有使用TTY终端的时候,这个选项当用来强制颜色模式::
force_color = 1
force_handlers

New in version 1.9.1.

即便这个用户崩溃,这个选项仍可以继续运行这个用户:

force_handlers = True

The default is False, meaning that handlers will not run if a failure has occurred on a host. This can also be set per play or on the command line. See _handlers_and_failure for more details. 如果这个选项是False. 如果一个主机崩溃了,handlers将不会再运行这个主机.这个选项也可以通过命令行临时使用.详见:doc:_handlers_and_failure.

forks

这个选项设置在与主机通信时的默认并行进程数.从Ansible 1.3开始,fork数量默认自动设置为主机数量或者潜在的主机数量, 这将直接控制有多少网络资源活着cpu可以被使用.很多用户把这个设置为50,有些设置为500或者更多.如果你有很多的主机, 高数值将会使得跨主机行为变快.默认值比较保守:

_forks=5
gathering

1.6版本中的新特性,这个设置控制默认facts收集(远程系统变量). 默认值为’implicit’, 每一次play,facts都会被手机,除非设置’gather_facts: False’. 选项‘explicit’正好相反,facts不会被收集,直到play中需要. ‘smart’选项意思是,没有facts的新hosts将不会被扫描, 但是如果同样一个主机,在不同的plays里面被记录地址,在playbook运行中将不会通信.这个选项当有需求节省fact收集时比较有用.

hash_behaviour

Ansible 默认将会以一种特定的优先级覆盖变量,详见:doc:playbooks_variables.拥有更高优先级的参数将会覆盖掉其他参数

有些用户希望被hashed的参数(python 中的数据结构’dictionaries’)被合并. 这个设置叫做‘merge’.这不是一个默认设置,而且不影响数组类型的数组.我不建议使用这个设置除非你觉得一定需要这个设置.官方实例中不使用这个选项:

hash_behaviour=replace

合法的值为’replace’(默认值)或者‘merge’.

hostfile

在1.9版本中,这不是一个合法设置.详见:ref:inventory.

host_key_checking

这个特性详见:doc:intro_getting_started,在Ansible 1.3或更新版本中将会检测主机密钥. 如果你了解怎么使用并且希望禁用这个功能,你可以将这个值设置为False:

host_key_checking=True
inventory

这个事默认库文件位置,脚本,或者存放可通信主机的目录:

inventory = /etc/ansible/hosts

在1.9版本中被叫做hostfile.

jinja2_extensions

这是一个开发者中心特性,允许开启Jinja2拓展模块:

jinja2_extensions = jinja2.ext.do,jinja2.ext.i18n

如果你不太清楚这些都是啥,还是不要改的好:)

library

这个事Ansible默认搜寻模块的位置:

library = /usr/share/ansible

Ansible知道如何搜寻多个用冒号隔开的路径,同时也会搜索在playbook中的“./library”.

log_path

如果出现在ansible.cfg文件中.Ansible 将会在选定的位置登陆执行信息.请留意用户运行的Ansible对于logfile有权限:

log_path=/var/log/ansible.log

这个特性不是默认开启的.如果不设置,ansible将会吧模块加载纪录在系统日志系统中.不包含用密码.

对于需要了解更多日志系统的企业及用户,你也许对:doc:tower 感兴趣.

lookup_plugins

这是一个开发者中心选项,允许模块插件在不同区域被加载:

lookup_plugins = ~/.ansible/plugins/lookup_plugins/:/usr/share/ansible_plugins/lookup_plugins

绝大部分用户将不会使用这个特性,详见:doc:developing_plugins

module_lang

这是默认模块和系统之间通信的计算机语言,默认为’C’语言.

module_name

这个是/usr/bin/ansible的默认模块名(-m). 默认是’command’模块. 之前提到过,command模块不支持shell变量,管道,配额. 所以也许你希望把这个参数改为’shell’:

module_name = command
nocolor

默认ansible会为输出结果加上颜色,用来更好的区分状态信息和失败信息.如果你想关闭这一功能,可以把’nocolor’设置为‘1’:

nocolor=0
nocows

默认ansible可以调用一些cowsay的特性,使得/usr/bin/ansible-playbook运行起来更加愉快.为啥呢,因为我们相信系统应该是一 比较愉快的经历.如果你不喜欢cows,你可以通通过将’nocows’设置为‘1’来禁用这一选项:

nocows=0
pattern

如果没有提供“hosts”节点,这是playbook要通信的默认主机组.默认值是对所有主机通信,如果不想被惊吓到,最好还是设置个个选项:

hosts=*

注意 /usr/bin/ansible 一直需要一个host pattern,并且不使用这个选项.这个选项只作用于/usr/bin/ansible-playbook.

poll_interval

对于Ansible中的异步任务(详见 异步操作和轮询), 这个是设置定义,当具体的poll interval 没有定义时,多少时间回查一下这些任务的状态, 默认值是一个折中选择15秒钟.这个时间是个回查频率和任务完成叫回频率和当任务完成时的回转频率的这种:

poll_interval=15
private_key_file

如果你是用pem密钥文件而不是SSH 客户端或秘密啊认证的话,你可以设置这里的默认值,来避免每一次提醒设置密钥文件位置``–ansible-private-keyfile``:

private_key_file=/path/to/file.pem
remote_port

这个设置是你系统默认的远程SSH端口,如果不指定,默认为22号端口:

remote_port = 22
remote_tmp

Ansible 通过远程传输模块到远程主机,然后远程执行,执行后在清理现场.在有些场景下,你也许想使用默认路径希望像更换补丁一样使用, 这时候你可以使用这个选项.:

remote_tmp = $HOME/.ansible/tmp

默认路径是在用户家目录下属的目录.Ansible 会在这个目录中使用一个随机的文件夹名称.

remote_user

这是个ansible使用/usr/bin/ansible-playbook链接的默认用户名. 注意如果不指定,/usr/bin/ansible默认使用当前用户名称:

remote_user = root
roles_path

roles 路径指的是’roles/’下的额外目录,用于playbook搜索Ansible roles.比如, 如果我们有个用于common roles源代码控制仓库和一个不同的 playbooks仓库,你也许会建立一个惯例去在 /opt/mysite/roles 里面查找roles.:

roles_path = /opt/mysite/roles

多余的路径可以用冒号分隔,类似于其他path字符串:

roles_path = /opt/mysite/roles:/opt/othersite/roles

Roles将会在playbook目录中开始搜索.如果role没有找到,这个参数指定了其它可能的搜索路径.

sudo_exe

如果在其他远程主机上使用另一种方式执行sudo草做, sudo程序的路径可以用这个参数更换,使用命令行标签来拟合标准sudo:

sudo_exe=sudo
sudo_flags

当使用sudo支持的时候,传递给sudo而外的标签. 默认值为”-H”, 意思是保留原用户的环境.在有些场景下也许需要添加或者删除 标签,大多数用户不需要修改这个选项:

sudo_flags=-H
sudo_user

这个是sudo使用的默认用户,如果``–sudo-user`` 没有特指或者’sudo_user’ 在Ansible playbooks中没有特指,在大多数的逻辑中 默认为: ‘root’

sudo_user=root
system_warnings

New in version 1.6.

允许禁用系统运行ansible相关的潜在问题警告(不包括操作主机):

system_warnings = True

这个包括第三方库或者一些需要解决问题的警告.

timeout

这个事默认SSH链接尝试超市时间:

timeout = 10
transport

如果”-c <transport_name>” 选项没有在使用/usr/bin/ansible 或者 /usr/bin/ansible-playbook 特指的话,这个参数提供了默认通信机制.默认 值为’smart’, 如果本地系统支持 ControlPersist技术的话,将会使用(基于OpenSSH)‘ssh’,如果不支持讲使用‘paramiko’.其他传输选项包括‘local’, ‘chroot’,’jail’等等.

用户通常可以这个设置为‘smart’,让playbook在需要的条件自己选择‘connectin:’参数.

vars_plugins

这是一个开发者中心选项,允许底层拓展模块从任何地方加载:

vars_plugins = ~/.ansible/plugins/vars_plugins/:/usr/share/ansible_plugins/vars_plugins

大部分的用户不会用到这个特性,详见:doc:developing_plugins

vault_password_file

New in version 1.7.

这个用来设置密码文件,也可以通过命令行指定``–vault-password-file``:

vault_password_file = /path/to/vault_password_file

在1.7版本中,这个文件也可以称为一个脚本的形式.如果你使用脚本而不是单纯文件的话,请确保它可以执行并且密码可以在标准输出上打印出来.如果你的脚本需要提示请求数据,请求将会发到标准错误输出中.

Paramiko Specific Settings

Paramiko 是商业版linux 6 的默认SSH链接.但在其他平台上不是默认使用的.请在[paramiko]头文件下激活它.

record_host_keys

默认设置会记录并验证通过在用户hostfile中新发现的的主机(如果host key checking 被激活的话). 这个选项在有很多主机的时候将会性能很差.在 这种情况下,建议使用SSH传输代替. 当设置为False时, 性能将会提升,在hostkey checking 被禁用时候,建议使用.:

record_host_keys=True
OpenSSH Specific Settings

在[ssh_connection]头文件之下,用来调整SSH的通信连接.OpenSSH是Ansible在操作系统上默认的通讯连接,对于支持ControlPersist足够新了.(意思除了Enterprise linux 6版以及更早的系统外的所有的操作系统).

ssh_args

如果设置了的话,这个选项将会传递一组选项给Ansible 然不是使用以前的默认值:

ssh_args = -o ControlMaster=auto -o ControlPersist=60s

用户可以提高ControlPersist值来提高性能.30 分钟通常比较合适.

control_path

这个是保存ControlPath套接字的位置. 默认值是:

control_path=%(directory)s/ansible-ssh-%%h-%%p-%%r

在有些系统上面,会遇到很长的主机名或者很长的路径名称(也许因为很长的用户名,或者比较深的家目录),这些都会 超出套接字文件名字符上限(对于大多数平台上限为108个字符).在这种情况下,你也许希望按照以下方式缩短字符串:

control_path = %(directory)s/%%h-%%r

Ansible 1.4 以后的版本会引导用户在这种情况下使用”-vvvv”参数,这样很容易分辨 Control Path 文件名是否过长.这个 问题在EC2上会频繁的遇到.

scp_if_ssh

又是用户操控一个一个没有开启SFTP协议的远程系统.如果这个设置为True,scp将代替用来为远程主机传输文件:

scp_if_ssh=False

如果没有遇到这样的问题没有必要来修改这个设置.当然修改这个设置也没有什么明显的弊端.大部分的系统环境都默认支持SFTP, 通常情况下不需要修改.

pipelining

在不通过实际文件传输的情况下执行ansible模块来使用管道特性,从而减少执行远程模块SSH操作次数.如果开启这个设置,将显著提高性能. 然而当使用”sudo:”操作的时候, 你必须在所有管理的主机的/etc/sudoers中禁用’requiretty’.

默认这个选项为了保证与sudoers requiretty的设置(在很多发行版中时默认的设置)的兼容性是禁用的. 但是为了提高性能强烈建议开启这个设置.详见:doc:playbooks_acceleration:

pipelining=False
Accelerated Mode Settings

在[accelerate]首部下, 以下设置可以调整,详见:doc:playbooks_acceleration.如果你不能在你的环境中开启:ref:pipelining , Accelertation 是一个很有用的性能特性. 但是如果你可以开启管道,这个选项也许对你无用.

accelerate_port

New in version 1.3.

在急速模式下使用的端口:

accelerate_port = 5099
accelerate_timeout

New in version 1.4.

这个设置时用来控制从客户机获取数据的超时时间.如果在这段时间内没有数据传输,套接字连接会被关闭. 一个保持连接(keepalive)数据包通常每15秒回发回给控制台,所以这个超时时间不应该低于15秒(默认值为30秒):

accelerate_timeout = 30
accelerate_connect_timeout

New in version 1.4.

这个设置空着套接字调用的超时时间.这个应该设置相对比较短.这个和`accelerate_port`连接在回滚到ssh或者paramiko(受限于你默认的连接设置)连接方式之前会尝试三次开始远程加速daemon守护进程.默认设置为1.0秒:

accelerate_connect_timeout = 1.0

注意,这个选项值可以设置为小于1秒钟,但是除非你拥有一个速度很快而且很可靠的网络,否则也许这样并不是一个很好的选择.如果你使用英特网访问你的系统,最好提高这个值.

accelerate_daemon_timeout

New in version 1.6.

This setting controls the timeout for the accelerated daemon, as measured in minutes. The default daemon timeout is 30 minutes:: 这个控制加速daemon守护进程的超时时间,用分钟来衡量.默认为30分钟:

accelerate_daemon_timeout = 30

注意, 在1.6版本之前,daemon发起的超时时间是硬编码的.对于1.6以后的版本,超时时间是根据daemon上一次活动信息和这个可设置的选项.

accelerate_multi_key

New in version 1.6.

If enabled, this setting allows multiple private keys to be uploaded to the daemon. Any clients connecting to the daemon must also enable this option:: 如果这个选项开启,这个设置将允许多个私钥被加载到daemon. 任何客户端要想连接daemon都需要开启这个选项:

accelerate_multi_key = yes

通过本地套接字文件连接的通过SSH上传密钥文件到目标节点的新客户端,必须在登陆daemon时使用原始的登陆密钥登陆.

Windows Support

windows下的运行方式

就如你刚所了解到的,Ansible默认是通过SSH协议来管理Linux/Unix服务器.

从1.7版本开始,Ansible也开始支持Windows机器的管理.不过是通过本机的PowerShell来实现远程管理,而不是SSH.

Ansible仍然通过一台Linux系统机器来进行集中管理,使用Python的 “winrm” 模块来和远程主机交互.

在管理的过程是 Ansible无需在远程主机上安装任何额外的软件,Ansible仍然使用 agentless(非c/s架构) 来保证其在 Linux/Unix的流行度.

需要注意的是有开始这章前你最好对对 Ansible 有一个预先的了解,如果你还没有写过一个 Playbook, 那最好先跳到playbook的章节先了解熟悉再开始本章内容.

安装管理机

On a Linux control machine:

pip install http://github.com/diyan/pywinrm/archive/master.zip#egg=pywinrm

如果你想通过活动目录连接域帐户进行发布(相对本地帐户在远程主机上创建):

pip install kerberos

Kerberos 在 OS X 和许多 Linux 发行版中是默认安装且配置好的.如果你的管理机上还没有安装,那你需要执行如上的命令.

Inventory

Ansible’s支持windows需要依赖于少量标准变量来表明远程主机的username, password, and connection type (windows).这些变量大部分都很容易被设置好.在 Ansible 中通过用来代替 SSH-keys 或 密码输入:

[windows]
winserver1.example.com
winserver2.example.com

在 group_vars/windows.yml,定义如下 inventory 变量:

# it is suggested that these be encrypted with ansible-vault:
# ansible-vault edit group_vars/windows.yml

ansible_ssh_user: Administrator
ansible_ssh_pass: SecretPasswordGoesHere
ansible_ssh_port: 5986
ansible_connection: winrm

需要注意的是这里的 ssh_port 不是真正的SSH协议的端口,but this is a holdover variable name from how Ansible is mostly an SSH-oriented system.(这句也没看懂)再重复一遍,Windows 管理主机不是通过SSH协议.

如果你已经安装了 kerberos 模块和 ansible_ssh_user 包括 @ (e.g. username@realm), Ansible会先尝试Kerberos认证. * 这种方式主要用你通过Kerberos在远程主机上的认证而不是 ansible_ssh_user * .如果上述办法失败了,要么是因为你没有在管理机上签署(signed into)Kerberos,要么是因为远程主机上对应的域帐户不可用,接着 Ansible 将返回原始(“plain”)username/password的认证方式.

当你使用 playbook 时,请不要忘记指定 –ask-vault-pass 提供密码来解锁文件.

使用如下命令来测试你的配置,尝试连接你的 Windows 节点.注意:这不是ICMP ping,只是利用 Windows 远程工具来检测 Ansible 的信道是否正常:

ansible windows [-i inventory] -m win_ping --ask-vault-pass

如果你还没有在你的系统上做任何准备工作,那上面的命令是无法正常工作的. 在下面最近的章节将会介绍 “how to enable PowerShell remoting” - 如果有需要的话也将介绍 “how to upgrade PowerShell to a version that is 3 or higher” .

你可以稍后再执行该命令,以确保一切都能正常工作.

Windows System Prep

为了 Ansible 能管理你的windows机器,你将必须开启并配置远程机器上PowerShell.

为了能自动化设置 WinRM,你可以在远程机器上执行 this PowerShell script

Admins有可能希望微调配置,例如延长证过期时间.

Note

Windows 7 和 Server 2008 R2 系统因为 Windows Management Framework 3.0的BUG,你必须安装 hotfix http://support.microsoft.com/kb/2842230 来避免内存溢出(OOM)和堆栈异常. 新安装的 Server 2008 R2 系统没有升级到最新版本的均存在这个问题.

Windows 8.1 and Server 2012 R2 不受影响是因为他们自身默认使用的是 Windows Management Framework 4.0.

Getting to PowerShell 3.0 or higher

多数 Ansible Windows 模块需要 PowerShell 3.0 或更高版本,同时也需要在其基础上运行安装脚本. 需要注意的是 PowerShell 3.0 只在 Windows 7 SP1 ,Windows Server 2008 SP1, 和更新的windows发布版才被支持.

找到 Ansible 的checkout版本,复制 copy the examples/scripts/upgrade_to_ps3.ps1 脚本到远程主机同时以Administrator角色的帐户运行 PowerShell 控制台. 你就可以运行 PowerShell 3 并可以通过上面介绍的 win_ping 技术来测试连通性.

可用的windows模块

大多数 Ansible 模块尤其核心Ansible设计来组合 Linux/Unix 机器和任意 web services. 尽管 “windows” subcategory of the Ansible module index 列举了各种各校的 Windows 模块.

浏览上面的索引查看可用模块.

很多情况下, 其实没有必要写或者使用 Ansible 模块.

尤其, “script” 模块可以用来执行任意 PowerShell 脚本,允许 Windows administrators 组所有用户通过 PowerSehll 以非常本地化的方式做任何事情.就像如下的 playbook:

- hosts: windows
  tasks:
    - script: foo.ps1 --argument --other-argument

注意: 有一小部分 Ansible 模块不是以 “win” 开头但依然是函数,包括 “slurp”,”raw”,和”setup”(fact 收集的工作原理).

开发者:支持的模块及工作原理

开发 ansible 模块主要在 later section of the documentation 介绍,专注于 Linux/Unix 平台. 如果你想编写 Windows 的 ansible 模块该怎么办呢?

Windows 平台主要通过 PowerShell 模块实现. 开始之前可以先略过 Linux/Unix 模块开发章节.

Windows 模块在 Ansible “library/” 子目录下的 “windows/” 子目录下. 例如,如果一个模块命名为 “library/windows/win_ping”,那将会在 “win_ping” 文件中嵌入一个文档,实际的 PowerShell 代码将存在 “win_ping.ps1” 文件. 看下源代码会有更深入的了解.

模块(ps1 files)文件应该以如下格式开头:

#!powershell
# <license>

# WANT_JSON
# POWERSHELL_COMMON

# code goes here, reading in stdin as JSON and outputting JSON

如上代码是为了告诉 ansible 合入一些代码并且 The above magic is necessary to tell Ansible to mix in some common code and also know how to push modules out. 常规代码包括好包装例如哈希数据结构,jason格式标准输出,还有一些更有用的东西.常规 Ansible 有着重复利用 Python 代码的理念 - 这点 Windows 也是等同的. 你刚看到的 windows/ 模块只是一个开始. 附加模块已经被 git push 到 github上了.

提醒:控制机必须是Linux系统

Windows 控制机不是这个项目的目标. Ansible 不会开发这个功能,因为受限于技术,产品和我们未来主要项目使用的代码. 一台Linux控制机是必须的,可以用来管理 Windows 机器. Cygwin 也是不被支持的,所以请不要要求 Ansible 基于 Cygwin 来运行.

Windows Facts

Just as with Linux/Unix, facts can be gathered for windows hosts, which will return things such as the operating system version. To see what variables are available about a windows host, run the following:

ansible winhost.example.com -m setup

Note that this command invocation is exactly the same as the Linux/Unix equivalent.

Windows Playbook Examples

Look to the list of windows modules for most of what is possible, though also some modules like “raw” and “script” also work on Windows, as do “fetch” and “slurp”.

Here is an example of pushing and running a PowerShell script:

- name: test script module
  hosts: windows
  tasks:
    - name: run test script
      script: files/test_script.ps1

Running individual commands uses the ‘raw’ module, as opposed to the shell or command module as is common on Linux/Unix operating systems:

- name: test raw module
  hosts: windows
  tasks:
    - name: run ipconfig
      raw: ipconfig
      register: ipconfig
    - debug: var=ipconfig

And for a final example, here’s how to use the win_stat module to test for file existence. Note that the data returned by the win_stat module is slightly different than what is provided by the Linux equivalent:

- name: test stat module
  hosts: windows
  tasks:
    - name: test stat module on file
      win_stat: path="C:/Windows/win.ini"
      register: stat_file

    - debug: var=stat_file

    - name: check stat_file result
      assert:
          that:
             - "stat_file.stat.exists"
             - "not stat_file.stat.isdir"
             - "stat_file.stat.size > 0"
             - "stat_file.stat.md5"

Again, recall that the Windows modules are all listed in the Windows category of modules, with the exception that the “raw”, “script”, and “fetch” modules are also available. These modules do not start with a “win” prefix.

Windows Contributions

Windows support in Ansible is still very new, and contributions are quite welcome, whether this is in the form of new modules, tweaks to existing modules, documentation, or something else. Please stop by the ansible-devel mailing list if you would like to get involved and say hi.

See also

Developing Modules
How to write modules
Playbooks
Learning ansible’s configuration management language
List of Windows Modules
Windows specific module list, all implemented in PowerShell
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

快速学习视频

我们录制了一个简短的视频来展示如何开始使用Ansible,当你阅读文档时可以用到它。

快速学习视频 长度大概为30分钟,介绍了刚开始使用Ansible的一些基本知识。

欢迎观看本视频,请确保阅读剩余的文档来进行进一步的学习。

Playbooks

Playbooks 是 Ansible的配置,部署,编排语言.他们可以被描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合.

如果 Ansible 模块你是工作室中的工具,那么 playbooks 就是你设置的方案计划.

在基础层面, playbooks 可以被用来管理用于部署到远程主机的配置文件.在更高的层面上,playbooks 可以依次对多层式架构上的服务器执行上线包括滚动更新在内的操作并可以将操作委托给其他主机包括在此过程中发生的与监视服务器,负载均衡服务器的交互操作在内.

虽然这里讲发很多,但是不需要立刻一次性全部学完.你可以从小功能开始,当你需要的时候再来这里找对应的功能即可.

Playbooks 被设计的非常简单易懂和基于text language二次开发.有多种办法来组织 playbooks 和其附属的文件,同时我们也会提供一些关于学习 Ansible 的建议.

这里强烈建议在阅读的 playbook 文档的时候同步参阅 Example Playbooks <https://github.com/ansible/ansible-examples> 章节. 这些例子是最佳实战以及如何将各种概念灵活贯穿结合在一起.

Playbooks 介绍

Playbooks 简介

Playbooks 与 adhoc 相比,是一种完全不同的运用 ansible 的方式,是非常之强大的.

简单来说,playbooks 是一种简单的配置管理系统与多机器部署系统的基础.与现有的其他系统有不同之处,且非常适合于复杂应用的部署.

Playbooks 可用于声明配置,更强大的地方在于,在 playbooks 中可以编排有序的执行过程,甚至于做到在多组机器间,来回有序的执行特别指定的步骤.并且可以同步或异步的发起任务.

我们使用 adhoc 时,主要是使用 /usr/bin/ansible 程序执行任务.而使用 playbooks 时,更多是将之放入源码控制之中,用之推送你的配置或是用于确认你的远程系统的配置是否符合配置规范.

在如右的连接中: ansible-examples repository ,有一些整套的playbooks,它们阐明了上述的这些技巧.我们建议你在另一个标签页中打开它看看,配合本章节一起看.

即便学完 playbooks 这个章节,仍有许多知识点只是入门的级别,完成本章的学习后,可回到文档索引继续学习.

Playbook 语言的示例

Playbooks 的格式是YAML(详见:YAML 语法),语法做到最小化,意在避免 playbooks 成为一种编程语言或是脚本,但它也并不是一个配置模型或过程的模型.

playbook 由一个或多个 ‘plays’ 组成.它的内容是一个以 ‘plays’ 为元素的列表.

在 play 之中,一组机器被映射为定义好的角色.在 ansible 中,play 的内容,被称为 tasks,即任务.在基本层次的应用中,一个任务是一个对 ansible 模块的调用,这在前面章节学习过.

‘plays’ 好似音符,playbook 好似由 ‘plays’ 构成的曲谱,通过 playbook,可以编排步骤进行多机器的部署,比如在 webservers 组的所有机器上运行一定的步骤, 然后在 database server 组运行一些步骤,最后回到 webservers 组,再运行一些步骤,诸如此类.

“plays” 算是一个体育方面的类比,你可以通过多个 plays 告诉你的系统做不同的事情,不仅是定义一种特定的状态或模型.你可以在不同时间运行不同的 plays.

对初学者,这里有一个 playbook,其中仅包含一个 play:

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  remote_user: root
  tasks:
  - name: ensure apache is at the latest version
    yum: pkg=httpd state=latest
  - name: write the apache config file
    template: src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    service: name=httpd state=started
  handlers:
    - name: restart apache
      service: name=httpd state=restarted

在下面,我们将分别讲解 playbook 语言的多个特性.

playbook基础

主机与用户

你可以为 playbook 中的每一个 play,个别地选择操作的目标机器是哪些,以哪个用户身份去完成要执行的步骤(called tasks).

hosts 行的内容是一个或多个组或主机的 patterns,以逗号为分隔符,详见 Patterns 章节.

remote_user 就是账户名:

---
- hosts: webservers
  remote_user: root

Note

参数 remote_user 以前写做 user,在 Ansible 1.4 以后才改为 remote_user.主要为了不跟 user 模块混淆(user 模块用于在远程系统上创建用户).

再者,在每一个 task 中,可以定义自己的远程用户:

---
- hosts: webservers
  remote_user: root
  tasks:
    - name: test connection
      ping:
      remote_user: yourname

Note

task 中的 remote_user 参数在 1.4 版本以后添加.

也支持从 sudo 执行命令:

---
- hosts: webservers
  remote_user: yourname
  sudo: yes

同样的,你可以仅在一个 task 中,使用 sudo 执行命令,而不是在整个 play 中使用 sudo:

---
- hosts: webservers
  remote_user: yourname
  tasks:
    - service: name=nginx state=started
      sudo: yes

你也可以登陆后,sudo 到不同的用户身份,而不是使用 root:

---
- hosts: webservers
  remote_user: yourname
  sudo: yes
  sudo_user: postgres

如果你需要在使用 sudo 时指定密码,可在运行 ansible-playbook 命令时加上选项 --ask-sudo-pass (-K). 如果使用 sudo 时,playbook 疑似被挂起,可能是在 sudo prompt 处被卡住,这时可执行 Control-C 杀死卡住的任务,再重新运行一次.

Important

当使用 sudo_user 切换到 非root 用户时,模块的参数会暂时写入 /tmp 目录下的一个随机临时文件. 当命令执行结束后,临时文件立即删除.这种情况发生在普通用户的切换时,比如从 ‘bob’ 切换到 ‘timmy’, 切换到 root 账户时,不会发生,如从 ‘bob’ 切换到 ‘root’,直接以普通用户或root身份登录也不会发生. 如果你不希望这些数据在短暂的时间内可以被读取(不可写),请避免在 sudo_user 中传递未加密的密码. 其他情况下,’/tmp’ 目录不被使用,这种情况不会发生.Ansible 也有意识的在日志中不记录密码参数.

Tasks 列表

每一个 play 包含了一个 task 列表(任务列表).一个 task 在其所对应的所有主机上(通过 host pattern 匹配的所有主机)执行完毕之后,下一个 task 才会执行.有一点需要明白的是(很重要),在一个 play 之中,所有 hosts 会获取相同的任务指令,这是 play 的一个目的所在,也就是将一组选出的 hosts 映射到 task.(注:此处翻译未必准确,暂时保留原文)

在运行 playbook 时(从上到下执行),如果一个 host 执行 task 失败,这个 host 将会从整个 playbook 的 rotation 中移除. 如果发生执行失败的情况,请修正 playbook 中的错误,然后重新执行即可.

每个 task 的目标在于执行一个 moudle, 通常是带有特定的参数来执行.在参数中可以使用变量(variables).

modules 具有”幂等”性,意思是如果你再一次地执行 moudle(译者注:比如遇到远端系统被意外改动,需要恢复原状),moudle 只会执行必要的改动,只会改变需要改变的地方.所以重复多次执行 playbook 也很安全.

对于 command module 和 shell module,重复执行 playbook,实际上是重复运行同样的命令.如果执行的命令类似于 ‘chmod’ 或者 ‘setsebool’ 这种命令,这没有任何问题.也可以使用一个叫做 ‘creates’ 的 flag 使得这两个 module 变得具有”幂等”特性 (不是必要的).

每一个 task 必须有一个名称 name,这样在运行 playbook 时,从其输出的任务执行信息中可以很好的辨别出是属于哪一个 task 的. 如果没有定义 name,‘action’ 的值将会用作输出信息中标记特定的 task.

如果要声明一个 task,以前有一种格式: “action: module options” (可能在一些老的 playbooks 中还能见到).现在推荐使用更常见的格式:”module: options” ,本文档使用的就是这种格式.

下面是一种基本的 task 的定义,service moudle 使用 key=value 格式的参数,这也是大多数 module 使用的参数格式:

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

比较特别的两个 modudle 是 commandshell ,它们不使用 key=value 格式的参数,而是这样:

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

使用 command module 和 shell module 时,我们需要关心返回码信息,如果有一条命令,它的成功执行的返回码不是0, 你或许希望这样做:

tasks:
  - name: run this command and ignore the result
    shell: /usr/bin/somecommand || /bin/true

或者是这样:

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

如果 action 行看起来太长,你可以使用 space(空格) 或者 indent(缩进) 隔开连续的一行:

tasks:
  - name: Copy ansible inventory file to client
    copy: src=/etc/ansible/hosts dest=/etc/ansible/hosts
            owner=root group=root mode=0644

在 action 行中可以使用变量.假设在 ‘vars’ 那里定义了一个变量 ‘vhost’ ,可以这样使用它:

tasks:
  - name: create a virtual host file for {{ vhost }}
    template: src=somefile.j2 dest=/etc/httpd/conf.d/{{ vhost }}

这些变量在 tempates 中也是可用的,稍后会讲到.

在一个基础的 playbook 中,所有的 task 都是在一个 play 中列出,稍后将介绍一种更合理的安排 task 的方式:使用 ‘include:’ 指令.

Action Shorthand

New in version 0.8.

在 0.8 及以后的版本中,ansible 更喜欢使用如下的格式列出 modules:

template: src=templates/foo.j2 dest=/etc/foo.conf

在早期的版本中,使用以下的格式:

action: template src=templates/foo.j2 dest=/etc/foo.conf

早期的格式在新版本中仍然可用,并且没有计划将这种旧的格式弃用.

Handlers: 在发生改变时执行的操作

上面我们曾提到过,module 具有”幂等”性,所以当远端系统被人改动时,可以重放 playbooks 达到恢复的目的. playbooks 本身可以识别这种改动,并且有一个基本的 event system(事件系统),可以响应这种改动.

(当发生改动时)’notify’ actions 会在 playbook 的每一个 task 结束时被触发,而且即使有多个不同的 task 通知改动的发生, ‘notify’ actions 只会被触发一次.

举例来说,比如多个 resources 指出因为一个配置文件被改动,所以 apache 需要重新启动,但是重新启动的操作只会被执行一次.

这里有一个例子,当一个文件的内容被改动时,重启两个 services:

- name: template configuration file
  template: src=template.j2 dest=/etc/foo.conf
  notify:
     - restart memcached
     - restart apache

‘notify’ 下列出的即是 handlers.

Handlers 也是一些 task 的列表,通过名字来引用,它们和一般的 task 并没有什么区别.Handlers 是由通知者进行 notify, 如果没有被 notify,handlers 不会执行.不管有多少个通知者进行了 notify,等到 play 中的所有 task 执行完成之后,handlers 也只会被执行一次.

这里是一个 handlers 的示例:

handlers:
    - name: restart memcached
      service:  name=memcached state=restarted
    - name: restart apache
      service: name=apache state=restarted

Handlers 最佳的应用场景是用来重启服务,或者触发系统重启操作.除此以外很少用到了.

Note

handlers 会按照声明的顺序执行

Roles 将在下一章节讲述.值得指出的是,handlers 会在 ‘pre_tasks’, ‘roles’, ‘tasks’, 和 ‘post_tasks’ 之间自动执行. 如果你想立即执行所有的 handler 命令,在1.2及以后的版本,你可以这样做:

tasks:
   - shell: some tasks go here
   - meta: flush_handlers
   - shell: some other tasks

在以上的例子中,任何在排队等候的 handlers 会在执行到 ‘meta’ 部分时,优先执行.这个技巧在有些时候也能派上用场.

执行一个 playbook

既然现在你已经学习了 playbook 的语法,那要如何运行一个 playbook 呢?这很简单,这里的示例是并行的运行 playbook,并行的级别 是10(译者注:是10个并发的进程?):

ansible-playbook playbook.yml -f 10

Ansible-Pull(拉取配置而非推送配置)

我们可不可以将 ansible 的体系架构颠倒过来,让托管节点从一个 central location 做 check in 获取配置信息,而不是 推送配置信息到所有的托管节点?是可以的.

Ansible-pull 是一个小脚本,它从 git 上 checkout 一个关于配置指令的 repo,然后以这个配置指令来运行 ansible-playbook.

假设你对你的 checkout location 做负载均衡,ansible-pull 基本上可以无限的提升规模.

可执行 ansible-pull --help 获取详细的帮助信息.

也有一个叫做 clever playbook 的东西: clever playbook . 这个可以通过 crontab 来配置 ansible-pull(from push mode).

提示与技巧

在 playbook 执行输出信息的底部,可以找到关于托管节点的信息.也可看到一般的失败信息,和严重的 “unreachable” 信息. 这两个是分开计数的.

如果你想看到执行成功的 modules 的输出信息,使用 --verbose flag(否则只有执行失败的才会有输出信息).这在 0.5 及以后的版本中可用.

如果安装了 cowsay 软件包,ansible playbook 的输出已经进行了广泛的升级.可以尝试一下!

在执行一个 playbook 之前,想看看这个 playbook 的执行会影响到哪些 hosts,你可以这样做:

ansible-playbook playbook.yml --list-hosts

See also

YAML 语法
Learn about YAML syntax
最佳实践
Various tips about managing playbooks in the real world
Ansible 文档
Hop back to the documentation index for a lot of special topics about playbooks
模块相关
Learn about available modules
Developing Modules
Learn how to extend Ansible by writing your own modules
Patterns
Learn about how to select hosts
Github examples directory
Complete end-to-end playbook examples
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups

Playbook Roles and Include Statements

介绍

当 playbook 文件越来越大的时候(你可以跳出来去学习 playbooks 了),最后一定会有文件重用时候,此刻就需要我们来重新组织 playbooks 了.

从最基本来讲, task files 请允许我们拆分配置策略到多个小文件. Task 可以从其它文件中读取 tasks. 因为 handler 也是tasks, 所以你也可以在在 ‘handlers:’ 区域引用 handler 文件.

你可以查阅 Playbooks 来复习该章节内容.

Playbooks 也可以引用其它 playbooks 文件中的命令条目.当所有文件均读取完毕后, 所有的命令条目将被插入到一个 playbook 中组合成一条长的命令列表.

当你开始思考 – tasks, handlers, variables等 – 开始形成大的想法概念,当你开始创造一些东西,而非模仿某些东西. It’s no longer “apply this handful of THINGS to these hosts” ,你决定 “这些 hosts 是 dbservers” 或者 这些 hosts 是 webservers”. 在编程语言中,我们称之为”封装”.举个例子,你会开车但不需要知道发动机工作原理.

Roles 在 Ansible中起到的宗旨是把配置文件整合到一起并最大程度保证其干净整洁,可重用 – 他允许我们把重点放在大局上只有在需要的时候才再深入了解.

了解 includes 的对深入 roles 有重要意义,但我们最终的目标是理解 roles – roles是非常伟大的产品,所以当我们写 playbooks 时一定要使用 roles.

阅读 ansible-examples <https://github.com/ansible/ansible-examples> 获取更多实例. 你可以打开单独的页面进行深入学习.

Task Include Files 和鼓励重用机制

猜想你希望在不同 tasks 之间plays 和 playbooks 可以重复调用. include files可以达成目的. 系统通过使用 include task 来完美实现 role 定义. 记住, playbook 中的 play 最终目的是映射系统群到多 roles中. 我们来举个例子吧...

只简单包括 tasks 的 task 文件如下示例:

---
# possibly saved as tasks/foo.yml

- name: placeholder foo
  command: /bin/foo

- name: placeholder bar
  command: /bin/bar

Include 指令类似如下,可以像普通 tasks 命令一样在 playbook 中混合使用:

tasks:

  - include: tasks/foo.yml

你也可以传输变量到 includes 指令, 我们称之为 ‘parameterized include’.

例如,如何分发多个 wordpress 实例,我可以包涵所有 wordpress 命令到一个 wordpress.yml 文件,按如下方式使用:

tasks:
  - include: wordpress.yml wp_user=timmy
  - include: wordpress.yml wp_user=alice
  - include: wordpress.yml wp_user=bob

如果你使用的是 Ansible 1.4以上版本(包括1.4), include 语法简化了匹配 roles, 同时允许传递参数列表和字典:

tasks:
 - { include: wordpress.yml, wp_user: timmy, ssh_keys: [ 'keys/one.txt', 'keys/two.txt' ] }

使用任意一种语法, 变量传递均可以在 included 文件中被使用. 我们将在 Variables 详细讨论. 你可以这样引用他们:

{{ wp_user }}

(除了明确声明定义参数,所有 vars 区块定义的变量在这里同样适用.)

从1.0开始, ansible 还支持另外一种变量传参到 include files 的方式-结构化变量,方式如下:

tasks:

  - include: wordpress.yml
    vars:
        wp_user: timmy
        ssh_keys:
          - keys/one.txt
          - keys/two.txt

Playbooks 也同样可以 include 引用其它 playbooks,但这部分内容将在另外章节介绍.

Note

截止1.0版本,task include 声明可以在任意层级目录使用.在这之前,变量只能同层级目录引用,所以 task includes 不能引用其实包含有 task includes 引用的task文件.

Includes 功能也可以被用在用 handlers 区域,例如,如果你希望定义如何重启apache,你只需要定义一个playbook,只需要做一次.编辑类似如下样例的 handers.yml:

---
# this might be in a file like handlers/handlers.yml
- name: restart apache
  service: name=apache state=restarted

然后像如下方式在 main playbook 的询问引用play即可:

handlers:
  - include: handlers/handlers.yml

Includes也可以在常规不包含 included 的tasks和handlers文件中混合引用. Includes常被用作将一个playbook文件中的命令导入到另外一个playbook.这种方式允许我们定义由其它playbooks组成的顶层playbook(top-level playbook).

For example:

- name: this is a play at the top level of a file
  hosts: all
  remote_user: root

  tasks:

  - name: say hi
    tags: foo
    shell: echo "hi..."

- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml

注意: 引用playbook到其它playbook时,变量替换功能将失效不可用.

Note

你不能有条件的指定位置的 include 文件,就像在你使用 ‘vars_files’ 时一样. 如果你发展你必须这么做,那请重新规划调整 playbook 的class/role 编排.这样 说其实是想明确告诉你不要妄想 include 指定位置的file. 所有被包含在 play 中的主机都将执行相同的tasks.(‘when‘提供了一些指定条件来跳过tasks)

Roles

New in version 1.2.

Now that you have learned about tasks and handlers, what is the best way to organize your playbooks? The short answer is to use roles! Roles are ways of automatically loading certain vars_files, tasks, and handlers based on a known file structure. Grouping content by roles also allows easy sharing of roles with other users.

Roles are just automation around ‘include’ directives as described above, and really don’t contain much additional magic beyond some improvements to search path handling for referenced files. However, that can be a big thing!

Example project structure:

site.yml
webservers.yml
fooservers.yml
roles/
   common/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/
   webservers/
     files/
     templates/
     tasks/
     handlers/
     vars/
     defaults/
     meta/

In a playbook, it would look like this:

---
- hosts: webservers
  roles:
     - common
     - webservers

This designates the following behaviors, for each role ‘x’:

  • If roles/x/tasks/main.yml exists, tasks listed therein will be added to the play
  • If roles/x/handlers/main.yml exists, handlers listed therein will be added to the play
  • If roles/x/vars/main.yml exists, variables listed therein will be added to the play
  • If roles/x/meta/main.yml exists, any role dependencies listed therein will be added to the list of roles (1.3 and later)
  • Any copy tasks can reference files in roles/x/files/ without having to path them relatively or absolutely
  • Any script tasks can reference scripts in roles/x/files/ without having to path them relatively or absolutely
  • Any template tasks can reference files in roles/x/templates/ without having to path them relatively or absolutely
  • Any include tasks can reference files in roles/x/tasks/ without having to path them relatively or absolutely

In Ansible 1.4 and later you can configure a roles_path to search for roles. Use this to check all of your common roles out to one location, and share them easily between multiple playbook projects. See Ansible的配置文件 for details about how to set this up in ansible.cfg.

Note

Role dependencies are discussed below.

If any files are not present, they are just ignored. So it’s ok to not have a ‘vars/’ subdirectory for the role, for instance.

Note, you are still allowed to list tasks, vars_files, and handlers “loose” in playbooks without using roles, but roles are a good organizational feature and are highly recommended. If there are loose things in the playbook, the roles are evaluated first.

Also, should you wish to parameterize roles, by adding variables, you can do so, like this:

---

- hosts: webservers
  roles:
    - common
    - { role: foo_app_instance, dir: '/opt/a',  port: 5000 }
    - { role: foo_app_instance, dir: '/opt/b',  port: 5001 }

While it’s probably not something you should do often, you can also conditionally apply roles like so:

---

- hosts: webservers
  roles:
    - { role: some_role, when: "ansible_os_family == 'RedHat'" }

This works by applying the conditional to every task in the role. Conditionals are covered later on in the documentation.

Finally, you may wish to assign tags to the roles you specify. You can do so inline::

---

- hosts: webservers
  roles:
    - { role: foo, tags: ["bar", "baz"] }

If the play still has a ‘tasks’ section, those tasks are executed after roles are applied.

If you want to define certain tasks to happen before AND after roles are applied, you can do this:

---

- hosts: webservers

  pre_tasks:
    - shell: echo 'hello'

  roles:
    - { role: some_role }

  tasks:
    - shell: echo 'still busy'

  post_tasks:
    - shell: echo 'goodbye'

Note

If using tags with tasks (described later as a means of only running part of a playbook), be sure to also tag your pre_tasks and post_tasks and pass those along as well, especially if the pre and post tasks are used for monitoring outage window control or load balancing.

Role Default Variables

New in version 1.3.

Role default variables allow you to set default variables for included or dependent roles (see below). To create defaults, simply add a defaults/main.yml file in your role directory. These variables will have the lowest priority of any variables available, and can be easily overridden by any other variable, including inventory variables.

Role Dependencies

New in version 1.3.

Role dependencies allow you to automatically pull in other roles when using a role. Role dependencies are stored in the meta/main.yml file contained within the role directory. This file should contain a list of roles and parameters to insert before the specified role, such as the following in an example roles/myapp/meta/main.yml:

---
dependencies:
  - { role: common, some_parameter: 3 }
  - { role: apache, port: 80 }
  - { role: postgres, dbname: blarg, other_parameter: 12 }

Role dependencies can also be specified as a full path, just like top level roles:

---
dependencies:
   - { role: '/path/to/common/roles/foo', x: 1 }

Role dependencies can also be installed from source control repos or tar files (via galaxy) using comma separated format of path, an optional version (tag, commit, branch etc) and optional friendly role name (an attempt is made to derive a role name from the repo name or archive filename). Both through the command line or via a requirements.yml passed to ansible-galaxy.

Roles dependencies are always executed before the role that includes them, and are recursive. By default, roles can also only be added as a dependency once - if another role also lists it as a dependency it will not be run again. This behavior can be overridden by adding allow_duplicates: yes to the meta/main.yml file. For example, a role named ‘car’ could add a role named ‘wheel’ to its dependencies as follows:

---
dependencies:
- { role: wheel, n: 1 }
- { role: wheel, n: 2 }
- { role: wheel, n: 3 }
- { role: wheel, n: 4 }

And the meta/main.yml for wheel contained the following:

---
allow_duplicates: yes
dependencies:
- { role: tire }
- { role: brake }

The resulting order of execution would be as follows:

tire(n=1)
brake(n=1)
wheel(n=1)
tire(n=2)
brake(n=2)
wheel(n=2)
...
car

Note

Variable inheritance and scope are detailed in the Variables.

Embedding Modules In Roles

This is an advanced topic that should not be relevant for most users.

If you write a custom module (see Developing Modules) you may wish to distribute it as part of a role. Generally speaking, Ansible as a project is very interested in taking high-quality modules into ansible core for inclusion, so this shouldn’t be the norm, but it’s quite easy to do.

A good example for this is if you worked at a company called AcmeWidgets, and wrote an internal module that helped configure your internal software, and you wanted other people in your organization to easily use this module – but you didn’t want to tell everyone how to configure their Ansible library path.

Alongside the ‘tasks’ and ‘handlers’ structure of a role, add a directory named ‘library’. In this ‘library’ directory, then include the module directly inside of it.

Assuming you had this:

roles/
   my_custom_modules/
       library/
          module1
          module2

The module will be usable in the role itself, as well as any roles that are called after this role, as follows:

- hosts: webservers
  roles:
    - my_custom_modules
    - some_other_role_using_my_custom_modules
    - yet_another_role_using_my_custom_modules

This can also be used, with some limitations, to modify modules in Ansible’s core distribution, such as to use development versions of modules before they are released in production releases. This is not always advisable as API signatures may change in core components, however, and is not always guaranteed to work. It can be a handy way of carrying a patch against a core module, however, should you have good reason for this. Naturally the project prefers that contributions be directed back to github whenever possible via a pull request.

Ansible Galaxy

Ansible Galaxy is a free site for finding, downloading, rating, and reviewing all kinds of community developed Ansible roles and can be a great way to get a jumpstart on your automation projects.

You can sign up with social auth, and the download client ‘ansible-galaxy’ is included in Ansible 1.4.2 and later.

Read the “About” page on the Galaxy site for more information.

See also

Ansible Galaxy
How to share roles on galaxy, role management
YAML 语法
Learn about YAML syntax
Playbooks
Review the basic Playbook language features
最佳实践
Various tips about managing playbooks in the real world
Variables
All about variables in playbooks
条件选择
Conditionals in playbooks
循环
Loops in playbooks
模块相关
Learn about available modules
Developing Modules
Learn how to extend Ansible by writing your own modules
GitHub Ansible examples
Complete playbook files from the GitHub project source
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups

Variables

已经存在的自动化技术使得重复做事变得更加容易,但你的所有系统有时则不会这样. 在有些系统中你想设置一些行为或者配置,这与其它系统稍有不同.

并且,远程系统的可视行为或状态会影响我们配置这些系统.(比如你需要得到一个系统的IP地址,甚至用该值来配置另一个系统).

你可能有一些非常相似的模板或配置文件,而有些变量则稍微不同. Ansible中的变量用来处理系统间的不同.

为了理解变量,你也需要深入阅读 条件选择循环.有用的模块(比如”group_by”模块和”when”条件)也可以结合变量使用,用于管理系统间的不同之处.

强烈建议你学习 ansible-examples github代码库,里面有大量使用变量的例子.

合法的变量名

在使用变量之前最好先知道什么是合法的变量名. 变量名可以为字母,数字以及下划线.变量始终应该以字母开头. “foo_port”是个合法的变量名.”foo5”也是. “foo-port”, “foo port”, “foo.port” 和 “12”则不是合法的变量名.

很简单吧,继续往下看.

在Inventory中定义变量

我们已经在其它文档中覆盖了大量关于使用变量的场景,所以这里没多少新的知识点,权当加深记忆.

通常你想基于一个机器位于哪个群组而设置变量.比如,位于波士顿的很多机器会使用 ‘boston.ntp.example.com’ 作为NTP服务器.

请看 Inventory文件 文档来学习在inventory中使用多种方式来定义变量.

在playbook中定义变量

在playbook中,可以直接定义变量,如下所示:

- hosts: webservers
  vars:
    http_port: 80

这种所见即所得的方式非常好.

在文件和role中定义变量

事实上在其它地方我们也讲过这点了. 正如在 Playbook Roles and Include Statements 描述的一样,变量也可以通过文件包含在playbook中,该变量可以作为或者不作为“Ansible Role”的一部分.使用role是首选,因为它提供了一个很好的组织体系.

使用变量: 关于Jinja2

我们已经知道很多关于定义变量的知识,那么你知道如何使用它们吗?

Ansible允许你使用Jinja2模板系统在playbook中引用变量.借助Jinja你能做很多复杂的操作,首先你要学习基本使用. 例如,在简单的模板中你可以这样做:

My amp goes to {{ max_amp_value }}

这就是变量替换最基本的形式. 你也可以在playbook中直接这样用,你偶尔想这样做:

template: src=foo.cfg.j2 dest={{ remote_install_path }}/foo.cfg

In the above example, we used a variable to help decide where to place a file. 在上述的例子中,我们使用变量来决定文件放置在哪里. 在模板中你自动会获取在主机范围之内的所有变量的访问权.事实上更多,你可以读取其它主机的变量.我们将演示如何做.

Note

在模板中Jinja2可以用循环和条件语句,而在playbook中则不行.Ansible playbook是纯粹的机器解析的YAML.这是一个非常重要的功能,这意味着根据文件可以生成代码,或者其它系统工具能够读取Ansible文件.虽然并不是所有人都需要这个功能,但我们不能封锁可能性.

Jinja2过滤器

Note

这并不是常用的特性.只在合适的时候使用它们,这是一个附加知识点.

Jinja2中的过滤器可以把一个模板表达式转换为另一个.Jinja2附带了很多这样的功能.请参见Jinja2官方模板文档中的 builtin filters.

另外,Ansible还支持其它特性.请看 playbooks_filters 文档中关于一系列可用的过滤器及示例.

YAML陷阱

YAML语法要求如果值以{{ foo }}开头的话我们需要将整行用双引号包起来.这是为了确认你不是想声明一个YAML字典.该知识点在 YAML 语法 页面有所讲述.

这样是不行的:

- hosts: app_servers
  vars:
      app_path: {{ base_path }}/22

你应该这么做:

- hosts: app_servers
  vars:
       app_path: "{{ base_path }}/22"

使用Facts获取的信息

还有其它地方可以获取变量,这些变量是自动发现的,而不是用户自己设置的.

Facts通过访问远程系统获取相应的信息. 一个例子就是远程主机的IP地址或者操作系统是什么. 使用以下命令可以查看哪些信息是可用的:

ansible hostname -m setup

这会返回巨量的变量数据,比如对于Ubutu 12.04系统,Ansible 1.4获取的信息显示如下:

"ansible_all_ipv4_addresses": [
    "REDACTED IP ADDRESS"
],
"ansible_all_ipv6_addresses": [
    "REDACTED IPV6 ADDRESS"
],
"ansible_architecture": "x86_64",
"ansible_bios_date": "09/20/2012",
"ansible_bios_version": "6.00",
"ansible_cmdline": {
    "BOOT_IMAGE": "/boot/vmlinuz-3.5.0-23-generic",
    "quiet": true,
    "ro": true,
    "root": "UUID=4195bff4-e157-4e41-8701-e93f0aec9e22",
    "splash": true
},
"ansible_date_time": {
    "date": "2013-10-02",
    "day": "02",
    "epoch": "1380756810",
    "hour": "19",
    "iso8601": "2013-10-02T23:33:30Z",
    "iso8601_micro": "2013-10-02T23:33:30.036070Z",
    "minute": "33",
    "month": "10",
    "second": "30",
    "time": "19:33:30",
    "tz": "EDT",
    "year": "2013"
},
"ansible_default_ipv4": {
    "address": "REDACTED",
    "alias": "eth0",
    "gateway": "REDACTED",
    "interface": "eth0",
    "macaddress": "REDACTED",
    "mtu": 1500,
    "netmask": "255.255.255.0",
    "network": "REDACTED",
    "type": "ether"
},
"ansible_default_ipv6": {},
"ansible_devices": {
    "fd0": {
        "holders": [],
        "host": "",
        "model": null,
        "partitions": {},
        "removable": "1",
        "rotational": "1",
        "scheduler_mode": "deadline",
        "sectors": "0",
        "sectorsize": "512",
        "size": "0.00 Bytes",
        "support_discard": "0",
        "vendor": null
    },
    "sda": {
        "holders": [],
        "host": "SCSI storage controller: LSI Logic / Symbios Logic 53c1030 PCI-X Fusion-MPT Dual Ultra320 SCSI (rev 01)",
        "model": "VMware Virtual S",
        "partitions": {
            "sda1": {
                "sectors": "39843840",
                "sectorsize": 512,
                "size": "19.00 GB",
                "start": "2048"
            },
            "sda2": {
                "sectors": "2",
                "sectorsize": 512,
                "size": "1.00 KB",
                "start": "39847934"
            },
            "sda5": {
                "sectors": "2093056",
                "sectorsize": 512,
                "size": "1022.00 MB",
                "start": "39847936"
            }
        },
        "removable": "0",
        "rotational": "1",
        "scheduler_mode": "deadline",
        "sectors": "41943040",
        "sectorsize": "512",
        "size": "20.00 GB",
        "support_discard": "0",
        "vendor": "VMware,"
    },
    "sr0": {
        "holders": [],
        "host": "IDE interface: Intel Corporation 82371AB/EB/MB PIIX4 IDE (rev 01)",
        "model": "VMware IDE CDR10",
        "partitions": {},
        "removable": "1",
        "rotational": "1",
        "scheduler_mode": "deadline",
        "sectors": "2097151",
        "sectorsize": "512",
        "size": "1024.00 MB",
        "support_discard": "0",
        "vendor": "NECVMWar"
    }
},
"ansible_distribution": "Ubuntu",
"ansible_distribution_release": "precise",
"ansible_distribution_version": "12.04",
"ansible_domain": "",
"ansible_env": {
    "COLORTERM": "gnome-terminal",
    "DISPLAY": ":0",
    "HOME": "/home/mdehaan",
    "LANG": "C",
    "LESSCLOSE": "/usr/bin/lesspipe %s %s",
    "LESSOPEN": "| /usr/bin/lesspipe %s",
    "LOGNAME": "root",
    "LS_COLORS": "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arj=01;31:*.taz=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lz=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.axv=01;35:*.anx=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.axa=00;36:*.oga=00;36:*.spx=00;36:*.xspf=00;36:",
    "MAIL": "/var/mail/root",
    "OLDPWD": "/root/ansible/docsite",
    "PATH": "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "PWD": "/root/ansible",
    "SHELL": "/bin/bash",
    "SHLVL": "1",
    "SUDO_COMMAND": "/bin/bash",
    "SUDO_GID": "1000",
    "SUDO_UID": "1000",
    "SUDO_USER": "mdehaan",
    "TERM": "xterm",
    "USER": "root",
    "USERNAME": "root",
    "XAUTHORITY": "/home/mdehaan/.Xauthority",
    "_": "/usr/local/bin/ansible"
},
"ansible_eth0": {
    "active": true,
    "device": "eth0",
    "ipv4": {
        "address": "REDACTED",
        "netmask": "255.255.255.0",
        "network": "REDACTED"
    },
    "ipv6": [
        {
            "address": "REDACTED",
            "prefix": "64",
            "scope": "link"
        }
    ],
    "macaddress": "REDACTED",
    "module": "e1000",
    "mtu": 1500,
    "type": "ether"
},
"ansible_form_factor": "Other",
"ansible_fqdn": "ubuntu2.example.com",
"ansible_hostname": "ubuntu2",
"ansible_interfaces": [
    "lo",
    "eth0"
],
"ansible_kernel": "3.5.0-23-generic",
"ansible_lo": {
    "active": true,
    "device": "lo",
    "ipv4": {
        "address": "127.0.0.1",
        "netmask": "255.0.0.0",
        "network": "127.0.0.0"
    },
    "ipv6": [
        {
            "address": "::1",
            "prefix": "128",
            "scope": "host"
        }
    ],
    "mtu": 16436,
    "type": "loopback"
},
"ansible_lsb": {
    "codename": "precise",
    "description": "Ubuntu 12.04.2 LTS",
    "id": "Ubuntu",
    "major_release": "12",
    "release": "12.04"
},
"ansible_machine": "x86_64",
"ansible_memfree_mb": 74,
"ansible_memtotal_mb": 991,
"ansible_mounts": [
    {
        "device": "/dev/sda1",
        "fstype": "ext4",
        "mount": "/",
        "options": "rw,errors=remount-ro",
        "size_available": 15032406016,
        "size_total": 20079898624
    }
],
"ansible_nodename": "ubuntu2.example.com",
"ansible_os_family": "Debian",
"ansible_pkg_mgr": "apt",
"ansible_processor": [
    "Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz"
],
"ansible_processor_cores": 1,
"ansible_processor_count": 1,
"ansible_processor_threads_per_core": 1,
"ansible_processor_vcpus": 1,
"ansible_product_name": "VMware Virtual Platform",
"ansible_product_serial": "REDACTED",
"ansible_product_uuid": "REDACTED",
"ansible_product_version": "None",
"ansible_python_version": "2.7.3",
"ansible_selinux": false,
"ansible_ssh_host_key_dsa_public": "REDACTED KEY VALUE"
"ansible_ssh_host_key_ecdsa_public": "REDACTED KEY VALUE"
"ansible_ssh_host_key_rsa_public": "REDACTED KEY VALUE"
"ansible_swapfree_mb": 665,
"ansible_swaptotal_mb": 1021,
"ansible_system": "Linux",
"ansible_system_vendor": "VMware, Inc.",
"ansible_user_id": "root",
"ansible_userspace_architecture": "x86_64",
"ansible_userspace_bits": "64",
"ansible_virtualization_role": "guest",
"ansible_virtualization_type": "VMware"

可以在playbook中这样引用以上例子中第一个硬盘的模型:

{{ ansible_devices.sda.model }}

同样,作为系统报告的主机名如以下所示:

{{ ansible_nodename }}

不合格的主机名显示了句号(.)之前的字符串:

{{ ansible_hostname }}

在模板和条件判断(请看 playbook_conditionals )中会经常使用Facts.

还可以使用Facts根据特定的条件动态创建主机群组,请查看 模块相关 文档中的 ‘group_by’ 小节获取详细内容.以及参见 条件选择 章节讨论的广义条件语句部分.

关闭Facts

如果你不需要使用你主机的任何fact数据,你已经知道了你系统的一切,那么你可以关闭fact数据的获取.这有利于增强Ansilbe面对大量系统的push模块,或者你在实验性平台中使用Ansible.在任何playbook中可以这样做:

- hosts: whatever
  gather_facts: no

本地Facts(Facts.d)

New in version 1.3.

正如在playbook章节讨论的一样,Ansible facts主要用于获取远程系统的数据,从而可以在playbook中作为变量使用.

通常facts中的数据是由Ansible中的 ‘setup’模块自动发现的.用户也可以自定义facts模块,在API文档中有说明.然而,如果不借助于fact模块,而是通过一个简单的方式为Ansible变量提供系统或用户数据?

比如,你想用户能够控制受他们管理的系统的一些切面,那么应该怎么做? “Facts.d”是这样的一种机制.

Note

可能 “局部facts”有点用词不当,它与 “中心供应的用户值”相对应,为”局部供应的用户值”,或者facts是 “局部动态测定的值”.

如果远程受管理的机器有一个 “/etc/ansible/facts.d” 目录,那么在该目录中任何以 ”.fact”结尾的文件都可以在Ansible中提供局部facts.这些文件可以是JSON,INI或者任何可以返回JSON的可执行文件.

例如建设有一个 /etc/ansible/facts.d/perferences.fact文件:

[general]
asdf=1
bar=2

这将产生一个名为 “general” 的哈希表fact,里面成员有 ‘asdf’ 和 ‘bar’. 可以这样验证:

ansible <hostname> -m setup -a "filter=ansible_local"

然后你会看到有以下fact被添加:

"ansible_local": {
        "preferences": {
            "general": {
                "asdf" : "1",
                "bar"  : "2"
            }
        }
 }

而且也可以在template或palybook中访问该数据:

{{ ansible_local.preferences.general.asdf }}

本地命名空间放置其它用户提供的fact或者playbook中定义的变量覆盖系统facts值.

如果你有个一个playook,它复制了一个自定义的fact,然后运行它,请显式调用来重新运行setup模块,这样可以让我们在该playbook中使用这些fact.否则,在下一个play中才能获取这些自定义的fact信息.这里有一个示例:

- hosts: webservers
  tasks:
    - name: create directory for ansible custom facts
      file: state=directory recurse=yes path=/etc/ansible/facts.d
    - name: install custom impi fact
      copy: src=ipmi.fact dest=/etc/ansible/facts.d
    - name: re-read facts after adding custom fact
      setup: filter=ansible_local

然而在该模式中你也可以编写一个fact模块,这只不过是多了一个选项.

Fact缓存

New in version 1.8.

正如该文档中其它地方所示,从一个服务器引用另一个服务器的变量是可行的.比如:

{{ hostvars['asdf.example.com']['ansible_os_family'] }}

如果禁用 “Fact Caching”,为了实现以上功能,Ansible在当前play之前已经与 ‘asdf.example.com’ 通讯过,或者在playbook有其它优先的play.这是ansible的默认配置.

为了避免这些,Ansible 1.8允许在playbook运行期间保存facts.但该功能需要手动开启.这有什么用处那?

想象一下,如果我们有一个非常大的基础设施,里面有数千个主机.Fact缓存可以配置在夜间运行,但小型服务器集群可以配置fact随时运行,或者在白天定期运行.即使开启了fact缓存,也不需要访问所有服务器来引用它们的变量和信息.

使用fact缓存可以跨群组访问变量,即使群组间在当前/user/bin/ansible-playbook执行中并没有通讯过.

为了启用fact缓存,在大多数plays中你可以修改 ‘gathering’ 设置为 ‘smart’ 或者 ‘explicit’,也可以设置 ‘gather_facts’ 为False.

当前,Ansible可以使用两种持久的缓存插件: redis和jsonfile.

可以在ansible.cfg中配置fact缓存使用redis:

[defaults]
gathering = smart
fact_caching = redis
fact_caching_timeout = 86400
# seconds

请执行适当的系统命令来启动和运行redis:

yum install redis
service redis start
pip install redis

请注意可以使用pip来安装Python redis库,在EPEL中的包版本对Ansible来说太旧了. 在当前Ansible版本中,该功能还处于试用状态,Redis插件还不支持端口或密码配置,以后会改善这点. 在ansible.cfg中使用以下代码来配置fact缓存使用jsonfile:

[defaults]
gathering = smart
fact_caching = jsonfile
fact_caching_connection = /path/to/cachedir
fact_caching_timeout = 86400
# seconds

fact_caching_connection 是一个放置在可读目录(如果目录不存在,ansible会试图创建它)中的本地文件路径.

注册变量

变量的另一个主要用途是在运行命令时,把命令结果存储到一个变量中.不同模块的执行结果是不同的.运行playbook时使用-v选项可以看到可能的结果值. 在ansible执行任务的结果值可以保存在变量中,以便稍后使用它.在 条件选择 章节有一些示例.

这里有一个语法示例,在上面文档中也有所提及:

- hosts: web_servers

  tasks:

     - shell: /usr/bin/foo
       register: foo_result
       ignore_errors: True

     - shell: /usr/bin/bar
       when: foo_result.rc == 5

在当前主机接下来playbook运行过程中注册的变量是有效地.这与Ansile中的 “facts” 生命周期一样. 实际上注册变量和facts很相似.

访问复杂变量数据

在该文档中我们已经讨论了一些与facts有关的高级特性.

有些提供的facts,比如网络信息等,是一个嵌套的数据结构.访问它们使用简单的 {{ foo }} 语法并不够用,当仍然很容易.如下所示:

{{ ansible_eth0["ipv4"]["address"] }}

或者这样写:

{{ ansible_eth0.ipv4.address }}

相似的,以下代码展示了我们如何访问数组的第一个元素:

{{ foo[0] }}

魔法变量,以及如何访问其它主机的信息

Ansible会自动提供给你一些变量,即使你并没有定义过它们.这些变量中重要的有 ‘hostvars’,’group_names’,和 ‘groups’.由于这些变量名是预留的,所以用户不应当覆盖它们. ‘environmen’ 也是预留的. hostvars可以让你访问其它主机的变量,包括哪些主机中获取到的facts.如果你还没有在当前playbook或者一组playbook的任何play中访问那个主机,那么你可以获取变量,但无法看到facts值. 如果数据库服务器想使用另一个节点的某个 ‘fact’ 值,或者赋值给该节点的一个inventory变量.可以在一个模板中甚至命令行中轻松实现:

{{ hostvars['test.example.com']['ansible_distribution'] }}

另外, group_names 是当前主机所在所有群组的列表(数组).所以可以使用Jinja2语法在模板中根据该主机所在群组关系(或角色)来产生变化:

{% if 'webserver' in group_names %}
   # some part of a configuration file that only applies to webservers
{% endif %}

groups 是inventory中所有群组(主机)的列表.可用于枚举群组中的所有主机.例如:

{% for host in groups['app_servers'] %}
   # something that applies to all app servers.
{% endfor %}

一个经常使用的范式是找出该群组中的所有IP地址:

{% for host in groups['app_servers'] %}
   {{ hostvars[host]['ansible_eth0']['ipv4']['address'] }}
{% endfor %}

比如,一个前端代理服务器需要指向所有的应用服务器,在服务器间设置正确的防火墙规则等.你需要确保所有主机的facts在使用前都已被获取到,例如运行一个play来检查这些facts是否已经被缓存起来(fact缓存是Ansible 1.8中的新特性).

另外, inventory_hostname 是Ansible inventory主机文件中配置的主机名称.由于其它一些神秘原因你不想使用自发现的主机名 ansible_hostname 时,你可以使用 inventory_hostname .如果主机的FQDN很长,那么*inventory_hostname_short*则会只包含域名第一个分号之前的部分,而舍弃其它部分.

play_hosts 是在当前play范围中可用的一组主机名.比如可以为多个主机填写模板,以便将这些主机注入负载均衡器规则.

delegate_to is the inventory hostname of the host that the current task has been delegated to using ‘delegate_to’.

delegate_to 是使用 ‘delegate_to’ 代理的任务中主机的inventory主机名.

不要担心以上东西,除非你需要使用它们.你会知道什么时候用它们.

inventory_dir 是保存Ansible inventory主机文件的目录路径, inventory_file 是指向Ansible inventory主机文件的路径和文件名.

最后, role_path 会返回当前role的目录名(1.8及以后).只有在role中才能使用该变量.

变量文件分割

把playbook置于源代码管理之下是个很好的注意,当你可能会想把playbook源码公开之余还想保持某些重要的变量私有.有时你也想把某些信息放置在不同的文件中,远离主playbook文件.

你可以使用外部的变量文件来实现:

---

- hosts: all
  remote_user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

  tasks:

  - name: this is just a placeholder
    command: /bin/echo foo

这可以保证你共享playbook源码时隔离敏感数据的风险.

每个变量文件的内容是一个简单的YAML文件,如下所示:

---
# in the above example, this would be vars/external_vars.yml
somevar: somevalue
password: magic

Note

It’s also possible to keep per-host and per-group variables in very similar files, this is covered in 分文件定义 Host 和 Group 变量.

Note

保持每个主机和群组的变量在非常小的文件中是可能,请参见 分文件定义 Host 和 Group 变量.

命令行中传递变量

除了`vars_prompt`和`vars_files`也可以通过Ansible命令行发送变量.如果你想编写一个通用的发布playbook时则特别有用,你可以传递应用的版本以便部署:

ansible-playbook release.yml --extra-vars "version=1.23.45 other_variable=foo"

其它场景中也很有用,比如为playbook设置主机群组或用户.

Example:

---

- hosts: '{{ hosts }}'
  remote_user: '{{ user }}'

  tasks:
     - ...

ansible-playbook release.yml --extra-vars "hosts=vipers user=starbuck"

Ansible 1.2中你也可以给extra-vars传递JSON,比如:

--extra-vars '{"pacman":"mrs","ghosts":["inky","pinky","clyde","sue"]}'

key=value形式非常简单,但很实用!

Ansible 1.3中,实用”@”语法可以为extra-vars传递JSON文件:

--extra-vars "@some_file.json"

同样在Ansible 1.3中,我们可以为extra-vars传递YAML格式,无论直接通过命令行还是放置在文件中.

变量的优先级: 我该在什么地方放置变量?

很多人都在问变量重载的规则是怎么样的.最终Ansible的哲学是你最好知道哪里放置变量,然后会简化变量覆盖的复杂度.

避免在47个地方定义 “x” 变量然后询问 “那个x会被使用”. 为什么那? 因为这不是Ansible做事的哲学.

世界上只有一个帝国大厦.也只有一个蒙娜丽莎.请弄明白在那里定义变量,而不要把事情搞复杂.

然而,我们还是来讨卵一下优先权的问题.它存在.你有可能会用到它.

如果同样名称的变量在多个地方都有定义,那么采纳是有个确定的顺序,如下:

* extra vars (-e in the command line) always win
* then comes connection variables defined in inventory (ansible_ssh_user, etc)
* then comes "most everything else" (command line switches, vars in play, included vars, role vars, etc)
* then comes the rest of the variables defined in inventory
* then comes facts discovered about a system
* then "role defaults", which are the most "defaulty" and lose in priority to everything.

* extra vars (在命令行中使用 -e)优先级最高
* 然后是在inventory中定义的连接变量(比如ansible_ssh_user)
* 接着是大多数的其它变量(命令行转换,play中的变量,included的变量,role中的变量等)
* 然后是在inventory定义的其它变量
* 然后是由系统发现的facts
* 然后是 "role默认变量", 这个是最默认的值,很容易丧失优先权

Note

In versions prior to 1.5.4, facts discovered about a system were in the “most everything else” category above.

Note

在1.5.4版本级以后,关于系统的自发现的facts也包含在大多数的其它变量中.

这样看起来太理论化了.让我们来看一段示例,

First off, group variables are super powerful.

Site wide defaults should be defined as a ‘group_vars/all’ setting. Group variables are generally placed alongside your inventory file. They can also be returned by a dynamic inventory script (see 动态 Inventory) or defined in things like Ansible Tower from the UI or API:

---
# file: /etc/ansible/group_vars/all
# this is the site wide default
ntp_server: default-time.example.com

Regional information might be defined in a ‘group_vars/region’ variable. If this group is a child of the ‘all’ group (which it is, because all groups are), it will override the group that is higher up and more general:

---
# file: /etc/ansible/group_vars/boston
ntp_server: boston-time.example.com

If for some crazy reason we wanted to tell just a specific host to use a specific NTP server, it would then override the group variable!:

---
# file: /etc/ansible/host_vars/xyz.boston.example.com
ntp_server: override.example.com

So that covers inventory and what you would normally set there. It’s a great place for things that deal with geography or behavior. Since groups are frequently the entity that maps roles onto hosts, it is sometimes a shortcut to set variables on the group instead of defining them on a role. You could go either way.

Remember: Child groups override parent groups, and hosts always override their groups.

Next up: learning about role variable precedence.

We’ll pretty much assume you are using roles at this point. You should be using roles for sure. Roles are great. You are using roles aren’t you? Hint hint.

Ok, so if you are writing a redistributable role with reasonable defaults, put those in the ‘roles/x/defaults/main.yml’ file. This means the role will bring along a default value but ANYTHING in Ansible will override it. It’s just a default. That’s why it says “defaults” :) See Playbook Roles and Include Statements for more info about this:

---
# file: roles/x/defaults/main.yml
# if not overridden in inventory or as a parameter, this is the value that will be used
http_port: 80

if you are writing a role and want to ensure the value in the role is absolutely used in that role, and is not going to be overridden by inventory, you should put it in roles/x/vars/main.yml like so, and inventory values cannot override it. -e however, still will:

---
# file: roles/x/vars/main.yml
# this will absolutely be used in this role
http_port: 80

So the above is a great way to plug in constants about the role that are always true. If you are not sharing your role with others, app specific behaviors like ports is fine to put in here. But if you are sharing roles with others, putting variables in here might be bad. Nobody will be able to override them with inventory, but they still can by passing a parameter to the role.

Parameterized roles are useful.

If you are using a role and want to override a default, pass it as a parameter to the role like so:

roles:
   - { role: apache, http_port: 8080 }

This makes it clear to the playbook reader that you’ve made a conscious choice to override some default in the role, or pass in some configuration that the role can’t assume by itself. It also allows you to pass something site-specific that isn’t really part of the role you are sharing with others.

This can often be used for things that might apply to some hosts multiple times, like so:

roles:
   - { role: app_user, name: Ian    }
   - { role: app_user, name: Terry  }
   - { role: app_user, name: Graham }
   - { role: app_user, name: John   }

That’s a bit arbitrary, but you can see how the same role was invoked multiple Times. In that example it’s quite likely there was no default for ‘name’ supplied at all. Ansible can yell at you when variables aren’t defined – it’s the default behavior in fact.

So that’s a bit about roles.

There are a few bonus things that go on with roles.

Generally speaking, variables set in one role are available to others. This means if you have a “roles/common/vars/main.yml” you can set variables in there and make use of them in other roles and elsewhere in your playbook:

roles:
   - { role: common_settings }
   - { role: something, foo: 12 }
   - { role: something_else }

Note

There are some protections in place to avoid the need to namespace variables. In the above, variables defined in common_settings are most definitely available to ‘something’ and ‘something_else’ tasks, but if “something’s” guaranteed to have foo set at 12, even if somewhere deep in common settings it set foo to 20.

So, that’s precedence, explained in a more direct way. Don’t worry about precedence, just think about if your role is defining a variable that is a default, or a “live” variable you definitely want to use. Inventory lies in precedence right in the middle, and if you want to forcibly override something, use -e.

If you found that a little hard to understand, take a look at the ansible-examples repo on our github for a bit more about how all of these things can work together.

如果你还感觉有点难以理解,你可以学习我们放在github中的 ansible-examples 代码库,来了解这些东西是如何一起协作的.

See also

Playbooks
An introduction to playbooks
条件选择
Conditional statements in playbooks
playbooks_filters
Jinja2 filters and their uses
循环
Looping in playbooks
Playbook Roles and Include Statements
Playbook organization by roles
最佳实践
Best practices in playbooks
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

条件选择

常常来说,一个play的结果经常取决于一个变量的值,事件(从远端系统得到事件), 或者之前任务的结果.在有些情况下,这些变量的值也会取决于其他变量. 进而,可以建立多余的组基于这些主机是否符合某些条件来操控主机, Ansible 提供了很多不同选项,来控制执行流. 让我们详细看看这些都是啥.

When 语句

有时候用户有可能需要某一个主机越过某一个特定的步骤.这个过程就可以简单的像在某一个特定版本的系统上 少装了一个包一样或者像在一个满了的文件系统上执行清理操作一样. 这些操作在Ansible上,若使用`when`语句都异常简单.When语句包含Jinja2表达式(参见:doc:playbooks_variables). 实际上真的很简单:

tasks:
  - name: "shutdown Debian flavored systems"
    command: /sbin/shutdown -t now
    when: ansible_os_family == "Debian"

一系列的Jinja2 “过滤器” 也可以在when语句中使用, 但有些是Ansible中独有的. 比如我们想忽略某一错误,通过执行成功与否来做决定,我们可以像这样:

tasks:
  - command: /bin/false
    register: result
    ignore_errors: True
  - command: /bin/something
    when: result|failed
  - command: /bin/something_else
    when: result|success
  - command: /bin/still/something_else
    when: result|skipped

我知道,在这里讨论’register’语句命令,有点过于超前,我们将会在这议长靠后一点的地方接触这个.

友情提示,如果想查看哪些事件在某个特定系统中时允许的,可以执行以下命令:

ansible hostname.example.com -m setup

提示: 有些时候你得到一个返回参数的值是一个字符串,并且你还想使用数学操作来比较它,那么你可以执行一下操作:

tasks:
  - shell: echo "only on Red Hat 6, derivatives, and later"
    when: ansible_os_family == "RedHat" and ansible_lsb.major_release|int >= 6

Note

以上事例需要目标主机上安装lsb_release软件包,来返回ansible_lsb.major_release 事件.

在playbooks 和 inventory中定义的变量都可以使用. 下面一个例子,就是基于布尔值来决定一个任务是否被执行:

vars:
  epic: true

一个条件选择执行也许看起来像这样:

tasks:
    - shell: echo "This certainly is epic!"
      when: epic

或者像这样:

tasks:
    - shell: echo "This certainly isn't epic!"
      when: not epic

如果一个变量不存在,你可以使用Jinja2的`defined`命令跳过或略过.例如:

tasks:
    - shell: echo "I've got '{{ foo }}' and am not afraid to use it!"
      when: foo is defined

    - fail: msg="Bailing out. this play requires 'bar'"
      when: bar is not defined

这个机制在选择引入变量文件时有时候特别有用,详情如下.

note当同时使用`when`he`with_items` (详见:doc:playbooks_loops), note`when`语句对于不同项目将会单独处理.这个源于原初设计:

tasks:
    - command: echo {{ item }}
      with_items: [ 0, 2, 4, 6, 8, 10 ]
      when: item > 5

加载客户事件

加载客户自己的事件,事实上也很简单,将在:doc:developing_modules 详细介绍.只要调用客户自己的事件,进而把所有的模块放在任务列表顶端, 变量的返回值今后就可以访问了:

tasks:
    - name: gather site specific fact data
      action: site_facts
    - command: /usr/bin/thingy
      when: my_custom_fact_just_retrieved_from_the_remote_system == '1234'

在roles 和 includes 上面应用’when’语句

note,如果你的很多任务都共享同样的条件语句的话,可以在选择语句后面添加inlcudes语句,参见下面事例. 这个特性并不适用于playbook的inclues,只有task 的 includes适用.所有的task都会被检验, 选择会应用到所有的task上面:

- include: tasks/sometasks.yml
  when: "'reticulating splines' in output"

或者应用于role:

- hosts: webservers
  roles:
     - { role: debian_stock_config, when: ansible_os_family == 'Debian' }

在系统中使用这个方法但是并不能匹配某些标准时,你会发现在Ansible中,有很多默认’skipped’的结果. 详情参见:doc:modules 文档中的 ‘group_by’ 模块, 你会找到更加赏心悦目的方法来解决这个问题.

条件导入

Note

这是一个很高级但是却被经常使用的话题.当然你也可以跳过这一节.

基于某个特定标准,又是你也许在一个playbook中你想以不同的方式做同一件事. 在不同平台或操作系统上使用痛一个playbook就是一个很好的例子.

举个例子,名字叫做Apache的包,在CentOS 和 Debian系统中也许不同, 但是这个问题可以一些简单的语法就可以被Ansible Playbook解决:

---
- hosts: all
  remote_user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is running
    service: name={{ apache }} state=running

Note

‘ansible_os_family’ 已经被导入到为vars_files定义的文件名列表中了.

提醒一下,很多的不同的YAML文件只是包含键和值:

---
# for vars/CentOS.yml
apache: httpd
somethingelse: 42

这个具体事怎么工作的呢? 如果操作系统是’CentOS’, Ansible导入的第一个文件将是’vars/CentOS.yml’,紧接着 是’/var/os_defaults.yml’,如果这个文件不存在.而且在列表中没有找到,就会报错. 在Debian,最先查看的将是’vars/Debian.yml’而不是’vars/CentOS.yml’, 如果没找到,则寻找默认文件’vars/os_defaults.yml’ 很简单.如果使用这个条件性导入特性,你需要在运行playbook之前安装facter 或者 ohai.当然如果你喜欢, 你也可以把这个事情推给Ansible来做:

# for facter
ansible -m yum -a "pkg=facter state=present"
ansible -m yum -a "pkg=ruby-json state=present"

# for ohai
ansible -m yum -a "pkg=ohai state=present"

Ansible 中的设置方式———— 从任务中把参数分开,这样可避免代码中有太多丑陋嵌套if等复杂语句. 这样可以使得配置条目更加的流畅的赏心悦目———— 特别是因为这样可以尽量减少决定点

基于变量选择文件和模版

Note

这是一个经常用到的高级话题.也可以跳过这章.

有时候,你想要复制一个配置文件,或者一个基于参数的模版. 下面的结构选载选第一个宿主给予的变量文件,这些可以比把很多if选择放在模版里要简单的多. 下面的例子展示怎样根据不同的系统,例如CentOS,Debian制作一个配置文件的模版:

- name: template a file
   template: src={{ item }} dest=/etc/myapp/foo.conf
   with_first_found:
     - files:
        - {{ ansible_distribution }}.conf
        - default.conf
       paths:
        - search_location_one/somedir/
        - /opt/other_location/somedir/

注册变量

经常在playbook中,存储某个命令的结果在变量中,以备日后访问是很有用的. 这样使用命令模块可以在许多方面除去写站(site)特异事件,据哥例子 你可以检测某一个特定程序是否存在

这个 ‘register’ 关键词决定了把结果存储在哪个变量中.结果参数可以用在模版中,动作条目,或者 when 语句. 像这样(这是一个浅显的例子):

- name: test play
  hosts: all

  tasks:

      - shell: cat /etc/motd
        register: motd_contents

      - shell: echo "motd contains the word hi"
        when: motd_contents.stdout.find('hi') != -1

就像上面展示的那样,这个注册后的参数的内容为字符串’stdout’是可以访问. 这个注册了以后的结果,如果像上面展示的,可以转化为一个list(或者已经是一个list),就可以在任务中的”with_items”中使用. “stdout_lines”在对象中已经可以访问了,当然如果你喜欢也可以调用 “home_dirs.stdout.split()” , 也可以用其它字段切割:

- name: registered variable usage as a with_items list
  hosts: all

  tasks:

      - name: retrieve the list of home directories
        command: ls /home
        register: home_dirs

      - name: add home dirs to the backup spooler
        file: path=/mnt/bkspool/{{ item }} src=/home/{{ item }} state=link
        with_items: home_dirs.stdout_lines
        # same as with_items: home_dirs.stdout.split()

See also

Playbooks
An introduction to playbooks
Playbook Roles and Include Statements
Playbook organization by roles
最佳实践
Best practices in playbooks
条件选择
Conditional statements in playbooks
Variables
All about variables
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

循环

通常你想在一个任务中干很多事,比如创建一群用户,安装很多包,或者重复一个轮询步骤直到收到某个特定结果.

本章将对在playbook中如何使用循环做全面的介绍.

标准循环

为了保持简洁,重复的任务可以用以下简写的方式:

- name: add several users
  user: name={{ item }} state=present groups=wheel
  with_items:
     - testuser1
     - testuser2

如果你在变量文件中或者 ‘vars’ 区域定义了一组YAML列表,你也可以这样做:

with_items: "{{somelist}}"

以上写法与下面是完全等同的:

- name: add user testuser1
  user: name=testuser1 state=present groups=wheel
- name: add user testuser2
  user: name=testuser2 state=present groups=wheel

yum和apt模块中使用with_items执行时会有较少的包管理事务.

请note使用 ‘with_items’ 用于迭代的条目类型不仅仅支持简单的字符串列表.如果你有一个哈希列表,那么你可以用以下方式来引用子项:

- name: add several users
  user: name={{ item.name }} state=present groups={{ item.groups }}
  with_items:
    - { name: 'testuser1', groups: 'wheel' }
    - { name: 'testuser2', groups: 'root' }

请note如果同时使用 whenwith_items (或其它循环声明),`when`声明会为每个条目单独执行.请参见 the_when_statement 示例.

嵌套循环

循环也可以嵌套:

- name: give users access to multiple databases
  mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
  with_nested:
    - [ 'alice', 'bob' ]
    - [ 'clientdb', 'employeedb', 'providerdb' ]

和以上介绍的’with_items’一样,你也可以使用预定义变量.:

- name: here, 'users' contains the above list of employees
  mysql_user: name={{ item[0] }} priv={{ item[1] }}.*:ALL append_privs=yes password=foo
  with_nested:
    - "{{users}}"
    - [ 'clientdb', 'employeedb', 'providerdb' ]

对哈希表使用循环

New in version 1.5.

假如你有以下变量:

---
users:
  alice:
    name: Alice Appleworth
    telephone: 123-456-7890
  bob:
    name: Bob Bananarama
    telephone: 987-654-3210

你想打印出每个用户的名称和电话号码.你可以使用 with_dict 来循环哈希表中的元素:

tasks:
  - name: Print phone records
    debug: msg="User {{ item.key }} is {{ item.value.name }} ({{ item.value.telephone }})"
    with_dict: "{{users}}"

对文件列表使用循环

with_fileglob 可以以非递归的方式来模式匹配单个目录中的文件.如下面所示:

---
- hosts: all

  tasks:

    # first ensure our target directory exists
    - file: dest=/etc/fooapp state=directory

    # copy each file over that matches the given pattern
    - copy: src={{ item }} dest=/etc/fooapp/ owner=root mode=600
      with_fileglob:
        - /playbooks/files/fooapp/*

Note

当在role中对 with_fileglob 使用相对路径时, Ansible会把路径映射到`roles/<rolename>/files`目录.

对并行数据集使用循环

Note

这是一个不常见的使用方式,但为了文档完整性我们还是把它写出来.你可能不会经常使用这种方式.

假设你通过某种方式加载了以下变量数据:

---
alpha: [ 'a', 'b', 'c', 'd' ]
numbers:  [ 1, 2, 3, 4 ]

如果你想得到’(a, 1)’和’(b, 2)’之类的集合.可以使用’with_together’:

tasks:
    - debug: msg="{{ item.0 }} and {{ item.1 }}"
      with_together:
        - "{{alpha}}"
        - "{{numbers}}"

对子元素使用循环

假设你想对一组用户做一些动作,比如创建这些用户,并且允许它们使用一组SSH key来登录.

如何实现那? 先假设你有按以下方式定义的数据,可以通过”vars_files”或”group_vars/all”文件加载:

---
users:
  - name: alice
    authorized:
      - /tmp/alice/onekey.pub
      - /tmp/alice/twokey.pub
    mysql:
        password: mysql-password
        hosts:
          - "%"
          - "127.0.0.1"
          - "::1"
          - "localhost"
        privs:
          - "*.*:SELECT"
          - "DB1.*:ALL"
  - name: bob
    authorized:
      - /tmp/bob/id_rsa.pub
    mysql:
        password: other-mysql-password
        hosts:
          - "db1"
        privs:
          - "*.*:SELECT"
          - "DB2.*:ALL"

那么可以这样实现:

- user: name={{ item.name }} state=present generate_ssh_key=yes
  with_items: "{{users}}"

- authorized_key: "user={{ item.0.name }} key='{{ lookup('file', item.1) }}'"
  with_subelements:
     - users
     - authorized

根据mysql hosts以及预先给定的privs subkey列表,我们也可以在嵌套的subkey中迭代列表:

- name: Setup MySQL users
  mysql_user: name={{ item.0.user }} password={{ item.0.mysql.password }} host={{ item.1 }} priv={{ item.0.mysql.privs | join('/') }}
  with_subelements:
    - users
    - mysql.hosts

Subelements walks a list of hashes (aka dictionaries) and then traverses a list with a given key inside of those records.

你也可以为字元素列表添加第三个元素,该元素可以放置标志位字典.现在你可以加入’skip_missing’标志位.如果设置为True,那么查找插件会跳过不包含指定子键的列表条目.如果没有该标志位,或者标志位值为False,插件会产生错误并指出缺少该子键.

这就是authorized_key模式中key的获取方式.

对整数序列使用循环

with_sequence 可以以升序数字顺序生成一组序列.你可以指定起始值、终止值,以及一个可选的步长值.

指定参数时也可以使用key=value这种键值对的方式.如果采用这种方式,’format’是一个可打印的字符串.

数字值可以被指定为10进制,16进制(0x3f8)或者八进制(0600).负数则不受支持.请看以下示例:

---
- hosts: all

  tasks:

    # create groups
    - group: name=evens state=present
    - group: name=odds state=present

    # create some test users
    - user: name={{ item }} state=present groups=evens
      with_sequence: start=0 end=32 format=testuser%02x

    # create a series of directories with even numbers for some reason
    - file: dest=/var/stuff/{{ item }} state=directory
      with_sequence: start=4 end=16 stride=2

    # a simpler way to use the sequence plugin
    # create 4 groups
    - group: name=group{{ item }} state=present
      with_sequence: count=4

随机选择

‘random_choice’功能可以用来随机获取一些值.它并不是负载均衡器(已经有相关的模块了).它有时可以用作一个简化版的负载均衡器,比如作为条件判断:

- debug: msg={{ item }}
  with_random_choice:
     - "go through the door"
     - "drink from the goblet"
     - "press the red button"
     - "do nothing"

提供的字符串中的其中一个会被随机选中.

还有一个基本的场景,该功能可用于在一个可预测的自动化环境中添加混乱和兴奋点.

Do-Until循环

有时你想重试一个任务直到达到某个条件.比如下面这个例子:

- action: shell /usr/bin/foo
  register: result
  until: result.stdout.find("all systems go") != -1
  retries: 5
  delay: 10

上面的例子递归运行shell模块,直到模块结果中的stdout输出中包含”all systems go”字符串,或者该任务按照10秒的延迟重试超过5次.”retries”和”delay”的默认值分别是3和5.

该任务返回最后一个任务返回的结果.单次重试的结果可以使用-vv选项来查看. 被注册的变量会有一个新的属性’attempts’,值为该任务重试的次数.

查找第一个匹配的文件

Note

这是一个不常见的使用方式,但为了文档完整性我们还是把它写出来.你可能不会经常使用这种方式.

这其实不是一个循环,但和循环很相似.如果你想引用一个文件,而该文件是从一组文件中根据给定条件匹配出来的.这组文件中部分文件名由变量拼接而成.针对该场景你可以这样做:

- name: INTERFACES | Create Ansible header for /etc/network/interfaces
  template: src={{ item }} dest=/etc/foo.conf
  with_first_found:
    - "{{ansible_virtualization_type}}_foo.conf"
    - "default_foo.conf"

该功能还有一个更完整的版本,可以配置搜索路径.请看以下示例:

- name: some configuration template
  template: src={{ item }} dest=/etc/file.cfg mode=0444 owner=root group=root
  with_first_found:
    - files:
       - "{{inventory_hostname}}/etc/file.cfg"
      paths:
       - ../../../templates.overwrites
       - ../../../templates
    - files:
        - etc/file.cfg
      paths:
        - templates

迭代程序的执行结果

Note

这是一个不常见的使用方式,但为了文档完整性我们还是把它写出来.你可能不会经常使用这种方式.

有时你想执行一个程序,而且按行循环该程序的输出.Ansible提供了一个优雅的方式来实现这一点.但请记住,该功能始终在控制机上执行,而不是本地机器:

- name: Example of looping over a command result
  shell: /usr/bin/frobnicate {{ item }}
  with_lines: /usr/bin/frobnications_per_host --param {{ inventory_hostname }}

好吧,这好像有点随意.事实上,如果你在做一些与inventory有关的事情,比如你想编写一个动态的inventory源(参见 动态 Inventory),那么借助该功能能够快速实现.

如果你想远程执行命令,那么以上方法则不行.但你可以这样写:

- name: Example of looping over a REMOTE command result
  shell: /usr/bin/something
  register: command_result

- name: Do something with each result
  shell: /usr/bin/something_else --param {{ item }}
  with_items: "{{command_result.stdout_lines}}"

使用索引循环列表

Note

这是一个不常见的使用方式,但为了文档完整性我们还是把它写出来.你可能不会经常使用这种方式.

如果你想循环一个列表,同时得到一个数字索引来标明你当前处于列表什么位置,那么你可以这样做.虽然该方法不太常用:

- name: indexed loop demo
  debug: msg="at array position {{ item.0 }} there is a value {{ item.1 }}"
  with_indexed_items: "{{some_list}}"

循环配置文件

ini插件可以使用正则表达式来获取一组键值对.因此,我们可以遍历该集合.以下是我们使用的ini文件:

[section1]
value1=section1/value1
value2=section1/value2

[section2]
value1=section2/value1
value2=section2/value2

以下是使用 with_ini 的例子:

- debug: msg="{{item}}"
  with_ini: value[1-2] section=section1 file=lookup.ini re=true

以下是返回的值:

{
      "changed": false,
      "msg": "All items completed",
      "results": [
          {
              "invocation": {
                  "module_args": "msg=\"section1/value1\"",
                  "module_name": "debug"
              },
              "item": "section1/value1",
              "msg": "section1/value1",
              "verbose_always": true
          },
          {
              "invocation": {
                  "module_args": "msg=\"section1/value2\"",
                  "module_name": "debug"
              },
              "item": "section1/value2",
              "msg": "section1/value2",
              "verbose_always": true
          }
      ]
  }

扁平化列表

Note

这是一个不常见的使用方式,但为了文档完整性我们还是把它写出来.你可能不会经常使用这种方式.

在罕见的情况下,你可能有几组列表,列表中会嵌套列表.而你只是想迭代所有列表中的每个元素.比如有一个非常疯狂的假定的数据结构:

----
# file: roles/foo/vars/main.yml
packages_base:
  - [ 'foo-package', 'bar-package' ]
packages_apps:
  - [ ['one-package', 'two-package' ]]
  - [ ['red-package'], ['blue-package']]

你可以看到列表中的包到处都是.那么如果想安装两个列表中的所有包那?:

- name: flattened loop demo
  yum: name={{ item }} state=installed
  with_flattened:
     - packages_base
     - packages_apps

这就行了!

循环中使用注册器

当对处于循环中的某个数据结构使用 register 来注册变量时,结果包含一个 results 属性,这是从模块中得到的所有响应的一个列表.

以下是在 with_items 中使用 register 的示例:

- shell: echo "{{ item }}"
  with_items:
    - one
    - two
  register: echo

返回的数据结构如下,与非循环结构中使用 register 的返回结果是不同的:

{
    "changed": true,
    "msg": "All items completed",
    "results": [
        {
            "changed": true,
            "cmd": "echo \"one\" ",
            "delta": "0:00:00.003110",
            "end": "2013-12-19 12:00:05.187153",
            "invocation": {
                "module_args": "echo \"one\"",
                "module_name": "shell"
            },
            "item": "one",
            "rc": 0,
            "start": "2013-12-19 12:00:05.184043",
            "stderr": "",
            "stdout": "one"
        },
        {
            "changed": true,
            "cmd": "echo \"two\" ",
            "delta": "0:00:00.002920",
            "end": "2013-12-19 12:00:05.245502",
            "invocation": {
                "module_args": "echo \"two\"",
                "module_name": "shell"
            },
            "item": "two",
            "rc": 0,
            "start": "2013-12-19 12:00:05.242582",
            "stderr": "",
            "stdout": "two"
        }
    ]
}

随后的任务可以用以下方式来循环注册变量,用来检查结果值:

- name: Fail if return code is not 0
  fail:
    msg: "The command ({{ item.cmd }}) did not have a 0 return code"
  when: item.rc != 0
  with_items: "{{echo.results}}"

自定义迭代

虽然你通常无需自定义实现自己的迭代,但如果你想按你自己的方式来循环任意数据结构,你可以阅读:doc:developing_plugins 来作为开始.以上的每个功能都以插件的方式来实现,所以有很多的实现可供引用.

See also

Playbooks
An introduction to playbooks
Playbook Roles and Include Statements
Playbook organization by roles
最佳实践
Best practices in playbooks
条件选择
Conditional statements in playbooks
Variables
All about variables
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

最佳实践

这里有些给使用和编写 Ansible playbook 的贴士.

你能在我们的 ansible-example repository.找到展示这些最佳实践的 playbook 样例.(注意: 这些示例用的也许不是最新版的中所有特性,但它们仍旧是极佳的参考.)

Content Organization

接下来的章节将向你展示一种组织 playbook 内容方式.

你对 Ansible 的使用应该符合你的需求而不是我们的,所以请随意根据你的需求来组织修改接下来的示例.

有件事绝对是你想要做的,那就是使用 “roles” 组织特性.它作为主要的 playbook 的主要部分被文档化.详情参见 Playbook Roles and Include Statements. 你绝对应该使用 roles. roles 是极好的. 快去使用 roles! roles! 重要的事情要重复说!roles 是极好的.(译者:..老外也知道重要的事情重复三遍啊!~~~)

Directory Layout

顶层目录结构应当包括下列文件和目录:

production                # inventory file for production servers 关于生产环境服务器的清单文件
stage                     # inventory file for stage environment 关于 stage 环境的清单文件

group_vars/
   group1                 # here we assign variables to particular groups 这里我们给特定的组赋值
   group2                 # ""
host_vars/
   hostname1              # if systems need specific variables, put them here 如果系统需要特定的变量,把它们放置在这里.
   hostname2              # ""

library/                  # if any custom modules, put them here (optional) 如果有自定义的模块,放在这里(可选)
filter_plugins/           # if any custom filter plugins, put them here (optional) 如果有自定义的过滤插件,放在这里(可选)

site.yml                  # master playbook 主 playbook
webservers.yml            # playbook for webserver tier Web 服务器的 playbook
dbservers.yml             # playbook for dbserver tier 数据库服务器的 playbook

roles/
    common/               # this hierarchy represents a "role" 这里的结构代表了一个 "role"
        tasks/            #
            main.yml      #  <-- tasks file can include smaller files if warranted
        handlers/         #
            main.yml      #  <-- handlers file
        templates/        #  <-- files for use with the template resource
            ntp.conf.j2   #  <------- templates end in .j2
        files/            #
            bar.txt       #  <-- files for use with the copy resource
            foo.sh        #  <-- script files for use with the script resource
        vars/             #
            main.yml      #  <-- variables associated with this role
        defaults/         #
            main.yml      #  <-- default lower priority variables for this role
        meta/             #
            main.yml      #  <-- role dependencies

    webtier/              # same kind of structure as "common" was above, done for the webtier role
    monitoring/           # ""
    fooapp/               # ""
Use Dynamic Inventory With Clouds

如果你正在使用云服务,你不应该在一个静态文件管理你的清单.详见 动态 Inventory.

这不仅适用于云环境 – 如果你的基础设施中还有其他系统维护着一系列标准系统,使用 动态清单 会是个好主意.

How to Differentiate Stage vs Production

If managing static inventory, it is frequently asked how to differentiate different types of environments. The following example shows a good way to do this. Similar methods of grouping could be adapted to dynamic inventory (for instance, consider applying the AWS tag “environment:production”, and you’ll get a group of systems automatically discovered named “ec2_tag_environment_production”.

如果你管理着静态清单,如何区分不同的环境类型是个常见的问题.接下来的示例会做一个很好地说明.

Let’s show a static inventory example though. Below, the production file contains the inventory of all of your production hosts.

It is suggested that you define groups based on purpose of the host (roles) and also geography or datacenter location (if applicable):

# file: production

[atlanta-webservers]
www-atl-1.example.com
www-atl-2.example.com

[boston-webservers]
www-bos-1.example.com
www-bos-2.example.com

[atlanta-dbservers]
db-atl-1.example.com
db-atl-2.example.com

[boston-dbservers]
db-bos-1.example.com

# webservers in all geos
[webservers:children]
atlanta-webservers
boston-webservers

# dbservers in all geos
[dbservers:children]
atlanta-dbservers
boston-dbservers

# everything in the atlanta geo
[atlanta:children]
atlanta-webservers
atlanta-dbservers

# everything in the boston geo
[boston:children]
boston-webservers
boston-dbservers
Group And Host Variables

本章节内容基于前一章节示例.

分组有利于组织结构,但不是所有的分组都是有益的.你也可以给他们赋值!比如说亚特兰大有它自己的网络时间协议, 所以当配置 ntp.conf 时,我们就该使用它.让我们现在设置它们:

---
# file: group_vars/atlanta
ntp: ntp-atlanta.example.com
backup: backup-atlanta.example.com

Variables aren’t just for geographic information either! Maybe the webservers have some configuration that doesn’t make sense for the database servers:

---
# file: group_vars/webservers
apacheMaxRequestsPerChild: 3000
apacheMaxClients: 900

If we had any default values, or values that were universally true, we would put them in a file called group_vars/all:

---
# file: group_vars/all
ntp: ntp-boston.example.com
backup: backup-boston.example.com

We can define specific hardware variance in systems in a host_vars file, but avoid doing this unless you need to:

---
# file: host_vars/db-bos-1.example.com
foo_agent_port: 86
bar_agent_port: 99

Again, if we are using dynamic inventory sources, many dynamic groups are automatically created. So a tag like “class:webserver” would load in variables from the file “group_vars/ec2_tag_class_webserver” automatically.

Top Level Playbooks Are Separated By Role

在 site.yml 中,我们包含了一个定义了整个基础设施的 playbook.注意这个 playbook 是非常短的, 因为它仅仅包含了其他 playbooks.记住, playbook 不过就是一系列的 plays:

---
# file: site.yml
- include: webservers.yml
- include: dbservers.yml

在诸如 like webservers.yml 的文件中(同样也在顶层结构),我们仅仅将 Web 服务器组与对应的 role 行为做映射.同样值得注意的是这也非常的短小精悍.例如:

---
# file: webservers.yml
- hosts: webservers
  roles:
    - common
    - webtier

理念是我们能够通过 “运行”(running) site.yml 来选择整个基础设施的配置.或者我们能够通过运行其子集 webservers.yml 来配置. 这与 Ansible 的 “–limit” 类似,而且相对的更为显式:

ansible-playbook site.yml --limit webservers
ansible-playbook webservers.yml
Task And Handler Organization For A Role

接下来的示例任务文件展示了一个 role 是如何工作的.我们这里的普通 role 仅仅用来配置 NTP,但是如果我们想的话,它可以做更多:

---
# file: roles/common/tasks/main.yml

- name: be sure ntp is installed
  yum: pkg=ntp state=installed
  tags: ntp

- name: be sure ntp is configured
  template: src=ntp.conf.j2 dest=/etc/ntp.conf
  notify:
    - restart ntpd
  tags: ntp

- name: be sure ntpd is running and enabled
  service: name=ntpd state=running enabled=yes
  tags: ntp

这是个处理文件样例.作为一种审核,它只有当特定的任务报告发生变化时会被触发,并在每个 play 结束时运行:

---
# file: roles/common/handlers/main.yml
- name: restart ntpd
  service: name=ntpd state=restarted

详情请参阅 Playbook Roles and Include Statements.

What This Organization Enables (Examples)

我们在前文分享了我们基础的组织结构.

那这种结构适用于何种应用场景? 很多!若我想重新配置整个基础设施,如此即可:

ansible-playbook -i production site.yml

那只重新配置所有的 NTP 呢?太容易了.:

ansible-playbook -i production site.yml --tags ntp

只重新配置我的 Web 服务器呢?:

ansible-playbook -i production webservers.yml

只重新配置我在波士顿的 Web服务器呢?:

ansible-playbook -i production webservers.yml --limit boston

前10台 和 接下来的10台呢?

ansible-playbook -i production webservers.yml –limit boston[0-10] ansible-playbook -i production webservers.yml –limit boston[10-20]

当然,只使用基础的 ad-hoc 也是 OK 的啦.:

ansible boston -i production -m ping
ansible boston -i production -m command -a '/sbin/reboot'

这里还有些有用的命令你需要知道(版本至少 1.1 或更高):

# confirm what task names would be run if I ran this command and said "just ntp tasks"
ansible-playbook -i production webservers.yml --tags ntp --list-tasks

# confirm what hostnames might be communicated with if I said "limit to boston"
ansible-playbook -i production webservers.yml --limit boston --list-hosts
Deployment vs Configuration Organization

The above setup models a typical configuration topology. When doing multi-tier deployments, there are going to be some additional playbooks that hop between tiers to roll out an application. In this case, ‘site.yml’ may be augmented by playbooks like ‘deploy_exampledotcom.yml’ but the general concepts can still apply.

Consider “playbooks” as a sports metaphor – you don’t have to just have one set of plays to use against your infrastructure all the time – you can have situational plays that you use at different times and for different purposes.

Ansible allows you to deploy and configure using the same tool, so you would likely reuse groups and just keep the OS configuration in separate playbooks from the app deployment.

Stage vs Production

如前所述,通过使用不同的清单文件来分离你的 stage 和 生产环境是个好方法.你可以通过 -i 来指定.把它们放在同一个文件中会有惊喜哦! 在部署到生产环境之前,先在 stage 环境中做测试是个好主意.你的环境不必保持同样的大小,你可以通过 分组变量来对不同的环境进行控制.

Rolling Updates

请理解 ‘serial’ 关键字.你会在批量升级中使用它来控制升级机器的数量.

See 委托,滚动更新,本地动作.

Always Mention The State

parameter in your playbooks to make it clear, especially as some modules support additional states. 对于很多模块来说 ‘state’ 参数是可选的.无论是 ‘state=present’ 亦或 ‘state=absent’ ,你最好在 playbook 中显式指定该参数,毕竟有些模块是支持附加的 ‘state’ 参数.

Group By Roles

在这条贴士中,我们某种程度上在重复自己,但这是值得的.一个系统可能被分成多分组.详情请查阅 Inventory文件Patterns. 在样例中,分组名之后的 webserversdbservers ,它们因为是很重要的概念所以反复出现.(译者:恩,重要的事情要重复三遍!)一个系统可以出现在多个分组中.

通过给 role 赋予特定的变量,这允许 playbooks 能基于角色来锁定机器.

See Playbook Roles and Include Statements.

Operating System and Distribution Variance

当处理在不同操作系统间参数值不同的参数时,使用 group_by 模块是个好主意.

这使宿主机的动态分组有了匹配的标准,即使该分组尚未在清单文件中被定义

---

# talk to all hosts just so we can learn about them
- hosts: all
  tasks:
     - group_by: key=os_{{ ansible_distribution }}

# now just on the CentOS hosts...

- hosts: os_CentOS
  gather_facts: False
  tasks:
     - # tasks that only happen on CentOS go here

这会抛出所有基于操作系统名的分组.

如果需要对特定分组做设定,这也是可以的.例:

---
# file: group_vars/all
asdf: 10

---
# file: group_vars/os_CentOS
asdf: 42

在上述的例子中, CentOS 的机器获取的 asdf 的值为 42,但其他机器获得是 ‘10’.这不止可以用于设置变量,也可以将特定的 role 应用于特定的操作系统.

相对的,如果只需要变量:

- hosts: all
  tasks:
    - include_vars: "os_{{ ansible_distribution }}.yml"
    - debug: var=asdf

这将根据操作系统名来拉取相应的值.

Bundling Ansible Modules With Playbooks

如果一个 playbook 有一个与它 YMAL 文件相关的 ”./library” 目录,该目录可以用于添加 Ansible 模块,它会被自动添加到 Ansible 模块的路径中.这是一个将 playbook 与其模块放置在一起的方式.如下面的目录结构样例所展示:

.. _whitespace:

Whitespace and Comments

鼓励使用空格来分隔内容,用 ‘#’ 来写注释.

Always Name Tasks

虽然推荐提供关于为什么要这么做的描述,但是直接给一个给定任务命名也是可以的.名字会在 playbook 运行时显示.

Keep It Simple

当你能简单的搞定某事时,就简单的搞定.不要试图一次性使用 Ansible 的所有的特性.仅仅使用对你有用的即可. 比如说你基本上不会需要一次性使用 vars , vars_files , vars_prompt--extra-vars 同时还是用一个外部的节点配置文件.

如果你感觉任务很复杂时,它可能真的很复杂,这也许是个简化它的好机会.

Version Control

请使用版本控制.保持你的 playbook 和 清单文件 在 git(或其他版本控制系统)中,并将你的修改做提交. 这样你就有审计轨迹来描述什么时候以及为什么你做了这样的修改.

See also

YAML 语法
Learn about YAML syntax
Playbooks
Review the basic playbook features
模块相关
Learn about available modules
Developing Modules
Learn how to extend Ansible by writing your own modules
Patterns
Learn about how to select hosts
GitHub examples directory
Complete playbook files from the github project source
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups

Playbooks: Special Topics

这里的一些 playbook 特性可能不是每个人都需要学习的,但是它们对于一些特定的应用来说是相当有用的.推荐你浏览这些主题,你也许能在其中发现有用的贴士. 但请先学习 Ansible 的基础知识,并仅在他们对你的环境看似有关或有用时再采用.

Accelerated Mode

New in version 1.3.

你也许不需要这个!

你在使用 Ansible 1.5 或者 之后的版本吗? 如果是的话,因为被称之为 “SSH pipelining” 的新特性,你也许就不需要加速模式了.详情请阅读:ref:pipelining 部分的章节.

对于使用 1.5 及之后版本的用户,加速模式只在以下情况下有用处: (A) 管理红帽企业版 Linux 6 或者更早的那些依然使用 paramiko 的版本 或者 (B) 像在文档中描述的那样:无法在 TTYs 中使用 sudo.

如果你能够使用pipelining,Ansible 将会降低通过 wire 传输文件的总量来提升有效率,并且在几乎所有情况下(甚至可能包括了传输大型文件)都能与加速模式相匹敌.归功于更少的移动文件块,管道几乎在所有的情况下优于加速模式.

加速模式将为了支持那些仍使用红帽企业版 Linux 6 做主控机或因其他环境因素受限制而保留.

加速模式详解

尽管 OpenSSH 使用的 ControlPersist 特性既快速又可伸缩,但这会在 SSH 连接时造成少量的开销.虽然很多人不会遇到这样一个需求,但是如果你当前运行的平台不支持 ControlPersist (如 一台 EL6 control machine), 你大概会对 tuning 选项更感兴趣.

加速模式只是使用来加速连接的,它仍需使用 SSH 来进行初始安全密钥交换.它没有额外增加需要管理的基础设施的公共key,也不需要诸如 NTP 或 DNS.

加速模式在任何情况下将比启用 ControlPersist 特性的 SSH 快2-6倍,10倍于 paramiko.

加速模式通过启动一个临时的 SSH 守护进程来工作.只要这个守护进程在运行,Ansible 将会直接通过 socket 来连接.Ansible 通过在连接时交换临时的 AES key 来确保安全(这个秘钥对每个主机都是不同的并且会定期重新生成).

默认配置下,Ansible 会为加速模式开启5099端口(此配置可修改).一旦运行了,守护进程将会维持连接 30 分钟,过了时限后该连接将会自动终结,你需要重启一个 SSH.

加速模式对它所基于的 fireball 模式(已被废弃)做了许多改进:

  • 不需要 bootstrapping,仅需在你想要运行加速模式的playbook上添加一行代码.
  • 支持 sudo 命令(下文参见详情)
  • 更少的依赖需求.ZeroMQ 不在需要,除了 python-keyczar 外再无其他依赖包需要安装.
  • Python 版本必须大于等于 2.5

只需在你的 play 中添加 accelerate: true 即可使用加速模式:

---

- hosts: all
  accelerate: true

  tasks:

  - name: some task
    command: echo {{ item }}
    with_items:
    - foo
    - bar
    - baz

如果你希望改变 Ansible 用于加速模式的端口,你只需添加 accelerated_port 选项:

---

- hosts: all
  accelerate: true
  # default port is 5099
  accelerate_port: 10000

accelerate_port 选项也同样能通过指定环境变量 ACCELERATE_PORT 或者在你的 ansible.cfg 中配置:

[accelerate]
accelerate_port = 5099

如先前所述,加速模式同样支持通过 sudo 命令来运行任务.但是有两点需要予以提醒:

  • 你必须移除 sudoers 选项中的 requiretty.
  • 目前仍不支持 sudo 密码提示,所以 NOPASSWD 选项是必须的.

如果是 Ansible 版本是 1.6,你同样可以允许多个连接多个秘钥来连接多个 Ansible 管理节点.你可以通过在你的 ansible.cfg 中添加如下配置:

accelerate_multi_key = yes

当启用时,守护进程将会打开一个 UNIX socket 文件(默认位于 $ANSIBLE_REMOTE_TEMP/.ansible-accelerate/.local.socket).来自 SSH 的新的连接能够通过这个 socket 文件来上载新的秘钥给守护进程.

异步操作和轮询

默认情况下playbook中的任务执行时会一直保持连接,直到该任务在每个节点都执行完毕.有时这是不必要的,比如有些操作运行时间比SSH超时时间还要长.

解决该问题最简单的方式是一起执行它们,然后轮询直到任务执行完毕.

你也可以对执行时间非常长(有可能遭遇超时)的操作使用异步模式.

为了异步启动一个任务,可以指定其最大超时时间以及轮询其状态的频率.如果你没有为 poll 指定值,那么默认的轮询频率是10秒钟:

---

- hosts: all
  remote_user: root

  tasks:

  - name: simulate long running op (15 sec), wait for up to 45 sec, poll every 5 sec
    command: /bin/sleep 15
    async: 45
    poll: 5

Note

async 并没有默认值,如果你没有指定 async 关键字,那么任务会以同步的方式运行,这是Ansible的默认行为.

另外,如果你不需要等待任务执行完毕,你可以指定 poll 值为0而启用 “启动并忽略”

---

- hosts: all
  remote_user: root

  tasks:

  - name: simulate long running op, allow to run for 45 sec, fire and forget
    command: /bin/sleep 15
    async: 45
    poll: 0

Note

对于要求排它锁的操作,如果你需要在其之后对同一资源执行其它任务,那么你不应该对该操作使用”启动并忽略”.比如yum事务.

Note

--forks 参数值过大会更快的触发异步任务.也会加快轮询的效率.

当你想对 “启动并忽略” 做个变种,改为”启动并忽略,稍后再检查”,你可以使用以下方式执行任务:

---
# Requires ansible 1.8+
- name: 'YUM - fire and forget task'
  yum: name=docker-io state=installed
  async: 1000
  poll: 0
  register: yum_sleeper

- name: 'YUM - check on fire and forget task'
  async_status: jid={{ yum_sleeper.ansible_job_id }}
  register: job_result
  until: job_result.finished
  retries: 30

Note

如果 async: 值太小,可能会导致 “稍后检查” 任务执行失败,因为 async_status:: 的临时状态文件还未被写入信息,而”稍后检查”任务就试图读取此文件.

See also

Playbooks
An introduction to playbooks
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

Check Mode (“Dry Run”)

New in version 1.1.

当以 --check 参数来运行 ansible-playbook 时,将不会对远程的系统作出任何更改.相对的,任何带有检测功能的模块(这几乎包含了所有的主要核心模块,但这不要求所有的模块都需支持.) 只要支持 ‘检测模式’ 将会报告它们会做出什么改变而不是直接进行改变.其他不支持检测模式的模块将既不响应也不提出相应的报告.

检测模式只是一种模拟.如果你的playbook是以先前命令的执行结果作为条件的话,那它可能对你就没有什么大用处了. 但是对于基于一次一节点的基础配置管理的使用情形来说是很有用.

Example:

ansible-playbook foo.yml --check

在测试模式下运行一个任务.

New in version 1.3.

有时候你甚至会想在检测模式中执行一个任务.为了达到这样的效果, 你需要在相应的任务上使用 always_run 子句.跟 when 子句一样,它的值是一个 Jinja2 表达式. 在一个简单的例子中,布尔值也会表达为一个适当的 YAML 值.

Example:

tasks:

  - name: this task is run even in check mode
    command: /something/to/run --even-in-check-mode
    always_run: yes

友情提示,带有 when 子句的任务会返回false,该任务将会被跳过,即使它还被添加了会返回true的 always_run 子句.

Showing Differences with --diff

New in version 1.1: 对 ansible-playbook 来说 --diff 选项与 --check (详情参下)配合使用效果奇佳,不过它也可以单独使用.当提供了相应的标识后,当远程系统上任何模板文件的变化时,ansible-playbook CLI 将会报告文件上任何文本的变化 (或者,如果使用了 --check 参数,将报告会发生的变化.).因为 diff 特性会产生大量的输出结果,所以它在一次检测一个主机时使用为佳,如:

ansible-playbook foo.yml --check --diff --limit foo.example.com

委托,滚动更新,本地动作

由于设计初衷是作为多用户,Anisible很擅长在某一个主机上代表另一个做事,或者参考远程主机做一些本地工作.

这个特性对于架设连续实现某些设施或者0暂停滚动升级,这里你可能会提到负载均衡或者监控系统.

更多的特性允许调试事情完成的顺序,和设置一批窗口,来确定多少机器在一次中滚动更新.

这节会介绍所有这些特性.`详情案例参见 <http://github.com/ansible/ansible-examples/>`_.这里有很多案例来说明对于不同程序来实行,0暂停更新步骤

你也可以参考:doc:`modules`部分,很多模块例如 ‘ec2_elb’, ‘nagios’, ‘bigip_pool’, ‘netscaler’ 的一些概念会在这里介绍.

你可能也想参考:doc:playbooks_roles,你可以找到类似’pre_task’和’post_task’的详细介绍和调用方式.

Rolling Update Batch Size 滚动更新批量尺寸 `````````````````````

New in version 0.7.

默认来说,Anisble 可以使图参考某一个play来并行操作所有主机.对于滚动更新案例, 你可以定义Ansible可以在一个特定时间同时控制多少主机,使用’‘serial’’ 关键词:

- name: test play
  hosts: webservers
  serial: 3

在上面的例子,如果我们有100台主机,3 台主机定义在组’webservers’ 可以在下面3个主机开始之前完全完成

这个’‘serial’’ 关键词在Ansible 1.8 以后可以被定义为百分数,用来定义每一次操作一个play中百分之多少主机:

- name: test play
  hosts: websevers
  serial: "30%"

如果主机数不能被passes数量整除,最后的pass将会包含提醒信息

Note

不管多小的百分比,每个pass的主机数一定会大于等于1.

最大失败百分比

New in version 1.3.

默认来说,Ansible 会持续执行行为只要在一个组中还有主机没有宕机. 在有些情况下,例如之前提到的滚动更新,也许理想的情况是当一个失败数上线达到时主动宕掉这个play.为了达到这个目的,在 1.3版本中,你可以设置最大失败半分比:

- hosts: webservers
  max_fail_percentage: 30
  serial: 10

在上面的例子中,如果在10个服务器中如果多余3个,其它的play就会主动宕掉.

Note

这个百分比必须被超过,不仅仅是相等.例如如果serial值呗设置为4,并且你希望任务主动在2个系统失败时候放弃.那么这个百分比应该设置为49而不是50.

委任

New in version 0.7.

This isn’t actually rolling update specific but comes up frequently in those cases. 这个虽然不属于滚动更新,但是在那些场景下经常会出现.

如果你想参考其它主机来在一个主机上执行一个任务,我们就可以使用’delegate_to’关键词在你要执行的任务上. 这个对于把节点放在一个负载均衡池里面活着从里面移除非常理想. 这个选项也对处理窗口中断非常有用. 使用’serial’关键词来控制一定数量的主机也是一个好想法:

---

- hosts: webservers
  serial: 5

  tasks:

  - name: take out of load balancer pool
    command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
    delegate_to: 127.0.0.1

  - name: actual steps would go here
    yum: name=acme-web-stack state=latest

  - name: add back to load balancer pool
    command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
    delegate_to: 127.0.0.1
这些命令可以在127.0.0.1上面运行,这个运行Ansible的主机.这个也是一个简写的语法用在每一个任务基础(per-task basis): ‘local_action’.以上就是这样一个playbook.但是使用的是简化后的语法在172.0.0.1上面做代理::

# ...

tasks:

  • name: take out of load balancer pool local_action: command /usr/bin/take_out_of_pool {{ inventory_hostname }}

# ...

  • name: add back to load balancer pool local_action: command /usr/bin/add_back_to_pool {{ inventory_hostname }}

A common pattern is to use a local action to call ‘rsync’ to recursively copy files to the managed servers. Here is an example:

---
# ...
  tasks:

  - name: recursively copy files from management server to target
    local_action: command rsync -a /path/to/files {{ inventory_hostname }}:/path/to/target/

注意你必须拥有不需要密码SSH密钥或者ssh-agent配置,不然的话rsync会需要询问密码.

Run Once

New in version 1.7.

有时候你有这样的需求,在一个主机上面只执行一次一个任务.这样的配置可以配置”run_once”来实现:

---
# ...

  tasks:

    # ...

    - command: /opt/application/upgrade_db.py
      run_once: true

    # ...

这样可以添加在”delegat_to”选项对中来定义要执行的主机:

- command: /opt/application/upgrade_db.py
  run_once: true
  delegate_to: web01.example.org

当”run_once” 没有喝”delegate_to”一起使用,这个任务将会被清单指定的第一个主机. 在一组被play制定主机.例如 webservers[0], 如果play指定为 “hosts: webservers”.

这个方法也很类似,虽然比使用条件更加简单粗暴,如下事例:

- command: /opt/application/upgrade_db.py
  when: inventory_hostname == webservers[0]

本地Playbooks

在本地使用playbook有时候比ssh远程使用更加有用.可以通过把playbook放在crontab中,来确保一个系统的配置,可以很有用. 在OS installer 中运行一个playbook也很有用.例如Anaconda kickstart.

要想在本地运行一个play,可以直接设置”host:” 与 “hosts:127.0.0.1”, 然后使用下面的命令运行:

ansible-playbook playbook.yml --connection=local

或者,一个本地连接也可以作为一个单独的playbook play应用在playbook中, 即便playbook中其他的plays使用默认远程 连接如下:

- hosts: 127.0.0.1
  connection: local

See also

Playbooks
An introduction to playbooks
Ansible Examples on GitHub
Many examples of full-stack deployments
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

配置环境 (在代理环境中)

New in version 1.1.

你完全有可能遇到一些更新包需要通过proxy才能正常获取,或者甚至一部分包需要通过proxy升级而另外一部分包则不需要通过proxy.或者可能你的某个脚本需要调用某个环境变量才能正常运行.

Ansible 使用 ‘environment’ 关键字对于环境部署的配置非常简单容易,下面是一个使用案例:

- hosts: all
  remote_user: root

  tasks:

    - apt: name=cobbler state=installed
      environment:
        http_proxy: http://proxy.example.com:8080

environment 也可以被存储在变量中,像如下方式访问:

- hosts: all
  remote_user: root

  # here we make a variable named "proxy_env" that is a dictionary
  vars:
    proxy_env:
      http_proxy: http://proxy.example.com:8080

  tasks:

    - apt: name=cobbler state=installed
      environment: proxy_env

虽然上面只展示了 proxy 设置,但其实可以同时其实支持多个设置. 大部分合合乎逻辑的地方来定义一个环境变量都可以成为 group_vars 文件,示例如下:

---
# file: group_vars/boston

ntp_server: ntp.bos.example.com
backup: bak.bos.example.com
proxy_env:
  http_proxy: http://proxy.bos.example.com:8080
  https_proxy: http://proxy.bos.example.com:8080

See also

Playbooks
An introduction to playbooks
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

Playbooks 中的错误处理

Ansible 通常默认会确保检测模块和命令的返回码并且会快速失败 – 专注于一个错误除非你另作打算.

有时一条命令会返回 0 但那不是报错.有时命令不会总是报告它 ‘改变’ 了远程系统.本章节描述了 如何将 Ansible 处理输出结果和错误处理的默认行为改变成你想要的.

忽略错误的命令

New in version 0.6.

通常情况下, 当出现失败时 Ansible 会停止在宿主机上执行.有时候,你会想要继续执行下去.为此 你需要像这样编写任务:

- name: this will not be counted as a failure
  command: /bin/false
  ignore_errors: yes

注意上面的系统仅仅处理那个特定的任务,所以当你在使用一个未定义的变量时, Ansible 仍然会报 错,需要使用者进行处理.

控制对失败的定义

New in version 1.4.

假设一条命令的错误码毫无意义只有它的输出结果能告诉你什么出了问题,比如说字符串 “FAILED” 出 现在输出结果中.

在 Ansible 1.4及之后的版本中提供了如下的方式来指定这样的特殊行为:

- name: this command prints FAILED when it fails
  command: /usr/bin/example-command -x -y -z
  register: command_result
  failed_when: "'FAILED' in command_result.stderr"

在 Ansible 1.4 之前的版本能通过如下方式完成:

- name: this command prints FAILED when it fails
  command: /usr/bin/example-command -x -y -z
  register: command_result
  ignore_errors: True

- name: fail the play if the previous command did not succeed
  fail: msg="the command failed"
  when: "'FAILED' in command_result.stderr"

覆写更改结果

New in version 1.3.

When a shell/command or other module runs it will typically report “changed” status based on whether it thinks it affected machine state. 当一个 shell或命令或其他模块运行时,它们往往都会在它们认为其影响机器状态时报告 “changed” 状态

有时你可以通过返回码或是输出结果来知道它们其实并没有做出任何更改.你希望覆写结果的

“changed” 状态使它不会出现在输出的报告或不会触发其他处理程序:

tasks:

  - shell: /usr/bin/billybass --mode="take me to the river"
    register: bass_result
    changed_when: "bass_result.rc != 2"

  # this will never report 'changed' status
  - shell: wall 'beep'
    changed_when: False

See also

Playbooks
An introduction to playbooks
最佳实践
Best practices in playbooks
条件选择
Conditional statements in playbooks
Variables
All about variables
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

标签

如果你有一个大型的 playbook,那能够只运行其中特定部分的配置而无需运行整个 playbook 将会很有用.

plays 和 tasks 都因这个理由而支持 “tags:”

例:

tasks:

    - yum: name={{ item }} state=installed
      with_items:
         - httpd
         - memcached
      tags:
         - packages

    - template: src=templates/src.j2 dest=/etc/foo.conf
      tags:
         - configuration

如果你只想运行一个非常大的 playbook 中的 “configuration” 和 “packages”,你可以这样做:

ansible-playbook example.yml --tags "configuration,packages"

另一方面,如果你只想执行 playbook 中某个特定任务 之外 的所有任务,你可以这样做:

ansible-playbook example.yml --skip-tags "notification"

你同样也可以对 roles 应用 tags:

roles:
  - { role: webserver, port: 5000, tags: [ 'web', 'foo' ] }

你同样也可以对基本的 include 语句使用 tag:

- include: foo.yml tags=web,foo

以上这样也有对每个 include 语句中的单个任务进行标签的功能.

See also

Playbooks
An introduction to playbooks
Playbook Roles and Include Statements
Playbook organization by roles
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

Vault

Ansible 1.5的新版本中, “Vault” 作为 ansible 的一项新功能可将例如passwords,keys等敏感数据文件进行加密,而非存放在明文的 playbooks 或 roles 中. 这些 vault 文件可以分散存放也可以集中存放.

通过`ansible-vault` 来编辑文件,经常用到的命令如 –ask-vault-pass , –vault-password-file . 这里,你可以在 ansible.cfg 中定义密码文件所在位置,这个选项就不需要在命令行中指定标志了.

Vault可以加密些什么

vault 可以加密任何 Ansible 使用的结构化数据文件. 甚至可以包括 “group_vars/” 或 “host_vars/” inventory 变量, “include_vars” 或 “vars_files” 加载的变量, 通过 ansible-playbook 命令行使用 “-e @file.yml” 或 “-e @file.json” 命令传输的变量文件. Role 变量和所有默认的变量都可以被 vault 加密.

因为 Ansible tasks, handlers等都是数据文件, 所有的这些均可以被 vault 加密. 如果你不喜欢你使用的变量被泄漏,你可以将整个 task 文件部分加密. 然后,这个工作量比较大而且可能给你的同事带来不便哦 :)

创建加密文件

执行如下命令,创建加密文件:

ansible-vault create foo.yml

首先你将被提示输出密码, 经过Vault加密过的文件如需查看需同时输入密码后才能进行.

提供密码后, 工具将加载你定义的 $EDITOR 的编辑工具默认是 vim, 一旦你关闭了编辑会话框,生成后的文件将会是加密文件.

默认加密方式是 AES (基于共享密钥)

Editing加密文件

编辑加密文件,使用 ansible-vault edit . 该命令会先加密文件为临时文件并允许你编辑这个文件,当完成编辑后会保存回你所命名的文件并删除临时文件:

ansible-vault edit foo.yml

密钥更新加密文件

如果你希望变更密码,使用如下 命令:

ansible-vault rekey foo.yml bar.yml baz.yml

如上命令可以同时批量修改多个文件的组织密码并重新设置新密码.

加密普通文件

如果你希望加密一个已经存在的文件,使用 ansible-vault encrypt . 该命令也可同时批量操作多个文件:

ansible-vault encrypt foo.yml bar.yml baz.yml

解密已加密文件

如果不希望继续加密一个已经加密过的文件,通过 ansible-vault decrypt 你可以永久解密. 命令将解密并保存到硬盘上,这样你不用再使用 ansible-vault edit 来编辑文件了:

ansible-vault decrypt foo.yml bar.yml baz.yml

查阅已加密文件

Available since Ansible 1.8

如果你不希望通过编辑的方式来查看文件, ansible-vault view 可以满足你的需要:

ansible-vault view foo.yml bar.yml baz.yml

在Vault下运行Playbook

执行 vault 加密后的playbook文件,最少需要提交如下两个标志之一. 交互式的指定 vault 的密码文件:

ansible-playbook site.yml --ask-vault-pass

该提示被用来解密(仅在内存中)任何 vault 加密访问过的文件. 目前这些文件中所有的指令请求将被使用相同的密码加密.

另外,密码也可以定义在一个文件或者一个脚本中,但是需要 Ansible 1.7 以上的版本才能支持. 当使用该功能时,一定要确认密码文件的权限是安全的以确保没有人可以随意访问或者变更密码文件:

ansible-playbook site.yml --vault-password-file ~/.vault_pass.txt

ansible-playbook site.yml --vault-password-file ~/.vault_pass.py

密码存储一行一个

如果你使用的是脚本而不是普通文件,确保脚本是可执行的,这样密码可以输出至标准设备.如果你的脚本需要提示输入数据,那提示可以被发送到标准错误.

如果你是从持续集成系统(例如Jenkins)中使用 Ansible 的话上面的这种情况你会用的到.

(–vault-password-file 参数可以在 Ansible-Pull(拉取配置而非推送配置) 命令中被使用,尽管这将需要分发keys到对应的节点上,所以 这些了解这些隐性问题后 – vault 更倾向使用 push 方式)

从指定任务开始运行palybook以及分步运行playbook

以下列出了几种方式来运行playbook.这对于测试或调试新的playbook很有帮助.

Start-at-task

如果你想从指定的任务开始执行playbook,可以使用``–start-at``选项:

ansible-playbook playbook.yml --start-at="install packages"

以上命令就会在名为”install packages”的任务开始执行你的playbook.

分步运行playbook

我们也可以通过``–step``选项来交互式的执行playbook:

ansible-playbook playbook.yml --step

这样ansible在每个任务前会自动停止,并询问是否应该执行该任务.

比如你有个名为``configure ssh``的任务,playbook执行到这里会停止并询问:

Perform task: configure ssh (y/n/c):

“y”回答会执行该任务,”n”回答会跳过该任务,而”c”回答则会继续执行剩余的所有任务而不再询问你.

模块相关

简介

模块(也被称为 “task plugins” 或 “library plugins”)是在 Ansible 中实际在执行的.它们就 是在每个 playbook 任务中被执行的.你也可以仅仅通过 ‘ansible’ 命令来运行它们.

让我们回顾一下我们是如何通过命令行来执行三个不同的模块:

ansible webservers -m service -a "name=httpd state=started"
ansible webservers -m ping
ansible webservers -m command -a "/sbin/reboot -t now"

每个模块都能接收参数. 几乎所有的模块都接受键值对(key=value)参数,空格分隔.一些模块 不接收参数,只需在命令行输入相关的命令就能调用.

在 playbook 中, Ansible 模块以类似的方式执行:

- name: reboot the servers
  action: command /sbin/reboot -t now

也可以简写成:

- name: reboot the servers
  command: /sbin/reboot -t now

另一种给模块传递参数的方式是使用 ymal 语法,这也被称为 ‘complex args’

- name: restart webserver
  service:
    name: httpd
    state: restarted

无论你是使用命令行还是 playbooks 所有模块都会返回以 JSON 格式组织的数据.一般来说,对此你 无需知道太多.但如果你在编写你自己的模块,那你需要在意,这意味着你不需要以特定的语言来编写 你的模块 – 你可以自行选择.

模块努力使自身幂等,这意味着它们会尽可能避免对系统做出改动除非那是必须的.当使用 Ansible playbooks 时,这些模块能够触发 ‘change events’,以这种形式通知 ‘handlers’ 去运行附加任务.

每个模块的文档能够通过命令行的 ansible-doc 工具来获取:

ansible-doc yum

列出所有已安装的模块文档:

ansible-doc -l

See also

Introduction To Ad-Hoc Commands
Examples of using modules in /usr/bin/ansible
Playbooks
Examples of using modules with /usr/bin/ansible-playbook
Developing Modules
How to write your own modules
Python API
Examples of using modules with the Python API
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

核心模块

这些模块是 ansible 团队维护的核心模块,同样也是 ansible 自带的模块,在收到的的请求中,它们有比 “extras” 源更高的优先级

核心模块的源码托管在 Github 的 ansible-modules-core repo.

如果你确信你在核心模块上发现了一个 bug ,同时你使用的是最新的稳定版或者开发版本的 Ansible ,首先你需要看看 github.com/ansible/ansible-modules-core 上的 “issue tracker” ,确保你的 bug 还没有被提交.如果还没被提交, 非常感谢你的提交

你肯能更想去问问题,而不是提交 bug ,欢迎在 Ansible-project 的 google-group 里咨询 https://groups.google.com/forum/#!forum/ansible-project ,在 Ansible 的 #ansible_chanel 咨询也是可以的,它们位于 irc.freenode.net .开发方向的应该用类似的讨论组,位于 <https://groups.google.com/forum/#!forum/ansible-devel>`_

这些模块的文档更新可以直接编辑在模块自身里面,然后提交一个 pull 请求到源码,不过你需要找到源码树的 “DOCUMENTATION” 块

额外模块

这些模块是当前ansible附带的,但是也可能在以后被分开.额外模块主要被社区人员维护.非核心模块仍然是完全可用的,但是在发出和拉取请求时可能收到稍微低的响应速率

受欢迎的的 “extras” 模块将来可能会提升为核心模块

这些额外的模块托管在Github上的,`ansible-modules-extras <http://github.com/ansible/ansible-modules-extras>`_ repo.

如果你确信你在额外模块上发现了一个 bug ,同时你使用的是最新的稳定版或者开发版本的 Ansible ,首先你需要看看 github.com/ansible/ansible-modules-extras 上的 “issue tracker” ,确保你的 bug 还没有被提交.如果还没被提交, 非常感谢你的提交

你肯能更想去问问题,而不是提交 bug ,欢迎在 Ansible-project 的 google-group 里咨询 https://groups.google.com/forum/#!forum/ansible-project ,在 Ansible 的 #ansible_chanel 咨询也是可以的,它们位于 irc.freenode.net .开发方向的应该用类似的讨论组,位于 <https://groups.google.com/forum/#!forum/ansible-devel>`_

这些模块的文档更新可以直接编辑在模块自身里面,然后提交一个 pull 请求到源码,不过你需要找到源码树的 “DOCUMENTATION” 块

获取开发模块的更多们帮助,请阅读,:doc:community, 帮助测试PR and Developing Modules.

共同的返回值

Ansible模块通常返回一个数据结构将其注册到变量中, 或者直接作为`ansible`程序的输出. 这里我们记录了所有模块的共同值, 每一个模块可以任意返回他们所独有的值. 因为这些文档的存在人们可以通过ansible-doc和https://docs.ansible.com看到.

Facts

一些模块返回’facts’(例如 setup), 这些是通过一个’ansible_facts’作为key和内部一些自动收集的值直接作为当前主机的变量并且他们不需要注册这些数据

Status

每一个模块都必须返回一个status, 来表示这个模块是成功的,是否有任何改变或没有. 当因为用户的条件(when: )或在检查模式下运行时发现该模块不支持, Ansible自己将会返回一个status并跳过这个模块.

其他的共同返回

通常在失败或者成功时返回一个’msg’, 这被用来解释执行失败的原因或者关于执行的过程说明 一些模块, 特别是那些执行shell或者commands指令, 将返回stdout和stderr, 如果ansible发现输出结果, 它将追加一条线, 这在输出上仅仅是一个列表或一条线.

See also

模块相关
Learn about available modules
GitHub Core modules directory
Browse source of core modules
Github Extras modules directory
Browse source of extras modules.
Mailing List
Development mailing list
irc.freenode.net
#ansible IRC chat channel

ansible 附带了很多可以直接在远端主机或者通过 Playbooks 执行的模块

用户也可以写出属于自己的模块.这些模块可以控制系统的资源 ,像服务,包管理,文件,或执行系统命令.

See also

Introduction To Ad-Hoc Commands
Examples of using modules in /usr/bin/ansible
Playbooks
Examples of using modules with /usr/bin/ansible-playbook
Developing Modules
How to write your own modules
Python API
Examples of using modules with the Python API
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

详细指南

这个部分是新的和展开的,这里的概念是更深的层面探索特定的用法在,同时提供了一个对基本特性的自顶向下的解释

Amazon Web Services Guide

简介

Ansible包含了大量的控制Amazon web service(AWS)模块.这个章节的目的是为了说明如何在AWS环境下组合ansible的模块使用ansible(The purpose of this section is to explain how to put Ansible modules together (and use inventory scripts) to use Ansible in AWS context)

AWS模块的需求(requiement)是很少的

所有需要的模块以及被最近的 boto 版本测试过了.你需要这个模块安装在你的控制机器上面.boto 可以从linux发行版或者使用python的pip安装

然而典型的ansible执行任务,对多个主机进行循环(Whereas classically ansible will execute tasks in its host loop against multiple remote machines),大部分的云控制步骤发生在你的本地机器所关联的区域

在你的 playbook 步骤里,我们会使用下面的样式演示步骤:

- hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - ...

认证

AWS 认证相关的模块通过指定访问密钥作为 ENV 的变量或者模块参数.

对于环境变量:

export AWS_ACCESS_KEY_ID='AK123'
export AWS_SECRET_ACCESS_KEY='abc123'

存储变量在一个 vars_file ,最好使用ansible-vault:

---
ec2_access_key: "--REMOVED--"
ec2_secret_key: "--REMOVED--"

供给(Provisioning)

在EC2中提供和取消实例的ec2模块

一个确保只有5个实例标签为“Demo”的例子

在下面的例子里,”exact_count”实例被设置为了5,这意味着如果0个实例存在,将会创建5个.如果两个实例创建,将会创建三个,如果8个实例存在,将会终止3个.

指定”count_tag”参数的将被计数, “instance_tags” 参数用于给新创建的实例应用标签:

# demo_setup.yml

- hosts: localhost
  connection: local
  gather_facts: False

  tasks:

    - name: Provision a set of instances
      ec2:
         key_name: my_key
         group: test
         instance_type: t2.micro
         image: "{{ ami_id }}"
         wait: true
         exact_count: 5
         count_tag:
            Name: Demo
         instance_tags:
            Name: Demo
      register: ec2

有关实例被常见的数据被保存在通过 “register” 关键字设置的”ec2”变量

我们将会使用 add_host 模块动态的创建主机组组成新的实例.这将会在后来的任务里面立即执行这些配置文件:

# demo_setup.yml

- hosts: localhost
  connection: local
  gather_facts: False

  tasks:

    - name: Provision a set of instances
      ec2:
         key_name: my_key
         group: test
         instance_type: t2.micro
         image: "{{ ami_id }}"
         wait: true
         exact_count: 5
         count_tag:
            Name: Demo
         instance_tags:
            Name: Demo
      register: ec2

   - name: Add all instance public IPs to host group
     add_host: hostname={{ item.public_ip }} groups=ec2hosts
     with_items: ec2.instances

在主机组添加之后,规则剧本底部的第二个演出将会开始一些配置步骤:

# demo_setup.yml

- name: Provision a set of instances
  hosts: localhost
  # ... AS ABOVE ...

- hosts: ec2hosts
  name: configuration play
  user: ec2-user
  gather_facts: true

  tasks:

     - name: Check NTP service
       service: name=ntpd state=started

主机清单

一旦你的节点开始运转起来了,你可能想去和它们通信.在云的配置下,最好不要维护静态的云主机名.最好的方式是使用ec2动态清单脚本来处理.

这将会动态的挑选节点甚至不是由Ansible创建的,同样允许Ansible管理它们.

阅读 doc:aws_example 查看如何使用,然后继续回到这个章节.

标签,组和变量

当使用ec2清单脚本的时候,主机基于它们如何在EC2里面的标签动态的出现在组里面

例如,如果一个主机给了 “class” 标签,同时给它”webserver”作为值,它会自动被动态组发现,就像这样:

- hosts: tag_class_webserver
  tasks:
    - ping

这是很好的根据他们的性能划分系统的方式.

在这个例子里,如果我们想去定义自动应用每台机器上面的变量,同时 “webserver” 带有标签 “class” , 在ansible里面可以使用的”group_vars”, 阅读:ref:splitting_out_vars.

对于区域和其它分类,相似的组是可用的,可以使用同一种机制分配相似的变量.(Similar groups are available for regions and other classifications, and can be similarly assigned variables using the same mechanism.)

使用Ansible Pull自动伸缩

Amazon有基于负载自动的增加和减少容量的特性. 在 cloud 文档里,也有一些 Ansible 模块可以配置自动伸缩策略.

当节点在线的时候,可能没有足够的时间等待下一个周期来临,让ansible命令配置那个节点.

为了这么做,(To do this, pre-bake machine images which contain the necessary ansible-pull invocation.).Ansible-pull 是从git服务器上面抓取playbook在本地运行的一个命令行工具.

这种方式的一个挑战在于在自动伸缩的环境里面需要一个中心化的方式存取 pull 命令的数据.因为这个原因,下面提供自动伸缩解决方案更好一些.

阅读 Ansible-Pull(拉取配置而非推送配置) 在pull-node playbook 获取更多的信息

使用Asnible Tower自动伸缩

Ansible Tower 同样包含了一个非常好的特性来自动伸缩.在这种方式下,简单的curl脚本可以调用定义的URL,服务器也会对请求”dial out”和配置正在运行的实例.这是一个很好的方式重新配置生存周期短暂的节点.阅读Tower安装和产品文档获取更多的信息.

在Tower使用回收机制有个好处是,任务结果仍然是中心化的,但是和远程主机分享更少的信息(A benefit of using the callback in Tower over pull mode is that job results are still centrally recorded and less information has to be shared with remote hosts.)

Ansible云构造

云构造是一个Amazon的技术,让云栈作为JSON文档

Ansible摸块提供了一个比云构造更容易的接口,不需要定义复杂的JSON文档.这是推荐用户使用的.

然而,当用户决定使用云构造,也有 Ansible 模块可以应用于云构造模板.

当使用Ansible配合云构造的时候,Ansible通常使用一个工具像 Packer 来构建镜像,CloudFormation 运行这些镜像, 或者通过用户数据,一旦镜像上线,ansible会被激发.(When using Ansible with CloudFormation, typically Ansible will be used with a tool like Packer to build images, and CloudFormation will launch those images, or ansible will be invoked through user data once the image comes online, or a combination of the two.)

请阅读ansible云构造的例子获取更多的细节.

使用Ansible 构造AWS镜像

Many users may want to have images boot to a more complete configuration rather than configuring them entirely after instantiation. To do this, one of many programs can be used with Ansible playbooks to define and upload a base image, which will then get its own AMI ID for usage with the ec2 module or other Ansible AWS modules such as ec2_asg or the cloudformation module. Possible tools include Packer, aminator, and Ansible’s ec2_ami module.

许多用户想去开机启动更完整的配置而不是安装之后配置.为了这样做,许多程序可以用于ansible playbook定义和上传基本的镜像,这让他们使用 ec2 模块后得到自己的AMI ID,或者其它的Ansible AWS模块例如ec2_asg 或者cloudformation 模块.可能的工具包含 Packer,aminator,和Ansible’s ec2_ami 模块

总的来说,我们发现许多用户使用Packer

Ansible Packer的文档可以在这里找到 https://www.packer.io/docs/provisioners/ansible-local.html.

如果你想采用Packer这时,配置一个基本的镜像使用Ansible在规则之后是可以接受的.

下一步:探索模块

Ansible附带许多模块来配置许多EC2服务.浏览 模块的 “Cloud” 目录查看完整的列表

See also

模块相关
All the documentation for Ansible modules
Playbooks
An introduction to playbooks
委托,滚动更新,本地动作
Delegation, useful for working with loud balancers, clouds, and locally executed steps.
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

Rackspace Cloud Guide

Introduction

Note

This section of the documentation is under construction. We are in the process of adding more examples about the Rackspace modules and how they work together. Once complete, there will also be examples for Rackspace Cloud in ansible-examples.

Ansible contains a number of core modules for interacting with Rackspace Cloud.

The purpose of this section is to explain how to put Ansible modules together (and use inventory scripts) to use Ansible in a Rackspace Cloud context.

Prerequisites for using the rax modules are minimal. In addition to ansible itself, all of the modules require and are tested against pyrax 1.5 or higher. You’ll need this Python module installed on the execution host.

pyrax is not currently available in many operating system package repositories, so you will likely need to install it via pip:

$ pip install pyrax

The following steps will often execute from the control machine against the Rackspace Cloud API, so it makes sense to add localhost to the inventory file. (Ansible may not require this manual step in the future):

[localhost]
localhost ansible_connection=local

In playbook steps, we’ll typically be using the following pattern:

- hosts: localhost
  connection: local
  gather_facts: False
  tasks:

Credentials File

The rax.py inventory script and all rax modules support a standard pyrax credentials file that looks like:

[rackspace_cloud]
username = myraxusername
api_key = d41d8cd98f00b204e9800998ecf8427e

Setting the environment parameter RAX_CREDS_FILE to the path of this file will help Ansible find how to load this information.

More information about this credentials file can be found at https://github.com/rackspace/pyrax/blob/master/docs/getting_started.md#authenticating

Running from a Python Virtual Environment (Optional)

Most users will not be using virtualenv, but some users, particularly Python developers sometimes like to.

There are special considerations when Ansible is installed to a Python virtualenv, rather than the default of installing at a global scope. Ansible assumes, unless otherwise instructed, that the python binary will live at /usr/bin/python. This is done via the interpreter line in modules, however when instructed by setting the inventory variable ‘ansible_python_interpreter’, Ansible will use this specified path instead to find Python. This can be a cause of confusion as one may assume that modules running on ‘localhost’, or perhaps running via ‘local_action’, are using the virtualenv Python interpreter. By setting this line in the inventory, the modules will execute in the virtualenv interpreter and have available the virtualenv packages, specifically pyrax. If using virtualenv, you may wish to modify your localhost inventory definition to find this location as follows:

[localhost]
localhost ansible_connection=local ansible_python_interpreter=/path/to/ansible_venv/bin/python

Note

pyrax may be installed in the global Python package scope or in a virtual environment. There are no special considerations to keep in mind when installing pyrax.

Provisioning

Now for the fun parts.

The ‘rax’ module provides the ability to provision instances within Rackspace Cloud. Typically the provisioning task will be performed from your Ansible control server (in our example, localhost) against the Rackspace cloud API. This is done for several reasons:

  • Avoiding installing the pyrax library on remote nodes
  • No need to encrypt and distribute credentials to remote nodes
  • Speed and simplicity

Note

Authentication with the Rackspace-related modules is handled by either specifying your username and API key as environment variables or passing them as module arguments, or by specifying the location of a credentials file.

Here is a basic example of provisioning an instance in ad-hoc mode:

$ ansible localhost -m rax -a "name=awx flavor=4 image=ubuntu-1204-lts-precise-pangolin wait=yes" -c local

Here’s what it would look like in a playbook, assuming the parameters were defined in variables:

tasks:
  - name: Provision a set of instances
    local_action:
        module: rax
        name: "{{ rax_name }}"
        flavor: "{{ rax_flavor }}"
        image: "{{ rax_image }}"
        count: "{{ rax_count }}"
        group: "{{ group }}"
        wait: yes
    register: rax

The rax module returns data about the nodes it creates, like IP addresses, hostnames, and login passwords. By registering the return value of the step, it is possible used this data to dynamically add the resulting hosts to inventory (temporarily, in memory). This facilitates performing configuration actions on the hosts in a follow-on task. In the following example, the servers that were successfully created using the above task are dynamically added to a group called “raxhosts”, with each nodes hostname, IP address, and root password being added to the inventory.

- name: Add the instances we created (by public IP) to the group 'raxhosts'
  local_action:
      module: add_host
      hostname: "{{ item.name }}"
      ansible_ssh_host: "{{ item.rax_accessipv4 }}"
      ansible_ssh_pass: "{{ item.rax_adminpass }}"
      groups: raxhosts
  with_items: rax.success
  when: rax.action == 'create'

With the host group now created, the next play in this playbook could now configure servers belonging to the raxhosts group.

- name: Configuration play
  hosts: raxhosts
  user: root
  roles:
    - ntp
    - webserver

The method above ties the configuration of a host with the provisioning step. This isn’t always what you want, and leads us to the next section.

Host Inventory

Once your nodes are spun up, you’ll probably want to talk to them again. The best way to handle his is to use the “rax” inventory plugin, which dynamically queries Rackspace Cloud and tells Ansible what nodes you have to manage. You might want to use this even if you are spinning up Ansible via other tools, including the Rackspace Cloud user interface. The inventory plugin can be used to group resources by metadata, region, OS, etc. Utilizing metadata is highly recommended in “rax” and can provide an easy way to sort between host groups and roles. If you don’t want to use the rax.py dynamic inventory script, you could also still choose to manually manage your INI inventory file, though this is less recommended.

In Ansible it is quite possible to use multiple dynamic inventory plugins along with INI file data. Just put them in a common directory and be sure the scripts are chmod +x, and the INI-based ones are not.

rax.py

To use the rackspace dynamic inventory script, copy rax.py into your inventory directory and make it executable. You can specify a credentials file for rax.py utilizing the RAX_CREDS_FILE environment variable.

Note

Dynamic inventory scripts (like rax.py) are saved in /usr/share/ansible/inventory if Ansible has been installed globally. If installed to a virtualenv, the inventory scripts are installed to $VIRTUALENV/share/inventory.

Note

Users of Ansible Tower will note that dynamic inventory is natively supported by Tower, and all you have to do is associate a group with your Rackspace Cloud credentials, and it will easily synchronize without going through these steps:

$ RAX_CREDS_FILE=~/.raxpub ansible all -i rax.py -m setup

rax.py also accepts a RAX_REGION environment variable, which can contain an individual region, or a comma separated list of regions.

When using rax.py, you will not have a ‘localhost’ defined in the inventory.

As mentioned previously, you will often be running most of these modules outside of the host loop, and will need ‘localhost’ defined. The recommended way to do this, would be to create an inventory directory, and place both the rax.py script and a file containing localhost in it.

Executing ansible or ansible-playbook and specifying the inventory directory instead of an individual file, will cause ansible to evaluate each file in that directory for inventory.

Let’s test our inventory script to see if it can talk to Rackspace Cloud.

$ RAX_CREDS_FILE=~/.raxpub ansible all -i inventory/ -m setup

Assuming things are properly configured, the rax.py inventory script will output information similar to the following information, which will be utilized for inventory and variables.

{
    "ORD": [
        "test"
    ],
    "_meta": {
        "hostvars": {
            "test": {
                "ansible_ssh_host": "1.1.1.1",
                "rax_accessipv4": "1.1.1.1",
                "rax_accessipv6": "2607:f0d0:1002:51::4",
                "rax_addresses": {
                    "private": [
                        {
                            "addr": "2.2.2.2",
                            "version": 4
                        }
                    ],
                    "public": [
                        {
                            "addr": "1.1.1.1",
                            "version": 4
                        },
                        {
                            "addr": "2607:f0d0:1002:51::4",
                            "version": 6
                        }
                    ]
                },
                "rax_config_drive": "",
                "rax_created": "2013-11-14T20:48:22Z",
                "rax_flavor": {
                    "id": "performance1-1",
                    "links": [
                        {
                            "href": "https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
                            "rel": "bookmark"
                        }
                    ]
                },
                "rax_hostid": "e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0",
                "rax_human_id": "test",
                "rax_id": "099a447b-a644-471f-87b9-a7f580eb0c2a",
                "rax_image": {
                    "id": "b211c7bf-b5b4-4ede-a8de-a4368750c653",
                    "links": [
                        {
                            "href": "https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653",
                            "rel": "bookmark"
                        }
                    ]
                },
                "rax_key_name": null,
                "rax_links": [
                    {
                        "href": "https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                        "rel": "self"
                    },
                    {
                        "href": "https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                        "rel": "bookmark"
                    }
                ],
                "rax_metadata": {
                    "foo": "bar"
                },
                "rax_name": "test",
                "rax_name_attr": "name",
                "rax_networks": {
                    "private": [
                        "2.2.2.2"
                    ],
                    "public": [
                        "1.1.1.1",
                        "2607:f0d0:1002:51::4"
                    ]
                },
                "rax_os-dcf_diskconfig": "AUTO",
                "rax_os-ext-sts_power_state": 1,
                "rax_os-ext-sts_task_state": null,
                "rax_os-ext-sts_vm_state": "active",
                "rax_progress": 100,
                "rax_status": "ACTIVE",
                "rax_tenant_id": "111111",
                "rax_updated": "2013-11-14T20:49:27Z",
                "rax_user_id": "22222"
            }
        }
    }
}
Standard Inventory

When utilizing a standard ini formatted inventory file (as opposed to the inventory plugin), it may still be advantageous to retrieve discoverable hostvar information from the Rackspace API.

This can be achieved with the rax_facts module and an inventory file similar to the following:

[test_servers]
hostname1 rax_region=ORD
hostname2 rax_region=ORD
- name: Gather info about servers
  hosts: test_servers
  gather_facts: False
  tasks:
    - name: Get facts about servers
      local_action:
        module: rax_facts
        credentials: ~/.raxpub
        name: "{{ inventory_hostname }}"
        region: "{{ rax_region }}"
    - name: Map some facts
      set_fact:
        ansible_ssh_host: "{{ rax_accessipv4 }}"

While you don’t need to know how it works, it may be interesting to know what kind of variables are returned.

The rax_facts module provides facts as followings, which match the rax.py inventory script:

{
    "ansible_facts": {
        "rax_accessipv4": "1.1.1.1",
        "rax_accessipv6": "2607:f0d0:1002:51::4",
        "rax_addresses": {
            "private": [
                {
                    "addr": "2.2.2.2",
                    "version": 4
                }
            ],
            "public": [
                {
                    "addr": "1.1.1.1",
                    "version": 4
                },
                {
                    "addr": "2607:f0d0:1002:51::4",
                    "version": 6
                }
            ]
        },
        "rax_config_drive": "",
        "rax_created": "2013-11-14T20:48:22Z",
        "rax_flavor": {
            "id": "performance1-1",
            "links": [
                {
                    "href": "https://ord.servers.api.rackspacecloud.com/111111/flavors/performance1-1",
                    "rel": "bookmark"
                }
            ]
        },
        "rax_hostid": "e7b6961a9bd943ee82b13816426f1563bfda6846aad84d52af45a4904660cde0",
        "rax_human_id": "test",
        "rax_id": "099a447b-a644-471f-87b9-a7f580eb0c2a",
        "rax_image": {
            "id": "b211c7bf-b5b4-4ede-a8de-a4368750c653",
            "links": [
                {
                    "href": "https://ord.servers.api.rackspacecloud.com/111111/images/b211c7bf-b5b4-4ede-a8de-a4368750c653",
                    "rel": "bookmark"
                }
            ]
        },
        "rax_key_name": null,
        "rax_links": [
            {
                "href": "https://ord.servers.api.rackspacecloud.com/v2/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                "rel": "self"
            },
            {
                "href": "https://ord.servers.api.rackspacecloud.com/111111/servers/099a447b-a644-471f-87b9-a7f580eb0c2a",
                "rel": "bookmark"
            }
        ],
        "rax_metadata": {
            "foo": "bar"
        },
        "rax_name": "test",
        "rax_name_attr": "name",
        "rax_networks": {
            "private": [
                "2.2.2.2"
            ],
            "public": [
                "1.1.1.1",
                "2607:f0d0:1002:51::4"
            ]
        },
        "rax_os-dcf_diskconfig": "AUTO",
        "rax_os-ext-sts_power_state": 1,
        "rax_os-ext-sts_task_state": null,
        "rax_os-ext-sts_vm_state": "active",
        "rax_progress": 100,
        "rax_status": "ACTIVE",
        "rax_tenant_id": "111111",
        "rax_updated": "2013-11-14T20:49:27Z",
        "rax_user_id": "22222"
    },
    "changed": false
}

Use Cases

This section covers some additional usage examples built around a specific use case.

Network and Server

Create an isolated cloud network and build a server

- name: Build Servers on an Isolated Network
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: Network create request
      local_action:
        module: rax_network
        credentials: ~/.raxpub
        label: my-net
        cidr: 192.168.3.0/24
        region: IAD
        state: present

    - name: Server create request
      local_action:
        module: rax
        credentials: ~/.raxpub
        name: web%04d.example.org
        flavor: 2
        image: ubuntu-1204-lts-precise-pangolin
        disk_config: manual
        networks:
          - public
          - my-net
        region: IAD
        state: present
        count: 5
        exact_count: yes
        group: web
        wait: yes
        wait_timeout: 360
      register: rax
Complete Environment

Build a complete webserver environment with servers, custom networks and load balancers, install nginx and create a custom index.html

---
- name: Build environment
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: Load Balancer create request
      local_action:
        module: rax_clb
        credentials: ~/.raxpub
        name: my-lb
        port: 80
        protocol: HTTP
        algorithm: ROUND_ROBIN
        type: PUBLIC
        timeout: 30
        region: IAD
        wait: yes
        state: present
        meta:
          app: my-cool-app
      register: clb

    - name: Network create request
      local_action:
        module: rax_network
        credentials: ~/.raxpub
        label: my-net
        cidr: 192.168.3.0/24
        state: present
        region: IAD
      register: network

    - name: Server create request
      local_action:
        module: rax
        credentials: ~/.raxpub
        name: web%04d.example.org
        flavor: performance1-1
        image: ubuntu-1204-lts-precise-pangolin
        disk_config: manual
        networks:
          - public
          - private
          - my-net
        region: IAD
        state: present
        count: 5
        exact_count: yes
        group: web
        wait: yes
      register: rax

    - name: Add servers to web host group
      local_action:
        module: add_host
        hostname: "{{ item.name }}"
        ansible_ssh_host: "{{ item.rax_accessipv4 }}"
        ansible_ssh_pass: "{{ item.rax_adminpass }}"
        ansible_ssh_user: root
        groups: web
      with_items: rax.success
      when: rax.action == 'create'

    - name: Add servers to Load balancer
      local_action:
        module: rax_clb_nodes
        credentials: ~/.raxpub
        load_balancer_id: "{{ clb.balancer.id }}"
        address: "{{ item.rax_networks.private|first }}"
        port: 80
        condition: enabled
        type: primary
        wait: yes
        region: IAD
      with_items: rax.success
      when: rax.action == 'create'

- name: Configure servers
  hosts: web
  handlers:
    - name: restart nginx
      service: name=nginx state=restarted

  tasks:
    - name: Install nginx
      apt: pkg=nginx state=latest update_cache=yes cache_valid_time=86400
      notify:
        - restart nginx

    - name: Ensure nginx starts on boot
      service: name=nginx state=started enabled=yes

    - name: Create custom index.html
      copy: content="{{ inventory_hostname }}" dest=/usr/share/nginx/www/index.html
            owner=root group=root mode=0644
RackConnect and Managed Cloud

When using RackConnect version 2 or Rackspace Managed Cloud there are Rackspace automation tasks that are executed on the servers you create after they are successfully built. If your automation executes before the RackConnect or Managed Cloud automation, you can cause failures and un-usable servers.

These examples show creating servers, and ensuring that the Rackspace automation has completed before Ansible continues onwards.

For simplicity, these examples are joined, however both are only needed when using RackConnect. When only using Managed Cloud, the RackConnect portion can be ignored.

The RackConnect portions only apply to RackConnect version 2.

Using a Control Machine
- name: Create an exact count of servers
  hosts: localhost
  connection: local
  gather_facts: False
  tasks:
    - name: Server build requests
      local_action:
        module: rax
        credentials: ~/.raxpub
        name: web%03d.example.org
        flavor: performance1-1
        image: ubuntu-1204-lts-precise-pangolin
        disk_config: manual
        region: DFW
        state: present
        count: 1
        exact_count: yes
        group: web
        wait: yes
      register: rax

    - name: Add servers to in memory groups
      local_action:
        module: add_host
        hostname: "{{ item.name }}"
        ansible_ssh_host: "{{ item.rax_accessipv4 }}"
        ansible_ssh_pass: "{{ item.rax_adminpass }}"
        ansible_ssh_user: root
        rax_id: "{{ item.rax_id }}"
        groups: web,new_web
      with_items: rax.success
      when: rax.action == 'create'

- name: Wait for rackconnect and managed cloud automation to complete
  hosts: new_web
  gather_facts: false
  tasks:
    - name: Wait for rackconnnect automation to complete
      local_action:
        module: rax_facts
        credentials: ~/.raxpub
        id: "{{ rax_id }}"
        region: DFW
      register: rax_facts
      until: rax_facts.ansible_facts['rax_metadata']['rackconnect_automation_status']|default('') == 'DEPLOYED'
      retries: 30
      delay: 10

    - name: Wait for managed cloud automation to complete
      local_action:
        module: rax_facts
        credentials: ~/.raxpub
        id: "{{ rax_id }}"
        region: DFW
      register: rax_facts
      until: rax_facts.ansible_facts['rax_metadata']['rax_service_level_automation']|default('') == 'Complete'
      retries: 30
      delay: 10

- name: Base Configure Servers
  hosts: web
  roles:
    - role: users

    - role: openssh
      opensshd_PermitRootLogin: "no"

    - role: ntp
Using Ansible Pull
---
- name: Ensure Rackconnect and Managed Cloud Automation is complete
  hosts: all
  connection: local
  tasks:
    - name: Check for completed bootstrap
      stat:
        path: /etc/bootstrap_complete
      register: bootstrap

    - name: Get region
      command: xenstore-read vm-data/provider_data/region
      register: rax_region
      when: bootstrap.stat.exists != True

    - name: Wait for rackconnect automation to complete
      uri:
        url: "https://{{ rax_region.stdout|trim }}.api.rackconnect.rackspace.com/v1/automation_status?format=json"
        return_content: yes
      register: automation_status
      when: bootstrap.stat.exists != True
      until: automation_status['automation_status']|default('') == 'DEPLOYED'
      retries: 30
      delay: 10

    - name: Wait for managed cloud automation to complete
      wait_for:
        path: /tmp/rs_managed_cloud_automation_complete
        delay: 10
      when: bootstrap.stat.exists != True

    - name: Set bootstrap completed
      file:
        path: /etc/bootstrap_complete
        state: touch
        owner: root
        group: root
        mode: 0400

- name: Base Configure Servers
  hosts: all
  connection: local
  roles:
    - role: users

    - role: openssh
      opensshd_PermitRootLogin: "no"

    - role: ntp
Using Ansible Pull with XenStore
---
- name: Ensure Rackconnect and Managed Cloud Automation is complete
  hosts: all
  connection: local
  tasks:
    - name: Check for completed bootstrap
      stat:
        path: /etc/bootstrap_complete
      register: bootstrap

    - name: Wait for rackconnect_automation_status xenstore key to exist
      command: xenstore-exists vm-data/user-metadata/rackconnect_automation_status
      register: rcas_exists
      when: bootstrap.stat.exists != True
      failed_when: rcas_exists.rc|int > 1
      until: rcas_exists.rc|int == 0
      retries: 30
      delay: 10

    - name: Wait for rackconnect automation to complete
      command: xenstore-read vm-data/user-metadata/rackconnect_automation_status
      register: rcas
      when: bootstrap.stat.exists != True
      until: rcas.stdout|replace('"', '') == 'DEPLOYED'
      retries: 30
      delay: 10

    - name: Wait for rax_service_level_automation xenstore key to exist
      command: xenstore-exists vm-data/user-metadata/rax_service_level_automation
      register: rsla_exists
      when: bootstrap.stat.exists != True
      failed_when: rsla_exists.rc|int > 1
      until: rsla_exists.rc|int == 0
      retries: 30
      delay: 10

    - name: Wait for managed cloud automation to complete
      command: xenstore-read vm-data/user-metadata/rackconnect_automation_status
      register: rsla
      when: bootstrap.stat.exists != True
      until: rsla.stdout|replace('"', '') == 'DEPLOYED'
      retries: 30
      delay: 10

    - name: Set bootstrap completed
      file:
        path: /etc/bootstrap_complete
        state: touch
        owner: root
        group: root
        mode: 0400

- name: Base Configure Servers
  hosts: all
  connection: local
  roles:
    - role: users

    - role: openssh
      opensshd_PermitRootLogin: "no"

    - role: ntp

Advanced Usage

Autoscaling with Tower

Ansible Tower also contains a very nice feature for auto-scaling use cases. In this mode, a simple curl script can call a defined URL and the server will “dial out” to the requester and configure an instance that is spinning up. This can be a great way to reconfigure ephemeral nodes. See the Tower documentation for more details.

A benefit of using the callback in Tower over pull mode is that job results are still centrally recorded and less information has to be shared with remote hosts.

Orchestration in the Rackspace Cloud

Ansible is a powerful orchestration tool, and rax modules allow you the opportunity to orchestrate complex tasks, deployments, and configurations. The key here is to automate provisioning of infrastructure, like any other piece of software in an environment. Complex deployments might have previously required manual manipulation of load balancers, or manual provisioning of servers. Utilizing the rax modules included with Ansible, one can make the deployment of additional nodes contingent on the current number of running nodes, or the configuration of a clustered application dependent on the number of nodes with common metadata. One could automate the following scenarios, for example:

  • Servers that are removed from a Cloud Load Balancer one-by-one, updated, verified, and returned to the load balancer pool
  • Expansion of an already-online environment, where nodes are provisioned, bootstrapped, configured, and software installed
  • A procedure where app log files are uploaded to a central location, like Cloud Files, before a node is decommissioned
  • Servers and load balancers that have DNS records created and destroyed on creation and decommissioning, respectively

Google Cloud Platform Guide

Introduction

Note

This section of the documentation is under construction. We are in the process of adding more examples about all of the GCE modules and how they work together. Upgrades via github pull requests are welcomed!

Ansible contains modules for managing Google Compute Engine resources, including creating instances, controlling network access, working with persistent disks, and managing load balancers. Additionally, there is an inventory plugin that can automatically suck down all of your GCE instances into Ansible dynamic inventory, and create groups by tag and other properties.

The GCE modules all require the apache-libcloud module, which you can install from pip:

$ pip install apache-libcloud

Note

If you’re using Ansible on Mac OS X, libcloud also needs to access a CA cert chain. You’ll need to download one (you can get one for here.)

Credentials

To work with the GCE modules, you’ll first need to get some credentials. You can create new one from the console by going to the “APIs and Auth” section and choosing to create a new client ID for a service account. Once you’ve created a new client ID and downloaded (you must click Generate new P12 Key) the generated private key (in the pkcs12 format), you’ll need to convert the key by running the following command:

$ openssl pkcs12 -in pkey.pkcs12 -passin pass:notasecret -nodes -nocerts | openssl rsa -out pkey.pem

There are two different ways to provide credentials to Ansible so that it can talk with Google Cloud for provisioning and configuration actions:

  • by providing to the modules directly
  • by populating a secrets.py file
Calling Modules By Passing Credentials

For the GCE modules you can specify the credentials as arguments:

  • service_account_email: email associated with the project
  • pem_file: path to the pem file
  • project_id: id of the project

For example, to create a new instance using the cloud module, you can use the following configuration:

- name: Create instance(s)
  hosts: localhost
  connection: local
  gather_facts: no

  vars:
    service_account_email: unique-id@developer.gserviceaccount.com
    pem_file: /path/to/project.pem
    project_id: project-id
    machine_type: n1-standard-1
    image: debian-7

  tasks:

   - name: Launch instances
     gce:
         instance_names: dev
         machine_type: "{{ machine_type }}"
         image: "{{ image }}"
         service_account_email: "{{ service_account_email }}"
         pem_file: "{{ pem_file }}"
         project_id: "{{ project_id }}"
Calling Modules with secrets.py

Create a file secrets.py looking like following, and put it in some folder which is in your $PYTHONPATH:

GCE_PARAMS = ('i...@project.googleusercontent.com', '/path/to/project.pem')
GCE_KEYWORD_PARAMS = {'project': 'project_id'}

Ensure to enter the email address from the created services account and not the one from your main account.

Now the modules can be used as above, but the account information can be omitted.

GCE Dynamic Inventory

The best way to interact with your hosts is to use the gce inventory plugin, which dynamically queries GCE and tells Ansible what nodes can be managed.

Note that when using the inventory script gce.py, you also need to populate the gce.ini file that you can find in the contrib/inventory directory of the ansible checkout.

To use the GCE dynamic inventory script, copy gce.py from contrib/inventory into your inventory directory and make it executable. You can specify credentials for gce.py using the GCE_INI_PATH environment variable – the default is to look for gce.ini in the same directory as the inventory script.

Let’s see if inventory is working:

$ ./gce.py --list

You should see output describing the hosts you have, if any, running in Google Compute Engine.

Now let’s see if we can use the inventory script to talk to Google.

$ GCE_INI_PATH=~/.gce.ini ansible all -i gce.py -m setup
hostname | success >> {
  "ansible_facts": {
    "ansible_all_ipv4_addresses": [
      "x.x.x.x"
    ],

As with all dynamic inventory scripts in Ansible, you can configure the inventory path in ansible.cfg. The recommended way to use the inventory is to create an inventory directory, and place both the gce.py script and a file containing localhost in it. This can allow for cloud inventory to be used alongside local inventory (such as a physical datacenter) or machines running in different providers.

Executing ansible or ansible-playbook and specifying the inventory directory instead of an individual file will cause ansible to evaluate each file in that directory for inventory.

Let’s once again use our inventory script to see if it can talk to Google Cloud:

$ ansible all -i inventory/ -m setup
hostname | success >> {
  "ansible_facts": {
    "ansible_all_ipv4_addresses": [
        "x.x.x.x"
    ],

The output should be similar to the previous command. If you’re wanting less output and just want to check for SSH connectivity, use “-m” ping instead.

Use Cases

For the following use case, let’s use this small shell script as a wrapper.

#!/usr/bin/env bash
PLAYBOOK="$1"

if [[ -z $PLAYBOOK ]]; then
  echo "You need to pass a playbook as argument to this script."
  exit 1
fi

export SSL_CERT_FILE=$(pwd)/cacert.cer
export ANSIBLE_HOST_KEY_CHECKING=False

if [[ ! -f "$SSL_CERT_FILE" ]]; then
  curl -O http://curl.haxx.se/ca/cacert.pem
fi

ansible-playbook -v -i inventory/ "$PLAYBOOK"
Create an instance

The GCE module provides the ability to provision instances within Google Compute Engine. The provisioning task is typically performed from your Ansible control server against Google Cloud’s API.

A playbook would looks like this:

- name: Create instance(s)
  hosts: localhost
  gather_facts: no
  connection: local

  vars:
    machine_type: n1-standard-1 # default
    image: debian-7
    service_account_email: unique-id@developer.gserviceaccount.com
    pem_file: /path/to/project.pem
    project_id: project-id

  tasks:
    - name: Launch instances
      gce:
          instance_names: dev
          machine_type: "{{ machine_type }}"
          image: "{{ image }}"
          service_account_email: "{{ service_account_email }}"
          pem_file: "{{ pem_file }}"
          project_id: "{{ project_id }}"
          tags: webserver
      register: gce

    - name: Wait for SSH to come up
      wait_for: host={{ item.public_ip }} port=22 delay=10 timeout=60
      with_items: gce.instance_data

    - name: Add host to groupname
      add_host: hostname={{ item.public_ip }} groupname=new_instances
      with_items: gce.instance_data

- name: Manage new instances
  hosts: new_instances
  connection: ssh
  sudo: True
  roles:
    - base_configuration
    - production_server

Note that use of the “add_host” module above creates a temporary, in-memory group. This means that a play in the same playbook can then manage machines in the ‘new_instances’ group, if so desired. Any sort of arbitrary configuration is possible at this point.

Configuring instances in a group

All of the created instances in GCE are grouped by tag. Since this is a cloud, it’s probably best to ignore hostnames and just focus on group management.

Normally we’d also use roles here, but the following example is a simple one. Here we will also use the “gce_net” module to open up access to port 80 on these nodes.

The variables in the ‘vars’ section could also be kept in a ‘vars_files’ file or something encrypted with Ansible-vault, if you so choose. This is just a basic example of what is possible:

- name: Setup web servers
  hosts: tag_webserver
  gather_facts: no

  vars:
    machine_type: n1-standard-1 # default
    image: debian-7
    service_account_email: unique-id@developer.gserviceaccount.com
    pem_file: /path/to/project.pem
    project_id: project-id

  roles:

    - name: Install lighttpd
      apt: pkg=lighttpd state=installed
      sudo: True

    - name: Allow HTTP
      local_action: gce_net
      args:
        fwname: "all-http"
        name: "default"
        allowed: "tcp:80"
        state: "present"
        service_account_email: "{{ service_account_email }}"
        pem_file: "{{ pem_file }}"
        project_id: "{{ project_id }}"

By pointing your browser to the IP of the server, you should see a page welcoming you.

Upgrades to this documentation are welcome, hit the github link at the top right of this page if you would like to make additions!

使用Vagrant和Ansible

简介

Vargrant是一个管理虚拟机环境的工具,允许你在不同的虚拟化和云平台 配置和使用可再生的工作环境.它也集成了Ansible作为对虚拟机的服务提供者,而且这两个工具配合的很好.

这个指南会叙述如何同时配合使用Vagrant和Ansible.

如果你对Vagrant还不了解,你应该看看这个文档 the documentation.

假设你已经安装了Ansible,在Git上检测,运行的也很好,查看下面的:doc:intro_installation 获取更多的信息.

配置Vagrant

第一步安装了Vagrant之后,创建一个 Vagrantfile ,修改它来适应你的需要.Vagrant文档里面已经包含了很多细节了,这里仅仅给出一个快速的参考实例

$ mkdir vagrant-test
$ cd vagrant-test
$ vagrant init precise32 http://files.vagrantup.com/precise32.box

这会创建名称为 Vagrantfile 的文件,你可以编辑它适应你的需要.默认的Vagrantfile有很多注释.这里是一个简化的例子包括了一个使用ansible提供服务的部分.

#Vagrant API/syntax 版本.不要修改它除非你知道你自己在做什么.

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
    config.vm.box = "precise32"
    config.vm.box_url = "http://files.vagrantup.com/precise32.box"

    config.vm.network :public_network

    config.vm.provision "ansible" do |ansible|
        ansible.playbook = "playbook.yml"
    end
end

Vagrantfile 有很多选项,但这些是最重要的.注意 config.vm.provision``部分,引用了叫做``playbook.yml 的 Ansible playbook,它与Vagrantfile的在同样的目录里面.Vagrant 一旦虚拟机启动和已经准备好了ssh访问的时候.运行这个提供的服务(prvisoner)

$ vagrant up

这将会启动VM和运行提供的playbook文件.

在你的Vagrantfile里面,有许多Ansible选项可以配置.有用的选项有 ansible.extra_vars, ansible.sudoansible.sudo_user , 和可以避免SSH对新的虚拟机的连接问题的 ansible.host_key_checking

查看 Ansible Provisioner documentation 获取更多信息

重新运行一个在已存在的VM上的playbook,运行

$ vagrant provision

这将会重新运行playbook

手动运行Ansible

有时你想手动运行Ansible,而不是机器.这相对来说很简单.

Vargrant自动的为Vagrant机器创建清单文件,存在相同的目录下面 .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory.它根据Vagrant自动创建的SSH管道配置清单文件,执行``ansible-playbook`` 使用正确的用户名和SSH密钥选项来访问.一个典型的自动创建清单文件的例子看起来就像下面这样.

# Generated by Vagrant

machine ansible_ssh_host=127.0.0.1 ansible_ssh_port=2222

如果你想运行Ansible手动的,你会想确保是否传递给``ansible`` 或者 ansible-playbook 命令正确的参数,和自动生成了清单文件.

这是一个例子

$ ansible-playbook -i .vagrant/provisioners/ansible/inventory/vagrant_ansible_inventory --private-key=.vagrant/machines/default/virtualbox/private_key -u vagrant playbook.yml

注意:Vagrant地域1.7.0的版本会使用私钥位于``~/.vagrant.d/insecure_private_key.``

See also

Vagrant Home
The Vagrant homepage with downloads
Vagrant Documentation
Vagrant Documentation
Ansible Provisioner
The Vagrant documentation for the Ansible provisioner
Playbooks
An introduction to playbooks

持续交付与滚动升级

介绍

持续交付是频繁对软件应用程序持续更新的概念.

这个想法使在大量频繁的更新面前, 你不必等待在一个指定的特殊时间点, 并且使你的组织在响应过程中变得更好.

一些 Ansible 用户每小时都在部署更新给他们的最终用户甚至更加频繁 – 每时每刻都有代码修改的批准. 要实现这一点, 你需要工具能在零停机的时间内快速的应用这些更新.

本文档详细介绍了如何现实这一目标, 使用 Ansible playbooks 作为一个完整例子的模板: lamp_haproxy. 这个例子使用了大量的 Ansible 特性: roles, templates 和 group variables, 并且它还配备了一个业务流程的 playbook 可以做到零停机滚动升级 web 应用程序栈.

这个 playbooks 基于 CentOS 部署 Apache, PHP, MySQL, Nagios, 和 HAProxy 这些服务.

在这里我们不去讨论如何运行这些 playbooks. 阅读包含在 github 项目中关于这个例子的 README 信息. 相反的, 我们将进一步观察这些 playbook 并且去解释它们.

部署网站

让我们首先使用 site.yml. 这是我们网站部署的 playbook. 它被用来部署我们最初的网站以及推送更新到所有的服务器:

---
# 这个 playbook 在这个网站上部署整个应用程序.

# 应用通用的配置到所有的主机上
- hosts: all

  roles:
  - common

# 配置和部署数据库服务器.
- hosts: dbservers

  roles:
  - db

# 配置和部署 web 服务器. 注意这里我们包含了两个 roles, 这个 'base-apache' role 用来简单设置 Apache, 而 'web' 则包含了我们的 web 应用程序.

- hosts: webservers

  roles:
  - base-apache
  - web

# 配置和部署 load balancer(s).
- hosts: lbservers

  roles:
  - haproxy

# 配置和部署 Nagios 监控节点(s).
- hosts: monitoring

  roles:
  - base-apache
  - nagios

Note

如果你不熟悉 playbooks 和 plays, 你应该回顾 Playbooks.

在这个 palybook 我们有 5 个 plays. 首先第一个目标 all (所有)主机和适用于所有主机的 common role. 这是整个网站要做的事像 yum 仓库的配置, 防火墙的配置, 和其他任何需要适用于所有服务器的配置.

接下来的这四个 plays 将运行于指定的主机组并特定的角色应用于这些服务器. 随着对 Nagios monitoring, 数据库角色, 和 web应用程序的应用, 我们可以通过 base-apache 角色安装和配置一个基本的 Apache. 这是 Nagios 主机和 web 应用程序所需要的.

可重用的: Roles

关于 roles 你现在应该有一点了解以及它们是如何工作的. Roles 是组织: tasks, handlers, templates, 和 files, 到可重用的组件中的方法.

这个例子有 6 个 roles: common, base-apache, db, haproxy, nagios, 和 web. 你如何组织你的 roles 是由你和你的应用程序所决定, 但是大部分网站都将适用一个或多个共同的 roles, 和一些列关于应用程序特定的 roles 来安装和配置这个网站的特定部分.

Roles 可以用变量和依赖关系, 你可以通过参数来调整它们的行为. 你可以在 Playbook Roles and Include Statements 章节中阅读更多关于 roles

配置: Group Variables

Group variables 是应用在服务器组上的. 通过设置和修改参数将他们应用在 templates 中来定义 playbooks 的行为. 他们被存储在和你的 inventory 相同目录下名为 group_vars 的目录中. 下面是 lamp_haproxy 的 group_vars/all 文件内容. 正如你所期望的, 这些变量将会应用到 inventory 中的所有服务器上:

---
httpd_port: 80
ntpserver: 192.168.1.2

这是一个 YAML 文件, 并且你可以创建列表和字典等更加复杂的变量结构. 在这种情况下, 我们只设置了两个变量, 一个做为 web server 的端口, 一个作为我们服务器所使用的时间同步 NTP 服务的地址.

这是另外一个 group variables 文件. 这个 group_vars/dbservers 适用于在 dbservers 组中的主机:

---
mysqlservice: mysqld
mysql_port: 3306
dbuser: root
dbname: foodb
upassword: usersecret

如果你看了这个例子, 你会发现对于 webservers 组合 lbservers 组的 group variables 十分相似.

这些变量可以用于任何地方. 你可以在 playbooks 中使用它们, 像这样, 在 roles/db/tasks/main.yml:

- name: Create Application Database
  mysql_db: name={{ dbname }} state=present

- name: Create Application DB User
  mysql_user: name={{ dbuser }} password={{ upassword }}
              priv=*.*:ALL host='%' state=present

你也可以在 templates 中使用这些变量, 想这样, 在 roles/common/templates/ntp.conf.j2:

driftfile /var/lib/ntp/drift

restrict 127.0.0.1
restrict -6 ::1

server {{ ntpserver }}

includefile /etc/ntp/crypto/pw

keys /etc/ntp/keys

你可以看到这些变量替换的语法 {{ and }} 和 templates 中的变量是相同的. 这种花括号格式是采用的jinj2语法, 你在对于内部的数据做各种操作及应用不同的过滤器. 在 templates, 你也可以使用循环和 if 语句来处理更加复杂的情况, 想这样, 在 roles/common/templates/iptables.j2:

{% if inventory_hostname in groups['dbservers'] %}
-A INPUT -p tcp  --dport 3306 -j  ACCEPT
{% endif %}

这是用来判断, 名为 (inventory_hostname) 的机器是否存在于组 dbservers. 如果这样的话, 该机器将会添加一条 目标端口为 3306 的 iptables 允许规则.

这有一些其他的例子, 来自相同的模板:

{% for host in groups['monitoring'] %}
-A INPUT -p tcp -s {{ hostvars[host].ansible_default_ipv4.address }} --dport 5666 -j ACCEPT
{% endfor %}

这里循环了一个组名为 monitoring 中的所有主机, 并且配置了源地址为所有监控主机的 IPV4 地址目标端口为 5666 的 iptables 允许规则到当前主机上, 正因为如此 Nagios 才可以监控这些主机.

你可以学到更多关于 Jinja2 的功能 here, 并且你可以读到更多关于 Ansible 所有的变量在这个 Variables 章节

滚动升级

现在你有了一个全面的网站包含 web servers, 一个 load balancer, 和 monitoring. 如何更新它? 这就是 Ansible 的特殊功能发挥作用. 尽管一些应用程序使用’业务流程’来编排命令执行的逻辑, Ansible将指挥编排这些机器, 并且拥有一个相当复杂的引擎.

Ansible 有能力在一次操作中协调多种应用程序, 使在进行更新升级我们的 web 应用程序时更加实现零停机时间. 这是一个单独的 playbook, 叫做 roleing_upgrade.yml.

看这个 playbook, 你可以看到它是由两个 plays 组成. 首先第一个看起来十分简单像这样:

- hosts: monitoring
  tasks: []

这里要做什么, 为什么没有 tasks? 你可能知道 Ansible 在运行之前会从服务上收集 “facts”. 这些 facts 是很多种有用的信息: 网络信息, OS/发行版本, 配置. 在我们的方案中, 在更新之前我们需要了解关于所有监控服务器的环境信息, 因此这个简单的 paly 将会在我们的所监控的服务器上强制收集 fact 信息. 你有时会见到这种模式, 这是一个有用的技巧.

接下来的部分是更新 play. 第一部分看起来是这样:

- hosts: webservers
  user: root
  serial: 1

我们仅仅是像通常一样在 webservers 组中定义了 play. 这个 serial 关键字告诉 Ansible 每次操作多少服务器. 如果它没有被指定, Ansible 默认根据配置文件中 “forks” 限制指定的值进行并发操作. 但是对于零停机时间的更新, 你可能不希望一次操作多个主机. 如果你仅仅有少数的 web 服务器, 你可能希望设置 serial 为 1, 在同一时间只执行一台主机. 如果你有 100 台, 你可以设置 serial 为 10, 同一时间执行 10 台.

下面是更新 play 接下来的部分:

pre_tasks:
- name: disable nagios alerts for this host webserver service
  nagios: action=disable_alerts host={{ inventory_hostname }} services=webserver
  delegate_to: "{{ item }}"
  with_items: groups.monitoring

- name: disable the server in haproxy
  shell: echo "disable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
  delegate_to: "{{ item }}"
  with_items: groups.lbservers

这个 pre_tasks 关键字仅仅是让在 roles 调用前列出运行的 tasks. 这段时间将十分有用. 如果你看到这些 tasks 的名称, 你会发现我们禁用了 Nagios 的报警并且将当前更新的服务器从 HAProxy load balancing pool 中移除.

参数``delegate_to`` 和 with_items 一起来使用, 因为 Ansible 循环每一个 monitoring 服务器和 load balancer, 并且针对循环的值在 monitoring 或 load balancing 上操作(delegate 代表操作). 从编程方面来说, 外部的循环是 web 服务器列表, 内部的循环是 monitoring 服务器列表.

请注意 HAProxy 的步骤看起来有点复杂. 我们使用它作为例子是因为它是免费的, 但如果你有(例如)一个 F5 或 Netscaler 在你的基础设施上(或者你有一个 AWS 弹性 IP 的设置?), 你可以使用 Ansible 的模块而不是直接和他们进行交互. 你也可能希望使用其他的 monitoring 模块来代替 nagios, 但是这仅仅是展示了在任务开始前的部分 – 把服务从监控中移除并且轮换它们.

下一步重新简单的使正确的角色应用在 web 服务器上. 这将导致一些名为 webbase-apache 的配置管理角色应用到 web 服务器上, 包含一个更新 web 应用程序自身代码. 我们不需要这样做 – 我们仅需要将其修改为纯碎的更新 web 程序, 但是这是一个很好的例子关于如何通过 roles 来重用这些任务:

roles:
- common
- base-apache
- web

最后, 在 post_tasks 部分, 我们反向的改变 Nagios 的配置并且将 web 服务器重新添加到 load balancing pool:

post_tasks:
- name: Enable the server in haproxy
  shell: echo "enable server myapplb/{{ inventory_hostname }}" | socat stdio /var/lib/haproxy/stats
  delegate_to: "{{ item }}"
  with_items: groups.lbservers

- name: re-enable nagios alerts
  nagios: action=enable_alerts host={{ inventory_hostname }} services=webserver
  delegate_to: "{{ item }}"
  with_items: groups.monitoring

再一次说明, 如果你在使用一个 Netscaler 或 F5 或 Elastic 的负载均衡器, 你仅仅需要替换为适合的模块对象.

管理其他的负载均衡

在这个例子中, 我们使用了简单的 HAProxy 负载均衡到后端的 web 服务器. 它是非常容易配置和管理的. 正如我们所提到的, Ansible 已经为其他的负载均衡器像 Citrix NetScaler, F5 BigIP, Amazon Elastic Load Balancers 等提供了内建的支持.阅读更多信息 模块相关

对于其他的负载均衡器, 如果公开一个负载均衡时, 你可能需要向它们发送 shell 命令 (像上面我们对 HAProxy 一样), 或者调用一些 API. 你可以越多更多关于 local actions 在这个 委托,滚动更新,本地动作 章节中. 对于一些硬件的开发将更加有趣, 他们没有一个核心模块, 所以你可以使用更好的模块将他们封装起来!

持续交付结束

现在你有一个自动化的方式来部署更新你的应用程序, 你将如何将他们绑定在一起? 许多组织使用持续集成的工具像 JenkinsAtlassian Bamboo 来完成开发, 测试, 发布, 和部署这样的流程步骤. 你也可以使用这些工具像 Gerrit 来添加一个 code review 的步骤来审查提交的应用程序的本身或者 Ansible playbooks.

根据你的环境, 你可能会部署到一个测试环境, 在这个环境中运行一些集成测试, 然后自动部署到生产环境. 你可以保持他们的简单性仅按需来进行滚动升级到测试或者指定的生产环境中. 这些你都随你决定.

与持续集成工具的结合, 你可以通过 ansible-playbook 命令行工具很容易的触发 playbook 的运行, 或者, 如果你使用 Ansible Tower, tower-cli 或者内置的 REST API. (这个 tower-cli 命令的 ‘joblaunch’ 将通过 REST API 远程产生一个 job 这非常棒).

Ansible 对于如何组合多层应用程序在任务编排和持续交付给客户的最终目标上给了你很好的主意. 你可以使用滚动升级的思路来扩展一些应用程序之间的不同部分; 也许向前端 web 服务器添加后端应用服务, 例如, 使用 MongoDB 或 Riak 来替换 SQL 数据库. Ansible 可以给你在复杂的环境中轻松完成常见的自动化操作.

See also

lamp_haproxy example
The lamp_haproxy example discussed here.
Playbooks
An introduction to playbooks
Playbook Roles and Include Statements
An introduction to playbook roles
Variables
An introduction to Ansible variables
Ansible.com: Continuous Delivery
An introduction to Continuous Delivery with Ansible

即将包含的,目前未定的主题包括: Docerk,Jenkins , Google Compute Engine, Linode/DigitalOcean, Continuous Deployment,等等

开发者须知

学习如何使用其他语言开发自己的模块,并且通过各种插件来扩展Ansible.探索Ansible的Python API,并且编写Python插件,将其结合到你自己系统的各种解决方案中,这是非常有意义的.

Python API

本章将展示几种有趣的Ansible API调用方式.你可以使用Ansible的Python API来管理节点,可以通过扩展Ansible来响应大量的Python事件,你可以写各种的插件,并且,你可以通过插件来调取外部数据源.本文主要向读者简单介绍一下 Runner 和 Playbook 的API.

如果你想使用除Python的其他方法调用Ansible,使用其异步回调事件,或者访问控制,日志管理,可以访问 Ansible Tower,它提供了非常丰富的 REST API.

此外,Ansible本身也是基于他本身的API来实现的,所以你将拥有足够的权限来进行二次封装.本章将讨论Python API的使用.

Python API

Ansible的Python API 功能十分强大,它造就了ansible CLI和ansible-playbook.

以下是一个简单调用的例子:

import ansible.runner

runner = ansible.runner.Runner(
   module_name='ping',
   module_args='',
   pattern='web*',
   forks=10
)
datastructure = runner.run()

该方法将返回每个host主机是否可以被ping通.返回类型详情请参阅 模块相关.:

{
    "dark" : {
       "web1.example.com" : "failure message"
    },
    "contacted" : {
       "web2.example.com" : 1
    }
}

每个模型均可以返回任意JSON格式数据,所以Ansible可以作为一个框架被封装在各种应用程序和脚本之中.

更具体的例子

以下的脚本将打印出所有机器的运行时间和系统负载信息:

#!/usr/bin/python

import ansible.runner
import sys

# 构造ansible runner 并且开启10个线程向远程主机执行uptime命令
results = ansible.runner.Runner(
    pattern='*', forks=10,
    module_name='command', module_args='/usr/bin/uptime',
).run()

if results is None:
   print "No hosts found"
   sys.exit(1)

print "UP ***********"
for (hostname, result) in results['contacted'].items():
    if not 'failed' in result:
        print "%s >>> %s" % (hostname, result['stdout'])

print "FAILED *******"
for (hostname, result) in results['contacted'].items():
    if 'failed' in result:
        print "%s >>> %s" % (hostname, result['msg'])

print "DOWN *********"
for (hostname, result) in results['dark'].items():
    print "%s >>> %s" % (hostname, result)

高级的开发人员可能会去阅读ansible的源码,但使用 Runner() API (使用它能提供的选项)可以增强命令行执行 ansibleansible-playbook 的功能.

See also

开发动态的Inventory数据源
Developing dynamic inventory integrations
Developing Modules
How to develop modules
Developing Plugins
How to develop plugins
Development Mailing List
Mailing list for development topics
irc.freenode.net
#ansible IRC chat channel

开发动态的Inventory数据源

动态 Inventory 所介绍,ansible可以从一个动态的数据源获取到inventory信息,包含云端数据源

怎么写一个自己的数据源?

很简单!我们仅仅需要创建一个在适当参数下,能够返回正确JSON格式数据的脚本或者程序,你可以使用任何语言来实现.

脚本规范

当我们在外部使用``–list``参数调用这个脚本时,这个脚本必须返回一个JSON散列/字典,它包含所管理的所有组.每个组的value应该是一个关于其包含的主机/IP哈希/字典,它可能是一个子组或者组的变量或者仅仅是一个主机/IP的列表, 例如:

{
    "databases"   : {
        "hosts"   : [ "host1.example.com", "host2.example.com" ],
        "vars"    : {
            "a"   : true
        }
    },
    "webservers"  : [ "host2.example.com", "host3.example.com" ],
    "atlanta"     : {
        "hosts"   : [ "host1.example.com", "host4.example.com", "host5.example.com" ],
        "vars"    : {
            "b"   : false
        },
        "children": [ "marietta", "5points" ]
    },
    "marietta"    : [ "host6.example.com" ],
    "5points"     : [ "host7.example.com" ]
}

New in version 1.0.

在版本1.0之前,每一个组只能是一个包含hostnames/IP Address的列表,像上面的webservers, marietta, 5points组

当我们使用``–host <hostname>``(这里的<hostname>只指相对上面数据中的host)参数调用时,这个脚本必须返回一条空的JSON 哈希/字典, 或者关于变量的JSON哈希/字典,这些变量将被用来模板或者playbooks. 返回变量是可选的,如果脚本不希望这样做,返回一条空的哈希/字典即可:

{
    "favcolor"   : "red",
    "ntpserver"  : "wolf.example.com",
    "monitoring" : "pack.example.com"
}

开启调用外部Inventory脚本

New in version 1.3.

这个inventory脚本系统在所有的Ansible版本中都将会被调用,但是当使用``–host``参数操作每一台主机时,这将是十分麻烦(低效率),尤其是当它用在调用远程子系统时.在Ansible 1.3以后的版本(包含1.3),如果inventory脚本返回的顶级元素为”_meta”,它可能会返回所有主机的变量.如果这个元素中包含一个名为”hostvars”的value,这个inventory脚本对每一台主机使用``–host``时将不会被调用.这将大大增加主机的执行效率,并且也使客户端更容易实现这个脚本的数据缓存.

这个数据将会被添加到JSON字典的顶级,像下面的格式:

{

    # results of inventory script as above go here
    # inventory脚本将到此终止
    # ...

    "_meta" : {
       "hostvars" : {
          "moocow.example.com"     : { "asdf" : 1234 },
          "llama.example.com"      : { "asdf" : 5678 },
       }
    }

}

See also

Python API
Python API to Playbooks and Ad Hoc Task Execution
Developing Modules
How to develop modules
Developing Plugins
How to develop plugins
Ansible Tower
REST API endpoint and GUI for Ansible, syncs with dynamic inventory
Development Mailing List
Mailing list for development topics
irc.freenode.net
#ansible IRC chat channel

Developing Modules

Ansible modules are reusable units of magic that can be used by the Ansible API, or by the ansible or ansible-playbook programs.

See 模块相关 for a list of various ones developed in core.

Modules can be written in any language and are found in the path specified by ANSIBLE_LIBRARY or the --module-path command line option.

By default, everything that ships with ansible is pulled from its source tree, but additional paths can be added.

The directory ”./library”, alongside your top level playbooks, is also automatically added as a search directory.

Should you develop an interesting Ansible module, consider sending a pull request to the modules-extras project. There’s also a core repo for more established and widely used modules. “Extras” modules may be promoted to core periodically, but there’s no fundamental difference in the end - both ship with ansible, all in one package, regardless of how you acquire ansible.

Tutorial

Let’s build a very-basic module to get and set the system time. For starters, let’s build a module that just outputs the current time.

We are going to use Python here but any language is possible. Only File I/O and outputting to standard out are required. So, bash, C++, clojure, Python, Ruby, whatever you want is fine.

Now Python Ansible modules contain some extremely powerful shortcuts (that all the core modules use) but first we are going to build a module the very hard way. The reason we do this is because modules written in any language OTHER than Python are going to have to do exactly this. We’ll show the easy way later.

So, here’s an example. You would never really need to build a module to set the system time, the ‘command’ module could already be used to do this. Though we’re going to make one.

Reading the modules that come with ansible (linked above) is a great way to learn how to write modules. Keep in mind, though, that some modules in ansible’s source tree are internalisms, so look at service or yum, and don’t stare too close into things like async_wrapper or you’ll turn to stone. Nobody ever executes async_wrapper directly.

Ok, let’s get going with an example. We’ll use Python. For starters, save this as a file named timetest.py:

#!/usr/bin/python

import datetime
import json

date = str(datetime.datetime.now())
print json.dumps({
    "time" : date
})

Testing Modules

There’s a useful test script in the source checkout for ansible:

git clone git@github.com:ansible/ansible.git --recursive
source ansible/hacking/env-setup
chmod +x ansible/hacking/test-module

Let’s run the script you just wrote with that:

ansible/hacking/test-module -m ./timetest.py

You should see output that looks something like this:

{u'time': u'2012-03-14 22:13:48.539183'}

If you did not, you might have a typo in your module, so recheck it and try again.

Reading Input

Let’s modify the module to allow setting the current time. We’ll do this by seeing if a key value pair in the form time=<string> is passed in to the module.

Ansible internally saves arguments to an arguments file. So we must read the file and parse it. The arguments file is just a string, so any form of arguments are legal. Here we’ll do some basic parsing to treat the input as key=value.

The example usage we are trying to achieve to set the time is:

time time="March 14 22:10"

If no time parameter is set, we’ll just leave the time as is and return the current time.

Note

This is obviously an unrealistic idea for a module. You’d most likely just use the shell module. However, it probably makes a decent tutorial.

Let’s look at the code. Read the comments as we’ll explain as we go. Note that this is highly verbose because it’s intended as an educational example. You can write modules a lot shorter than this:

#!/usr/bin/python

# import some python modules that we'll use.  These are all
# available in Python's core

import datetime
import sys
import json
import os
import shlex

# read the argument string from the arguments file
args_file = sys.argv[1]
args_data = file(args_file).read()

# for this module, we're going to do key=value style arguments
# this is up to each module to decide what it wants, but all
# core modules besides 'command' and 'shell' take key=value
# so this is highly recommended

arguments = shlex.split(args_data)
for arg in arguments:

    # ignore any arguments without an equals in it
    if "=" in arg:

        (key, value) = arg.split("=")

        # if setting the time, the key 'time'
        # will contain the value we want to set the time to

        if key == "time":

            # now we'll affect the change.  Many modules
            # will strive to be 'idempotent', meaning they
            # will only make changes when the desired state
            # expressed to the module does not match
            # the current state.  Look at 'service'
            # or 'yum' in the main git tree for an example
            # of how that might look.

            rc = os.system("date -s \"%s\"" % value)

            # always handle all possible errors
            #
            # when returning a failure, include 'failed'
            # in the return data, and explain the failure
            # in 'msg'.  Both of these conventions are
            # required however additional keys and values
            # can be added.

            if rc != 0:
                print json.dumps({
                    "failed" : True,
                    "msg"    : "failed setting the time"
                })
                sys.exit(1)

            # when things do not fail, we do not
            # have any restrictions on what kinds of
            # data are returned, but it's always a
            # good idea to include whether or not
            # a change was made, as that will allow
            # notifiers to be used in playbooks.

            date = str(datetime.datetime.now())
            print json.dumps({
                "time" : date,
                "changed" : True
            })
            sys.exit(0)

# if no parameters are sent, the module may or
# may not error out, this one will just
# return the time

date = str(datetime.datetime.now())
print json.dumps({
    "time" : date
})

Let’s test that module:

ansible/hacking/test-module -m ./time -a time=\"March 14 12:23\"

This should return something like:

{"changed": true, "time": "2012-03-14 12:23:00.000307"}

Module Provided ‘Facts’

The ‘setup’ module that ships with Ansible provides many variables about a system that can be used in playbooks and templates. However, it’s possible to also add your own facts without modifying the system module. To do this, just have the module return a ansible_facts key, like so, along with other return data:

{
    "changed" : True,
    "rc" : 5,
    "ansible_facts" : {
        "leptons" : 5000,
        "colors" : {
            "red"   : "FF0000",
            "white" : "FFFFFF"
        }
    }
}

These ‘facts’ will be available to all statements called after that module (but not before) in the playbook. A good idea might be make a module called ‘site_facts’ and always call it at the top of each playbook, though we’re always open to improving the selection of core facts in Ansible as well.

Common Module Boilerplate

As mentioned, if you are writing a module in Python, there are some very powerful shortcuts you can use. Modules are still transferred as one file, but an arguments file is no longer needed, so these are not only shorter in terms of code, they are actually FASTER in terms of execution time.

Rather than mention these here, the best way to learn is to read some of the source of the modules that come with Ansible.

The ‘group’ and ‘user’ modules are reasonably non-trivial and showcase what this looks like.

Key parts include always ending the module file with:

from ansible.module_utils.basic import *
if __name__ == '__main__':
    main()

And instantiating the module class like:

module = AnsibleModule(
    argument_spec = dict(
        state     = dict(default='present', choices=['present', 'absent']),
        name      = dict(required=True),
        enabled   = dict(required=True, choices=BOOLEANS),
        something = dict(aliases=['whatever'])
    )
)

The AnsibleModule provides lots of common code for handling returns, parses your arguments for you, and allows you to check inputs.

Successful returns are made like this:

module.exit_json(changed=True, something_else=12345)

And failures are just as simple (where ‘msg’ is a required parameter to explain the error):

module.fail_json(msg="Something fatal happened")

There are also other useful functions in the module class, such as module.sha1(path). See lib/ansible/module_common.py in the source checkout for implementation details.

Again, modules developed this way are best tested with the hacking/test-module script in the git source checkout. Because of the magic involved, this is really the only way the scripts can function outside of Ansible.

If submitting a module to ansible’s core code, which we encourage, use of the AnsibleModule class is required.

Check Mode

New in version 1.1.

Modules may optionally support check mode. If the user runs Ansible in check mode, the module should try to predict whether changes will occur.

For your module to support check mode, you must pass supports_check_mode=True when instantiating the AnsibleModule object. The AnsibleModule.check_mode attribute will evaluate to True when check mode is enabled. For example:

module = AnsibleModule(
    argument_spec = dict(...),
    supports_check_mode=True
)

if module.check_mode:
    # Check if any changes would be made but don't actually make those changes
    module.exit_json(changed=check_if_system_state_would_be_changed())

Remember that, as module developer, you are responsible for ensuring that no system state is altered when the user enables check mode.

If your module does not support check mode, when the user runs Ansible in check mode, your module will simply be skipped.

Common Pitfalls

You should also never do this in a module:

print "some status message"

Because the output is supposed to be valid JSON.

Modules must not output anything on standard error, because the system will merge standard out with standard error and prevent the JSON from parsing. Capturing standard error and returning it as a variable in the JSON on standard out is fine, and is, in fact, how the command module is implemented.

If a module returns stderr or otherwise fails to produce valid JSON, the actual output will still be shown in Ansible, but the command will not succeed.

Always use the hacking/test-module script when developing modules and it will warn you about these kind of things.

Conventions/Recommendations

As a reminder from the example code above, here are some basic conventions and guidelines:

  • If the module is addressing an object, the parameter for that object should be called ‘name’ whenever possible, or accept ‘name’ as an alias.
  • If you have a company module that returns facts specific to your installations, a good name for this module is site_facts.
  • Modules accepting boolean status should generally accept ‘yes’, ‘no’, ‘true’, ‘false’, or anything else a user may likely throw at them. The AnsibleModule common code supports this with “choices=BOOLEANS” and a module.boolean(value) casting function.
  • Include a minimum of dependencies if possible. If there are dependencies, document them at the top of the module file, and have the module raise JSON error messages when the import fails.
  • Modules must be self-contained in one file to be auto-transferred by ansible.
  • If packaging modules in an RPM, they only need to be installed on the control machine and should be dropped into /usr/share/ansible. This is entirely optional and up to you.
  • Modules must output valid JSON only. The toplevel return type must be a hash (dictionary) although they can be nested. Lists or simple scalar values are not supported, though they can be trivially contained inside a dictionary.
  • In the event of failure, a key of ‘failed’ should be included, along with a string explanation in ‘msg’. Modules that raise tracebacks (stacktraces) are generally considered ‘poor’ modules, though Ansible can deal with these returns and will automatically convert anything unparseable into a failed result. If you are using the AnsibleModule common Python code, the ‘failed’ element will be included for you automatically when you call ‘fail_json’.
  • Return codes from modules are not actually not significant, but continue on with 0=success and non-zero=failure for reasons of future proofing.
  • As results from many hosts will be aggregated at once, modules should return only relevant output. Returning the entire contents of a log file is generally bad form.

Documenting Your Module

All modules included in the CORE distribution must have a DOCUMENTATION string. This string MUST be a valid YAML document which conforms to the schema defined below. You may find it easier to start writing your DOCUMENTATION string in an editor with YAML syntax highlighting before you include it in your Python file.

Example

See an example documentation string in the checkout under examples/DOCUMENTATION.yml.

Include it in your module file like this:

#!/usr/bin/python
# Copyright header....

DOCUMENTATION = '''
---
module: modulename
short_description: This is a sentence describing the module
# ... snip ...
'''

The description, and notes fields support formatting with some special macros.

These formatting functions are U(), M(), I(), and C() for URL, module, italic, and constant-width respectively. It is suggested to use C() for file and option names, and I() when referencing parameters; module names should be specified as M(module).

Examples (which typically contain colons, quotes, etc.) are difficult to format with YAML, so these must be written in plain text in an EXAMPLES string within the module like this:

EXAMPLES = '''
- action: modulename opt1=arg1 opt2=arg2
'''

The EXAMPLES section, just like the documentation section, is required in all module pull requests for new modules.

Building & Testing

Put your completed module file into the ‘library’ directory and then run the command: make webdocs. The new ‘modules.html’ file will be built and appear in the ‘docsite/’ directory.

Tip

If you’re having a problem with the syntax of your YAML you can validate it on the YAML Lint website.

Tip

You can set the environment variable ANSIBLE_KEEP_REMOTE_FILES=1 on the controlling host to prevent ansible from deleting the remote files so you can debug your module.

Module Paths

If you are having trouble getting your module “found” by ansible, be sure it is in the ANSIBLE_LIBRARY environment variable.

If you have a fork of one of the ansible module projects, do something like this:

ANSIBLE_LIBRARY=~/ansible-modules-core:~/ansible-modules-extras

And this will make the items in your fork be loaded ahead of what ships with Ansible. Just be sure to make sure you’re not reporting bugs on versions from your fork!

To be safe, if you’re working on a variant on something in Ansible’s normal distribution, it’s not a bad idea to give it a new name while you are working on it, to be sure you know you’re pulling your version.

Getting Your Module Into Ansible

High-quality modules with minimal dependencies can be included in Ansible, but modules (just due to the programming preferences of the developers) will need to be implemented in Python and use the AnsibleModule common code, and should generally use consistent arguments with the rest of the program. Stop by the mailing list to inquire about requirements if you like, and submit a github pull request to the extras project. Included modules will ship with ansible, and also have a chance to be promoted to ‘core’ status, which gives them slightly higher development priority (though they’ll work in exactly the same way).

Module checklist

  • The shebang should always be #!/usr/bin/python, this allows ansible_python_interpreter to work

  • Documentation: Make sure it exists
    • required should always be present, be it true or false
    • If required is false you need to document default, even if the default is ‘None’ (which is the default if no parameter is supplied). Make sure default parameter in docs matches default parameter in code.
    • default is not needed for required: true
    • Remove unnecessary doc like aliases: [] or choices: []
    • The version is not a float number and value the current development version
    • Verify that arguments in doc and module spec dict are identical
    • For password / secret arguments no_log=True should be set
    • Requirements should be documented, using the requirements=[] field
    • Author should be set, name and github id at least
    • Made use of U() for urls, C() for files and options, I() for params, M() for modules?
    • GPL 3 License header
    • Does module use check_mode? Could it be modified to use it? Document it
    • Examples: make sure they are reproducible
    • Return: document the return structure of the module
  • Exceptions: The module must handle them. (exceptions are bugs)
    • Give out useful messages on what you were doing and you can add the exception message to that.
    • Avoid catchall exceptions, they are not very useful unless the underlying API gives very good error messages pertaining the attempted action.
  • The module must not use sys.exit() –> use fail_json() from the module object

  • Import custom packages in try/except and handled with fail_json() in main() e.g.:

    try:
        import foo
        HAS_LIB=True
    except:
        HAS_LIB=False
    
  • The return structure should be consistent, even if NA/None are used for keys normally returned under other options.

  • Are module actions idempotent? If not document in the descriptions or the notes

  • Import module snippets from ansible.module_utils.basic import * at the bottom, conserves line numbers for debugging.

  • Call your main() from a conditional so that it would be possible to test them in the future example:

    if __name__ == '__main__':
        main()
    
  • Try to normalize parameters with other modules, you can have aliases for when user is more familiar with underlying API name for the option

  • Being pep8 compliant is nice, but not a requirement. Specifically, the 80 column limit now hinders readability more that it improves it

  • Avoid ‘action/command‘, they are imperative and not declarative, there are other ways to express the same thing

  • Sometimes you want to split the module, specially if you are adding a list/info state, you want a _facts version

  • If you are asking ‘how can I have a module execute other modules’ ... you want to write a role

  • Return values must be able to be serialized as json via the python stdlib json library. basic python types (strings, int, dicts, lists, etc) are serializable. A common pitfall is to try returning an object via exit_json(). Instead, convert the fields you need from the object into the fields of a dictionary and return the dictionary.

  • When fetching URLs, please use either fetch_url or open_url from ansible.module_utils.urls rather than urllib2; urllib2 does not natively verify TLS certificates and so is insecure for https.

Windows modules checklist

  • Favour native powershell and .net ways of doing things over calls to COM libraries or calls to native executables which may or may not be present in all versions of windows

  • modules are in powershell (.ps1 files) but the docs reside in same name python file (.py)

  • look at ansible/lib/ansible/module_utils/powershell.ps1 for commmon code, avoid duplication

  • start with:

    #!powershell
    
then::
<GPL header>
then::
# WANT_JSON # POWERSHELL_COMMON
  • Arguments:
    • Try and use state present and state absent like other modules

    • You need to check that all your mandatory args are present:

      If ($params.state) {
          $state = $params.state.ToString().ToLower()
          If (($state -ne 'started') -and ($state -ne 'stopped') -and ($state -ne 'restarted')) {
              Fail-Json $result "state is '$state'; must be 'started', 'stopped', or 'restarted'"
          }
      }
      
    • Look at existing modules for more examples of argument checking.

  • Results
    • The result object should allways contain an attribute called changed set to either $true or $false

    • Create your result object like this:

      $result = New-Object psobject @{
      changed = $false
      other_result_attribute = $some_value
      };
      
      If all is well, exit with a
      Exit-Json $result
      
    • Ensure anything you return, including errors can be converted to json.

    • Be aware that because exception messages could contain almost anything.

    • ConvertTo-Json will fail if it encounters a trailing in a string.

    • If all is not well use Fail-Json to exit.

  • Have you tested for powershell 3.0 and 4.0 compliance?

Deprecating and making module aliases

Starting in 1.8 you can deprecate modules by renaming them with a preceding _, i.e. old_cloud.py to _old_cloud.py, This will keep the module available but hide it from the primary docs and listing.

You can also rename modules and keep an alias to the old name by using a symlink that starts with _. This example allows the stat module to be called with fileinfo, making the following examples equivalent

EXAMPLES = ‘’’ ln -s stat.py _fileinfo.py ansible -m stat -a “path=/tmp” localhost ansible -m fileinfo -a “path=/tmp” localhost ‘’‘

See also

模块相关
Learn about available modules
Developing Plugins
Learn about developing plugins
Python API
Learn about the Python API for playbook and task execution
GitHub Core modules directory
Browse source of core modules
Github Extras modules directory
Browse source of extras modules.
Mailing List
Development mailing list
irc.freenode.net
#ansible IRC chat channel

Developing Plugins

Ansible is pluggable in a lot of other ways separate from inventory scripts and callbacks. Many of these features are there to cover fringe use cases and are infrequently needed, and others are pluggable simply because they are there to implement core features in ansible and were most convenient to be made pluggable.

This section will explore these features, though they are generally not common in terms of things people would look to extend quite as often.

Connection Type Plugins

By default, ansible ships with a ‘paramiko’ SSH, native ssh (just called ‘ssh’), ‘local’ connection type, and there are also some minor players like ‘chroot’ and ‘jail’. All of these can be used in playbooks and with /usr/bin/ansible to decide how you want to talk to remote machines. The basics of these connection types are covered in the 新手上路 section. Should you want to extend Ansible to support other transports (SNMP? Message bus? Carrier Pigeon?) it’s as simple as copying the format of one of the existing modules and dropping it into the connection plugins directory. The value of ‘smart’ for a connection allows selection of paramiko or openssh based on system capabilities, and chooses ‘ssh’ if OpenSSH supports ControlPersist, in Ansible 1.2.1 an later. Previous versions did not support ‘smart’.

More documentation on writing connection plugins is pending, though you can jump into lib/ansible/plugins/connections and figure things out pretty easily.

Lookup Plugins

Language constructs like “with_fileglob” and “with_items” are implemented via lookup plugins. Just like other plugin types, you can write your own.

More documentation on writing lookup plugins is pending, though you can jump into lib/ansible/plugins/lookup and figure things out pretty easily.

Vars Plugins

Playbook constructs like ‘host_vars’ and ‘group_vars’ work via ‘vars’ plugins. They inject additional variable data into ansible runs that did not come from an inventory, playbook, or command line. Note that variables can also be returned from inventory, so in most cases, you won’t need to write or understand vars_plugins.

More documentation on writing vars plugins is pending, though you can jump into lib/ansible/inventory/vars_plugins and figure things out pretty easily.

If you find yourself wanting to write a vars_plugin, it’s more likely you should write an inventory script instead.

Filter Plugins

If you want more Jinja2 filters available in a Jinja2 template (filters like to_yaml and to_json are provided by default), they can be extended by writing a filter plugin. Most of the time, when someone comes up with an idea for a new filter they would like to make available in a playbook, we’ll just include them in ‘core.py’ instead.

Jump into lib/ansible/plugins/filter for details.

Callbacks

Callbacks are one of the more interesting plugin types. Adding additional callback plugins to Ansible allows for adding new behaviors when responding to events.

Examples

Example callbacks are shown in lib/ansible/plugins/callback.

The log_plays callback is an example of how to intercept playbook events to a log file, and the mail callback sends email when playbooks complete.

The osx_say callback provided is particularly entertaining – it will respond with computer synthesized speech on OS X in relation to playbook events, and is guaranteed to entertain and/or annoy coworkers.

Configuring

To activate a callback drop it in a callback directory as configured in ansible.cfg.

Development

More information will come later, though see the source of any of the existing callbacks and you should be able to get started quickly. They should be reasonably self-explanatory.

Distributing Plugins

Plugins are loaded from both Python’s site_packages (those that ship with ansible) and a configured plugins directory, which defaults to /usr/share/ansible/plugins, in a subfolder for each plugin type:

* action_plugins
* lookup_plugins
* callback_plugins
* connection_plugins
* filter_plugins
* vars_plugins

To change this path, edit the ansible configuration file.

In addition, plugins can be shipped in a subdirectory relative to a top-level playbook, in folders named the same as indicated above.

See also

模块相关
List of built-in modules
Python API
Learn about the Python API for task execution
开发动态的Inventory数据源
Learn about how to develop dynamic inventory sources
Developing Modules
Learn about how to write Ansible modules
Mailing List
The development mailing list
irc.freenode.net
#ansible IRC chat channel

帮助测试PR

作为一名开发者,最具能展现自我价值的事情就是在github上看讨论列表来帮助修复bug。我们通常在解决了bug之后再进行新功能的开发,因此解决bug将是一个非常有价值的事情。

即使你不是一个开发者,帮助测试bug的修复情况以及新功能还是非常有必要的。

这同样适用于测试新功能以及测试错误修正。

通常情况下,编码工作应当包含测试用例来保证编码的正确性,但这并不总能照顾到代码的方方面面,尤其在各平台下测试用户没有足够的访问权限,或者使用的是API或者web服务。

在这种情况下,在真实的环境下上线测试将会比自动测试更有价值。在任何情况下,都是应该进行一次人工测试。

幸运的是在你充分了解ansible的工作机制的情况下,帮助测试ansible是一件非常简单的事情。

开始测试

你可以在ansible主分支上切出一个分支来保持和主分支隔离,合并GitHub上的问题,测试,然后在GitHub对这一特定问题做一个回馈。具体方法如下:

Note

帮助测试GitHub上那些提交合并请求的代码是否存在风险,这些风险可能包括存在错误或恶意代码。我们建议在虚拟机上测试,无论是云,或在本地。有些人喜欢Vagrant,或Docker,这是可以的,但我们并不推荐。 在不同的Linux系统环境下进行测试也是非常有意义的,因为某些功能(诸如APT和yum等包管理工具)专用于这些操作系统。

当然配置您的测试环境来运行我们的测试套件需要一系列工具。以下软件是必须的:

git
python-nosetests (sometimes named python-nose)
python-passlib
python-mock

如果你想运行完整的集成测试套件,你还需要安装以下软件包:

svn
hg
python-pip
gem

当准备完以上环境后,您可以从github上拉取Ansible的原代码进行测试了:

git clone https://github.com/ansible/ansible.git --recursive
cd ansible/

Note

如果您已经Fork了我们的代码,您就可以从你自己代码仓库里克隆了。

Note

如果你打算更新你的仓库作为测试一些相关模块,请使用”git rebase origin/devel”,并且使用”git submodule update”更新子模块,如不更新,您将使用旧版本的模块。

使用开发环境

Ansible源代码包括一个脚本,可以让你直接使用Ansible从源代码,而无需完全安装,这对于的Ansible开发者来说十分便利。

使用以下命令进入开发环境,这主要针对的是Linux/Unix的终端测试环境:

source ./hacking/env-setup

该脚本修改了PYTHONPATH(以及一些其他的东西),这仅仅对于当次shell 会话有效。

如果你想永久使测试环境生效,你可以将其放入开机启动脚本中(例如,.bash_profile)。

找到对应分支并测试

接下来,手动合并你想测试的提交请求,并且记录下源和仓库的信息。它会是这个样子:

Someuser wants to merge 1 commit into ansible:devel from someuser:feature_branch_name

Note

请务必将提交合并请求提交到ansible:devel分支,我们不会接受您提交到其他分支。版本的更新将由我们的工作人员手动进行。

用户名和分支名是十分重要的,这将显示在以下命令行中:

git checkout -b testing_PRXXXX devel
git pull https://github.com/someuser/ansible.git feature_branch_name

第一行命令表示在devel分支上新建一个新分支名叫testing_PRXXXX,而XXXX是实际合并请求申请的ID号(例如,1234),并切换到该分支下。第二行命令则表示拉取对应用户的对应分支的代码。

Note

If the GitHub user interface shows that the pull request will not merge cleanly, we do not recommend proceeding if you are not somewhat familiar with git and coding, as you will have to resolve a merge conflict. This is the responsibility of the original pull request contributor.

Note

Some users do not create feature branches, which can cause problems when they have multiple, un-related commits in their version of devel. If the source looks like someuser:devel, make sure there is only one commit listed on the pull request.

For Those About To Test, We Salute You

At this point, you should be ready to begin testing!

If the PR is a bug-fix pull request, the first things to do are to run the suite of unit and integration tests, to ensure the pull request does not break current functionality:

# Unit Tests
make tests

# Integration Tests
cd test/integration
make

Note

Ansible does provide integration tests for cloud-based modules as well, however we do not recommend using them for some users due to the associated costs from the cloud providers. As such, typically it’s better to run specific parts of the integration battery and skip these tests.

Integration tests aren’t the end all beat all - in many cases what is fixed might not HAVE a test, so determining if it works means checking the functionality of the system and making sure it does what it said it would do.

Pull requests for bug-fixes should reference the bug issue number they are fixing.

We encourage users to provide playbook examples for bugs that show how to reproduce the error, and these playbooks should be used to verify the bugfix does resolve the issue if available. You may wish to also do your own review to poke the corners of the change.

Since some reproducers can be quite involved, you might wish to create a testing directory with the issue # as a sub- directory to keep things organized:

mkdir -p testing/XXXX # where XXXX is again the issue # for the original issue or PR
cd testing/XXXX
<create files or git clone example playbook repo>

While it should go without saying, be sure to read any playbooks before you run them. VMs help with running untrusted content greatly, though a playbook could still do something to your computing resources that you’d rather not like.

Once the files are in place, you can run the provided playbook (if there is one) to test the functionality:

ansible-playbook -vvv playbook_name.yml

If there’s not a playbook, you may have to copy and paste playbook snippets or run a ad-hoc command that was pasted in.

Our issue template also included sections for “Expected Output” and “Actual Output”, which should be used to gauge the output from the provided examples.

If the pull request resolves the issue, please leave a comment on the pull request, showing the following information:

  • “Works for me!”
  • The output from ansible –version.

In some cases, you may wish to share playbook output from the test run as well.

Example!:

Works for me!  Tested on `Ansible 1.7.1`.  I verified this on CentOS 6.5 and also Ubuntu 14.04.

If the PR does not resolve the issue, or if you see any failures from the unit/integration tests, just include that output instead:

This doesn't work for me.

When I ran this my toaster started making loud noises!

Output from the toaster looked like this:

   ```
   BLARG
   StrackTrace
   RRRARRGGG
   ```

When you are done testing a feature branch, you can remove it with the following command:

git branch -D someuser-feature_branch_name

We understand some users may be inexperienced with git, or other aspects of the above procedure, so feel free to stop by ansible-devel list for questions and we’d be happy to help answer them.

开发者同时可能也对完全发现感兴趣,可以参考 Ansible Tower.这是一个将Ansible集成一体的非常棒的工具.

Ansible Tower

Ansible Tower (以前叫’AWX’)是能够帮助任何IT团队更容易使用Ansible的解决方案。该方案基于web。

Tower允许对用户进行权限控制,即使某用户不能传送某SSH凭证,你也可以通过Tower来对该用户共享该凭证。我们可以通过图形化界面来管理Inventory,也可以对各种各样的云资源做同步。Tower可以记录所有job的日志,也可以与LDAP集成,并且拥有强大的可浏览的REST API。Tower也提供了命令行工具,可以与Jenkins轻松集成。Provisioning回调对自动伸缩拓扑图提供了强大的支持。

请在`Ansible Tower 页面 <http://ansible.com/tower>`_了解Tower更多的功能,并且了解如何下载使用。Tower的免费版本最多支持10个节点,并且Ansible公司会提供强大的支持。正如你期望的那样,可以用Ansible playbook来安装Tower!

Community Information & Contributing 社区信息与贡献 ````````````````````````````````

Ansible是一个开源项目,它被设计于让开发者和管理员在共同构建自动化解决方案的时候更好的合作.

你想要参与进来吗 – 不论是问问题,帮助其它用户,像新人介绍ansible,或者帮助软件和文档的完善,我们都非常欢迎你对这个项目的贡献.

Ansible用户

我有个问题

我们很乐意帮助你!

Ansible问题最好在Ansible的google谈论组上面询问.`Ansible Google Group Mailing List <http://groups.google.com/group/ansible-project>`_.

这里有非常多的回答问题的列表和分享的技巧.任何人都可以加入进来,如果你只想在线阅读的话,发送邮件是可选的.为了减少垃圾邮件,尽管投递很快就被批准,你的第一次投递还是可能比较慢.

在问问题的时候,确保让大家知道你运行的相关命令,输出内容,一些细节,和你使用的Ansible版本

在需要的使用实例场合,尽量链接到 github 上展示,而不是在邮件列表中发送附件.

推荐你在问问题之前使用 Google 搜索,查看是否相关问题已经被回答过了,但是如果在注释中发现时间很久了,相关主题可能不再回复.

在问问题之前,确保使用的是最新的稳定版本的 Ansible ,你可以通过对比命令 ‘ansible –version’ 的输出和 PyPi <https://pypi.python.org/pypi/ansible> 上面的版本来进行检查.

同样,你可以加入 IRC 频道 - #ansible on irc.freenode.net .这也是一个很活跃的频道, 如果还没在邮件列表中没有找到你想要的答案,因为邮件是异步的,请暂停发送邮件,你的邮件可能会引起核心开发人员的注意.

我想跟上版本发布公告

版本通知发布在 ansible 的项目上面,如果你不想跟上最新的邮件列表,你可以加入 Ansible 匿名邮件列表 Ansible Announce Mailing List

这是一个低流量的只读邮件列表,我们指挥在这里发布版本通知和 可选的通向到 Ansible 大事件的链接

我想帮助分享和改善Ansible

你可以通过告诉朋友和同事,写博客,来把 Ansible 分享给其它人,或者出现在用户讨论组上面(像 DevOps组,或者本地 LUG)

你可以注册一个免费的账户标记为 “Ansible”,在 speakerdeck 上面分享你的幻灯片.在推特上,你可以和 ansible 一起分享,或者跟随我们 follow us.

我想让Ansible开发的更快

如果你是一个开发者,最有价值的事情就是查看 github issue 列表,帮助修复 bug.我们总是在特性开发时,优先考虑修复 bug 问题,因此你可以做的最好的事情就是清楚 bug .

如果你不是开发者,帮助测试提交的 bug 修复问题,也是很有价值的.你可以检验 ansible,在主分支下,开一个测试分支,合并 github 上的问题,测试,然后在指定的 issue 上面注释.

我想报告 Bug

Ansible实际使用中暴露的问题 – 如果和安全相关,请发邮件到 security@ansible.com <mailto:security@ansible.com>,而不是发到 Google 讨论组上面 .

有关核心语言的 Bug 应该被报道到 github.com/ansible/ansible <https://github.com/ansible/ansible> .在报告一个 Bug 之前首先检查 bug/issue 查看有关 issue 是否被报告了.

模块相关的 Bugs 应该基于模块的分类发到 ansible-modules-core <https://github.com/ansible/ansible-modules-core> 或者 ansible-modules-extras <https://github.com/ansible/ansible-modules-extras> .这会被列到模块文档的底部.

当你填bug信息的时候,模块请使用 issue template <https://github.com/ansible/ansible/raw/devel/ISSUE_TEMPLATE.md> 提供所有相关的信息,不管你正在填什么类型的表格. (When filing a bug, please use the issue template to provide all relevant information, regardless of what repo you are filing a ticket against.)

知道你ansible的版本,和你运行的具体的命令,你期望什得到什么结果,将会帮助我们和每个人世间,更快的知道问题.

不要使用类似,”我如何做(how do I do this)” 类型的问题.这里都是 IRC 的参与者回答有用的问题,而不是讨论有什么问题的.学会提问.

尊重审稿人,使审稿人有时间帮助更多的忍,请提供 良好注释,语言简洁的playbook,包括playbook的片段和输出内容.有用的信息尽量提出来,省略无用的信息

当在 playbook 分享 YAML 时候,格式可以被保存通过使用 code blocks <https://help.github.com/articles/github-flavored-markdown#fenced-code-blocks>

对于多文件的内容,推荐使用 gist.github.com. 在线 pastebin 内容可能会过期,因此如果他们被长时间的引用,最好时间放久一点.(For multiple-file content, we encourage use of gist.github.com. Online pastebin content can expire, so it’s nice to have things around for a longer term if they are referenced in a ticket.)

如果你不确定你提供的是否为 bug,欢迎到 IRC 邮件列表提问一些相关的事情.

因为我们是一个大文献的项目,如果你确定你有一个 bug ,请确保你打开 issue ,确保我们有这个与你有关的issue记录.不要依靠社区的其他人代替你上传这个bug.

得到你的报告可能会花一些世间,具体信息查看下面的优先级标识.

我想帮助改善文档

ansible 的文档也是一个社区项目.

如果你想帮助改善文档,纠正错别字,或者改善一些章节,或者写一个新特性的文档, 提交一个 github pull 请求,代码位于“docsite/rst” 子目录,同样也有一个 “Edit On Github”连接到哪里的.

模块文档嵌入在源码模块的底部,模块可能是 ansible-modules-core 或者 ansible-modules-extra 取决于在github上的模块.关于这的信息一直列在网页文档的每个模块底部

除了模块,主文档也在重建文本格式.(Aside from modules, the main docs are in restructured text format. )

如果你对新的重组的文本不满意,你可以在 github 上打开一个的标签,关于你发现的错误,或者你想添加的部分.更多的信息或者创建 pull 请求,请参考 github help guide.

对当前和未来的开发人员

我想学习如何在 Ansible 上开发

如果你刚开始使用 Ansible,想弄明白 Ansible 内部的工作机制,停止 Ansible-devel 邮件列表,像我们打个招呼,我们会带你开始的.

一个好的开始方式可以是在模块网站上阅读一些开发文档,然后找到一个 bug 然后修复,或者添加一些新的小特性.

模块最容易开始地方.

贡献代码(特性或者修复bug)

Ansible 项目的源代码托管在 github 上 ,核心应用位于 github.com/ansible/ansible ,还有两个模块相关的子项目 github.com/ansible/ansible-modules-core. 如果你想知道一个模块是核心模块(“core”)还是额外模块(“extras”),查阅那个模块的网页文档.

在提交代码之前,先到 ansible-devel 邮件列表讨论一下特性问题,这可以有效的避免后期重复的工作.如果你不确定一个新的特性是否合适,先去开发邮件列表讨论一下,这样相对后来不得不修改一个 pull 请求更容易一些.

提交补丁的时候,一定要先运行单元测试“make tests”, 有一些基本的测试会自动运行当创建PR时候. 有更多的深入测试在测试/集成目录,分为 destructive 和 non_destructive,运行这些如果他们属于你的修改.他们被设置了标签,这样你就可以运行子集,一些测试需要云凭证和只有他们提供的时候才会运行.当添加修复 bug 的新的特性的时候,最好添加新的测试防止后期重新回滚.

使用 “git rebase” vs “git merge”(让git pull 别名为git pull -rebase 是一个好主意) ,来避免合并提交.也有一些基础测试可以运行在 “test/integration” 目录

为了保证历史代码的整洁,和对新假如的代码做更好的审计,我们会要求那些包含合并注释的重新提交.使用”git pull –rebase” 而不是 “git pull” 和 “git rebase” 而不是 “git merge”.同样确保有主要分支在使用其他的分支的时候,这样你才不会丢失注释信息.(Also be sure to use topic branches to keep your additions on different branches, such that they won’t pick up stray commits later.)

如果你犯错了,你不需要关闭你的 PR ,创建一个清洁的本地分支然后推送到github上面使用 –force 选项,轻质覆盖已存在的分支(在没人使用哪个分支作为参考的情况下是允许的).代码注释不会丢失,他们只是不会连接到现有的分支

然后我们将审阅你的贡献和参与你的问题等等.

因为我们有一个非常大的和活跃的社区,我们可能需要一段时间才能看到你的贡献,看一下后面的优先级部分来了解一下我们的工作队列.要有耐心,你的要求可能不会马上合并,我们也让 devel 能够使用,因此我们需要小心的测试pull 请求,而这需要花费时间.

补丁应该一直在开发分支之上.

记住,小而专请求更容易检查和接受,如果有实例,会更加帮助我们理解 bug 修复的工具和新的特性.

贡献可以是新的特性,像模块,或者是修复一些你或其他人发现的 bug .如果你对写新模块感兴趣,请参考 module development documentation.

Ansible的理念鼓励简单、可读的代码和 一致的,保守扩展, 向后兼容的改进.代码开发Ansible需要支持Python 2.6 +, 而代码模块运行需要在Python 2.4之上.请使用4个空格的缩进,而不是tab,(we do not enforce 80 column lines, we are fine with 120-140. We do not take ‘style only’ requests unless the code is nearly unreadable, we are “PEP8ish”, but not strictly compliant.)

你也可以通过测试和修改其他请求贡献,特别是如果它是一个你用着有趣的东西.请保持你的评论清楚和中肯,礼貌的和有建设性的, ticket 不是一个好开始讨论的地方( ansible-devel 和 IRC 是专门为 tickets 的).

技巧:为了更容易的从一个分支运行,source ”./hacking/env-setup” 就这样,不需要安装.

其它主题

Ansible 职员

Ansible 一家支持Ansible和基于 Ansible 构建额外的解决方案的公司.我们会服务和支持那些有趣的东西.我们还提供了一个企业 web 前端 Ansible(见下面的 Tower ).

我们最重要的任务是使 ansible 社区发生一些大事,包括组织Ansible的软件版本.想获取更多的信息,联系 info@ansible.com

在 IRC 上,你可以找到我们 jimi_c, abadger1999, Tybstar, bcoca.在邮件列表上,我们使用 @ansible.com 的地址发送.

邮件列表信息

Ansible有一些邮件列表,因为审核的原因,你的第一次投递邮件可能时间稍长,请允许一天时间的延迟.

Ansible Project List 分享 Ansible的技巧,问题解答,用户讨论.

Ansible Development List 学习如何在Ansible上开发,询问ansible未来的设计特性,讨论扩展ansible或者正在进行的ansible特性.

Ansible Announce list 关于ansible版本号的只读共享信息,小频率的ansible事件信息.例如:通知AnsibleFest的出现.

Ansible Lockdown List 关于ansible lockdown项目的所有信息,包括DISA STIG 自动化和 CIS Benchmarks

对于非google账户订阅一个组,你可以发送邮件到这订阅地址请求订阅,例如:ansible-devel+subscribe@googlegroups.com

版本号

以 ”.0” 结尾的版本是朱版本,同时将会有很多新的特性.以其他整数结尾的 ,像”0.X.1” 和 “0.X.2”是小版本,这些仅仅包含 bug 修复

通常来说,我们不会发布小版本号(保存用于大的项目),但是如果现在具体下次发布会有很长时间的话,偶尔可能决定去除包含大量修复的小版本.

版本号基于没有其他人使用 Van Halen 的歌曲命名.

Tower 支持问题

Ansible Tower <http://ansible.com/tower> 是一个对 ansible 提供的用户接口,服务,应用程序接口等等.

如果你有关于 tower的问题,发送邮件到 support@ansible.com <mailto:support@ansible.com> 而不是在IRC频道上,或者一般邮件列表上提问

IRC 频道

Ansible 有IRC 频道 #ansible on irc.freenode.net.

注意优先级的标识

在2013年,Ansible 位于 github 上开源软件的前 5 名,到目前为止,有 800 多个对此项目贡献者,更不用说一个非常大的用户社区,下载了这个应用超过一百万次了.因此,我们有将会有很多的活动.

下面,我们会告诉你如何处理新来的请求的.

在我们的 bug traker 中你会注意到一些标签- P1,P2,P3,P4和P5.这是我们的内部用于对提交的 bug 排序的.

除了一些例外,便于合并(比如文档类型), 我们都会首先花时间处理 P1 和 P2 item,包括 pull 请求.这些通常与重大的 bug 有关,同时影响大量的用户群里.因此,如果你看到一些 “P3 or P4 的分类,那些将不会得到立即的关注.

这些标签没有定义,它们只是简单的排序.然而,有些东西影响核心模块(yum,apt,等等)可能会有更高的优先级,相比那些影响少数用户的模块来说.

因为我们非常强调测试和代码审查,可能需要几个月的小功能合并.

但是不要担心,我们也会定期的给迪有限的队列做定期的清理,给予一些关注,由其在新模块的改变上面.因此,这不意味着我们把精力都花费在高优先级的东西上,而忽略了你的 请求(ticket)

任何努力都会有帮助的,如果你促进快P3的 pull request 特性 ,你可以做的最好的事情是帮助处理 P2 bug 报告.

社区代码和产品

社区欢迎所有类型的用户,什么背景,什么技术级别都可以.请尊敬其他人就像你想让其他人尊敬你一样,保持讨论的活跃氛围,不要产生冲突,避免各种歧视,亵渎,避免无用的争论(例如:vi和emace那个更好一样.)

在社区事件上面也是希望大家好好相处

邮件列表应该集中在IT自动化上面.滥用社区的指南将不会被容忍,后果是禁用社区资源

贡献执照许可

通过贡献,你被授予一个完整的,不可吊销的版权执照,依据这个项目的执照,这个执照对这个项目的所有用户和开发者都有效.

Ansible Galaxy

“Ansible Galaxy” 指的是一个网站共享和下载 Ansible 角色,也可以是者是帮助 roles 更好的工作的命令行工具。

The Website 网站 ```````

这个网站 Ansible Galaxy,是一个免费的用于查找,下载,评论各种社区开发的 Ansible 角色,在你的自动化项目中引入一些角色也是不错的。

你可以使用 social auth 注册和使用 “ansible-galaxy” 下载客户端,”ansible-galaxy”在 Ansible 1.4.2 就已经被包含了。

阅读 Galaxy 网站的 “About” 页面获取更多信息。

ansible-galaxy命令行工具

ansible-galaxy 有许多不同的子命令

安装角色

很明显从 Ansible Galaxy 网站下载角色

ansible-galaxy install username.rolename

构建角色架构

也可以用于初始化一个新角色的基本文件结构,节省创建不同的目录和main.yml的时间了。

ansible-galaxy init rolename

从一个文件安装多个角色

想安装多个角色,ansible-galaxy 命令行可以通过一个 requirements 文件实现。各种版本的ansible 都允许使用下面的语法从 Ansible galaxy 网站安装角色。

ansible-galaxy install -r requirements.txt

requirements.txt 文件看起来就像这样

username1.foo_role username2.bar_role

想得到指定版本(tag)的role,使用下面的语法

username1.foo_role,version username2.bar_role,version

可用的版本在 Ansible Galaxy 网页上都有列出来。

Requirements 文件高级用法

一些控制从哪里下载角色,支持远程源的用法,在 Ansible 1.8 之后支持通过 YMAL 语法的 requirements 文件实现,但是必须以 yml为文件扩展名。就像这样

ansible-galaxy install -r requirements.yml

扩展名是很重要的,如果 .yml 扩展忘记写了, ansible-galaxy 命令行会假设这个文件是普通格式的,而且会失败,

这里有个例子展示通过多个源下载一些指定版本。 其中也实现了覆盖下载角色的名字到其它名字。

# from galaxy - src: yatesr.timezone

# from github - src: https://github.com/bennojoy/nginx

# from github installing to a relative path - src: https://github.com/bennojoy/nginx

path: vagrant/roles/

# from github, overriding the name and specifying a specific tag - src: https://github.com/bennojoy/nginx

version: master name: nginx_role

# from a webserver, where the role is packaged in a tar.gz - src: https://some.webserver.example.com/files/master.tar.gz

name: http-role

# from bitbucket, if bitbucket happens to be operational right now :) - src: git+http://bitbucket.org/willthames/git-ansible-galaxy

version: v1.4

# from bitbucket, alternative syntax and caveats - src: http://bitbucket.org/willthames/hg-ansible-galaxy

scm: hg

从上面你可以看到,有许多控制命令可以使用去自定义那些角色从哪里获得,保存为什么角色名称。

Roles pulled from galaxy work as with other SCM sourced roles above. To download a role with dependencies, and automatically install those dependencies, the role must be uploaded to the Ansible Galaxy website. 有些角色之间会有依赖关系,想要从依赖关系中自动安装,这个角色需要被上传到 Ansible Galaxy 网站上。

See also

Playbook Roles and Include Statements
关于 Ansible role 的内容
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel

测试策略

Ansible Playbooks 的集成测试

很多时候, 人们问, “我怎样才能最好的将 Ansible playbooks 和测试结合在一起?” 这有很多选择. Ansible 的设计实际上是一个”fail-fast”有序系统, 因此它可以很容易地嵌入到 Ansible playbooks. 在这一章节, 我们将讨论基础设施的集成测试及合适的测试等级.

Note

这是一个关于测试你部署应用程序的章节, 不是如何测试开发的 Ansible 模块. 对于那些内容, 请移步开发区域.

通过将某种程度的测试部署合并到部署工作流中, 当代码运行在生产环境中这将减少意外的产生, 在许多情况下, 测试可以避免在生产中因为更新失败而导致的迁移整个安装. 因为它是基于推送的, 它可以很容易的运行在 localhost 或者测试服务器上. Ansible 允许你添加尽可能多的检查在你的升级流程中.

正确的测试等级

Ansible 资源是期望状态的模块. 因此测试服务是否处于运行, 包是否已经安装, 或其他这样的事情它不是必要的. Ansible 确保这些系统中的这些声明是真实的. 相反, 在你的 playbooks 中 assert 这些事情.

tasks:
  - service: name=foo state=started enabled=yes

如果你认为该服务可能没有启动, 最好的事情就是要求它处于启动状态. 如果这个服务启动失败, Ansible 将适当的抛出. (这不应该和服务是否正在做一些功能性的事情混淆, 而我们应该更多的展示如何做这些事).

Check 模块作为主要测试

在上面的设置中, –check 模块在 Ansible 中可以作为一层测试. 如果在一个现有的系统部署 playbook, 使用 –check 选项 ansible 命令将会报告出 Ansible 使系统进入一个期望状态所做的任何更改.

这可以让你知道前面任何需要不如到给定系统的信息. 一般的脚本和命令不运行在检查模式, 所以如果你想要某些步骤总是在检查模式下执行, 例如调用 script 模块, 添加 ‘always_run’ 标记:

roles:
  - webserver

tasks:
  - script: verify.sh
    always_run: True

用于测试的模块

某些 playbook 模块对于测试特别友好. 下面的例子保证端口处于打开状态:

tasks:

  - wait_for: host={{ inventory_hostname }} port=22
    delegate_to: localhost

这是使用 URI 模块来确保 web service 有正确的返回:

tasks:

  - action: uri url=http://www.example.com return_content=yes
    register: webpage

  - fail: msg='service is not happy'
    when: "'AWESOME' not in webpage.content"

可以很容易的将任意(语言)的脚本推送到远程主机上, 如果这个脚本有一个非零的返回码, 它将自动失效:

tasks:

  - script: test_script1
  - script: test_script2 --parameter value --parameter2 value

如果使用 roles(你应该这样做, roles 是伟大的!), 脚本模块可以推送 role 下的 ‘files/’ 目录下的脚本

添加 assert 模块, 它可以很容易的验证各种真理:

tasks:

   - shell: /usr/bin/some-command --parameter value
     register: cmd_result

   - assert:
       that:
         - "'not ready' not in cmd_result.stderr"
         - "'gizmo enabled' in cmd_result.stdout"

如果你觉得需要测试通过 Ansible 设置的文件是否存在, ‘stat’ 模块是一个不错的选择:

tasks:

   - stat: path=/path/to/something
     register: p

   - assert:
       that:
         - p.stat.exists and p.stat.isdir

如上所处, 哪些没必要检查的东西如命令的返回码. Ansible 自动检查它们. 如果检查用户存在, 考虑使用 user 模块使其存在.

Ansible 是一个 fail-fast 系统, 所以当创建用户失败, 它将停止 playbook 的运行. 你不必检查它背后的原因.

测试的生命周期

如果将你的应用程序的基本验证写入了你的 playbooks 中, 在你每次部署的适合他们都将运行.

因此部署到本地开发的虚拟机和临时的环境都将根据你的生产环境的部署计划来验证这一切.

你的工作流可能是这样:

- 使用相同的 playbook 在开发过程中嵌入测试
- 使用 playbook 部署一个临时环境(使用相同的playbooks)来模拟生产
- 运行一个由 QA 团建编写的集成测试用例
- 使用相同的总和测试部署到生产.

如果你是一个产品服务, 一些像集成测试系列需要通过你的 QA 团队来编写. 这将包含诸如测试用例或自动化 API 测试, 这些通常不是嵌入到 Ansible 的 playbooks 中的.

然而, 它包含基本的健康的检查使 playbooks 有意义, 而且在某些情况下它可能会相对于远程节点运行一些 QA 的子集合. 这是下一节所涵盖的内容.

结合滚动更新测试

如果你已经读到 委托,滚动更新,本地动作 滚动更新的扩展好处可以迅速变得明显, 你可以使用 playbook 运行的成功或失败来决定是否增加机器到负载均衡器中.

这是嵌入测试的显著结果:

---

- hosts: webservers
  serial: 5

  pre_tasks:

    - name: take out of load balancer pool
      command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

  roles:

     - common
     - webserver
     - apply_testing_checks

  post_tasks:

    - name: add back to load balancer pool
      command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

在上述过程中, “task out of the pool” 和 “add back” 的步骤将会代替 Ansible 调用负载均衡模块或对应的 shell 命令. 您还可以使用监控模块对机器进行创建和关闭中断的窗口.

然而, 你可以从上面看出测试作为入口 – 如果”apply_testing_checks”这一步不执行, 这个机器将不会被添加到池中.

阅读关于”max_fail_percentage”的代表章节, 你可以控制有多少失败的测试后停止程序的滚动更新.

这种方式也可以被修改为先在测试机器上执行测试步骤然后在远程机器执行测试的步骤:

---

- hosts: webservers
  serial: 5

  pre_tasks:

    - name: take out of load balancer pool
      command: /usr/bin/take_out_of_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

  roles:

     - common
     - webserver

  tasks:
     - script: /srv/qa_team/app_testing_script.sh --server {{ inventory_hostname }}
       delegate_to: testing_server

  post_tasks:

    - name: add back to load balancer pool
      command: /usr/bin/add_back_to_pool {{ inventory_hostname }}
      delegate_to: 127.0.0.1

在上面的例子中, 从测试服务器上执行一个脚本紧接着将一个远程的节点添加到 pool 中.

在出现问题时, 解决一些服务器不能使用 Ansible 自动生成的重试文件重复部署这些服务器.

实现连续部署

如果需要, 上述技术可以扩展到启用连续部署中.

这个工作流可能像这样:

- 编写和使用自动部署本地开发虚拟机
- 有一个 CI 系统像 Jenkins, 来将每一次的代码变更部署到临时环境中
- 这个部署任务调用测试脚本, 参考通过/失败来确定每一次的部署是否进行 build
- 如果部署任务成功, 它将在生产环境中运行相同的部署 playbook

一些 Ansible 用户使用上述方式, 在一个小时内多次部署使它们所有的基础设施不下线. 如果你想达到那种水平, 一个自动 QA 系统是至关重要的.

如果你仍然在大量使用人工 QA, 你仍然需要决定手动部署是否是最好的, 但它仍然可以帮助滚动更新前的一部分工作, 包括基本的健康检查使用模块 ‘script’, ‘stat’, ‘uri’, 和 ‘assert’.

结尾

Ansible 相信你应该不需要另一个框架来验证你的基础设施是正确的. 这种情况是因为 Ansible 是基于顺序的系统, 处理失败后将立即在主机上引发错误, 并阻止该主机进一步的配置. 这迫使 Ansible 在运行结束后将错误作为摘要显示在顶端.

然而, Ansible 作为一个多层编排系统, 它可以很轻松的将测试合并到 playbook 中运行完毕, 使用 tasks 或 roles. 当使用滚动更新时, 测试步骤可以决定是否要把一台服务器添加到负载均衡池中.

最后, 因为 Ansible 错误会通过所有的方式进行传播以及 Ansible 程序本身的返回码, Ansible 默认运行在一个简单的推送模式, 如果你想使用它作为部署系统, 持续集成/持续交付道路上的组成部分, Ansible 是作为构建环境的最好的阶段, 如上面部分的介绍.

重点不应该放在基础设施测试上, 而是在应用程序的测试, 所以我们强烈鼓励你和你的 QA 团队商量出对于每一次部署的开发虚拟机什么样的测试是有意义的, 并将他们希望对每个部署的临时环境的测试进行排序. Ansible 描述了资源应该所处的状态, 所以你不需要考虑这些. 如果存在你需要确定的东西, 使用 stat/assert 这些伟大的模块来实现你的目的, 这将是最棒的.

总之, 测试是一个组织非常明确的事情. 每一个人都应该这样做, 但是这些要根据你要部署什么以及谁在使用它们来确定最适合的方案 – 但每个人肯定都会从一个更加强大和可靠的部署系统中受益.

See also

模块相关
All the documentation for Ansible modules
Playbooks
An introduction to playbooks
委托,滚动更新,本地动作
Delegation, useful for working with loud balancers, clouds, and locally executed steps.
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

常见问题

这是一些常见问题和回答

我可以为一个任务(task)或剧本(playbook)设置 PATH 或者其它环境变量吗?

可以通过 environment 关键字设置环境变量,可以用在 task 或者 play 上

environment:
PATH: “{{ ansible_env.PATH }}:/thingy/bin” SOME: value

如何处理需要不同账户与端口登录的不同机器?

设置清单(inventory)文件是最简单的方式

例如,假设这些主机有不同的用户名和端口

[webservers] asdf.example.com ansible_ssh_port=5000 ansible_ssh_user=alice jkl.example.com ansible_ssh_port=5001 ansible_ssh_user=bob

你也可以指定什么类型的连接。

[testcluster] localhost ansible_connection=local /path/to/chroot1 ansible_connection=chroot foo.example.com bar.example.com

你可能想保存这些组变量,或者一些变量文件。 看剩余的文档获取更多有关如何组织变量的信息

如何让 ansible 重用连接,启用 Kerberized SSH,或者让Ansible 注意本地的 SSH config 文件。

转换默认连接类型,在配置文件里面设置为,’ssh’,或者使用 ‘-c ssh’选项使用OpenSSH连接,而不是python的paramiko库。在 Ansible 1.2.1之后,’ssh’会默认使用。

paramiko在刚开始的时候是不错的,但是OpenSSH提供更多的高级选项。如果你正在使用这种连接类型的话,你可能会想在一个支持 ControlPersist 的新机器上运行 Ansible。你同样可以管理老的客户端。如果你正在用 RHEL6,CentOS6,SLES 10或 SLES 11,OpenSSH的版本仍然有些过时,因此考虑使用Fedora或OpenSUSE客户端来管理节点,或者使用paramiko。

我们默认让paramiko作为默认选项,如果你第一次安装Ansible在一个EL box上,它提供了更好的用户体验。

如何在EC2内加速管理?

不要试着用你的笔记本电脑管理一群 EC2 机器。连接到EC2内的管理节点然后在里面运行Ansible

如何处理远程机器上没有 /usr/bin/python 路径?

尽管你可以使用其他语言编写 Ansible 模块,但大部分 Ansible 模块是用 Python 写的 ,而且一些事非常重要的核心模块

默认情况下, Ansible 假定它可以在远程机器上找到 2.x版本以上的 /usr/bin/python ,指定为2.4或者更高的版本。

设置 inventory 变量 ‘ansible_python_interpreter’ ,允许 Ansible自动替换掉默认的 python解释器。因此你可以指向任何版本的 python ,尽管/usr/bin/python不存在

一些 Linux 操作系统,例如 Arch 可能默认安装的是 Python 3. 这会让你在运行模块的时候出现语法错误信息。 Python 3和 Python 2 在本质上还是有些区别的。Ansible 当前需要支持哪些更老版本的 Python 用户,因此还没有支持 Python 3.0。这不是一个问题,只需要安装 Python2 就可以解决问题。

当 Ansible 或 Python3.0 后来变得更加主流的时候,会支持Python 3.0

不要替换 python 模块的 shebang 行,Ansible 在部署的时候会自动处理。

让内容重用和重新分发的最好方式是什么?

如果你还没有做好, 请阅读 playbooks 文档的 “Roles” 部分。 这会让你更好的理解 playbook 的内容。(This helps you make playbook content self-contained, and works well with things like git submodules for sharing content with others.)

如果你对这些插件很陌生,查看 API 文档获取更多的有关扩展 Ansible 的细节信息 .. _configuration_file:

配置文件在那个地方,我如何配置它?

Ansible的配置文件.

如何禁止 cowsay?

如果你确定你想运行在没有cowsay的环境下,你可以卸载 cowsay,或者设置环境变量

export ANSIBLE_NOCOWS=1

How do I see a list of all of the ansible_ variables? 如何查看所有的 ansible_variables? ++++++++++++++++++++++++++++++++++++++++++++++++++++++

默认情况下,Ansible 收集 有关机器的 “facts” ,这些 facts 可以被Playbook或templates访问。想要查看相关机器的所有的facts,运行 “setup” 模块。

ansible -m setup hostname

这会打印指定主机上的所有的字典形式的facts。

如何遍历某一组内的所有主机,在模板中?

一个通用的做法是遍历组内的所有主机,你可以访问 “$groups” 字典在模板中,就像这样

{% for host in groups[‘db_servers’] %}
{{ host }}

{% endfor %}

如果你需要访问有关这些主机的 facts ,例如每个主机的IP地址,你需要确保 facts 已经被 populated 了。例如

  • hosts: db_servers tasks:

    • # doesn’t matter what you do, just that they were talked to previously.

然后你可以使用 facts 在模板里面,就像这样

{% for host in groups[‘db_servers’] %}
{{ hostvars[host][‘ansible_eth0’][‘ipv4’][‘address’] }}

{% endfor %}

如何以编程方式访问变量名

可能出现这种情况,我们需要一个任意的ipv4地址接口,同时这个接口是通过角色提供参数或其他输入提供的。变量名可以通过组合字符串来构建,就像这样:

{{ hostvars[inventory_hostname]['ansible_' + which_interface]['ipv4']['address'] }}

这个遍历主机变量的技巧是必要的,因为它是一变量名称扣减的字典。’inventory_hostname’ 是一个神奇的变量,因为它告诉你你在主机组循环中当前的主机是谁。

如何访问组内第一个主机的变量?

如果我们想要在 webservers 组内的第一个 webserver 的 ip 地址怎么办?我们可以这么做。注意如果再使用动态 inventory , ‘first’ 的主机可能不会一致 ,因此你不希望这样,除非你耳朵 inventory 是静态。(如果你在用 Ansible Tower,它会使用数据库指令,因此这不是个问题尽管你正在使用基于云环境的 inventory 脚本)

这里是技巧:

{{ hostvars[groups[‘webservers’][0]][‘ansible_eth0’][‘ipv4’][‘address’] }}

注意我们如何获得 webserver 组内的第一台机器的主机名的。如果你也在在模板中这么做,你可以用 Jinja2 “#set” 指令来简化这,或者在一个基本中,你也可以设置 fact

  • set_fact: headnode={{ groups[[‘webservers’][0]] }}
  • debug: msg={{ hostvars[headnode].ansible_eth0.ipv4.address }}

注意我们如何交换花括号的语法点(Notice how we interchanged the bracket syntax for dots)。

如何递归的宝贝文件到目标主机上?

“copy” 模块有递归的参数,如果你想更加高徐璈的处理大量的文件,看一下 “synchronize”模块,封装了rsync。自行看一些模块索引获取一些他们的信息。

如何查看 shell 环境变量?

如果是只是想看看,使用 env 查看。例如,如果想查看在管理机器上 HOME 环境变量的值。

— # ...

vars:
local_home: “{{ lookup(‘env’,’HOME’) }}”

如果你是想设置环境变量,查看高级的有关环境的 Playbook 部分。

Ansible 1.4 will also make remote environment variables available via facts in the ‘ansible_env’ variable:: Ansible1.4也会让远程的环境变量可用通过 facts 在 ‘ansible_env’ 变量。

{{ ansible_env.SOME_VARIABLE }}

如何为用户模块生成加密密码?

mkpasswd工具在大多数linux系统上都可以使用,是一个不错的选项

mkpasswd –method=SHA-512

如果这个工具在你系统上面没安装,你可以简单的通过 Python 生成密码。首先确保 Passlib 密码哈西库已经安装了。

pip install passlib

一旦库准备好了,SHA512密码值可以被生成通过下面命令生成。

python -c “from passlib.hash import sha512_crypt; import getpass; print sha512_crypt.encrypt(getpass.getpass())”

如何获得Ansible培训到商业支持?

Yes! 看我们的 services page 获得更多的信息关于我们的服务和培训服务。支持也包含在 Ansible Tower 。发邮件到`info@ansible.com <mailto:info@ansible.com>`_ 获取更深的细节。

我们也会提供免费的培训课程在基础上。 看 webinar page 获得更多信息在下面的研讨会上。

有网络接口 / REST API / etc?

Yes!Ansible 做了很好的产品让 Ansible 更加的强大容器使用,看 Ansible Tower

如何提交文档改变信息?

不错的问题! Ansible 文档保存在主项目git 源下面,指导贡献可以在 docs README `viewable on GitHub <https://github.com/ansible/ansible/blob/devel/docsite/README.md>`_找到。谢谢!

如何加密我的剧本数据?

如果你想加密数据,仍然想要在源码控制上分享给大家。看 Vault.

在 Ansible 1.8后,如果你有一个任务,你不想显示结果,或者给了命令 -v 选项,下面的例子很有用

  • name: secret task shell: /usr/bin/do_something –value={{ secret_value }} no_log: True

这个对保持详细的输出,但是从其他人那里隐藏了敏感的信息。

no_log属性也可以应用在整个 play 里面。

  • hosts: all no_log: True

尽管这回让play很难调试。推荐使用这个应用到单一任务上。

在这里我没看到我的问题

请看下面的部分链接到 IRC 和 Google Group,你可以在那里提问你的问题。

See also

Ansible 文档
The documentation index
Playbooks
An introduction to playbooks
最佳实践
Best practices advice
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

Glossary

The following is a list (and re-explanation) of term definitions used elsewhere in the Ansible documentation.

Consult the documentation home page for the full documentation and to see the terms in context, but this should be a good resource to check your knowledge of Ansible’s components and understand how they fit together. It’s something you might wish to read for review or when a term comes up on the mailing list.

Action

An action is a part of a task that specifies which of the modules to run and the arguments to pass to that module. Each task can have only one action, but it may also have other parameters.

Ad Hoc

Refers to running Ansible to perform some quick command, using /usr/bin/ansible, rather than the orchestration language, which is /usr/bin/ansible-playbook. An example of an ad-hoc command might be rebooting 50 machines in your infrastructure. Anything you can do ad-hoc can be accomplished by writing a playbook, and playbooks can also glue lots of other operations together.

Async

Refers to a task that is configured to run in the background rather than waiting for completion. If you have a long process that would run longer than the SSH timeout, it would make sense to launch that task in async mode. Async modes can poll for completion every so many seconds, or can be configured to “fire and forget” in which case Ansible will not even check on the task again, it will just kick it off and proceed to future steps. Async modes work with both /usr/bin/ansible and /usr/bin/ansible-playbook.

Callback Plugin

Refers to some user-written code that can intercept results from Ansible and do something with them. Some supplied examples in the GitHub project perform custom logging, send email, or even play sound effects.

Check Mode

Refers to running Ansible with the --check option, which does not make any changes on the remote systems, but only outputs the changes that might occur if the command ran without this flag. This is analogous to so-called “dry run” modes in other systems, though the user should be warned that this does not take into account unexpected command failures or cascade effects (which is true of similar modes in other systems). Use this to get an idea of what might happen, but it is not a substitute for a good staging environment.

Connection Type, Connection Plugin

By default, Ansible talks to remote machines through pluggable libraries. Ansible supports native OpenSSH (‘ssh’), or a Python implementation called ‘paramiko’. OpenSSH is preferred if you are using a recent version, and also enables some features like Kerberos and jump hosts. This is covered in the getting started section. There are also other connection types like ‘accelerate’ mode, which must be bootstrapped over one of the SSH-based connection types but is very fast, and local mode, which acts on the local system. Users can also write their own connection plugins.

Conditionals

A conditional is an expression that evaluates to true or false that decides whether a given task will be executed on a given machine or not. Ansible’s conditionals are powered by the ‘when’ statement, and are discussed in the playbook documentation.

Diff Mode

A --diff flag can be passed to Ansible to show how template files change when they are overwritten, or how they might change when used with --check mode. These diffs come out in unified diff format.

Facts

Facts are simply things that are discovered about remote nodes. While they can be used in playbooks and templates just like variables, facts are things that are inferred, rather than set. Facts are automatically discovered by Ansible when running plays by executing the internal ‘setup’ module on the remote nodes. You never have to call the setup module explicitly, it just runs, but it can be disabled to save time if it is not needed. For the convenience of users who are switching from other configuration management systems, the fact module will also pull in facts from the ‘ohai’ and ‘facter’ tools if they are installed, which are fact libraries from Chef and Puppet, respectively.

Filter Plugin

A filter plugin is something that most users will never need to understand. These allow for the creation of new Jinja2 filters, which are more or less only of use to people who know what Jinja2 filters are. If you need them, you can learn how to write them in the API docs section.

Forks

Ansible talks to remote nodes in parallel and the level of parallelism can be set either by passing --forks, or editing the default in a configuration file. The default is a very conservative 5 forks, though if you have a lot of RAM, you can easily set this to a value like 50 for increased parallelism.

Gather Facts (Boolean)

Facts are mentioned above. Sometimes when running a multi-play playbook, it is desirable to have some plays that don’t bother with fact computation if they aren’t going to need to utilize any of these values. Setting gather_facts: False on a playbook allows this implicit fact gathering to be skipped.

Globbing

Globbing is a way to select lots of hosts based on wildcards, rather than the name of the host specifically, or the name of the group they are in. For instance, it is possible to select “www*” to match all hosts starting with “www”. This concept is pulled directly from Func, one of Michael’s earlier projects. In addition to basic globbing, various set operations are also possible, such as ‘hosts in this group and not in another group’, and so on.

Group

A group consists of several hosts assigned to a pool that can be conveniently targeted together, and also given variables that they share in common.

Group Vars

The “group_vars/” files are files that live in a directory alongside an inventory file, with an optional filename named after each group. This is a convenient place to put variables that will be provided to a given group, especially complex data structures, so that these variables do not have to be embedded in the inventory file or playbook.

Handlers

Handlers are just like regular tasks in an Ansible playbook (see Tasks), but are only run if the Task contains a “notify” directive and also indicates that it changed something. For example, if a config file is changed then the task referencing the config file templating operation may notify a service restart handler. This means services can be bounced only if they need to be restarted. Handlers can be used for things other than service restarts, but service restarts are the most common usage.

Host

A host is simply a remote machine that Ansible manages. They can have individual variables assigned to them, and can also be organized in groups. All hosts have a name they can be reached at (which is either an IP address or a domain name) and optionally a port number if they are not to be accessed on the default SSH port.

Host Specifier

Each Play in Ansible maps a series of tasks (which define the role, purpose, or orders of a system) to a set of systems.

This “hosts:” directive in each play is often called the hosts specifier.

It may select one system, many systems, one or more groups, or even some hosts that are in one group and explicitly not in another.

Host Vars

Just like “Group Vars”, a directory alongside the inventory file named “host_vars/” can contain a file named after each hostname in the inventory file, in YAML format. This provides a convenient place to assign variables to the host without having to embed them in the inventory file. The Host Vars file can also be used to define complex data structures that can’t be represented in the inventory file.

Lazy Evaluation

In general, Ansible evaluates any variables in playbook content at the last possible second, which means that if you define a data structure that data structure itself can define variable values within it, and everything “just works” as you would expect. This also means variable strings can include other variables inside of those strings.

Lookup Plugin

A lookup plugin is a way to get data into Ansible from the outside world. These are how such things as “with_items”, a basic looping plugin, are implemented, but there are also lookup plugins like “with_file” which loads data from a file, and even ones for querying environment variables, DNS text records, or key value stores. Lookup plugins can also be accessed in templates, e.g., {{ lookup('file','/path/to/file') }}.

Multi-Tier

The concept that IT systems are not managed one system at a time, but by interactions between multiple systems, and groups of systems, in well defined orders. For instance, a web server may need to be updated before a database server, and pieces on the web server may need to be updated after THAT database server, and various load balancers and monitoring servers may need to be contacted. Ansible models entire IT topologies and workflows rather than looking at configuration from a “one system at a time” perspective.

Idempotency

The concept that change commands should only be applied when they need to be applied, and that it is better to describe the desired state of a system than the process of how to get to that state. As an analogy, the path from North Carolina in the United States to California involves driving a very long way West, but if I were instead in Anchorage, Alaska, driving a long way west is no longer the right way to get to California. Ansible’s Resources like you to say “put me in California” and then decide how to get there. If you were already in California, nothing needs to happen, and it will let you know it didn’t need to change anything.

Includes

The idea that playbook files (which are nothing more than lists of plays) can include other lists of plays, and task lists can externalize lists of tasks in other files, and similarly with handlers. Includes can be parameterized, which means that the loaded file can pass variables. For instance, an included play for setting up a WordPress blog may take a parameter called “user” and that play could be included more than once to create a blog for both “alice” and “bob”.

Inventory

A file (by default, Ansible uses a simple INI format) that describes Hosts and Groups in Ansible. Inventory can also be provided via an “Inventory Script” (sometimes called an “External Inventory Script”).

Inventory Script

A very simple program (or a complicated one) that looks up hosts, group membership for hosts, and variable information from an external resource – whether that be a SQL database, a CMDB solution, or something like LDAP. This concept was adapted from Puppet (where it is called an “External Nodes Classifier”) and works more or less exactly the same way.

Jinja2

Jinja2 is the preferred templating language of Ansible’s template module. It is a very simple Python template language that is generally readable and easy to write.

JSON

Ansible uses JSON for return data from remote modules. This allows modules to be written in any language, not just Python.

Library

A collection of modules made available to /usr/bin/ansible or an Ansible playbook.

Limit Groups

By passing --limit somegroup to ansible or ansible-playbook, the commands can be limited to a subset of hosts. For instance, this can be used to run a playbook that normally targets an entire set of servers to one particular server.

Local Connection

By using “connection: local” in a playbook, or passing “-c local” to /usr/bin/ansible, this indicates that we are managing the local host and not a remote machine.

Local Action

A local_action directive in a playbook targeting remote machines means that the given step will actually occur on the local machine, but that the variable ‘{{ ansible_hostname }}’ can be passed in to reference the remote hostname being referred to in that step. This can be used to trigger, for example, an rsync operation.

Loops

Generally, Ansible is not a programming language. It prefers to be more declarative, though various constructs like “with_items” allow a particular task to be repeated for multiple items in a list. Certain modules, like yum and apt, are actually optimized for this, and can install all packages given in those lists within a single transaction, dramatically speeding up total time to configuration.

Modules

Modules are the units of work that Ansible ships out to remote machines. Modules are kicked off by either /usr/bin/ansible or /usr/bin/ansible-playbook (where multiple tasks use lots of different modules in conjunction). Modules can be implemented in any language, including Perl, Bash, or Ruby – but can leverage some useful communal library code if written in Python. Modules just have to return JSON or simple key=value pairs. Once modules are executed on remote machines, they are removed, so no long running daemons are used. Ansible refers to the collection of available modules as a ‘library’.

Notify

The act of a task registering a change event and informing a handler task that another action needs to be run at the end of the play. If a handler is notified by multiple tasks, it will still be run only once. Handlers are run in the order they are listed, not in the order that they are notified.

Orchestration

Many software automation systems use this word to mean different things. Ansible uses it as a conductor would conduct an orchestra. A datacenter or cloud architecture is full of many systems, playing many parts – web servers, database servers, maybe load balancers, monitoring systems, continuous integration systems, etc. In performing any process, it is necessary to touch systems in particular orders, often to simulate rolling updates or to deploy software correctly. Some system may perform some steps, then others, then previous systems already processed may need to perform more steps. Along the way, emails may need to be sent or web services contacted. Ansible orchestration is all about modeling that kind of process.

paramiko

By default, Ansible manages machines over SSH. The library that Ansible uses by default to do this is a Python-powered library called paramiko. The paramiko library is generally fast and easy to manage, though users desiring Kerberos or Jump Host support may wish to switch to a native SSH binary such as OpenSSH by specifying the connection type in their playbook, or using the “-c ssh” flag.

Playbooks

Playbooks are the language by which Ansible orchestrates, configures, administers, or deploys systems. They are called playbooks partially because it’s a sports analogy, and it’s supposed to be fun using them. They aren’t workbooks :)

Plays

A playbook is a list of plays. A play is minimally a mapping between a set of hosts selected by a host specifier (usually chosen by groups, but sometimes by hostname globs) and the tasks which run on those hosts to define the role that those systems will perform. There can be one or many plays in a playbook.

Pull Mode

By default, Ansible runs in push mode, which allows it very fine-grained control over when it talks to each system. Pull mode is provided for when you would rather have nodes check in every N minutes on a particular schedule. It uses a program called ansible-pull and can also be set up (or reconfigured) using a push-mode playbook. Most Ansible users use push mode, but pull mode is included for variety and the sake of having choices.

ansible-pull works by checking configuration orders out of git on a crontab and then managing the machine locally, using the local connection plugin.

Push Mode

Push mode is the default mode of Ansible. In fact, it’s not really a mode at all – it’s just how Ansible works when you aren’t thinking about it. Push mode allows Ansible to be fine-grained and conduct nodes through complex orchestration processes without waiting for them to check in.

Register Variable

The result of running any task in Ansible can be stored in a variable for use in a template or a conditional statement. The keyword used to define the variable is called ‘register’, taking its name from the idea of registers in assembly programming (though Ansible will never feel like assembly programming). There are an infinite number of variable names you can use for registration.

Resource Model

Ansible modules work in terms of resources. For instance, the file module will select a particular file and ensure that the attributes of that resource match a particular model. As an example, we might wish to change the owner of /etc/motd to ‘root’ if it is not already set to root, or set its mode to ‘0644’ if it is not already set to ‘0644’. The resource models are ‘idempotent’ meaning change commands are not run unless needed, and Ansible will bring the system back to a desired state regardless of the actual state – rather than you having to tell it how to get to the state.

Roles

Roles are units of organization in Ansible. Assigning a role to a group of hosts (or a set of groups, or host patterns, etc.) implies that they should implement a specific behavior. A role may include applying certain variable values, certain tasks, and certain handlers – or just one or more of these things. Because of the file structure associated with a role, roles become redistributable units that allow you to share behavior among playbooks – or even with other users.

Rolling Update

The act of addressing a number of nodes in a group N at a time to avoid updating them all at once and bringing the system offline. For instance, in a web topology of 500 nodes handling very large volume, it may be reasonable to update 10 or 20 machines at a time, moving on to the next 10 or 20 when done. The “serial:” keyword in an Ansible playbook controls the size of the rolling update pool. The default is to address the batch size all at once, so this is something that you must opt-in to. OS configuration (such as making sure config files are correct) does not typically have to use the rolling update model, but can do so if desired.

Runner

A core software component of Ansible that is the power behind /usr/bin/ansible directly – and corresponds to the invocation of each task in a playbook. The Runner is something Ansible developers may talk about, but it’s not really user land vocabulary.

Serial

See “Rolling Update”.

Sudo

Ansible does not require root logins, and since it’s daemonless, definitely does not require root level daemons (which can be a security concern in sensitive environments). Ansible can log in and perform many operations wrapped in a sudo command, and can work with both password-less and password-based sudo. Some operations that don’t normally work with sudo (like scp file transfer) can be achieved with Ansible’s copy, template, and fetch modules while running in sudo mode.

SSH (Native)

Native OpenSSH as an Ansible transport is specified with “-c ssh” (or a config file, or a directive in the playbook) and can be useful if wanting to login via Kerberized SSH or using SSH jump hosts, etc. In 1.2.1, ‘ssh’ will be used by default if the OpenSSH binary on the control machine is sufficiently new. Previously, Ansible selected ‘paramiko’ as a default. Using a client that supports ControlMaster and ControlPersist is recommended for maximum performance – if you don’t have that and don’t need Kerberos, jump hosts, or other features, paramiko is a good choice. Ansible will warn you if it doesn’t detect ControlMaster/ControlPersist capability.

Tags

Ansible allows tagging resources in a playbook with arbitrary keywords, and then running only the parts of the playbook that correspond to those keywords. For instance, it is possible to have an entire OS configuration, and have certain steps labeled “ntp”, and then run just the “ntp” steps to reconfigure the time server information on a remote host.

Tasks

Playbooks exist to run tasks. Tasks combine an action (a module and its arguments) with a name and optionally some other keywords (like looping directives). Handlers are also tasks, but they are a special kind of task that do not run unless they are notified by name when a task reports an underlying change on a remote system.

Templates

Ansible can easily transfer files to remote systems, but often it is desirable to substitute variables in other files. Variables may come from the inventory file, Host Vars, Group Vars, or Facts. Templates use the Jinja2 template engine and can also include logical constructs like loops and if statements.

Transport

Ansible uses “Connection Plugins” to define types of available transports. These are simply how Ansible will reach out to managed systems. Transports included are paramiko, SSH (using OpenSSH), and local.

When

An optional conditional statement attached to a task that is used to determine if the task should run or not. If the expression following the “when:” keyword evaluates to false, the task will be ignored.

Van Halen

For no particular reason, other than the fact that Michael really likes them, all Ansible releases are codenamed after Van Halen songs. There is no preference given to David Lee Roth vs. Sammy Lee Hagar-era songs, and instrumentals are also allowed. It is unlikely that there will ever be a Jump release, but a Van Halen III codename release is possible. You never know.

Vars (Variables)

As opposed to Facts, variables are names of values (they can be simple scalar values – integers, booleans, strings) or complex ones (dictionaries/hashes, lists) that can be used in templates and playbooks. They are declared things, not things that are inferred from the remote system’s current state or nature (which is what Facts are).

YAML

Ansible does not want to force people to write programming language code to automate infrastructure, so Ansible uses YAML to define playbook configuration languages and also variable files. YAML is nice because it has a minimum of syntax and is very clean and easy for people to skim. It is a good data format for configuration files and humans, but also machine readable. Ansible’s usage of YAML stemmed from Michael’s first use of it inside of Cobbler around 2006. YAML is fairly popular in the dynamic language community and the format has libraries available for serialization in many languages (Python, Perl, Ruby, etc.).

See also

常见问题
Frequently asked questions
Playbooks
An introduction to playbooks
最佳实践
Best practices advice
User Mailing List
Have a question? Stop by the google group!
irc.freenode.net
#ansible IRC chat channel

YAML 语法

这个页面提供一个正确的 YAML 语法的基本概述, 它被用来描述一个 playbooks(我们的配置管理语言).

我们使用 YAML 是因为它像 XML 或 JSON 是一种利于人们读写的数据格式. 此外在大多数变成语言中有使用 YAML 的库.

你可能希望读 Playbooks 实践中如何使用的.

基本的 YAML

对于 Ansible, 每一个 YAML 文件都是从一个列表开始. 列表中的每一项都是一个键值对, 通常它们被称为一个 “哈希” 或 “字典”. 所以, 我们需要知道如何在 YAML 中编写列表和字典.

YAML 还有一个小的怪癖. 所有的 YAML 文件(无论和 Ansible 有没有关系)开始行都应该是 ---. 这是 YAML 格式的一部分, 表明一个文件的开始.

列表中的所有成员都开始于相同的缩进级别, 并且使用一个 "- " 作为开头(一个横杠和一个空格):

---
# 一个美味水果的列表
- Apple
- Orange
- Strawberry
- Mango

一个字典是由一个简单的 键: 的形式组成(这个冒号后面必须是一个空格):

---
# 一位职工的记录
name: Example Developer
job: Developer
skill: Elite

字典也可以使用缩进形式来表示, 如果你喜欢这样的话:

---
# 一位职工的记录
{name: Example Developer, job: Developer, skill: Elite}

Ansible并不是太多的使用这种格式, 但是你可以通过以下格式来指定一个布尔值(true/fase):

---
create_key: yes
needs_agent: no
knows_oop: True
likes_emacs: TRUE
uses_cvs: false

让我们把目前所学到的 YAML 例子组合在一起. 这些在 Ansible 中什么也干不了, 但这些格式将会给你感觉:

---
# 一位职工记录
name: Example Developer
job: Developer
skill: Elite
employed: True
foods:
    - Apple
    - Orange
    - Strawberry
    - Mango
languages:
    ruby: Elite
    python: Elite
    dotnet: Lame

这就是你开始编写 Ansible playbooks 所需要知道的所有 YAML 语法.

Gotchas

尽管 YAML 通常是友好的, 但是下面将会导致一个 YAML 语法错误:

foo: somebody said I should put a colon here: so I did

你需要使用引号来包裹任何包含冒号的哈希值, 像这样:

foo: "somebody said I should put a colon here: so I did"

然后这个冒号将会被结尾.

此外, Ansible 使用 “{{ var }}” 来引用变量. 如果一个值以 “{” 开头, YAML 将认为它是一个字典, 所以我们必须引用它, 像这样:

foo: "{{ variable }}"

See also

Playbooks
Learn what playbooks can do and how to write/run them.
YAMLLint
YAML Lint (online) helps you debug YAML syntax if you are having problems
Github examples directory
Complete playbook files from the github project source
Mailing List
Questions? Help? Ideas? Stop by the list on Google Groups
irc.freenode.net
#ansible IRC chat channel