在
项目开发中的CICD中介绍了企业用户使用 GitLab 做持续集成和交付CICD
,并提供了一段实际工作中的配置来进行示范。但是那段示例对于比较简单的项目还是挺适用的。这里的简单项目是指服务比较少,架构比较简单,比如只包含前端,后端,数据库三个模块的项目。但是对于复杂的项目还使用这样的 CICD 流程会带来很多问题。所以本文基于 Docker Swarm 部署的微服务项目来对该持续集成交付的机制流程进行优化。
介绍
微服务 Microservices
在
维基百科中的定义是一种软件架构风格,是以专注于单一责任与功能的小型功能区块为基础,利用模块化的方式组合出复杂的大型应用程序,各功能区块使用与语言无关的API集相互通信。直白点就是一个应用程序是由一个或多个微服务组成,每个微服务是以业务功能来进行划分服务,每一个服务都能够独立自主的运行,并对外开放不受语言限制的 API (最常用的是 HTTP)。
常和微服务进行对比的是单体式应用程序。单体式应用表示一个应用程序内包含了所有需要的业务功能,并且使用像主从式架构(Client/Server)或是多层次架构(N-tier)实现。虽然它也是能以分布式应用程序来实现,但是在单体式应用内,每一个业务功能是不可分割的。若要对单体式应用进行扩展则必须将整个应用程序都放到新的运算资源(如:虚拟机) 内,但事实上应用程序中最吃耗费资源、需要运算资源的仅有某个业务部分(例如图谱构建,抽取模型训练等),但因为单体式应用无法分割该部分,因此无形中会有大量的资源浪费的现象。
微服务运用了以业务功能的设计概念,应用程序在设计时就能先以业务功能或流程设计先行分割,将各个业务功能都独立实现成一个能自主运行的个体服务,然后再利用相同的协议将所有应用程序需要的服务都组合起来,形成一个应用程序。若需要针对特定业务功能进行扩展时,只要对该业务功能的服务进行扩展就好,不需要整个应用程序都扩展。同时,由于微服务是以业务功能导向的实现,因此不会受到应用程序的干扰,微服务的管理员可以视运算资源的需要来配置微服务到不同的运算资源内,或是布建新的运算资源并将它配置进去。
近年来,得益于容器化技术的不断改进,微服务的可行性也越来越高。现在,您可以借助 Linux 容器在同一硬件上单独运行一个应用的多个部分,还能更好地控制每个部分及其生命周期。目前,容器化微服务已经成为云原生应用的重要基础。
微服务部署痛点
在 项目开发中的CICD 中我们介绍了 GitLab 如何做持续集成和交付,并以 API 服务做了示范。大致的流程如下所示:
上面的流程对于单体式应用程序而言已经够用了,但是对于微服务应用程序而言还是有很大的不足。主要有以下痛点:
- 各个微服务和部署仓库没有联动
- API 服务更新了,通过 CICD 上线 dev/test/prod 环境,还需要将变更定期提交到部署仓库 YSKG-DEPLOY
- BUILD 等服务更新了配置文件,部署仓库 YSKG-DEPLOY 中挂载的配置可能没有更新,导致启动异常
- YSKG-DEPLOY 仓库在 dev 环境更新了,到 test/prod/自己环境拉取最新版本时发现 docker-compose.yml 冲突了(主要是修改了端口映射)
- 发版后的镜像打包,数据包,镜像包,MODEL 服务的模型都需要打包到一起。镜像包后续需要代码加密(初步想法是master打包时就加密)
解决方案
针对上面的问题做了以下的解决方案:
- HTML,API,BUILD,MODEL 等子服务的代码提交和合并会触发 CICD 流程,自动打包镜像,并更新 YSKG-DEPLOY 仓库的 docker-compose.yml 和配置文件
- 拆分 docker-compose.yml 文件,将端口映射的配置至于 docker-compose-overwrite.yml 中
- YSKG-DEPLOY 仓库打 release_* 标签时会触发 CICD 发版流程,会将拉取 release_* 标签库,拉取镜像包和模型文件,并打包推到ftp上。
微服务应用整体的 CICD 流程图如下:
子服务的CICD流程
1stages:
2 #- test
3 - build
4 - develop
5 - product
6
7variables:
8 GIT_STRATEGY: none
9 PROJECT_REPO_NAMESPACE: fmkg
10 PROJECT_REPO_NAME: kg-api
11 DEPLOY_REPO_NAME: yskg-3.0-deploy
12
13# 各个job的准备工作:克隆仓库,dockerhub登陆,镜像tag
14before_script:
15 - export ROOT_PATH=$(pwd)
16 - echo 'root path:' $ROOT_PATH
17 - echo 'user:' $DOCKER_USER
18 - eval $(ssh-agent -s)
19 - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
20 - mkdir -p ~/.ssh
21 - chmod 700 ~/.ssh
22 - echo "$DOCKER_PW" | docker login http://dockerhub.xxxx.com -u $DOCKER_USER --password-stdin
23 - git clone -b $CI_COMMIT_REF_NAME --depth=1 ssh://git@git.xxxx.com:58422/$PROJECT_REPO_NAMESPACE/$PROJECT_REPO_NAME.git
24 - cd $PROJECT_REPO_NAME
25 - echo 'commit id:' $CI_COMMIT_SHA
26 - echo 'commit user:' $GITLAB_USER_NAME
27 - echo 'commit e-mail:' $GITLAB_USER_EMAIL
28 - export COMMIT_MESSAGE=$(git log -p -1 --pretty=format:"%s"|head -1)
29 - echo 'commit message:' $COMMIT_MESSAGE
30 - export DATE=$(git log --pretty=format:"%cd %H" --date=format:'%Y%m%d' | grep ${CI_COMMIT_SHA} | awk '{print $1}')
31 - echo 'date:' $DATE
32 - export DOCKER_TAG_SUFFIX="$CI_COMMIT_REF_NAME"_$DATE"_"${CI_COMMIT_SHA:0:7}
33 - echo 'docker tag suffix:' $DOCKER_TAG_SUFFIX
34
35#test_stage:
36# stage: test
37# script:
38# - echo "test stage"
39
40
41build_docker:
42 stage: build
43 script:
44 - cd $ROOT_PATH/$PROJECT_REPO_NAME
45 - sh docker/build.sh $DOCKER_TAG_SUFFIX
46 only:
47 - dev
48 - master
49 allow_failure: false
50
51
52# 将打包的镜像和配置文件同步到部署仓库 dev 分支
53deploy_dev:
54 stage: develop
55 script:
56 - CONF_DIR="data/kg_api/conf/"
57 - cd $ROOT_PATH
58 - git clone -b dev --depth=1 ssh://git@git.xxxx.com:58422/$PROJECT_REPO_NAMESPACE/$DEPLOY_REPO_NAME.git
59 - cp $ROOT_PATH/$PROJECT_REPO_NAME/app/configs/sysconf.py $ROOT_PATH/$DEPLOY_REPO_NAME/$CONF_DIR
60 - cd $ROOT_PATH/$DEPLOY_REPO_NAME
61 - sed -i "s/ image:\s*dockerhub.xxxx.com\/yskg\/kg-api:.*$/ image:\ dockerhub.xxxx.com\/yskg\/kg-api:$DOCKER_TAG_SUFFIX/" docker-compose.yml
62 - git add docker-compose.yml $CONF_DIR
63 - git config user.name 'cicd_api'
64 - git config user.email 'api@yskg.com'
65 - git commit -m "$GITLAB_USER_NAME update kg-api:$COMMIT_MESSAGE"
66 - git push -u origin dev
67 only:
68 - dev
69 allow_failure: false
70
71# 将打包的镜像和配置文件同步到部署仓库 master 分支
72deploy_prod:
73 stage: product
74 script:
75 - CONF_DIR="data/kg_api/conf/"
76 - cd $ROOT_PATH
77 - git clone -b master --depth=1 ssh://git@git.xxxx.com:58422/$PROJECT_REPO_NAMESPACE/$DEPLOY_REPO_NAME.git
78 - cp $ROOT_PATH/$PROJECT_REPO_NAME/app/configs/sysconf.py $ROOT_PATH/$DEPLOY_REPO_NAME/$CONF_DIR
79 - cd $ROOT_PATH/$DEPLOY_REPO_NAME
80 - sed -i "s/ image:\s*dockerhub.xxxx.com\/yskg\/kg-api:.*$/ image:\ dockerhub.xxxx.com\/yskg\/kg-api:$DOCKER_TAG_SUFFIX/" docker-compose.yml
81 - git add docker-compose.yml $CONF_DIR
82 - git config user.name 'cicd_api'
83 - git config user.email 'api@yskg.com'
84 - git commit -m "$GITLAB_USER_NAME update kg-api:$COMMIT_MESSAGE"
85 - git push -u origin master
86 only:
87 - master
88 allow_failure: false
89
部署仓库的CICD流程
1stages:
2 - develop
3 - testing
4 - product
5 - release
6
7variables:
8 GIT_STRATEGY: none
9 PROJECT_REPO_NAMESPACE: fmkg
10 PROJECT_REPO_NAME: yskg-3.0-deploy
11 SERVER: 127.0.0.1
12 PORT: 58442
13 USER: spark
14
15# 各个job的准备工作:dockerhub登陆
16before_script:
17 - export ROOT_PATH=$(pwd)
18 - echo 'root path:' $ROOT_PATH
19 - echo 'user:' $DOCKER_USER
20 - eval $(ssh-agent -s)
21 - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
22 - mkdir -p ~/.ssh
23 - chmod 700 ~/.ssh
24 - echo "$DOCKER_PW" | docker login http://dockerhub.xxxx.com -u $DOCKER_USER --password-stdin
25 - git clone -b $CI_COMMIT_REF_NAME --depth=1 ssh://git@git.xxx.com:58422/$PROJECT_REPO_NAMESPACE/$PROJECT_REPO_NAME.git
26 - cd $PROJECT_REPO_NAME
27 - echo 'commit id:' $CI_COMMIT_SHA
28 - echo 'commit user:' $GITLAB_USER_NAME
29 - echo 'commit e-mail:' $GITLAB_USER_EMAIL
30 - export COMMIT_MESSAGE=$(git log -p -1 --pretty=format:"%s"|head -1)
31 - echo 'commit message:' $COMMIT_MESSAGE
32 - export DATE=$(git log --pretty=format:"%cd %H" --date=format:'%Y%m%d' | grep ${CI_COMMIT_SHA} | awk '{print $1}')
33 - echo 'date:' $DATE
34
35
36#test_stage:
37# stage: test
38# script:
39# - echo "test stage"
40
41# 开发环境部署
42deploy_dev:
43 stage: develop
44 script:
45 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper -L $PORT:172.17.132.120:22 -N
46 - cmd="cd /data/spark/yskg-3.0-deploy && sh tools/cicd_deploy.sh "
47 - echo $cmd
48 - ssh -o "StrictHostKeyChecking no" $USER@$SERVER -p $PORT $cmd
49 when: manual
50 only:
51 - dev
52 allow_failure: false
53
54# 测试环境部署
55deploy_test:
56 stage: testing
57 variables:
58 PORT: 58443
59 script:
60 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper -L $PORT:172.17.102.133:22 -N
61 - cmd="cd /data/yskg-3.0-test && sh tools/cicd_deploy.sh "
62 - echo $cmd
63 - ssh -o "StrictHostKeyChecking no" root@$SERVER -p $PORT $cmd
64 when: manual
65 only:
66 - dev
67 allow_failure: false
68
69# 生产环境部署
70deploy_prod:
71 stage: product
72 script:
73 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper -L $PORT:172.17.132.121:22 -N
74 - cmd="cd /data/spark/yskg-3.x-deploy && sh tools/cicd_deploy.sh "
75 - echo $cmd
76 - ssh -o "StrictHostKeyChecking no" $USER@$SERVER -p $PORT $cmd
77 when: manual
78 only:
79 - master
80 allow_failure: false
81
82# 发版打包
83deploy_release:
84 stage: release
85 script:
86 - DOCKER_TAG_SUFFIX=${CI_COMMIT_REF_NAME:8}
87 - echo 'release tag suffix:' $DOCKER_TAG_SUFFIX
88 - ssh -f -p58422 -o "StrictHostKeyChecking no" haojunyu@jumper -L $PORT:172.17.132.121:22 -N
89 - cmd="sh /data/spark/yskg-3.x-deploy/tools/cicd_release.sh /data/spark/nfs_data/yskg-3.x-release "${DOCKER_TAG_SUFFIX}
90 - echo $cmd
91 - ssh -o "StrictHostKeyChecking no" $USER@$SERVER -p $PORT $cmd
92 when: manual
93 only:
94 - /^release_.*$/
95 allow_failure: false