运维驿站python自动化运维DevOPS

授之以渔-运维平台发布模块一(Jenkins篇)

2017-06-08  本文已影响2526人  大Q的梦想

本着步子迈得太大容易扯蛋的原则,平台设计初衷就是能调用开源产品肯定不自己做,这样平台只作为一个综合调度中心使用,无需考虑后面具体的功能实现逻辑。

使用Jenkins还是要追溯到很久很久之前认知的一家公司,当时的技术总监张晓峰让我学到了持续集成引擎Hudson,也就是后来的Jenkins。以前公司是Jenkins结合Maven,Ant做敏捷式开发,而我只是取巧,用了其中的一些最基本的功能来实现系统发布更新。

传统的运维发布

优点:粗暴简单
缺点

采用系统方案:YUM

Paste_Image.png

我当时的思路:公司是等保三级的单位,在当初我制定内网规则的时候,强烈建议SSH登录范围必须限定,统一的入口可以极大的减少被黑客跳板式攻击的可能,所以我想到了是用YUM更新的方式:

  1. 发布:把代码从SVN上迁出后,打成RPM包(强烈推介FPM)

  2. 更新:通过YUM的特性,更新的程序包每次保持版本号+1,例如test-519-1.x86_64(519就是Jenkins的发布版本号),服务器每次只需要执行以下2条命令即可。

    yum clean all
    yum install test
    
  3. 批量操作:通过Saltstack去通知每台服务器去进行Yum的动作啦。。。

  4. 回退: 就更简单了,粗暴点在YUM服务器直接 mv test-518-1.x86_64 test-520-1.x86_64即可,斯文点当然还是回调Jenkins的接口,使用TAG回滚。

具体逻辑及实施

那么下面先来解决打RPM包,更新YUM源的问题(我的Jenkins就是我们内网的YUM源):

配置Jenkins

首先我们需要打开Jenkins中的batch tasks(批处理,其实就是脚本),不会用Jenkins自己百度吧。

mkdir -p /home/release/$JOB_NAME && \
fpm -s dir -t rpm -n $JOB_NAME -v $BUILD_NUMBER --prefix /home/www/bbs -C /var/lib/jenkins/workspace/$JOB_NAME -p /home/release/$JOB_NAME ./ && \
createrepo --update /home/release/$JOB_NAME/ && \
curl -d "job_id=$JOB_NAME" http://salt master IP/cmdb/salt_jenkins_post/

这段Jenkins脚本的大体意思:

Saltstack接口(salt_jenkins_post)

由于我的平台和Salt master是同一台(主要是省事),省去了调用API,直接调用了本地Saltstack已经封装的一些yum install 之类的命令。

我在接口处理的每一步后都会验证返回的主机是否跟数据库预设的项目主机一样,只有一样了才会进行下一步(比如接口只返回了一台服务器通过salt执行的结果,而数据库里该项目是两台服务器,我会认为这个发布有问题,而进行中断)这样也是为了避免有的主机更新成功了,有的主机没更新成功,导致线上用户体验不好(目前已经成功从深信服公司要到了负载均衡的API)后面要做的就是采用灰度发布,从负载上摘除一个然后就更新一个,更新完毕再加回负载。

-- coding: utf-8 --
import json
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt


try:
    import salt.client
except:
    pass
from cmdb.models import *


class Salt_jenkins:
    def init(self, host_list, job):
        self.client = salt.client.LocalClient()
        self.host_list = host_list
        self.type = type
        self.job = job

def upgradeavailable(self):
    """检测目标主机组项目在yum上是否有新版本更新,返回可以更新的主机"""
    ret = self.client.cmd('%s'% self.host_list, 'pkg.upgrade_available', ['%s'% self.job],expr_form='list',ret='return_redis')
    true_hostlist = []
    for host in ret.keys():
        if ret['%s' % host]:
            true_hostlist.append(host)
        else:
            pass
    return true_hostlist

def install(self):
    """YUM安装项目RPM包"""
    ret = self.client.cmd('%s'% self.host_list, 'pkg.install', ['%s'% self.job],expr_form='list',ret='return_redis')
    true_hostlist = []
    for host in ret.keys():
        if ret['%s' % host] != {}:
            install_ret = ret['%s' % host]['%s' % self.job]
            if install_ret != '':
                true_hostlist.append(host)
            else:
                pass
        else:
            pass
    return true_hostlist

def modrepo(self):
    """创建项目YUM源"""
    ret = self.client.cmd('%s'% self.host_list, 'pkg.mod_repo',['repo=%s'% self.job,'baseurl=http://172.18.11.98/release/%s'% self.job,'enabled=1','gpgcheck=0','name=%s'% self.job,'priority=10'],expr_form='list',ret='return_redis')
    true_hostlist = []
    for host in ret.keys():
        if type(ret['%s' % host]) == dict:
           true_hostlist.append(host)
        else:
            pass
    return  true_hostlist

def getrepo(self):
    """验证项目YUM源是否存在"""
    ret = self.client.cmd('%s'% self.host_list, 'pkg.get_repo', ['repo=%s'% self.job],expr_form='list',ret='return_redis')
    true_hostlist = []
    for host in ret.keys():
        if ret['%s' % host] != {}:
           true_hostlist.append(host)
        else:
            pass
    return  true_hostlist

def intro(self):
    """执行svn_intro内命令"""
    command = Svn.objects.get(svn_name = self.job).svn_intro
    ret = self.client.cmd('%s'% self.host_list, 'cmd.run', ['%s'% command],expr_form='list',ret='return_redis')
    return ret

@csrf_exempt
def salt_jenkins_post(request):
    if request.method == 'POST':
        ip = request.META.get("REMOTE_ADDR", None)
        """验证Jenkins IP,安全考虑"""
        if ip == '172.18.11.98':
            job =  request.POST.get('job_id')
            """根据项目名获得对应的发布主机"""
            job_hosts =  Svn.objects.get(svn_name=job).svn_hosts
            if job =='cms_template.cn' or job =='cms_assets.cn':
                pass
            else:
                """初始化Salt_jenkins"""
                salt_jenkins = Salt_jenkins(job_hosts, job)


            """目标主机检查YUM源是否存在"""
            if sorted(salt_jenkins.getrepo()) == sorted(str(job_hosts).split(',')):
                pass
            else:
                """不存在就创建YUM源"""
                salt_jenkins.modrepo()
            """目标主机检查YUM源是否更新"""
            if sorted(salt_jenkins.upgradeavailable()) == sorted(str(job_hosts).split(',')):
                if sorted(salt_jenkins.install()) == sorted(str(job_hosts).split(',')):
                    """目标主机执行命令"""
                    salt_jenkins.install()
                    """目标主机执行命令"""
                    salt_jenkins.intro()
                    return HttpResponse('install success')
                else:
                    return HttpResponse('install fail')
            else:
                return HttpResponse('upgradeavailable fail')
    else:
        return HttpResponse('ip deny')
else:
    return HttpResponse('get deny')

后续会介绍发布的状态返回,也就是Saltstack的MasterEvent及通过Jenkins结合Saltstack创新发布项目。

上一篇下一篇

猜你喜欢

热点阅读