流程介绍
CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。这篇文章中,我将会介绍基于 GitLab CI/CD 的自动化构建与发布实践。如下图所示,整个流程将分为几个部分:
- 1.首先开发人员在本地完成项目的开发之后,将代码推送到 Gitlab 仓库中。
- 2.当代码提交到 Gitlab 仓库时,会触发 Pipeline,Gitlab Runner 会根据
.gitlab-ci.yml
配置文件运行 Pipeline 中各阶段的任务。我总共定义了 3 个阶段:compile,build,deploy。 - 3.在 compile 阶段,Gitlab Runner 将项目编译成 jar 包,使用 MinIO 作为缓存,首次编译项目时会从 Maven 官网拉取依赖,之后会将依赖压缩后上传至 MinIo,在下一次编译时就可以直接从 MinIO 下载依赖文件。
- 4.在 build 阶段,Gitlab Runner 使用在 compile 阶段编译生成的 jar 包构建 Docker 镜像,并将镜像推送至镜像仓库。
- 5.在 deploy 阶段,Gitlab Runner 使用构建好 Docker 镜像在 Kubernetes 集群中部署应用。
GitLab 介绍
GitLab
[1] 是一个利用 Ruby on Rails
开发的开源应用程序,实现一个自托管的 Git 项目仓库,可通过 Web 界面进行访问公开的或者私有的项目。它拥有与 GitHub 类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。
GitLab CI/CD 介绍
Gitlab CI/CD
[2] 是一个内置在 GitLab 中的工具,用于通过持续方法进行软件开发。
- 持续集成(Continuous Integration):频繁地(一天多次)将代码集成到主干。让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。
- 持续交付(Continuous Delivery):频繁地将软件的新版本,交付给质量团队或者用户,以供评审。如果评审通过,代码就进入生产阶段。持续交付可以看作持续集成的下一步。它强调的是,不管怎么更新,软件是随时随地可以交付的。
- 持续部署(continuous Deployment):代码通过评审以后,自动部署到生产环境。是持续部署是持续交付的下一步,持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。
GitLab Runner 介绍
GitLab Runner
[3] 用于执行 Gitlab CI/CD 触发的一系列作业,并将结果发送回 Gitlab。GitLab Runner 可以在 Docker 容器内运行或部署到 Kubernetes 集群中。
Pipeline
Pipeline
[4] 中文称为流水线,是分阶段执行的构建任务。如:安装依赖、运行测试、打包、部署开发服务器、部署生产服务器等流程,合起来称为 Pipeline。
Stage
Stage
[5] 表示构建阶段,可以理解为上面所说安装依赖、运行测试等环节的流程。我们可以在一次 Pipeline 中定义多个 Stage。
Job
Job
[6] 表示构建的作业(或称之为任务),表示某个 Stage 里面执行的具体任务。我们可以在 Stages 里面定义多个 Jobs。
Pipeline,Stage 和 Job 的关系可以用下图表示。
MinIO 介绍
MinIO 是一款分布式,高性能的对象存储服务,专为大型私有云环境而设计。MinIO 兼容 Amazon S3 对象存储接口,非常适合存储大容量的非结构化数据,例如图片、视频、日志文件、镜像等等。本文将使用 MinIO 作为编译 Springboot 项目时使用的缓存,首次编译项目时会从 Maven 官网拉取依赖,之后会将依赖压缩后上传至 MinIo,在下一次编译时就可以直接从 MinIO 下载依赖文件。
环境搭建
前提条件
- 部署好一套 Kubernetes 集群。
- 安装好 Helm 工具,关于 Helm 安装可以参考
安装 Helm
[7]。
MinIO 部署
Helm 是 Kubernetes 生态系统中的一个软件包管理工具,方便我们快速部署应用。这里选择使用 Helm 在 Kubernetes 集群中部署 MinIO。
添加 Helm 仓库。
helm repo add minio https://helm.min.io/
使用以下命令安装 Helm。设置用户名为 admin,密码为 admin123456,在 minio 命名空间中部署。
helm install minio \ --set accessKey=admin,secretKey=admin123456 \ --namespace minio --create-namespace \ minio/minio #返回结果 minio/minio NAME: minio LAST DEPLOYED: Wed Aug 18 13:23:45 2021 NAMESPACE: minio STATUS: deployed REVISION: 1 TEST SUITE: None NOTES: Minio can be accessed via port 9000 on the following DNS name from within your cluster: minio.minio.svc.cluster.local To access Minio from localhost, run the below commands: 1. export POD_NAME=$(kubectl get pods --namespace minio -l "release=minio" -o jsonpath="{.items[0].metadata.name}") 2. kubectl port-forward $POD_NAME 9000 --namespace minio Read more about port forwarding here: http://kubernetes.io/docs/user-guide/kubectl/kubectl_port-forward/ You can now access Minio server on http://localhost:9000. Follow the below steps to connect to Minio server with mc client: 1. Download the Minio mc client - https://docs.minio.io/docs/minio-client-quickstart-guide 2. Get the ACCESS_KEY=$(kubectl get secret minio -o jsonpath="{.data.accesskey}" | base64 --decode) and the SECRET_KEY=$(kubectl get secret minio -o jsonpath="{.data.secretkey}" | base64 --decode) 3. mc alias set minio-local http://localhost:9000 "$ACCESS_KEY" "$SECRET_KEY" --api s3v4 4. mc ls minio-local Alternately, you can use your browser or the Minio SDK to access the server - https://docs.minio.io/categories/17
为了在本地电脑可以访问到 MinIO 的 Web 界面,使用以下命令开启端口转发。你也可以选择通过 NodePort 或者其他方式将 MinIO 服务暴露到集群外部。
export POD_NAME=$(kubectl get pods --namespace minio -l "release=minio" -o jsonpath="{.items[0].metadata.name}") kubectl port-forward $POD_NAME 9000 --namespace minio
浏览器输入 http://localhost:9000 访问 MinIO 界面。用户名:admin,密码 admin123456,是我们前面用 helm install 安装 minio 时设置的。
kubectl create secret -n acp generic s3access \ --from-literal=accesskey=admin \ --from-literal=secretkey=admin123456
添加 Gilab Helm 仓库,并下载 Gitlab Runner Helm 资源文件。
helm repo add gitlab https://charts.gitlab.io helm pull gitlab/gitlab-runner --untar
编辑 gitlab-runner 目录下的 values.yaml 文件,有以下几个配置需要修改。
- gitlabUrl:设置 Gitlab 的 IP 地址。
- runnerRegistrationToken:设置 Gitlab Runner 注册的 token。进入项目 -> Settings -> CI/CD -> Runners settings 查看注册 Gitlab Runner 所需的 registration token。
- cache:设置缓存。
# gitlab IP 地址 gitlabUrl: http://gitlab ip地址/ # 注册 gitlab runner 的 token runnerRegistrationToken: "o_4r2uvptQYmmr79e2uF" runners: # 设置缓存 cache: ## General settings cacheType: s3 cachePath: "gitlab-runner-elasticsearch-api" # 缓存路径,gitlab runner 会自动在 bucket 下创建该目录 cacheShared: true ## S3 settings s3ServerAddress: minio.minio.svc.cluster.local:9000 # kubernetes 集群 clusterip service 访问的地址 s3BucketName: gitlab-runner-cache-maven # bucket 名字 s3BucketLocation: s3CacheInsecure: true # http 登录 secretName: s3access # 使用 Minio 用户名密码创建的 secert
配置完成后,使用以下命令安装 Gitlab Runner。
helm install -n acp gitlab-runner-elasticsearch-api gitlab-runner
一切顺利的话,可以在 Gitlab 上看到 Gitlab Runner 成功注册上来。查看在 Kubernetes 集群中创建的 Gitlab Runner 的 Pod
❯ kubectl get pod -n acp | grep runner gitlab-runner-elasticsearch-api-gitlab-runner-88f7b64fc-rdfch 1/1 Running 0 113s
配置 .gitlab-ci.yml 文件
Gitlab CI/CD 通过 .gitlab-ci.yml
配置文件中定义流水线(Pipeline)的各个阶段(Stage),以及各个阶段中的若干作业(Job)。例如以下配置文件,我们定义了 3 个 Stage:
- 1.compile:使用 openjdk 镜像编译项目。
- 2.build:使用 compile 阶段编译好的 jar 包构建 Docker 镜像,并推送至镜像仓库。
- 3.deploy:在 Kubernetes 集群中使用构建好的 Docker 镜像部署应用。
stages: - compile - build - deploy variables: KUBECONFIG: /tmp/config CI_REGISTRY: 你的镜像仓库 IP CI_REGISTRY_IMAGE: 你的镜像仓库项目路径 # maven 依赖缓存 cache: paths: - cache # 编译项目 compile: stage: compile image: openjdk:8-jdk-alpine # 只有打 tag 时才会触发任务 only: - tags # 编译项目,跳过单元测试,指定本地依赖目录 script: - ./mvnw package -Dmaven.test.skip=true -Dmaven.repo.local=cache # 将编译好的 jar 包传递给下一个阶段,用于 kaniko 构建 docker 镜像 artifacts: paths: - target # 构建镜像 build: image: gcr.io/kaniko-project/executor:debug # 可能需要手动提前下载 stage: build only: - tags script: # 使用 kaniko 构建 docker 镜像 - mkdir -p /kaniko/.docker - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$REGISTRY_USER\",\"password\":\"$REGISTRY_PASS\"}}}" > /kaniko/.docker/config.json - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG #部署到 kubernetes 环境中 deploy: image: bitnami/kubectl:1.19 stage: deploy only: - tags script: # 设置 kubectl 容器的 kubeconfig 文件,使 kubectl 容器有在 Kubernetes 中部署应用的权限 - echo $kube_config | base64 -id > $KUBECONFIG - sed -i "s/CI_COMMIT_TAG/$CI_COMMIT_TAG/g" manifests/deployment.yaml - cat manifests/*.yaml - kubectl apply -f manifests/
注意事项:
- 为了安全性,我们将镜像仓库的用户名、密码以及 Kubernetes 集群的 kubeconfig 文件设置在 Gitlab 的 Secret variables 中。进入项目 -> Settings -> CI/CD -> Secret variables。注意 kubeconfig 文件的值是 base64 加密后的,镜像仓库的用户名和密码正常设置即可。需要在项目根路径下创建一个 cache 目录,用于临时存放从 MinIo 下载的依赖,这个目录名可以自定义,要和 .gitlab-ci.yml 文件中设置的 cache path 一致。在 compile 阶段指定
-Dmaven.repo.local=cache
参数使用 cache 作为依赖仓库,这样就可以用上从 MinIO 中提取的依赖了。
- 在 build 阶段使用 compile 阶段编译好的 jar 包构建 Docker 镜像,Dockerfile 内容如下。
FROM openjdk:8-jdk-alpine # 设置工作目录 WORKDIR /app # 语言,时区设置 ENV TZ=Asia/Shanghai ENV LANG=en_US.UTF-8 ENV LANGUAGE=en_US:en ENV iC_ALL=en_US.UTF-8 EXPOSE 8080 # 拷贝 compile 阶段编译好的 jar 包 COPY target/*.jar elasticsearch-api.jar CMD ["java","-jar","elasticsearch-api.jar"]
- deploy 阶段用于在 Kubernetes 环境中部署的资源文件在 manifests 目录中,根据你自身项目的情况来编写。
流程验证
完成项目的开发之后,将代码推送到 Gitlab 仓库中。
git add . git commit -m "首次触发任务" git push
此时并不会触发 Pipline,因为我们在 .gitlab-ci.yml 配置文件中设置了只有打了 tag 才会触发 Pipline。推送 tag 触发 Pipeline。
git tag 3.0.4 git push origin 3.0.4
等待 Pipeline 执行完成。
参考资料
- [1][Gitlab] (https://about.gitlab.com/)
- [2][Gitlab CI/CD] (https://docs.gitlab.com/ee/ci/index.html)
- [3][GitLab Runner] (https://docs.gitlab.com/runner/)
- [4][Pipelines] (https://docs.gitlab.com/ce/ci/pipelines.html)
- [5][Stages] (https://docs.gitlab.com/ce/ci/yaml/README.html#stages)
- [6][Jobs] (https://docs.gitlab.com/ce/ci/pipelines.html#jobs)
- [7][安装 Helm] (https://helm.sh/zh/docs/intro/install/)
- [Use kaniko to build Docker images] (https://docs.gitlab.com/ee/ci/docker/using_kaniko.html)
- [常用容器镜像构建工具和方案介绍] (https://cloud.51cto.com/art/202108/679757.htm)
- [如何在 Docker 中使用 Docker] (https://mp.weixin.qq.com/s/vARJJeNLBKgxn4T7oqC41Q)
- [配置使用分布式缓存] (http://docs.idevops.site/gitlabci/chapter05/01/05-%E9%85%8D%E7%BD%AE%E4%BD%BF%E7%94%A8minio%E5%88%86%E5%B8%83%E5%BC%8F%E5%AD%98%E5%82%A8/)
- [用 GitLab 做 CI/CD 是什么感觉,太强了!!] (https://cloud.tencent.com/developer/article/1684099)
- [GitLab CI/CD 介绍和使用] (https://blinkfox.github.io/2018/11/22/ruan-jian-gong-ju/devops/gitlab-ci-jie-shao-he-shi-yong/)
- [GitLab CI/CD 官网] (https://docs.gitlab.com/ee/ci/)
- [DevOps之Gitlab-CICD实践篇] (https://zhuanlan.zhihu.com/p/105157319)
- [GitLab CI/CD 实践] (https://xiangflight.github.io/gitlab-cd-practice/)
- [CI/CD是什么?如何理解持续集成、持续交付和持续部署] (https://www.redhat.com/zh/topics/devops/what-is-ci-cd)