Ansible 是一个开源的基于 OpenSSH 的自动化配置管理工具。可以用它来配置系统、部署软件和编排更高级的 IT 任务,比如持续部署或零停机更新。Ansible 的主要目标是简单和易用,并且它还高度关注安全性和可靠性。基于这样的目标,Ansible 适用于开发人员、系统管理员、发布工程师、IT 经理,以及介于两者之间的所有人。Ansible 适合管理几乎所有的环境,从拥有少数实例的小型环境到有数千个实例的企业环境。

使用 Ansible 无须在被管理的机器上安装代理,所以不存在如何升级远程守护进程的问题,也不存在由于卸载了守护进程而无法管理系统的问题。

Ansible 的主要功能

管理员可以通过 Ansible 在成百上千台计算机上同时执行指令(任务)。
对于管理员来说,经常需要执行下面的任务:

  • 维护现存的比较复杂的服务器时,手动登录的方式很容易遗漏一些操作,或者是执行一些未预期的操作。
  • 手动初始化新的服务器耗时耗力!

对于这两种情况,如果完全通过 shell 脚本实现。脚本会过于复杂,极难维护。当然我们也可以使用同类的工具,比如 Puppet and Chef。这两个工具的特点是:需要学习新的知识栈(其实 Ansible 也是有学习成本的)。

相比 Puppet 和 Chef 使用 Ansible 可以延续之前使用 shell 脚本的工作习惯和方式,因而其学习成本会低一些。下面是 Ansible 的一些优势:

  • 可以逐行的执行 shell 命令。
  • 不需要另外的客户端工具(linux 一般会自带 ssh 工具)。
  • 相同的配置只被执行一次(多次执行同一配置不会出问题)。

Ansible 的工作方式

使用 Ansible 无须在被管理的客户端电脑上安装代理之类的组件。它通过普通的 SSH 进行通信,以便从远程计算机检索信息、发出命令和复制文件。这是 Ansible 简化服务器管理的一种方式。任何公开 SSH 端口的服务器都可以通过 Ansible 进行配置和管理。

Ansible 采用模块化的设计,所以非常容易扩展到各种特定的使用场景。模块可以用任何语言编写,并使用标准 JSON 进行通信。Ansible 的配置文件是用 YAML 格式编写的,因为它使用起来非常简单,并且与主流的标记语言很相似。除了通过命令行工具 Ansible 还可以通过配置脚本(Playbooks)与客户端交互。

安装 Ansible

本文介绍在 Ubuntu 16.04 环境中安装并使用 Ansible。由于 Ubuntu 官方库提供的版本比较老,所以我们从第三方的库安装,这样就能安装到比较新的版本:

$ sudo apt-add-repository -y ppa:ansible/ansible
$ sudo apt-get update
$ sudo apt-get install -y ansible

安装完成后检查一下版本:

$ ansible --version

Ansible 简介-LMLPHP

2.7.1 是笔者在写本文时的最新版本。

配置客户端主机的 SSH 秘钥

对于自动化来说,最后是通过秘钥进行认证,这样就不会把用户的密码以明文的方式写在脚本里。下面的命令把安装了 ansible 的主机上当前用户的 SSH 公钥安装到了被管理的客户端 192.168.21.145 和 192.168.21.148 上:

$ ssh-copy-id -i ~/.ssh/id_rsa.pub nick@192.168.21.145
$ ssh-copy-id -i ~/.ssh/id_rsa.pub nick@192.168.21.148

说明:这两台被管理的客户端主机运行的也都是 Ubuntu 16.04。

然后尝试通过下面的命令以不输密码的方式连接到远程主机中:

$ ssh nick@192.168.21.145
$ ssh nick@192.168.21.148

如果能够成功登陆,说明 SSH 的配置已经 OK 了。

配置用户执行 sudo 时不需要密码
在我们执行的自动化操作中,有很大一部分是需要 root 权限的,比如执行更新、安装软件等等。这样的操作会因为在以 sudo 方式执行是提升用户输入密码而失败,比如下面的命令:

$ ansible testservers -b -u nick -a "apt update"

Ansible 简介-LMLPHP

-b 选项默认把用户 nick 提升为 root 权限,但是需要输入用户 nick 的密码,所以自动化执行的命令失败了。当然我们可以同时添加 -K 选项,这是 ansible 会停下来与用户交互,等待用户输入密码:

Ansible 简介-LMLPHP

但这真的不是我想要的结果,我需要的是脚本能够自动化的不需要交互的完成任务!

这个问题的解决方法是把用户设置为执行 sudo 命令时不需要输入密码,让我们在客户机 192.168.21.148 上执行下面的命令:

$ sudo visudo

为用户 nick 添加下面的行:

nick    ALL=(ALL)     NOPASSWD: ALL

该行内容配置用户 nick 在执行 sudo 命令时不需要输入密码,保存后让我们再次执行下面的命令:

$ ansible testservers -b -u nick -a "apt update"

