我们做实验经常要使用远程服务器,而运行一些固定的任务流程,例如环境配置、文件同步、运行实验等等是常见的需求。手动编写 Shell 脚本较为繁琐,尤其是需要管理多台机器时格外麻烦。使用 Ansible 即可通过编写 YAML 格式配置文件,一句命令即可在一组服务器上同时进行操作,高效实用。

安装

Ansible Commuity 是开源免费的版本。Ansible 只需要在一台控制机器上安装,它会使用 SSH 和 Python 来进行服务器的管理。因此你只需要保证每台被管理的机器可以正常用 SSH 登陆(最好是免密登陆),且装有 Python 3.5+(Python 2.7 也可以,但并不推荐)。

在控制机器上安装 Ansible 非常简单,Linux 上通常可以用包管理工具进行安装。此外,还可以使用 pip 进行安装。macOS 和 Win 推荐用 pip 安装。

一些功能简介

Ansible 的核心功能在于 Playbook,Playbook 就是用编写好的配置文件管理服务器组。与之相对的有 Ad-hoc 等,是指对一组服务器执行一句即时命令,显然通过配置文件管理是我们最想要的功能,所以我们重点关注 Playbook。

使用 Ansible Playbook 主要关注两个文件,一个是 Inventory 文件,一个是 YAML 格式的配置文件。所谓 Inventory 文件,就是服务器的列表。我们把我们想管理的机器写到这个文件里,可以进行分组管理,可以为不同的机器保存一些不同的变量等等。而在 YAML 格式的配置文件中,编写我们想要的任务逻辑。

执行任务

YAML 是一种很简单的文件格式,通过键值对和列表的嵌套表达信息。如果读者知道 json 格式,那么 YAML 与 json 是很相似的。下面是一个简单的例子:

- 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

第一行中,我们指定了我们要对哪些服务器进行管理,Ansible 会自动去 Inventory 文件中寻找相应的服务器组。这里就是我们定义好的一个叫 webservers 的服务器组,我们想要对它们进行操作。

随后 vars 指定了一些变量。remote_user 指定了服务器上的登陆用户。

tasks 是本次要执行的任务列表,可以看到这里有三个任务。每个任务都有一个名字和任务内容,例如第一个任务是用 yum 安装 httpd 这个包,确保是最新的版本。第二个任务是更新配置文件,并且更新后通过 notify 调用另一个在 handlers 中定义的任务,由此相当于触发一连串的更新。同时可以由此进行任务依赖关系的设定

如果我们将文件保存为 playbook.yml,并将 Inventory 文件保存为 hosts.ini (默认是 INI 格式的,也支持 YAML 格式),那么执行以下命令即可执行这些任务:

$ ansible-playbook -i hosts.ini playbook.yml

同步文件

编写这样的任务可以将文件进行分发:

- copy:
    src: /srv/myfiles/foo.conf
    dest: /etc/foo.conf
    owner: foo
    group: foo
    mode: u=rw,g=r,o=r

将本地的 /srv/myfiles/foo.conf 文件复制到所有服务器的 /etc/foo.conf 上。

变量替换

每台服务器上的配置经常可能只有一些细微的区别,对此,Ansible 集成了 Jinja2,一个模板引擎,我们把配置文件写成模板,其中蕴含一些变量,之后为每台机器设置不同的变量值,这样 Jinja2 会自动将变量替换为相应的值,同时还能支持 iffor loop 等控制流操作。

例如一个简单的例子,如果某个路径每台机器都不同,你可以把这个路径字符串赋值给一个变量 foo_path,然后在 Playbook 中写:

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

两个花括号之间的 foo_path 就会被进行变量替换,你在不同的机器上设置不同的值,最终就会得到不同的路径。

相关资源

本文只是一个抛砖引玉,Ansible 功能非常丰富强大,读者应该查阅相关手册,一步步递进学习就好,需要什么就现用现查。

Ansible 英文文档:官方,写的很好。

Ansible 中文权威指南:官方文档的中文翻译,但个人觉得翻译有些生硬,如果英语水平可以还是建议看英文。