# 最佳实践

提示

视频里简单介绍了以下几种示例,至于模板里面的最终逻辑需要使用人根据自己的需求进行排版,编写等,不限制语言。

戳这里观看OpenDevOps任务平台示例视频 (opens new window)

点我展开
  • 示例1. 平台上提交一个最简单的任务,不带参数的(视频)

  • 示例2. 平台上提交一个带参数的任务。(PARAMETER)(视频)

  • 示例3. 平台上基于JSON自定义提交一个任务

{
	"task_name": "这是通过JSON提交的任务",
	"submitter": "yanghongfei", 
	"temp_id": "1", 
	"schedule": "ready",   
	"exec_time": "2019-5-24 09:09:50", 
	"associated_user": "{'group-1': ['杨红飞']}",
	"args": "{'VERSION':'你可以看到我这次传入的参数是VERSION,但是显示成了版本,是因为我在参数管理里面设置了别名'}",
	"details": "这里是备注",
	"hosts": "{1: '172.16.0.101'}"  
}
  • 示例4. 不登陆平台的情况下怎么提交一个任务(POST API提交)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Fred Yangxiaofei
# @File    : codo_post_task.py
# @Role    : 利用脚本向平台提交一个任务

import json
import datetime
import requests


def post_task(temp_id, schedule):
    """
    :param temp_id:  模板ID,平台里面任务模板里面查看
    :param task_status: 提交任务的状态,常用的为:ready: 表示不需人工干预,平台直接执行,new: 平台需要选择审批。根据审批时间执行。
    :return:

    """

    #平台接口连接,这里只需要修改域名即可, 任务API连接都是我们内置进去的,在【用户管理】-【权限列表】 权限名称:钩子提交任务
    accept_task_url = 'https://<域名地址>/api/task/v2/task/accept/'

    #这里就是一个长期Token,管理员可以在用户列表选择一个用户进行生成一个长期Token,这个用户需要有权限名称:钩子提交任务的权限
    auth_key = "yODk4MDX19.BgP8UAoNiBkzJNSN1pa-eQVjlAxrKGxYZf0YgvXv39k"


    # 执行主机IP地址, 1代码第一组
    hosts = {1: '172.16.0.101'}
    #这里都是传入的参数
    args = {
        "TAG": 'codo-beta0.3',
        "GIT_URL": "git@gitlab.xxxxxxx.com:xxxxx/xxxxx.git",
        "APP_NAME": "Web"
    }
    the_body = json.dumps({
        "task_name": "脚本任务", #修改此项
        "temp_id": temp_id,
        "args": str(args),
        "details": "通过脚本提交一个自定义的任务",
        "hosts": str(hosts),
        "submitter": "yanghongfei",
        "exec_time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        "schedule": schedule,
        "executor": "yanghongfei"
    })


    req1 = requests.get(accept_task_url, cookies=dict(auth_key=auth_key))
    csrf_key = json.loads(req1.text)['csrf_key']
    cookies = dict(auth_key=auth_key, csrf_key=csrf_key)
    req = requests.post(accept_task_url, data=the_body, cookies=cookies)
    req_code = json.loads(req.text)['code']
    if req_code != 0:
        # print(json.loads(req.text)['msg'])
        print('task commit failed Please contact  OPS Group')
        exit(-111)
    else:
        # print(json.loads(req.text)['msg'])
        print('Asynchronous task has been submitted successfully!!!')
        print('Please do not submit multiple times to prevent the task from jamming!!!')


if __name__ == '__main__':
    print(post_task('1', 'new'))

  • 示例5. 怎么根据Git Hooks来提交一个任务 主要基于GitLab Custom HooksGitLab Tag借助opendevops平台实现自动化发布,以下为所用到的功能简单介绍。 这里基本上和上面一样,就是多一个怎么用update钩子获取Tag

为什么使用Git TAG发布,然后再接到平台里面呢?

  • 进度可视化
  • 平台发布记录
  • Git Tag 可快速回滚
  • 权限控制,可由PM登陆平台审核后发布
  • 可定时执行,报错可重装,不同项目需求可根据实际编写脚本进行排版任务等。

配置钩子

GitLab Hooks (opens new window)分为2种:

  • Hooks:全局钩子,适用于所有项目
  • Custom Hooks: 项目钩子,针对项目来做钩子触发任务

如何使用?

