契机

由于工作中较多的使用了puppet, 而puppet在大版本升级之后做了较大的改变, 而且由于puppet为ruby编写, 没有这方面的技术栈, 只能复用别人的模块. 所以也在 闲暇时间调研了一下替代品, 发现了Ansible, 当然相似的工具还有chef, saltstack, 不过我只是简单的使用一下Ansible, 如果未来有机会再学习一下这两个软件吧.

若是深入使用Ansible, 请查阅官方文档, 本文只谈一些自己的使用以及理解, 不当之处也请大家指出.

简单使用

安装

在我的机器上, 可以直接使用yaourt -S ansible, 其他Linux发行版也可以试试 自己的包管理工具, 应该也是有的.

开始连接第一台机器

Ansible是使用ssh连接的, 也就意味着, 我们需要给它提供一些SSH参数, 这里我 直接贴出自己的配置, 给大家做个参考:

1
2
3
4
5
6
7
8
9
10
# 将配置信息保存在hosts这个文件中, 注意, 不是/etc/hosts, 这是两回事
ansible_study ❤️ cat hosts git:master*
# ansible组名称: web
[web]
# 主机标识: ubt
# IP地址: 192.168.137.165
# SSH端口: 22
# 用户 root
# 私钥存放位置
ubt ansible_host=192.168.137.165 ansible_port=22 ansible_user=root ansible_ssh_private_key_file=~/.ssh/id_rsa_raspberry

如果不确定你能不能连接, 最好先使用ssh连接一下看看, 只要确保能够连接上, 就可以了.

1
ssh root@192.168.137.165 -p 22 -i ~/.ssh/id_rsa_raspberry

查看一些元数据信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 使用-i hosts, 指定输入文件
# ubt: 指定主机
# -m setup: 运行setup模块, 展示所有名称
ansible_study ❤️ ansible -i hosts ubt -m setup git:master*
ubt | SUCCESS => {
"ansible_facts": {
"ansible_all_ipv4_addresses": [
"192.168.137.165"
],
"ansible_all_ipv6_addresses": [
"fe80::a00:27ff:febe:a041"
],
...
}
}

可能结果比较长, 建议使用 ansible -i hosts ubt -m setup | less, 可以上下翻查.

第一个playbook

通过使用playbook, 可以将配置固化, 从而能够复用. playbook官方文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ansible_study ❤️  cat site_test.yml                                  git:master*
---
# 指定主机名
- hosts: ubt
tasks:
- name: Test
# 打印根目录
command: ls /

# 执行这个playbook.
ansible_study ❤️ ansible-playbook -i hosts -v site_test.yml git:master*
Using /etc/ansible/ansible.cfg as config file
/home/corvo/Projects/ansible_study/hosts did not meet host_list requirements, check plugin documentation if this is unexpected
/home/corvo/Projects/ansible_study/hosts did not meet script requirements, check plugin documentation if this is unexpected

PLAY [ubt] *********************************************************************

TASK [Gathering Facts] *********************************************************
ok: [ubt]

TASK [Test] ********************************************************************
changed: [ubt] => {"changed": true, "cmd": ["ls", "/"], "delta": "0:00:00.002360", "end": "2019-03-18 17:51:39.505902", "rc": 0, "start": "2019-03-18 17:51:39.503542", "stderr": "", "stderr_lines": [], "stdout": "bin\nboot\ndev\netc\nhome\ninitrd.img\ninitrd.img.old\nlib\nlib64\nlost+found\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nsnap\nsrv\nsys\ntmp\nusr\nvar\nvmlinuz\nvmlinuz.old", "stdout_lines": ["bin", "boot", "dev", "etc", "home", "initrd.img", "initrd.img.old", "lib", "lib64", "lost+found", "media", "mnt", "opt", "proc", "root", "run", "sbin", "snap", "srv", "sys", "tmp", "usr", "var", "vmlinuz", "vmlinuz.old"]}

PLAY RECAP *********************************************************************
ubt : ok=2 changed=1 unreachable=0 failed=0

可以看到任务[Test]所打印出的结果.

Ansible的一个可视化界面

在使用过Ansible之后, 也找到了一个GUI工具: ara, 是Redhat开源的. 具体的使用方式就是:

1
2
3
4
5
6
7
8
9
10
11
12
# Install ARA
pip install ara

# 载入ara的环境
source <(python -m ara.setup.env)

# 这是使用ansible-playbook执行脚本
# ansible-playbook myplaybook.yml

# 脚本执行结束后, 可以看到结果
ara-manage runserver
# Browse http://127.0.0.1:9191

ara

Ansible & Puppet 一些操作的对比

接下来我将介绍一下我目前使用过程中的一些实践, 包括Ansible以及puppet, 由于下面 这些部分日常中都能用的到, 所以我着重对比了这几项, 不当之处请指出哈.

创建文件, 文件夹

使用puppet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 创建文件夹
file { "/etc/nginx/conf.d":
ensure => directory,
owner => "root",
group => "root",
}

# 创建文件
file { "/etc/nginx/nginx.conf":
ensure => present,
owner => "root",
group => "root",
content => template("nginx/nginx.conf.erb"),
require => Package["nginx"],
}

# 对于软链接的创建, 将ensure修改为link即可

使用Ansible

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- name: Create nginx dir
file:
path: /var/log/nginx
state: directory

- name: Install nginx config
template:
src: nginx.conf
dest: /etc/nginx/nginx.conf
owner: root
group: root
mode: 0775

