Django实现自动发布(1数据模型)

公司成立之初,业务量较小,一个程序包揽了所有的业务逻辑,此时服务器数量少,上线简单,基本开发-测试-上线都是由开发人员完成。
随着业务量逐渐上升,功能增多,代码量增大,而单一功能上线需要重新编译整个程序,编译时间由原来的几秒到几分甚至几十分钟,一方面效率降低,另一方面横向扩容带来的处理性能提升效果逐渐减弱。所以由大一统拆分出各个子模块,将大而全的程序“微服务”化。
微服务的好处自然是不言而喻,但是许多个服务部署、变更也确实让人头疼。
如何解决这个问题呢?百度一下大把的服务治理、服务标准化、流程化,各种名词层出不穷,但就是没有一个具体的实现,对我这种新手真的很不友好。
所以结合具体公司业务,满足线上需求,实现了一套自动发布系统。

背景

我司开发人员完成新功能,测试通过后,再通过脚本上传程序包、启动进程以及一些其他的操作。
对于不是经常变更的业务来说,这么干确实没什么问题,但是我司已经拆出几百个微服务,部署在几百个主机上,而且这样的操作每天都要进行几十甚至上百次的时候,弊端就显现出来了:

  • 编译打包本身耗时很长,再上传到服务器又是漫长的等待
  • 发布时面对一堆零散的脚本,老司机虽然游刃有余,但是这些重复性的工作着实让人厌倦
  • 突发状况需要回退时,又是一阵操作猛如虎

怎么办呢。。。
打包上传时间长?提前把程序包放在仓库,要用的时候直接拿行不行
一堆离散的脚本?老夫来接管脚本,提供一个统一的调用接口ok不
难回退?版本管理想用哪个版本就用哪个版本

为了实现版本管理和发布,我们需要记录服务的版本信息,服务发布实际上就是将对应的主机和服务的某个版本关联。

现有的主机管理是由前一位大佬搭建,技术栈是 Django 1.10.1 ,很老的版本了,为了快速实现功能就在此基础上开发吧。

说干就干,老夫写代码就是一把梭,ctrl+c、 ctrl+v 已是炉火纯青。

服务版本管理

一个服务会有很多个版本,如果用一张表来记录所有的信息,势必会有很多冗余的字段,所以拆分为服务、服务版本两张表,做外键关联。对应到 django 的 model 如下:

我司的代码由自建的gitlab管理,构建也是直接用了gitlab-ci,所以服务表里记录gitlab对应project的地址

from django.conf import settings
from django.db import models

class MicroService(models.Model):
    LANGUAGE_TYPE = (
        ('cpp', 'cpp'),
        ('go', 'go'),
        ('python', 'python')
        ('other', 'other')
    )
    name = models.CharField(u'服务名称', max_length=64)
    language = models.CharField(u'语言类型', max_length=16, choices=LANGUAGE_TYPE)
    build_orig = models.CharField(u'构建来源', max_length=16, default='git') 
    build_url = models.CharField(u'构建地址', max_length=128) # gitlab or jenkins 的构建地址,用于触发构建任务
    description = models.CharField(u'描述', max_length=256, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='created_by', on_delete=models.DO_NOTHING)
    updated = models.DateTimeField(auto_now=True)
    updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='updated_by', on_delete=models.DO_NOTHING)

    class Meta:
        db_table = 'micro_service'
        unique_together = (('name', ), )
        ordering = ['-created']

通过gitlab触发一个构建任务时,服务的某个版本的构建状态有预备、运行中、成功、失败、超时、重复构建,可以使用枚举来记录状态的变化;版本创建完成后不能修改,所以只有创建人和创建时间

from enum import Enum, unique

@unique
class BuildStatus(Enum):
    pending = 0
    building = 1
    success = 2
    timeout = 3
    failed = 4
    duplicate = 5

class MicroServiceVersion(models.Model):
    BUILD_STATUS = tuple([(v.value, k) for k, v in BuildStatus.__members__.items()])
  
    microservice = models.ForeignKey(MicroService, on_delete=models.DO_NOTHING)
    version = models.CharField(u'版本', max_length=48) # 版本号格式: 时间戳-提交分支-提交id
    description = models.CharField(u'描述', max_length=128, null=True, blank=True)
    created = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    status = models.PositiveSmallIntegerField(choices=BUILD_STATUS,default=1)
    file_path = models.FilePathField(u'程序包存放路径', max_length=128, null=True, blank=True)
    ref = models.CharField(u'构建分支', max_length=128, default='master')

    class Meta:
        db_table = 'micro_service_version'
        unique_together=(('microservice', 'version'),)
        ordering = ['-created', 'version']

服务实例

服务发布实际上就是将主机和服务的某个版本关联:

  • 部署,在实例表里创建一条主机、服务版本的关联记录
  • 升级、回退,修改对应实例的版本
  • 删除,删除相关数据
状态

当我们进行部署、升级、回退、删除等操作时,可能由于一些原因导致失败,所以用一个字段来记录实例的状态,方便进行后续的重试等操作。

锁定

假如 A用户 正在对 service_a 的实例进行升级操作,那么其他用户不能操作 service_a 的实例,可以将这些实例全部锁定,A用户的操作完成后再解除锁定

对应的 model :

from asset.models import Asset

@unique
class InstanceStatus(Enum):
    running = 0 # 运行中 大部分时候处于此状态
    installing = 1
    upgrading  = 2
    reverting  = 3
    deleting   = 4
    install_failed = 11
    upgrade_failed = 12
    revert_failed = 13
    delete_failed = 14

class MicroServiceInstance(models.Model):
    STATUS = tuple([(v.value, k) for k, v in InstanceStatus.__members__.items()])

    microservice = models.ForeignKey(MicroService, on_delete=models.DO_NOTHING)
    version = models.ForeignKey(MicroServiceVersion, on_delete=models.DO_NOTHING)
    host = models.ForeignKey(Asset, on_delete=models.DO_NOTHING)
    port = models.IntegerField(u'端口号', null=True, blank=True, )
    tag = models.CharField(u'标签', max_length=64, blank=True, null=True)
    weight = models.PositiveSmallIntegerField(u'权重', default=100)
    description = models.CharField(u'描述', max_length=128, blank=True, null=True)
    created = models.DateTimeField(auto_now_add=True)
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.DO_NOTHING)
    status = models.PositiveSmallIntegerField(u'实例状态', choices=STATUS, default=0)
    locked = models.BooleanField(u'操作锁定', default=False)
    is_maintain = models.BooleanField(u'是否维护中', default=False) # 保留字段

    class Meta:
        db_table = 'micro_service_instance'
        ordering = ['-created']

理清了服务、版本、实例的关系,建好了数据模型,接下来就是视图了。

posted @ 2019-11-30 10:41  葡萄不吐皮  阅读(408)  评论(0编辑  收藏  举报