Gitlab的CICD实际生产环境应用

Gitlab 和 Jenkins 一样,都是很流行的 CI/CD 工具,当然,本站之前推过国人自产的东西 onedev,那个也相当不错,但是用的人毕竟还是少,这回还是用大家都耳熟能详的东西。

本篇就是 Giblab 在生产环境打包发布的一个全流程。

1666673866024

解释一下上图:

首先有两套Git,一套是程序员的Code仓库,另一套是运维的操作代码,里面是一些yaml啊,一些ansible脚本啊

然后流程就是先取出程序员的Code,build出来jar,然后打成镜像推到仓库,然后再取出运维的代码,进行合并,生成k8s的yaml文件,最后推到 kubernetes 中去,这样整个 GitOPS 的流程就完备了

在 gitlab 中非常简单,就是编辑 .gitlab-ci.yaml 文件

 1image: docker:19.03.12
 2variables:
 3  DOCKER_DRIVER: overlay2
 4  DOCKER_TLS_CERTDIR: ""
 5  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
 6  TIMEZONE: "Asia/Shanghai"
 7  http_proxy: ""
 8  https_proxy: ""
 9  no_proxy: ""
10
11cache:
12  paths:
13    - .m2/repository/
14    - target/
15
16stages:
17  - build
18  - push
19  - deploy
20
21
22Build:
23  stage: build
24  image: maven:3.5-jdk-8-alpine
25  before_script:
26    - export COMMIT_TIME=$(TZ=CST-8 date +%F-%H-%M)
27    - echo $COMMIT_TIME
28    - echo "COMMIT_TIME=$COMMIT_TIME" >> build.env
29  script:
30    - ./mvnw package
31  artifacts:
32    reports:
33      dotenv: build.env
34  tags:
35    - yunwei
36
37Push:
38  stage: push
39  before_script:
40    - docker info || true
41    - echo "$HARBOR_REGISTRY $HARBOR_USERNAME $HARBOR_PASSWORD"
42    - echo "echo Dingfangwen | docker login 172.18.31.28 -u dingfangwen --password-stdin"
43    - echo -n $HARBOR_PASSWORD | docker login $HARBOR_REGISTRY -u $HARBOR_USERNAME --password-stdin
44  script:
45    - docker pull $HARBOR_REGISTRY_IMAGE:latest || true
46    - >
47      docker build
48      --cache-from $HARBOR_REGISTRY_IMAGE:latest
49      --build-arg http_proxy=$http_proxy
50      --build-arg https_proxy=$https_proxy
51      --build-arg no_proxy=$no_proxy
52      --cache-from HARBOR_REGISTRY_IMAGE:latest
53      --tag $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
54      --tag $HARBOR_REGISTRY_IMAGE:latest
55      .      
56    - docker push $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
57    - docker push $HARBOR_REGISTRY_IMAGE:latest
58    - docker logout $HARBOR_REGISTRY
59  dependencies:
60    - Build
61  tags:
62    - yunwei
63
64Deploy:
65  stage: deploy
66  cache: {}
67  image: cnych/kustomize:v1.0
68  script:
69    - echo $COMMIT_TIME
70    - git config --global user.email "gitlab@git.k8s.local"
71    - git config --global user.name "GitLab CI/CD"
72    - git clone git://172.18.31.50:30000/test-k8s.git
73    - cd test-k8s/prod
74    - kustomize edit set image $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
75    - cat kustomization.yaml
76    - git commit -am 'PROD image update'
77    - git push origin master
78  dependencies:
79    - Build
80  tags:
81    - yunwei
82  only:
83    - master
84  when: manual

先不着急看这个文件,再普及一下 gitlab 的运行机制,它是通过 gitlab-runner 来执行cd的过程的。配置gitlab runner其实也是一项非常复杂的工作,里面可以配的东西太多了,不是本篇的范畴。我们这里简单来说,runner就是一个Docker容器。更准确的说,就是Docker in Docker。

好,接下来一步步分析整个部署文件:

 1image: docker:19.03.12
 2variables:
 3  DOCKER_DRIVER: overlay2
 4  DOCKER_TLS_CERTDIR: ""
 5  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
 6  TIMEZONE: "Asia/Shanghai"
 7  http_proxy: ""
 8  https_proxy: ""
 9  no_proxy: ""
10  
11cache:
12  paths:
13    - .m2/repository/
14    - target/

首先指定了 Build 过程使用的 Docker 容器的底版,是 docker:19.03.12,设置了 MAVEN_OPTS 的参数,这一点也非常重要,因为每次启动一个 Docker ,在容器里运行 mvn build 的时候,你不会想每次都重新下载所有的依赖包吧。

下面的 cache 部分,设置了整个 build 过程中乃至以后,都保留中间产物。target 目录中存放的是 build 出来的 jar 包。