本章主要使用项目钩子,不建议使用全局钩子,这样会出问题,亲自尝试,毕竟我们的需求是针对xxx项目进行做自动化发布的。

登陆GitLab服务器操作


$ cd <project_name>.git/
$ mkdir -p custom_hooks ; chown -R git.git custom_hooks; chmod 777 custom_hooks

创建Update (opens new window)钩子


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2019/5/24 10:31
# @Author  : Fred Yangxiaofei
# @File    : git_tag_post_task.py
# @Role    : 基于Git Tag来提交一个任务



import sys
import json
import datetime
import requests
import re

def post_task(tag, temp_id, schedule):
    """
    :param temp_id:  模板ID,平台里面任务模板里面查看
    :param task_status: 提交任务的状态,常用的为:ready: 表示不需人工干预,平台直接执行,new: 平台需要选择审批。根据审批时间执行。
    :return:

    """

    #平台接口连接,这里只需要修改域名即可, 任务API连接都是我们内置进去的,在【用户管理】-【权限列表】 权限名称:钩子提交任务
    accept_task_url = 'https://<域名地址>/api/task/v2/task/accept/'

    #这里就是一个长期Token,管理员可以在用户列表选择一个用户进行生成一个长期Token,这个用户需要有权限名称:钩子提交任务的权限
    auth_key = "eyJ0eXAiOiTUyODk4MDg1LCJpc3McxODA2MCIsImRhdGEiOnsidXNlcl9pZCI6IWJsaXNoIiwibmlja25hbWUiOiJwdWJsaXNoIiwiaXNfc3VwZXJ1c2VyIjpmYWxzZX19.BgP8UAoNiBkzJNSN1pa-eQVjlAxrKGxYZf0YgvXv39k"


    # 执行主机IP地址, 1代码第一组
    hosts = {1: '172.16.0.101'}
    #这里都是传入的参数
    args = {
        "TAG": tag,
        "GIT_URL": "git@gitlab.xxxxxxx.com:xxxxx/xxxxx.git",
        "APP_NAME": "Web"
    }
    the_body = json.dumps({
        "task_name": "脚本任务", #修改此项
        "temp_id": temp_id,
        "args": str(args),
        "details": "通过脚本提交一个自定义的任务",
        "hosts": str(hosts),
        "submitter": "yanghongfei",
        "exec_time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        "schedule": schedule,
        "executor": "yanghongfei"
    })


    req1 = requests.get(accept_task_url, cookies=dict(auth_key=auth_key))
    csrf_key = json.loads(req1.text)['csrf_key']
    cookies = dict(auth_key=auth_key, csrf_key=csrf_key)
    req = requests.post(accept_task_url, data=the_body, cookies=cookies)
    req_code = json.loads(req.text)['code']
    if req_code != 0:
        # print(json.loads(req.text)['msg'])
        print('task commit failed Please contact  OPS Group')
        exit(-111)
    else:
        # print(json.loads(req.text)['msg'])
        print('Asynchronous task has been submitted successfully!!!')
        print('Please do not submit multiple times to prevent the task from jamming!!!')


def main():
    '''获取TAG'''
    tag = sys.argv[1].split("/")[-1]
    qa = re.match(r'\Aqa-yanghongfei[\w]*',tag)     #测试 ,直接执行,ready
    release = re.match(r'\Arelease-yanghongfei[\w]*',tag)   #正式,需要审核,new

    if qa:
        print(post_task(tag, '1','ready'))
    elif release:
        print(post_task(tag, '1','new'))
    else:
         print(tag)



if __name__ == '__main__':
    main()

配置好钩子后,接下来克隆版本库进行提交测试


git clone git@gitlab.xxxxxx:xxx/xxx.git
git pull

echo "README" > README.md
git add --all
git commit -m "[Add] README,测试发布"
git push -u origin origin
git tag release-yanghongfei-v1  
git push -u origin release-yanghongfei-v1  #把tag推送上去

# 基于任务平台实现Git发布示例

提示

本章节实验环境主要基于GitLab Custom HooksGitLab Tag借助opendevops平台实现自动化发布,以下为所用到的功能简单介绍。

点我展开

你需要具备以下环境:

发布为什么选择过OpenDevOps平台

  • 进度可视化
  • 平台发布记录
  • Git Tag 可快速回滚
  • 权限控制,可由PM登陆平台审核后发布
  • 可定时执行,报错可重装,不同项目需求可根据实际编写脚本进行排版任务等。