# 对于软链接的创建, 将state修改为link即可

变量定义以及模板使用

两者略有不同, puppet使用的是ruby模板, 而Ansible使用的是jinja模板. 真正的使用起来, 其实差别并不大, 二者除了支持普通的变量, 当然也支持 if, for这样的语句. 真正写起来的时候, 读者一定会使用的.

使用puppet

1
2
3
4
5
6
7
8
9
10
11
12
# 这是在class中定义变量, 只能在class中使用
$serf_home = "/home/serf"

file { "$serf_home/serfev_handler.py":
source => "puppet:///config/serf/serfev_handler.py",
ensure => present,
}

# 如果我们在json或是ini文件中定义了一些属性, 可以使用factor查看
# 在er文件中, 可以这样引用:

requirepass <%= @password %> # 这是在设置redis的密码

使用Ansible

1
2
3
4
5
6
7
8
# Ansible的变量我是这么使用的
---
- hosts: ubt
vars:
http_port: 8

# 而后在jinja模板中, 可以这样来用
{{ http_port }}

crontab的创建

在使用过程中, 我发现使用puppet创建的crontab配置可以直接使用crontab -e查看, 而Ansible生成的crontab文件放在了/etc/cron.d目录中, 使用的时候也可以注意一下.

使用puppet

1
2
3
4
5
6
cron { "do-something":
user => "root",
command => "bash /bin/run.sh",
hour => '4',
minute => '0',
}

使用Ansible

Ansible的cron相关文档

1
2
3
4
5
6
7
8
9
- name: Creates a cron file under /etc/cron.d
cron:
name: apt autoupdate
weekday: 2
minute: 0
hour: 12
user: root
job: "apt-get update"
cron_file: ansible_yum-autoupdate

debian系的包管理

我只在debian与ubuntu上做了简单的测试, yum的使用, 请读者自己尝试了.

使用puppet

1
2
3
4
5
6
7
8
9
10
11
# deb包
package { "redis-server":
tnsure => present,
provider => dpkg,
source => "/tmp/redis-server_2.8.17-1~dotdeb.1_amd64.deb",
require => Package["redis-tools"],
}
# 通过apt-get安装
package { "libmysqlclient-dev":
provider => apt,
}

使用Ansible

1
2
- name: Install git
apt: name=git state=presen

有条件的执行某项任务

就是说在何时执行, 在何时绕过这个任务, puppet与Ansible都是支持的. 不过我个人 偏爱puppet的版本, if语句让人感觉舒适, yaml中是没法使用if语句的, 不过你可以使用 when, 而且也支持jinja语句嵌入.

使用puppet

1
2
3
4
5
6
7
if $test_var == "my_vars" {
file { "/etc/nginx/conf.d":
ensure => directory,
owner => "root",
group => "root",
}
}

使用Ansible

1
2
3
4
5
6
7
8
9
10
11
12
# 比较复杂的when语句, 也还是实用的.
- name: Copy my vim files
copy:
src: '~/.vim/vundlerc.vim'
dest: "{{ vim_dir }}/vundlerc.vim"
mode: 0655
owner: "{{ me }}"
group: "{{ me }}"
backup: yes

when: (ansible_distribution == 'Debian' and ansible_distribution_version is version('8', '>')) or
(ansible_distribution == 'Ubuntu' and ansible_distribution_version is version('18.04', '>='))

systemctl与service操作

这部分主要是说更新了某些配置项之后, 重载一些程序, 我就以重载Nginx为例, 其实 两者本质上的差别并不大.

使用puppet

1
2
3
4
5
exec { "reload nginx":
user => 'root',
command => "/usr/sbin/nginx -t && /usr/sbin/nginx -s reload",
refreshonly => true,
}

使用Ansible

1
2
3
---
- name: restart nginx
service: name=nginx state=reloaded enabled=yes

puppet与Ansible的对比总结

puppet中, 许多的功能都是由module提供的, 例如, filebeat, mysql, 它们以module 的形式引入. 可以使用类似函数调用的形式传递值, 给人一种偏向过程式的调用. 另外puppet中, 任务的依赖是显示的通过require来指定的, 你可以在任意位置写任务, 最后只要把require加好就可以了. puppet也有自己语法, 总体来看, 这个语法 比较容易理解, 基本看一段也就能确定它是做什么的了. 而且, 官方提供了一个module的管理工具, 需要什么module可以直装, 在我看来, 唯一 不好的问题就是puppet3, 4版本升级带来了许多的不兼容, 这也是我开始调研Ansible的 一个原因.

Ansible中, playbook的所有配置文件都是yaml格式的. 感觉更加的偏向描述类语言. 有一点不错的就是yaml配置文件会使用jinja进行渲染, 你还是可以在配置文件中使用变量. 相比于puppet的module, Ansible中有一个比较抽象的结构是role, 不像puppet自带了 module管理, Ansible的role仓库分散在Github上的各处, 没有一个官方提供的管理 工具. 在我看来, Ansible现阶段来看还是适合写一些轻巧的部署配置, 大型的配置 从头开始写并不是一件容易的事情.

一些建议

从我的感觉来看, 因为我是Python开发者, 还是推荐Ansible, 但是由于历史原因 使用了puppet, 也不需要挣扎着去更换, 建议使用puppet时, 最好能够跟着新的版本, 有些module并不支持4.0以下的版本, 你一定不会想着自己去写puppet的module. (要是你考虑自己写, 当菜鸡的我没有讲).