继续,整个部署过程分三个步骤,1、build 出 jar 包,2、打包成镜像推到仓库去,3、最后发布到k8s去

1stages:
2  - build
3  - push
4  - deploy

首先第一步,build jar包

 1Build:
 2  stage: build
 3  image: maven:3.5-jdk-8-alpine
 4  before_script:
 5    - export COMMIT_TIME=$(TZ=CST-8 date +%F-%H-%M)
 6    - echo $COMMIT_TIME
 7    - echo "COMMIT_TIME=$COMMIT_TIME" >> build.env
 8  script:
 9    - ./mvnw package
10  artifacts:
11    reports:
12      dotenv: build.env
13  tags:
14    - yunwei

很简单,其实就是又起了一个容器,底版是maven:3.5-jdk-8-alpine,在容器里执行了一条命令, mvnw package,这条命令如果成功执行,会在当前目录下新建一个 target 目录,生成一个jar包。

上面比较莫名的地方是这个镜像吧,居然不支持 date +%Y-%m-%d 方式,所以八戒查了半天,用了一个巨别扭的命令生成了时间戳,然后呢把这个时间戳放到一个build.env文件里,然后通过 artifacts 产物,把这个文件以环境变量的方式传到随后的 docker 容器里,这样就可以在随后的容器里用环境变量引用这个一开始就生成的时间戳了。

第二步,生成镜像并push到自己的harbor镜像仓库中去

注意,程序员的源代码里是有 Dockerfile 这个文件的

 1Push:
 2  stage: push
 3  before_script:
 4    - docker info || true
 5    - echo "$HARBOR_REGISTRY $HARBOR_USERNAME $HARBOR_PASSWORD"
 6    - echo "echo Dingfangwen | docker login 172.18.31.28 -u dingfangwen --password-stdin"
 7    - echo -n $HARBOR_PASSWORD | docker login $HARBOR_REGISTRY -u $HARBOR_USERNAME --password-stdin
 8  script:
 9    - docker pull $HARBOR_REGISTRY_IMAGE:latest || true
10    - >
11      docker build
12      --cache-from $HARBOR_REGISTRY_IMAGE:latest
13      --build-arg http_proxy=$http_proxy
14      --build-arg https_proxy=$https_proxy
15      --build-arg no_proxy=$no_proxy
16      --cache-from HARBOR_REGISTRY_IMAGE:latest
17      --tag $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
18      --tag $HARBOR_REGISTRY_IMAGE:latest
19      .      
20    - docker push $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
21    - docker push $HARBOR_REGISTRY_IMAGE:latest
22    - docker logout $HARBOR_REGISTRY
23  dependencies:
24    - Build
25  tags:
26    - yunwei

上面就是在docker容器里build一个镜像并推到我们的私有harbor仓库172.18.31.28去,这个仓库是需要登录验证的。

然后 docker build 的时候又有一些技巧,docker 本身文件是分层的,–cache-from 可以加快 build 的过程

同样我们看到直接引用 $COMMIT_TIME 就可以引用第一步生成的时间戳,注意我们推了两遍,保留了历史时间戳版本,和最新版本。

给出 Dockerfile 的内容:

 1FROM openjdk:8-jre-alpine
 2
 3ENV LANG C.UTF-8 
 4ENV LANGUAGE C.UTF-8
 5ENV LC_ALL C.UTF-8
 6
 7EXPOSE 8080
 8
 9WORKDIR /apps
10
11COPY target/spring-petclinic-2.4.5.jar /apps/app.jar
12
13RUN echo "Asia/Shanghai" > /etc/timezone
14
15ENTRYPOINT ["java","-jar","/apps/app.jar"]

最后一步的发布过程:

 1Deploy:
 2 stage: deploy
 3 cache: {}
 4 image: cnych/kustomize:v1.0
 5 script:
 6   - echo $COMMIT_TIME
 7   - git config --global user.email "gitlab@git.k8s.local"
 8   - git config --global user.name "GitLab CI/CD"
 9   - git clone git://172.18.31.50:30000/test-k8s.git
10   - cd test-k8s/prod
11   - kustomize edit set image $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
12   - cat kustomization.yaml
13   - git commit -am 'PROD image update'
14   - git push origin master
15 dependencies:
16   - Build
17 tags:
18   - yunwei
19 only:
20   - master
21 when: manual

这个就没什么说的了,用kustomize的底版,git下载yaml的模板文件,然后用 kustomize 定制化,最后push到git里面去,做好版本记录。然后kubectl apply -f *.yaml的步骤没有做,大家可以自己加上就没有问题了。

over,这就是一个生产的实际例子。


Sed的进阶用法
Python下Django环境的准备
comments powered by Disqus