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

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

1666673866024

解释一下上图:

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

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

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

image: docker:19.03.12
variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: ""
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  TIMEZONE: "Asia/Shanghai"
  http_proxy: ""
  https_proxy: ""
  no_proxy: ""

cache:
  paths:
    - .m2/repository/
    - target/

stages:
  - build
  - push
  - deploy


Build:
  stage: build
  image: maven:3.5-jdk-8-alpine
  before_script:
    - export COMMIT_TIME=$(TZ=CST-8 date +%F-%H-%M)
    - echo $COMMIT_TIME
    - echo "COMMIT_TIME=$COMMIT_TIME" >> build.env
  script:
    - ./mvnw package
  artifacts:
    reports:
      dotenv: build.env
  tags:
    - yunwei

Push:
  stage: push
  before_script:
    - docker info || true
    - echo "$HARBOR_REGISTRY $HARBOR_USERNAME $HARBOR_PASSWORD"
    - echo "echo Dingfangwen | docker login 172.18.31.28 -u dingfangwen --password-stdin"
    - echo -n $HARBOR_PASSWORD | docker login $HARBOR_REGISTRY -u $HARBOR_USERNAME --password-stdin
  script:
    - docker pull $HARBOR_REGISTRY_IMAGE:latest || true
    - >
      docker build
      --cache-from $HARBOR_REGISTRY_IMAGE:latest
      --build-arg http_proxy=$http_proxy
      --build-arg https_proxy=$https_proxy
      --build-arg no_proxy=$no_proxy
      --cache-from HARBOR_REGISTRY_IMAGE:latest
      --tag $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
      --tag $HARBOR_REGISTRY_IMAGE:latest
      .
    - docker push $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
    - docker push $HARBOR_REGISTRY_IMAGE:latest
    - docker logout $HARBOR_REGISTRY
  dependencies:
    - Build
  tags:
    - yunwei

Deploy:
  stage: deploy
  cache: {}
  image: cnych/kustomize:v1.0
  script:
    - echo $COMMIT_TIME
    - git config --global user.email "gitlab@git.k8s.local"
    - git config --global user.name "GitLab CI/CD"
    - git clone git://172.18.31.50:30000/test-k8s.git
    - cd test-k8s/prod
    - kustomize edit set image $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
    - cat kustomization.yaml
    - git commit -am 'PROD image update'
    - git push origin master
  dependencies:
    - Build
  tags:
    - yunwei
  only:
    - master
  when: manual

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

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

image: docker:19.03.12
variables:
  DOCKER_DRIVER: overlay2
  DOCKER_TLS_CERTDIR: ""
  MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"
  TIMEZONE: "Asia/Shanghai"
  http_proxy: ""
  https_proxy: ""
  no_proxy: ""
  
cache:
  paths:
    - .m2/repository/
    - target/

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

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

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

stages:
  - build
  - push
  - deploy

首先第一步,build jar包

Build:
  stage: build
  image: maven:3.5-jdk-8-alpine
  before_script:
    - export COMMIT_TIME=$(TZ=CST-8 date +%F-%H-%M)
    - echo $COMMIT_TIME
    - echo "COMMIT_TIME=$COMMIT_TIME" >> build.env
  script:
    - ./mvnw package
  artifacts:
    reports:
      dotenv: build.env
  tags:
    - yunwei

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

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

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

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

Push:
  stage: push
  before_script:
    - docker info || true
    - echo "$HARBOR_REGISTRY $HARBOR_USERNAME $HARBOR_PASSWORD"
    - echo "echo Dingfangwen | docker login 172.18.31.28 -u dingfangwen --password-stdin"
    - echo -n $HARBOR_PASSWORD | docker login $HARBOR_REGISTRY -u $HARBOR_USERNAME --password-stdin
  script:
    - docker pull $HARBOR_REGISTRY_IMAGE:latest || true
    - >
      docker build
      --cache-from $HARBOR_REGISTRY_IMAGE:latest
      --build-arg http_proxy=$http_proxy
      --build-arg https_proxy=$https_proxy
      --build-arg no_proxy=$no_proxy
      --cache-from HARBOR_REGISTRY_IMAGE:latest
      --tag $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
      --tag $HARBOR_REGISTRY_IMAGE:latest
      .
    - docker push $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
    - docker push $HARBOR_REGISTRY_IMAGE:latest
    - docker logout $HARBOR_REGISTRY
  dependencies:
    - Build
  tags:
    - yunwei

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

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

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

给出 Dockerfile 的内容:

FROM openjdk:8-jre-alpine

ENV LANG C.UTF-8 
ENV LANGUAGE C.UTF-8
ENV LC_ALL C.UTF-8

EXPOSE 8080

WORKDIR /apps

COPY target/spring-petclinic-2.4.5.jar /apps/app.jar

RUN echo "Asia/Shanghai" > /etc/timezone

ENTRYPOINT ["java","-jar","/apps/app.jar"]

最后一步的发布过程:

Deploy:
 stage: deploy
 cache: {}
 image: cnych/kustomize:v1.0
 script:
   - echo $COMMIT_TIME
   - git config --global user.email "gitlab@git.k8s.local"
   - git config --global user.name "GitLab CI/CD"
   - git clone git://172.18.31.50:30000/test-k8s.git
   - cd test-k8s/prod
   - kustomize edit set image $HARBOR_REGISTRY_IMAGE:$COMMIT_TIME
   - cat kustomization.yaml
   - git commit -am 'PROD image update'
   - git push origin master
 dependencies:
   - Build
 tags:
   - yunwei
 only:
   - master
 when: manual

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

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