Ansible基本使用

Ansible,一个可使配置管理和部署自动化和简化的工具。

在macOS安装#

官方文档是通过pip安装

我是通过brew安装的。

1
brew install ansible

最简单的执行#

准备工作#

假设我有一台服务器proto,已经将Mac本地用户公钥设置到proto服务器上了,即我可以不需要密码而是通过密钥登录。

我还设置了Mac本地的~/.ssh/

1
2
host proto
HostName proto.ohtly.com

这样可以直接通过下面命令登录:

1
ssh proto

配置inventory#

inventory文件,主要目的是设置主机名的列表和相关信息。

默认的路径是/etc/ansible/hosts

如果是通过brew安装的,默认路径在/usr/local/etc/ansible/hosts

将需要ansible处理的主机名/域名/ip地址写入这个文件:

1
proto

执行最简单的命令uname#

按照Ansible官方文档

现在可通过ansible执行最简单的命令:

1
ansible proto -a 'uname -a'

那么将返回类似这样的结果:

1
2
proto | SUCCESS | rc=0 >>
Linux proto 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

ad-hoc 临时命令#

上文执行的命令,在Ansible中叫ad-hoc,临时命令,使用-a表示。

以下列出一些比较有用的临时命令。

在多个主机上执行#

比如在hosts文件中设置了多个主机:

1
2
proto
test

可通过all在所有主机上执行相同的命令:

1
ansible all -a 'uname -a'

看到类似下面的结果:

1
2
3
4
5
test | SUCCESS | rc=0 >>
Linux test 4.7.0-x86_64-linode72 #1 SMP Thu Aug 4 15:15:18 EDT 2016 x86_64 x86_64 x86_64 GNU/Linux

proto | SUCCESS | rc=0 >>
Linux proto 4.4.0-59-generic #80-Ubuntu SMP Fri Jan 6 17:47:47 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

如果hosts配置分组的话:

1
2
3
4
5
[p1]
proto

[t1]
test

那么可以这样指定分组中的主机执行:

1
ansible p1 -a 'uname -a'

同时执行#

上述执行多个主机的时候是逐个执行的,如果主机较多会明显感觉较慢。

可以这样同时多个进程执行:

1
ansible all -a 'uname -a' -f 2

文件传输#

将本地文件上传到服务器指定路径:

1
ansible proto -m copy -a "src=/Users/marshal/tmp/index.html dest=/home/proto/index.html"

删除:

1
ansible proto -m file -a "dest=/home/proto/index.html state=absent"

apt-get安装#

比如要安装nmap

1
ansible proto -m apt -a 'name=nmap state=present' --become

这里假设已经设置登录账号为sudo免密码。

执行git clone#

比如从github clone公开的repository:

1
ansible proto -m git -a "repo=https://github.com/MarshalW/hello-service.git dest=/home/proto/hello-service version=HEAD"

使用playbook命令#

第一个playbook任务:创建空文件#

编写脚本./test.yml:

1
2
3
4
5
---
- hosts: proto
tasks:
- name: 创建空文件
file: path=/home/proto/flag state=touch

执行命令:

1
ansible-playbook test.yml

设置sudo#

如果需要sudo,可以:

1
2
3
4
5
6
---
- hosts: proto
tasks:
- name: 创建空文件
file: path=/home/proto/flag state=touch
become: yes

多任务及变量的使用#

执行多个任务:

  • 创建空文件
  • 执行ls命令,并使用变量files保存命令的标准输出
  • 显示files变量的内容

yml文件内容:

1
2
3
4
5
6
7
8
9
10
11
---
- hosts: proto
tasks:
- name: 创建空文件
file: path=/home/proto/flag state=touch
become: yes
- name: 文件列表
command: ls -hl
register: files
- name: 显示文件列表
debug: msg={{files.stdout_lines}}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
PLAY [proto] *********************************************************************

TASK [setup] *******************************************************************
ok: [proto]

TASK [创建空文件] *******************************************************************
changed: [proto]

TASK [文件列表] ********************************************************************
changed: [proto]

TASK [显示文件列表] ******************************************************************
ok: [proto] => {
"msg": [
"total 4.0K",
"-rw-r--r-- 1 root root 0 Mar 14 02:51 flag",
"drwxrwxr-x 3 proto proto 4.0K Mar 5 04:08 test"
]
}

PLAY RECAP *********************************************************************
proto : ok=4 changed=2 unreachable=0 failed=0

使用模版和变量#

创建jinja2的模版页面./index.j2:

1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
</head>
<body>

</body>
</html>

yml文件:

1
2
3
4
5
6
7
---
- hosts: proto
vars:
title: 首页
tasks:
- name: 创建index.html
template: src=./index.j2 dest=~/index.html

执行这个playbook,则将用模版和变量创建远程主机的~/index.html文件。

jinja2也支持主要的:

  • 控制语句
  • 循环
  • 异常的处理

基本上模版引擎的主要功能均支持,可以实现各种复杂的模版文档生成。

变量的赋值形式很多,还可以从命令行中传入:

1
ansible-playbook test.yml --extra-vars "title=主页"

另外也可以:

  • inventory文件(hosts)中设置
  • 通过include指令在其他yml文件中设置

更多形式见Variables

通知和处理器#

如果修改的是nginx的配置文件,希望能在修改后重启nginx,做法是在yml文件中加入notifyhandlers

1
2
3
4
5
6
7
8
handlers:
- name: restart nginx
service: name=nginx state=restarted
...
- name: template configuration file
template: src=nginx.j2 dest=/etc/nginx/sites-available/proto.ohtly.com
notify:
- restart nginx

模块的使用#

使用Ansible提供的模块#

上文已经在使用Ansible提供的模块了:

  • shell
  • command
  • template
  • file

可以在Module Index找到所有的Ansible模块。

编写自己的模块#

如果Ansible的模块不能满足需要,可以编写自己的模块。

创建./play.yml文件:

1
2
3
4
5
6
7
8
---
- hosts: proto
tasks:
- name: 测试自定义模块
my_module:
name: "Hello-World"
register: result
- debug: var=result

这里我要向自定义的my_module传入一个字符串参数name

模块文件./library/my_module.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/usr/bin/python

from ansible.module_utils.basic import *

def main():
fields = {
"name": {"required": True, "type": "str"},
}
module = AnsibleModule(argument_spec=fields)
response = {"hello": module.params['name']}
module.exit_json(changed=False, meta=response)


if __name__ == '__main__':
main()

运行结果类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PLAY [proto] *********************************************************************

TASK [setup] *******************************************************************
ok: [proto]

TASK [测试自定义模块] *****************************************************************
ok: [proto]

TASK [debug] *******************************************************************
ok: [proto] => {
"result": {
"changed": false,
"meta": {
"hello": "Hello-World"
}
}
}

PLAY RECAP *********************************************************************
proto : ok=3 changed=0 unreachable=0 failed=0

如需编写复杂模块,可参考Developing Modules

插件#

插件我没有使用。

大致看了下文档Developing Plugins

Ansible提供了一些插件,见ansible/lib/ansible/plugins/

也可以按照插件分类来编写自己的插件。可参考:

include和role#

include#

如果yml文件过大,或者为了复用方便,可使用include:

1
2
tasks:
- include: tasks/foo.yml

role#

针对复杂的Ansible项目,可使用Roles来组织。