Ansible 简介-LMLPHP

这次终于可以执行 root 权限的命令了!

清单(inventory)

清单是 ansible 的一个配置文件,在清单中我们可以指定被管理的客户端机器。Ansible 默认的清单文件为 /etc/ansible/hosts,当然我们也可以通过 -i 选项指定其它的清单文件,比如下面的例子:

$ ansible myservers -i /etc/ansible/myhosts -b -u nick -a "apt update"

在清单文件中,我们可以指定 ansible 命令操作的主机对象。对于单个的主机,可以在清单中写主机域名,也可以直接写 IP 地址:

Ansible 简介-LMLPHP

如果要同时对对个主机进行操作,可以把它们定义在一个组中:

Ansible 简介-LMLPHP

在执行 ansible 命令时,指定清单中定义的主机名称或者组名就可以了。比如我们在 /etc/ansible/hosts 文件中定义了一个名称为 testservers 的组,它包含了两个主机:

Ansible 简介-LMLPHP

然后通过下面的命令分别在这两台主机上执行 df -h 命令:

$ ansible testservers -u nick -a "df -h"

Ansible 简介-LMLPHP

从输出的结果可以看出 df -h 命令在两台目标主机上都执行了。

模块

Ansible 把类似的操作封装到模块中,这样就可以通过插件的方式对 Ansible 进行扩展了。每个模块都能接收参数,几乎所有的模块都接受键值对(key=value)参数,这些参数通过空格进行分隔。也有一些模块不接收参数,只需在命令行输入相关的命令就能调用。如果要执行单个命令,可以使用 command 模块:

$ ansible testservers -m command -u nick -a "df -h"
$ ansible webservers -m command -a "/sbin/reboot -t now"

因为 command 是 ansible 执行命令是使用的默认模块,所以我们可以在命令行中省略它:

$ ansible testservers -u nick -a "df -h"
$ ansible testservers -b -u nick -a "/sbin/reboot -t now"

这样的写法本质上和前面的写法是一样的。

如果要执行其它模块中的命令就需要通过 -m 选项显式的指定模块的名称,比如执行 service 模块中的命令:

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

如果要把文件从本机拷贝到客户端主机上去,就需要使用 copy 模块:

$ ansible testservers -m copy -u nick  -a "src=./app.js dest=./myapp/app.js"

Ansible 默认内置了很多好用的模块,你可以从其官方文档中的模块部分了解更多模块相关的内容。

playbook

如果 Ansible 的功能仅仅是能够执行当个的命令和脚本就显得太弱了。Ansible 的 laybook 功能支持把命令以 yaml 的格式写在配置文件中,然后一次性执行配置文件中的所有命令(这一点类似于 chef 中的 cookbook)。比如我们可以把前面演示的 df -h 命令以配置文件的方式写在 playbook 中:

---
- hosts: testservers
  become: true
  become_user: root
  tasks:
    - name: check disk
      command: df -h

把上面的代码保存在文件 playbook.yml 中,当然你可以根据自己的喜好命名这个文件。其中 hosts 表示对哪些主机进行操作,become 就是我们在命令行上用过的 -b 选项,这里我们通过 become_user: root 显式的指定把当前用户的权限提升为 root 用户权限来执行命令。下面的 tasks 则是对任务的定义,name 是独一无二的一个任务名(如果有多个同名的 task,只执行第一个),接着是 task 中的命令,这里我们还是简单的执行 df -h 命令。然后执行下面的命令,注意这次执行的是 ansible-playbook 命令,并且需要指定编辑好的 playbook 的文件名称作为参数:

$ ansible-playbook -u nick playbook.yml

Ansible 简介-LMLPHP

这样一个简单的 playbook 就可以正常工作了,当然实际的生产环境中你可能会把 playbook 编写的非常复杂!

跳过首次 ssh 连接时的确认提示

这是一个在自动化的过程中经常碰到的问题,所以有必要提一下。如果你不是通过 ssh-copy-id 命令把公钥添加到目标机器上的(多数的环境都不是这么做的),在首次执行 ansible 命令时需要用户确认连接的安全性:

Ansible 简介-LMLPHP

这是非常悲催的,因为我们要实现的目标是自动化的执行命令,而不需要进行交互式的操作。
这个问题的解决方案是配置 ansible,跳过这一步的检查。具体的做法是在配置文件 /etc/ansible/ansible.cfg 中找到行 host_key_checking = False,并去掉行头的注释字符:

Ansible 简介-LMLPHP

然后再执行 ansible 命令就不会提示用户进行交互式验证了。

总结

Ansible 是一个强大的自动化工具,并以其简介的用法,对开发者(系统运维工程师)友好的特点在自动化的流程中占据了一席之地。在这个 devops 已经成为主流的时代,如果能够熟练的使用 ansible ,相信必定会让你的 devops 实践如虎添翼。

参考:
Ansible Doc
Ansible中文权威指南
How to Install and Configure Ansible on Ubuntu 16.04

11-06 16:29