快速使用

这里废话不多说,直接上实践,至于Tag、Hooks、Depoly Keys不熟悉的请参考以上链接,也可自行百度。 发布流程:enable depoly keys->git clone–>git pull—>git checkout <tag>—>rsync_code—>code_backup—>publish code->publish OK~,平台历史记录可查看发布信息

创建模板

配置钩子

GitLab Hooks (opens new window)分为2种:

  • Hooks:全局钩子,适用于所有项目
  • Custom Hooks: 项目钩子,针对项目来做钩子触发任务

如何使用?

本章主要使用项目钩子,不建议使用全局钩子,这样会出问题,亲自尝试,毕竟我们的需求是针对xxx项目进行做自动化发布的。

登陆GitLab服务器操作


$ cd <project_name>.git/
$ mkdir -p custom_hooks ; chown -R git.git custom_hooks; chmod 777 custom_hooks

创建Update (opens new window)钩子

这里我有已经运行2年的脚本供各位参考。

 
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author  : Fred Yangxiaofei
# @File    : new_hook_for_update.py
# @Role    : 利用gitlab Tag触发钩子像平台提交任务

import sys
import json
import datetime
import requests
import re


def post_task(tag, temp_id, schedule):
    """
    :param tag:      从gitlab钩子获取TAG信息
    :param temp_id:  模板ID,平台里面任务模板里面查看
    :param task_status: 提交任务的状态,常用的为:ready: 表示不需人工干预,平台直接执行,new: 平台需要选择审批。根据审批时间执行。
    :return:

    """
    #这里是浏览器Cookie里面auth_key,可使用超级管理员用户,平台获取长期auth_key
    auth_key = "va2VuIsImRhdGEiOnsi9.BgP8UAoNiBkzJNSN1pa-eQVjlAxrKGxYZf0YgvXv39k"  
    # 这里hosts必须是IP,CODO平台只走的是SSH协议,需要在执行用户配置链接主机的key,或者自行打通。
    hosts = {1: '118.25.xx.xx'}  # 上海Salt IP,执行主机IP。
    args = {
        "TAG": tag,
        "GIT_URL": "git@gitlab.xxxxxxx.com:xxxxx/xxxxx.git",  # 修改此项
        "APP_NAME": "DBTT"  # 修改此项
    }
    the_body = json.dumps({
        "task_name": "DBTT", #修改此项
        "temp_id": temp_id,
        "args": str(args),
        "details": "git hook auto exec",
        "hosts": str(hosts),
        "submitter": "githook",
        "exec_time": datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
        "schedule": schedule,
        "executor": "githook"
    })

    accept_task_url = 'https://codo-v1.domain.com/api/task/v2/task/accept/'
    req1 = requests.get(accept_task_url, cookies=dict(auth_key=auth_key))
    csrf_key = json.loads(req1.text)['csrf_key']
    cookies = dict(auth_key=auth_key, csrf_key=csrf_key)
    req = requests.post(accept_task_url, data=the_body, cookies=cookies)
    req_code = json.loads(req.text)['code']
    if req_code != 0:
        # print(json.loads(req.text)['msg'])
        print('task commit failed Please contact  OPS Group')
        exit(-111)
    else:
        # print(json.loads(req.text)['msg'])
        print('Asynchronous task has been submitted successfully!!!')
        print('Please do not submit multiple times to prevent the task from jamming!!!')

def get_tag():
    '''获取TAG'''
    tag = sys.argv[1].split("/")[-1]
    qa = re.match(r'\Aqa-dbtt[\w]*',tag)     #测试
    release = re.match(r'\Arelease-dbtt[\w]*',tag)   #正式

    if qa:
        print(post_task(tag, '32','ready'))  #这是模板ID
    elif release:
        print(post_task(tag, '32','ready'))  #这是模板ID
    else:
         print(tag)


if __name__ == '__main__':
    get_tag()


配置好钩子后,接下来克隆版本库进行提交测试


git clone git@gitlab.xxxxxx:xxx/xxx.git
git pull

echo "README" > README.md
git add --all
git commit -m "[Add] README,测试发布"
git push -u origin origin
git tag release-dbtt-server-20190320-02  #这里只要能让钩子里面正则匹配到dbtt-release都会被触发
git push -u origin release-dbtt-server-20190320-02  #把tag推送上去

最终的结果

# 基于配置中心管理dnsmasq示例

提示

