在DevOps工作流中,CICD持续集成持续部署是重要的一环。GitLab CI/CD 是 GitLab 的一部分,因此不需要单独安装。 如果你已经在使用 GitLab 进行代码管理,GitLab CI/CD 无缝集成,直接在同一个平台上管理代码、审查合并请求、自动化测试和部署。 这种集成度简化了开发流程,使用起来非常方便。
前提条件:已经在使用Gitlab做代码管理、机器已安装Docker环境 目标:Docker搭建gitlab-runner,用gitgitlab-ci.yml脚本、Maven打包Springboot微服务、上传到Docker镜像仓库、远程部署到K8s集群
docker安装gitlab-runner 在Gitlab中Runner可以注册到 项目(Project)、群组(Group) 或 所有项目(All projects),这里注册到最大的,所有项目和组都可以用这个Runner
还需要查看当前在用的Gitlab的安装版本,选择一致的版本。避免gitlab-runner版本跨度过大导致出现问题。
1.先注册生成得到一个配置文件config.toml
1 2 // 注意替换成自己的版本号,然后回车,开始注册runner到gitlab流程 docker run --rm -it -v $(pwd )/runner-config:/etc/gitlab-runner gitlab/gitlab-runner:v16.11.3 register
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // 第一步:输入gitlab的访问地址即可 Enter the GitLab instance URL (for example, https://gitlab.com/): http://192.168.168.168:8888 // 第二步:输入 从gitlab管理页面上的复制token Enter the registration token: pAovXYzGHsy5EWF7zKiU // 第三步:输入runner名称 比如:docker-microservice-runner Enter a description for the runner: [94030fed2ba3]: docker-microservice-runner // 第四步:输入Tag 比如:blog Enter tags for the runner (comma-separated): blog // 第五步:输入说明备注 直接回车跳过 Enter optional maintenance note for the runner: WARNING: Support for registration tokens and runner parameters in the 'register' command has been deprecated in GitLab Runner 16.11 and will be replaced with support for authentication tokens. For more information, see https://gitlab.com/gitlab-org/gitlab/-/issues/380872 Registering runner... succeeded runner=pAovXYzG // 第六步:这个时候可以看到上面日志已经注册完成,选择执行器 docker Enter an executor: docker, parallels, virtualbox, docker+machine, docker-ssh+machine, instance, kubernetes, custom, docker-ssh, shell, ssh: docker // 第七步:选择默认基础镜像,这个镜像其实无所谓,因为我们在实际CICD中会指定镜像。这里就直接填 docker:24.0.2 Enter the default Docker image (for example, ruby:2.7): docker:24.0.2 Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded! Configuration (with the authentication token) was saved in "/etc/gitlab-runner/config.toml"
现在我们得到了配置文件,还需要修改一项重要配置(重要)!!!
因为我们使用Docker部署的Runner,也就是使用 Docker-in-Docker (DinD) 模式进行部署。 需要挂载/var/run/docker.sock,以允许容器内的 Docker 引擎访问宿主机的 Docker。
1 2 3 4 5 // 编辑配置文件 vim runner-config/config.toml // 修改volumes这一行,改为如下即可 volumes = ["/var/run/docker.sock:/var/run/docker.sock" , "/cache" ]
保存之后,整个runner的配置文件就准备完成了!
2.启动gitlab-runner 配置文件OK之后,就可以启动Runner了,执行如下命令(注意所在目录需要跟上面保持一致)
1 2 3 4 docker run -d --name gitlab-runner \ -v $(pwd )/runner-config:/etc/gitlab-runner \ -v /var/run/docker.sock:/var/run/docker.sock \ gitlab/gitlab-runner:v16.11.3
启动之后,就可以在Gitlab上看到 Runner了。
构建JAVA微服务CICD 整个流程:maven打包得到jar包 -> Dockerfile构建镜像并上传仓库 -> Kubectl部署到K8s集群 在这个过程中,牵扯到账号信息,为了安全方便可以配置成CICD变量。
配置CICD变量
DOCKER_REGISTRY docker地址 比如:registry.cn-hangzhou.aliyuncs.com/blog
DOCKER_REGISTRY_USER docker登录名
DOCKER_REGISTRY_PASSWORD docker密码
K8S_KUBECONFIG_TEST k8s测试集群的config文件
K8S_KUBECONFIG_PROD k8s生产集群的config文件
JAVA_MAVEN_SETTINGS maven的setting.xml配置
注意K8sconfig变量类型为 File 文件内容就是集群的KubeConfig
Maven的setting.xml文件如下,需要替换成自己的私服即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 <?xml version="1.0" encoding="UTF-8" ?> <settings xmlns ="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd" > <mirrors > <mirror > <id > nexus-aliyun</id > <mirrorOf > central</mirrorOf > <name > Nexus aliyun</name > <url > http://maven.aliyun.com/nexus/content/groups/public</url > </mirror > </mirrors > </settings >
增加部署文件 进入springboot项目仓库,创建 Dockerfile 、k8sdeploy_test.yaml 、k8sdeploy_prod.yaml 项目结构如下:
1 2 3 4 5 6 7 8 项目名称 ├── src/main │ └── java │ └── resources ├── pom.xml ├── Dockerfile ├── k8sdeploy_test.yaml ├── k8sdeploy_prod.yaml
1 2 3 4 5 6 FROM registry.cn-hangzhou.aliyuncs.com/blog/jre17MAINTAINER dollcodeARG JAR_FILECOPY ${JAR_FILE} app.jar EXPOSE 8080 ENTRYPOINT ["java" ,"-jar" ,"/app.jar" ]
k8sdeploy_test.yaml 内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 apiVersion: apps/v1 kind: Deployment metadata: name: APPLICATIONNAME namespace: blog spec: replicas: 1 selector: matchLabels: app: APPLICATIONNAME template: metadata: labels: app: APPLICATIONNAME spec: containers: - name: APPLICATIONNAME image: APPLICATIONIMAGE imagePullPolicy: Always env: - name: SPRING_PROFILES_ACTIVE value: test ports: - containerPort: APPLICATIONPORT --- apiVersion: v1 kind: Service metadata: name: APPLICATIONNAME-svc spec: ports: - port: APPLICATIONPORT protocol: TCP targetPort: APPLICATIONPORT selector: app: APPLICATIONNAME type: ClusterIP
k8sdeploy_prod.yaml 内容差不多 只是环境不一样,就不贴了。
编写CICD脚本 依次点击 CICD -> Editor 编辑脚本内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 stages: - maven_build - docker_build - deploy_k8s variables: PROJECT_PORT: "8080" PROJECT_VERSION: "1.0" MAVEN_CLI_OPTS: "-s /root/.m2/settings.xml" REGISTRY_TAG: "ci-$CI_PIPELINE_ID" REGISTRY_APPNAME: "$DOCKER_REGISTRY/blog/$CI_PROJECT_NAME" DEPLOY_ENV: "dev" K8S_NAMESPACE: "default" before_script: - mkdir -p .m2/repository ~/.kube - if [ "$CI_COMMIT_BRANCH" == "dev" ]; then export DEPLOY_ENV=test; mv $K8S_KUBECONFIG_TEST $HOME/.kube/config; elif [ "$CI_COMMIT_BRANCH" == "main" ]; then export DEPLOY_ENV=prod; mv $K8S_KUBECONFIG_PROD $HOME/.kube/config; else echo "This branch is not configured for deployment." ; exit 1 ; fi - export REGISTRY_APPNAME=$DOCKER_REGISTRY/blog_$DEPLOY_ENV/$CI_PROJECT_NAME; mavenjar_job: stage: maven_build image: maven:3.8.6-jdk-11 cache: key: global-cache paths: - .m2/repository policy: pull-push script: - mkdir -p ~/.m2 - echo "$JAVA_MAVEN_SETTINGS" > ~/.m2/settings.xml - mvn $MAVEN_CLI_OPTS clean package -DskipTests -Dmaven.repo.local=.m2/repository artifacts: paths: - target/$CI_PROJECT_NAME-$PROJECT_VERSION.jar tags: - blog container_job: stage: docker_build image: docker:24.0.2 script: - docker build --build-arg JAR_FILE="target/$CI_PROJECT_NAME-$PROJECT_VERSION.jar" --tag $REGISTRY_APPNAME:$REGISTRY_TAG --tag $REGISTRY_APPNAME:latest . - docker login -u "$DOCKER_REGISTRY_USER" -p "$DOCKER_REGISTRY_PASSWORD" $DOCKER_REGISTRY - docker push $REGISTRY_APPNAME:$REGISTRY_TAG - docker push $REGISTRY_APPNAME:latest tags: - blog deploy_job: stage: deploy_k8s image: name: bitnami/kubectl:1.28 entrypoint: ["" ] script: - sed -i s/APPLICATIONPORT/$PROJECT_PORT/ k8sdeploy_$DEPLOY_ENV.yaml - sed -i s/APPLICATIONNAME/$CI_PROJECT_NAME/ k8sdeploy_$DEPLOY_ENV.yaml - sed -i s%APPLICATIONIMAGE%$REGISTRY_APPNAME:$REGISTRY_TAG% k8sdeploy_$DEPLOY_ENV.yaml - kubectl apply -f k8sdeploy_$DEPLOY_ENV.yaml --namespace $K8S_NAMESPACE tags: - blog
部署 提交代码 或者 手动触发 部署完成!!!
踩坑汇总 Maven依赖缓存 如果没有设置依赖缓存,每一次都会重新拉取依赖jar。而Springboot的依赖是非常多的,每次拉取超级耗时。 这里有一个坑:gitlab CICD只能缓存项目工作空间下的目录。 所以需要创建.m2/repository 并且在maven build命令中指定目录
bitnami/kubectl无法执行命令 在deploy_job执行中,日志报错如下:
1 2 3 4 5 Using docker image sha256:be85f4da59a1c49507de843111579c94b9adf3 for bitnami/kubectl:1.28 with digest bitnami/kubectl@sha256:9657fd84779759711e59a51f4993567562 ... E0816 09:35:39.752914 1 run.go:120] "command failed" err="unknown command \"sh\" for \"kubectl\"\n\nDid you mean this?\n\tset\n\tcp\n" Cleaning up project directory and file based variables 00:01 ERROR: Job failed: exit code 1
因为bitnami/kubectl 镜像的默认入口点是 kubectl,因此执行 sh 命令时,它会认为这是一个 kubectl 的子命令,而不是一个 Shell 命令。 必须要显式覆盖镜像的入口点或命令,以便运行 sh。所以需要指定 entrypoint: [""]