一、面临的问题
一个完整的程序开发流程少不了部署发布这个环节,而部署发布是一个重复的过程,最基本的操作包含停止系统服务、更新软件包、重启系统服务,复杂的还需要做好监控、灰度发布、回滚等。在只有少量服务器的情况下,大多数运维人员会选择手动更新,减少自动化部署发布的开发成本。而当服务器数量增加,甚至服务器可能存在于跨地域的不同机房情况下,如何减少部署发布的人力和时间成本,实现自动化部署发布和无缝发布,而且在部署发布期间仍然能够正常提供服务,就成为一个至关重要的问题。
在我们发布风控情报服务的时候,就遇到了自动化部署发布的问题。由于风控服务在用户场景中处于非常重要的地位,对SLA要求极高,需要提供毫秒级别的访问质量,为了达到这一点,消除掉公网的消耗,需要支持多机房服务,而同时带来的问题就是,如何保持各机房的软件版本统一,能够做到快速的统一发布、运维,则成为了一大难题。
二、如何解决问题
目前自动化部署发布领域已经有了比较成熟的方案,中小公司会维护一些自动化脚本或开源软件,而大公司由于复杂的网络环境更多会选择维护一套专属的部署发布系统。而在我们这样的创业型公司,为了减少人力资源成本,首先选择的成熟的开源软件。
(一)工具对比
我们自己的项目后端开发语言为Python,目前主流的开源自动化配置工具有puppet、ansible、saltstack等,ansible和saltstack是基于Python开发,能够很好的支持Python程序发布。在服务器数量不多,不需要考虑大规模并发性能的情况下,我们对比了ansible和saltstack,最终选用了ansible作为部署发布工具。
1.通讯方式
ansible 无需安装服务端和客户端,管理机通过ssh协议将命令推送到服务器端执行,只需要管理机上安装ansible,即可实现统一管理,同时ansible也支持使用ZeroMQ、Kerberos、LDAP等方式推送命令。而saltstack需要分别安装master和minion,master和minion之间可以通过ZeroMQ、RAET消息队列进行通信,salt在升级时,master版本需向后兼容,minion版本不能高于master。
2.响应速度
saltstack的master和minion是通过ZeroMQ推送命令,而ansible通过标准ssh推送命令,ZeroMQ的传输速度比标准SSH连接会快很多,在大规模服务器并发的情况下,saltstack执行效率会比ansible好,而在一般的运维场景下,ansible可以满足需求。
3.二次开发
ansible和saltstack是基于Python开发,支持使用Python进行二次开发。ansible 附带很多可以直接在远端主机或者通过Playbooks执行的模块,用户可以开发自己的模块或者插件,而saltstack也有一些预装的formulas,同样可以执行自定义的formula,而他们都覆盖了常用的软件模块,如文件传输、web服务器、MySQL命令等。
4.安全性
ansible使用标准ssh协议通讯,标准ssh是加密传输,并且远程服务器不需要运行守护进程,使得远程服务器不容易受到攻击。而saltstack虽然可以通过数据加密方法配置数据传输加密方式,但是远程服务器必须运行守护进程,暴露了可攻击的点。
在综合考虑了上述几点,结合了项目特点,在不需要维护大规模服务器的情况下,且项目远程服务器部署在公有云上,需要通过访问接口完成服务器的拉出集群、拉入集群操作,我们选用了ansible,开发插件简单,只需要维护一台可以连接到所有远程服务器的管理机,尽量避免暴露网络端口减少被攻击的可能性,并且执行效率可以满足需求。
三、Ansible 总体介绍
(一)Ansible 基础架构
上图为ansible的基础架构图,其由以下部分组成:
ansible:核心
core modules:ansible自带的核心模块
custom modules:自定义模块
plugins:ansible插件,包括邮件插件、日志插件、连接插件等
playbooks:剧本,ansible配置、部署、编排语言,定义主机执行的task集合
host inventory:ansible管理远程主机和组之间的关系清单,记录主机ssh端口、账号密码等
在管理主机上,ansible模块通过标准ssh协议(ZeroMQ、Kerberos)执行inventory文件中的主机对应的playbook task集合。
(二)Ansible 常用命令
ansible
核心命令,用于执行ad-hoc命令,既单条命令,可以通过ansible -h获得帮助。ansible-doc
该命令用于查看模块信息,参数-l可以列出所有已安装的模块,参数-s可以查看具体某个模块的用户,如想查询ping模块的相关信息。
$ ansible-doc ping
PING
A trivial test module, this module always returns pong' on successful contact. It does not make sense in playbooks, but it is useful from
/usr/bin/ansible' to verify the ability to login and that a usable
python is configured. This is NOT ICMP ping, this is just a trivial test module.
EXAMPLES:
Test we can logon to 'webservers' and execute python with json lib.
ansible webservers -m ping
MAINTAINERS: Ansible Core Team, Michael DeHaan
ansible-galaxy
用于从官方站点下载第三方扩展模块,类似于centos的yum、Python的pip指令。ansible-playbook
该命令是使用最多的命令,通过读取playbook文件,执行相应的操作。ansible-vault
配置文件中如果包含密码等敏感信息,可以通过ansible-vault加密、解密文件。
四、Ansible 初试
(一)Ansible 安装
官方提供了多种安装方式,可从github的ansible project下载源码编译安装,也可通过yum、apt-get指令安装,这里仅介绍通过pip安装ansible,其他可参考官方网站。
$ sudo pip install ansible
(二)第一条命令
为了避免在建立ssh连接时,重复输入密码,可以设置远程主机免密码登录。
$ ssh-keygen –t rsa –P "
$ ssh-copy-id -i ~/.ssh/id_rsa.pub user@remote_host
编辑(或创建)/etc/ansible/hosts文件,并在其中加入远程主机,例:192.168.1.2
$ ansible all -m ping
192.168.1.2 | SUCCESS => {
"changed": false,
"ping": "pong"
}
(三)Inventory 文件
/etc/ansible/hosts文件格式与ini配置文件类似,可以指定连接方式,也可以指定连接用户名。同时,再分配变量时,可以指定主机分配变量,也可以指定组分配变量。变量可以在多个地方定义,有优先级的差别。例如以下代码显示。
mail.example.com
[webservers]
foo.example.com
bar.example.com
[databases]
localhost ansible_connection=local
other1.example.com ansible_connection=ssh ansible_ssh_user=mpdehaan
other2.example.com ansible_connection=ssh ansible_ssh_user=mdehaan
[raleigh]
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
(四)Playbooks
playbooks的文件格式为yaml,远程主机被定义成不同角色,每个角色需要根据playbook中不同的task执行不同的指令,比如一组主机在inventory文件中被定义成webservers,则可能会执行web服务器重启等操作。
如下playbook,执行一组定义为webservers的主机,使用root账号登录,并且定义了http_port、max_clients变量,会执行三个task,分别为安装最新hhtpd服务、拷贝/srv/httpd.j2文件到远程主机的/etc/httpd.conf、重启httpd服务。’notify‘ action表示在playbook每一个task结束时被触发,只会被触发一次。handlers是一些task列表,通过名字引用。则该playbook中,配置文件拷贝结束后,执行'restart apache' task。
---
- 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实现了多个环境的自动化部署发布。
├── group_vars
│ ├── all
│ ├── production
│ │ ├── build_server.yml
│ │ ├── web_server_bj.yml
│ │ └── web_server_sh.yml
│ └── staging
│ ├── build_server.yml
│ ├── web_server_bj.yml
│ └── web_server_sh.yml
├── library
│ ├── slb_op.py
├── roles
│ ├── build
│ │ └── tasks
│ │ └── main.yml
│ └── publish
│ ├── tasks
│ │ └── main.yml
│ └── templates
│ ├── config.conf
│ ├── nginx.conf
│ └── supervisord.conf
├── production
├── production.yml
├── staging
└── staging.yml
如上目录结构,是目前ansible比较受欢迎的实现方式。例如项目有生产和测试环境,则分为两个inventory文件,分别为production、staging,inventory文件中定义对应环境的服务器所在的组,以staging为例,web_server_sh、web_server_bj表示两个机房的主机,build_server为打包机器。
[web_server_sh]
192.168.0.2
[web_server_bj]
192.168.0.3
[build_server]
localhost ansible_connection=local
而每个inventory对应的playbook则为production.yml、staging.yml,指定不同的组需要执行的角色task。以staging.yml为例,build_server组的主机需要执行build角色的task,并且变量文件为
group_vars/staging/build_server.yml,而web_server_sh、web_server_bj组的主机需要执行publish角色的task,并且指定变量文件不相同。
---
- hosts: build_server
serial: 1
vars_files:
- group_vars/staging/build_server.yml
roles:
- build
- hosts: web_server_sh
remote_user: root
serial: 1
vars_files:
- group_vars/staging/web_server_sh.yml
roles:
- publish
- hosts: web_server_bj
remote_user: root
serial: 1
vars_files:
- group_vars/staging/web_server_bj.yml
roles:
- publish
library目录则为自定义插件,可直接在playbook中使用,slb_op.py为自定义的集群拉入拉出模块。
六、总结
ansible 很好的帮助了我们解决了自动化部署发布的事情,现在项目同步更新到几个机房,已经只需要几分钟就可以完成,节省了许多人力。
在部署发布工具的选择上,工具没有好坏,应该结合自身项目来选择,都能够很好地提高工作效率。
ansible解决了燃眉之急,而目前已经很好地与jenkins、docker结合,当集群数量越来越多,越来越难以维护时,是否需要更换工具,或是结合最新技术,都是一个考验的难题。
作者 叶柴柴 岂安科技软件工程师
负责岂安科技业务风险分析平台后台开发。铲屎官里会写代码的,写代码里会铲屎的,一个爱柯基、爱Python、爱生活的铲屎官。