这是一篇关于如何基于配置中心来图形化管理你的内网DNS,一般来说互联网企业里面都会部署自己内部的DNS服务,来提升解析速度。本示例dnsmasq,关于bind DNS域名管理是平台的一个单独模块。

点我展开

环境说明

Q: 为什么要配置到平台上?

A:假如你内网DNS有多台,即使是管理简单的dnsmasq服务,你也要手动登录机器进行编辑配置文件,很麻烦,机器上直接操作是非常危险的,且没有回滚功能,一不小心就可能导致出错,Server 挂掉等。所以在此借助配置中心模块简单记录下,配置中心支持图形化操作、对比、回滚等操作

  • OpenDevOps平台
  • 配置中心模块
  • 任务模板
  • 内网DNS服务(dnsmasq)

如何使用

开始之前你可以点击以下视频链接,看下最终效果,看此操作是否可以满足你的需求

Youtube (opens new window) | BiliBili (opens new window)

登录配置中心新建一个项目

对项目进行赋权

新建的项目默认只有创建人才有权限, 需要赋权,其余用户才可以看到/拉取配置文件详情

获取一个用户的长期Token

这个用户必须有你这个项目的权限才可以,然后获取到这个用户的长期Token,后面脚本要用到

服务器上拉取配置

这里需要在服务器上放一个拉取配置中心配置的一个脚本,逻辑就是,通过一个有权限的用户—拉取指定的配置信息—将配置同步到你的server上—-reload服务

这里提供一个配置脚本示例(dnsmasq),更详情的脚本可参考配置中心 (opens new window)

示例脚本放到DNS服务器目录:/data1/shell/dns_publish.py

#!/usr/bin/env python
# -*-coding:utf-8-*-
"""
Contact : 191715030@qq.com
Author  : shenshuo
Date    : 2019/1/29
Desc    : 获取配置文件内容,
          要确保有全局权限 /kerrigan/v1/conf/publish/
          要确保有当前项目配置获取权限
"""

import os
import requests
import json
import subprocess



def exec_shell(cmd):
    '''执行shell命令函数'''
    sub2 = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    stdout, stderr = sub2.communicate()
    ret = sub2.returncode
    return ret,stdout.decode('utf-8').strip()



class ConfApi:
    def __init__(self):
        # self.auth_key 是一个长期Token,基于用户管理里面,管理员选中用户生成长期Token,默认发送到用户邮箱
        self.auth_key = 'eyJ0eXAiOiJKV1QiLCJhbmZpZyIsIm5pY2XcFBbGciOiJIUzI1NiJ9.eyJleHAiOjE2NTMwMzQyNzYsIm5iZiI6MTU1Nzk5NDI1NiwiaWF0IjoxNTU3OTk0Mj3ZjZlXHU3NTI4XHU2MjM3IY2LCJpc3MiOiJhdXRoOiBzcyIsInN1YiI6Im15IHRva2VuIiwiaWQiOiIxNTYxODcxODA2MCIsImRhdGEiOnsidXNlcl9pZCI6NjYsInVzZXJuYW1lIjoiZ2V0X2NvFOOo'
        self.conf_path ='/tmp'
        self.conf_config_api = "https://codo.domain.com/api/kerrigan/v1/conf/publish/config/"   #配置中心获取API



    def get_config_details(self, project_code, environment, service, filename):
        # 获取配置文件内容,  2019-04-28支持URL auth_key登陆,不需要再登陆进行获取auth_key,直接生成长期Token用
        #__token = self.login()

        __token = self.auth_key
        try:
            _params = {'project_code': project_code, 'environment': environment,'service':service,'filename':filename, 'auth_key': __token}
            res = requests.get(self.conf_config_api, params=_params)
            ret = json.loads(res.content)
            if ret['code'] == 0: return ret['data']
        except Exception as e:
            print('[Error:] 发布配置接口连接失败,错误信息:{}'.format(e))
            exit(-2)



    def create_config_file(self, project_code, environment, service, filename):
        # 生成配置文件
        config_data = self.get_config_details(project_code, environment, service, filename)
        for k,v in config_data.items():
            config_file = self.conf_path + k
            dir_name, _ = os.path.split(config_file)
            if not os.path.exists(dir_name):
                os.makedirs(dir_name)
            with open(config_file ,'w') as f:
                f.write(v)
        #     print('config file path is {}'.format(config_file))
        # print('success')
        return config_file  #返回文件路径


    def rsync_file(self, config_file):
        """
        同步文件
        :return:
        """
        #目标文件名:
        target_file_name = config_file.split('/')[-1]
        cmd = 'rsync -avz {}  /etc/{}'.format(config_file,target_file_name)
        ret, stdout = exec_shell(cmd)
        if ret != 0:
            print('[ERROR]: 文件同步失败'.format(target_file_name))
            return False
        print('[Sucess]: {}文件同步成功'.format(target_file_name))

    def reload_service(self):
        cmd = 'service dnsmasq reload'
        ret, stdout = exec_shell(cmd)
        if ret != 0:
            print('[ERROR]: 服务启动失败')
            return False
        print('[Sucess]: 服务reload成功')


