CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法。CI/CD 的核心概念是持续集成、持续交付和持续部署。这篇文章中,我将会介绍基于 GitLab CI/CD 的自动化构建与发布实践。如下图所示,整个流程将分为几个部分:
.gitlab-ci.yml
配置文件运行 Pipeline 中各阶段的任务。我总共定义了 3 个阶段:compile,build,deploy。GitLab
[1] 是一个利用 Ruby on Rails
开发的开源应用程序,实现一个自托管的 Git 项目仓库,可通过 Web 界面进行访问公开的或者私有的项目。它拥有与 GitHub 类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。
Gitlab CI/CD
[2] 是一个内置在 GitLab 中的工具,用于通过持续方法进行软件开发。
GitLab Runner
[3] 用于执行 Gitlab CI/CD 触发的一系列作业,并将结果发送回 Gitlab。GitLab Runner 可以在 Docker 容器内运行或部署到 Kubernetes 集群中。
Pipeline
[4] 中文称为流水线,是分阶段执行的构建任务。如:安装依赖、运行测试、打包、部署开发服务器、部署生产服务器等流程,合起来称为 Pipeline。
Stage
[5] 表示构建阶段,可以理解为上面所说安装依赖、运行测试等环节的流程。我们可以在一次 Pipeline 中定义多个 Stage。
Job
[6] 表示构建的作业(或称之为任务),表示某个 Stage 里面执行的具体任务。我们可以在 Stages 里面定义多个 Jobs。
Pipeline,Stage 和 Job 的关系可以用下图表示。
以 Gitlab 中的某个实际的 Pipeline 为例解释 Pipeline,Stage,Job 的含义,具体请看下图。
MinIO 是一款分布式,高性能的对象存储服务,专为大型私有云环境而设计。MinIO 兼容 Amazon S3 对象存储接口,非常适合存储大容量的非结构化数据,例如图片、视频、日志文件、镜像等等。
本文将使用 MinIO 作为编译 Springboot 项目时使用的缓存,首次编译项目时会从 Maven 官网拉取依赖,之后会将依赖压缩后上传至 MinIo,在下一次编译时就可以直接从 MinIO 下载依赖文件。
安装 Helm
[7]。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 时设置的。
创建一个 Bucket,命名为 gitlab-runner-cache-maven 用于存放编译项目的依赖文件。
创建 Secret 保存 MinIO 的用户名和密码,之后 Gitlab Runner 容器会使用这个这个用户名和密码来登录 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 文件,有以下几个配置需要修改。
# 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/CD 通过 .gitlab-ci.yml
配置文件中定义流水线(Pipeline)的各个阶段(Stage),以及各个阶段中的若干作业(Job)。例如以下配置文件,我们定义了 3 个 Stage:
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/
注意事项:
-Dmaven.repo.local=cache
参数使用 cache 作为依赖仓库,这样就可以用上从 MinIO 中提取的依赖了。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"]
完成项目的开发之后,将代码推送到 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 执行完成。
点击 Pipeline 编号查看详情,可以看到里面有 3 个 Stage。
第一次 Pipeline 在 compile 阶段会比较慢,因为编译项目时需要从公网下载依赖,在编译完成以后会将 cache 目录中的依赖压缩打包后上传到 Minio,等到下一次编译就可以直接拿来使用,避免重复去公网下载,速度会加快不少。
在 MinIO 上可以看到 Gitlab Runner 上传上来的依赖的压缩文件。
build 阶段会从 compile 阶段中 artifact 设置的 target 目录中获取编译好了 jar 包构建 Docker 镜像。
deploy 阶段将镜像部署到 Kubernetes 环境中。
至此就完成了这一整套 Gitlab CI/CD 流程:
❯ kubectl get pod -n acp | grep elasticsearch
elasticsearch-api-7d9656cf5d-5sngv 2/2 Running 0 30s
elasticsearch-api-7d9656cf5d-9zgzt 2/2 Running 0 30s
elasticsearch-api-7d9656cf5d-tthx5 2/2 Running 0 30s
现在我们对代码做了修改,现在再次推送新的 tag 触发 Pipeline。
git add .
git commit -m "第二次触发任务"
git push
git tag 3.0.5
git push origin 3.0.5
可以看到这次在 compile 阶段并没有去公网下载依赖,而是直接使用从 Minio 下载了依赖。整个 Pipeline 流程也从 5 分钟缩短至 1 分钟。