使用ansible + git + python搭建部署系统

Ansible

ansible是个什么东西呢?官方的title是“Ansible is Simple IT Automation”——简单的自动化IT工具。这个工具的目标有这么几项:让我们自动化部署APP;自动化管理配置项;自动化的持续交付;自动化的(AWS)云服务管理,基于 paramiko 开发。 这个paramiko是什么呢?它是一个纯Python实现的ssh协议库。所以就不需要在远程主机上安装client/agents,因为它们是基于ssh来和远程主机通讯的。

安装

以centos为例

sudo yum install ansible

安装完成,配置文件默认在/etc/ansible/ansible.cfg 为了方便后续的开发,需要修改配置文件

#失败后不在本地生成retry文件,终端也不输出
retry_files_enabled = False

#将执行过程及结果以json形式返回
stdout_callback = json

Inventory文件

Ansible 可同时操作属于一个组的多台主机,组和主机之间的关系通过 inventory 文件配置. 默认的文件路径为 /etc/ansible/hosts 除默认文件外,你还可以同时使用多个 inventory 文件,也可以从动态源,或云上拉取 inventory 配置信息. 以本次项目为例,我们需要从云端查询线上机器IP列表(例如:阿里云SLB下的具体ECS IP列表),最后以JSON格式返回

$ cat ./hosts.py
#!/usr/bin/python
import json
#为了演示方便,此处使用初始化好的ip文件
list_txt_file="/home/git/deploylist.txt"

f=open(list_txt_file, 'r')

ips={"hosts":[]}
for line in f.readlines():
        ips["hosts"].append(line.strip())

print json.dumps(ips)

给文件加上可执行权限

$ chmod +x ./hosts.py

执行查看输出

$ ./hosts.py
{"hosts": ["192.168.x.x", "..."]}

Playbooks

Playbooks 是 Ansible的配置,部署,编排语言.他们可以被描述为一个需要希望远程主机执行命令的方案,或者一组IT程序运行的命令集合. 如果 Ansible 模块是你工作室中的工具,那么 playbooks 就是你设置的方案计划. Playbooks使用yaml文件管理ansible命令。 接下来可以编写我们的部署逻辑,检测项目根目录是否存在,不存在则创建,最后更新版本库代码

---
- hosts: all
  remote_user: git
  tasks:
    - name: check www
      stat: path="{{www_dir}}"
      register: www_stat

    - name: init www
      file: 
          path: "{{www_dir}}"
          state: directory
          owner: git
          group: git
          recurse: yes
      when: not www_stat.stat.exists

    - name: update code
      git:
        repo: "{{repo}}"
        dest: "{{www_dir}}"
        accept_hostkey: yes
        version: "{{version}}"
        update: yes
        key_file: /home/git/.ssh/id_rsa
      become_user: git

此yaml会接收三个参数 www_dir: 部署目标路径 repo: Git版本库 version: Git版本号,可以使具体的版本号,也可以是分支或master 注意:此处git授权使用秘钥,所以git server需要预先设置好对git用户的认证(/home/git/.ssh/id_rsa),一般情况下,所有的线上机器都会使用同一个SSH秘钥,方便机器的SSH管理及GIT管理。

我们使用终端测试功能是否能正常运行

$ ansible-playbook -i ./hosts.py ./deploy.yml  --extra-vars 'version=master&repo=git@gitlab.a.com:test.git&www_dir=/home/git/demo8'
{
    "plays": [
        {
            "play": {
                "id": "00163e0a-7e89-518c-8220-000000000008",
                "name": "all"
            },
            "tasks": [
                {
                    "hosts": {
                        ...
    ],
    "stats": {
        "192.168.x.x": {
            "changed": 3,
            "failures": 0,
            "ok": 0,
            "skipped": 0,
            "unreachable": 0
        }
    }
}

我们关注stats ok及failures数据即可。

CGI

接下来我们使用Apache cgi来搭建我们的WEB部署接口。

sudo yum install httpd

修改apache配置文件,以支持cgi运行

$ vi /etc/httpd/conf/httpd.conf 

Listen 8080
ScriptAlias /cgi/ "/home/git/httpd/cgi/"

<Directory "/home/git/httpd/cgi">
#    AllowOverride None
#Options None
        Options +ExecCGI
#    Order allow,deny
#    Allow from all
</Directory>

AddHandler cgi-script .cgi .py

LoadModule cgi_module modules/mod_cgi.so
LoadModule cgid_module modules/mod_cgid.so

DocumentRoot "/home/git/httpd"
<Directory "/home/git/httpd">
    Options Indexes FollowSymLinks
    AllowOverride None
    Order allow,deny
    Allow from all
</Directory>

启动并测试Apache运行是否正常,假设服务器内网IP为192.168.0.1

$ sudo apachectl start
$ curl -v 'http://192.168.0.1:8080'
< HTTP/1.1 200 OK

最后编写我们的cgi脚本,用来接收POST参数并调用本机的ansible-playbook部署代码并返回执行结果

#!/usr/bin/python
#cgi文件必须要声明的mime类型
print "Content-Type:application/json; Charset=utf-8\n\n"

import cgi, cgitb, json, sys, commands, time, random

default_dir = '/home/git/www'
default_ver = 'HEAD'
error_log = '/home/git/logs/cgi/%s'

form = cgi.FieldStorage() 
repo = form.getvalue('repo')
www_dir = form.getvalue('www_dir')
version = form.getvalue('version')

if not version: version = default_ver
if not www_dir: www_dir = default_dir

if not repo:
    print json.dumps({"err_code":400, "err_msg":"Bad Args"})
    sys.exit()

command="/usr/local/bin/ansible-playbook -i /home/git/ansible/hosts.py /home/git/ansible/deploy.yml  --extra-vars 'repo=%s www_dir=%s version=%s'"%(repo, www_dir, version)
status,deploy_ret = commands.getstatusoutput(command)

ret_json = json.loads(deploy_ret)

if not ret_json.has_key('stats'):
    print json.dumps({"err_code":500, "err_msg":"SysError", "err_trace":deploy_ret})
    sys.exit()

ret = {"success":{"list":[]}, "error":{"list":[], "debug":[]}}

err_count = 0
for ip in ret_json['stats']:
    is_error = ret_json['stats'][ip]['failures']

    if is_error:
        ret['error']['list'].append(ip)
        ret_json['stats'][ip]['ip'] = ip
        ret['error']['debug'].append(ret_json['stats'][ip])
        err_count = err_count+1
    else:
        ret['success']['list'].append(ip)

if err_count:
    error_file = error_log%( time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime()))
    fo = open(error_file, 'a+')
    fo.write(deploy_ret)
    fo.close()

print json.dumps(ret)

测试接口功能

$ curl -d 'version=master&repo=git@gitlab.a.com:test.git&www_dir=/home/git/demo8' 'http://192.168.0.1:8080/cgi/deploy.py'
{"success": {"list": ["192.168.x.x", "192.168.x.x", "xxx"]}, "error": {"debug": [], "list": []}}

接下来我们就可以在前端结合gitlab api以及我们的部署API,进行可视化展示、部署、回滚等操作,这里就不在赘述了。

发表评论

电子邮件地址不会被公开。 必填项已用*标注