def main():
    obj = ConfApi()
    config_file = obj.create_config_file('DNS', 'release', 'dnsmasq', 'dnsmasq.conf')
    hosts_config_file = obj.create_config_file('DNS', 'release', 'dnsmasq', 'dnsmasq_hosts')
    if config_file:
        obj.rsync_file(config_file)

    if hosts_config_file:
        obj.rsync_file(hosts_config_file)

    obj.reload_service()

if __name__ == '__main__':
    main()

运行查看结果:

python3 /data1/shell/dns_publish.py
[Sucess]: dnsmasq.conf文件同步成功
[Sucess]: dnsmasq_hosts文件同步成功
[Sucess]: 服务reload成功

其实到了这一步,基本配置已经完成了,由于这个DNS每次修改后配置都要reload服务,所以不建议放到crontab里面进行轮训配置,接下来配置下发布,IT人员修改后配置,点下发布进行触发

配置DNS发布

新建一个Tag,将机器添加进去

新建命令和模板配置

这里是发布的时候选择我们自己排序的模块任务

提交一个自定义任务

审批任务

执行结果

如果出错可以查看日志进排查,也可以重做,终止等操作

# 基于私有对象存储管理示例

提示

本文档用来讲解如何使用私有读写的对象存储通过网关做鉴权(阿里云OSS示例)

点我展开

设置Bucket 阿里云创建对象存储Bucket,权限设置为私有。

创建私有Bucket

设置CDN OSS私有读访问鉴权比较复杂,经过测试我们选型通过CDN做访问鉴权,

这样既可以使用CDN加速访问降低选型走内网传输对网络造成的压力,并且可以不改变访问URL的情况下做鉴权。

配置阿里云OSS私有Bucket回源。 阿里云CDN开启私有bucket回源

配置远程鉴权

最简单的处理,只判断是否登录,没登陆的状态码是401,

所以我们只用配置鉴权失败状态码为401,超时动作设置为拒绝;

后续我们也可以在鉴权接口上配置是否有权限,并做日志记录。

阿里云CDN远程鉴权

因为鉴权都在网关,后端接口可以不用做什么改动

class  CDNAuth(BaseHandler):
    def get(self):
        # 记录CDN日志,暂不需要
        pass 

上传接口使用

  • 请求URL:/api/mg/v1/storage/file/private/

  • 请求方式:POST

  • 表头:Content-Type:"multipart/form-data",file-dir="" file-dir 代表上传的目录,需要放入header,如果没有则默认为file目录

  • 参数:

    参数名 必选 类型 说明
    filename str 文件名称
    body 文件内容
  • 输入示例

[
        ('field1', ('foo.png', open("/tmp/filePath1", 'rb'), 'image/png')),
        ('field2', ('bar.png', open('/tmp/filePath1', 'rb'), 'image/png'))
]
  • r = requests.post(url, data, files=files) 使用requests.post的files参数
  • 返回:
  {
      "code":0,
      "msg":"上传成功"
  }

前端访问

上述已经讲解如何上传和CDN配置,下面我们讲怎么访问:

  • 鉴权交互图

    阿里云远程鉴权交互流程

  • 用户通过CDN访问OSS资源的时候会带着访问的headers(我们统一登录的时候会把JWT数据写入前端的headers),CDN会使用携带的headers数据去我们的天门网关鉴权(天门网关需开启jwt auth 或者codo rbac插件),如果headers数据正常则可以访问,否则返回401状态,CDN则返回用户403。

    远端鉴权失败数据

  • 示例在我们的自定义表单的上传组件中使用

    自定义表单使用上传接口

  • 上传成功可以直接预览

    自定义表单使用上传接口成功