第六章:实战应用 Go 项目 Gitlab CI/CD 搭建
发布时间 2023年11月23日 (更新时间 2024年4月1日) • 4 分钟 读完 • 822 字今天分享 Go 项目通过 Gitlab 上 CI/CD 快速完成项目部署。提供多种解决方案,让你的项目快速部署。

公司团队较小,没运维、没测试人员,每一次的版本发布都是一次次胆颤,生怕操作失误导致问题,后来为了不出错就将命令整理到了文档,一行一行执行,再后来将命令编写成为了bash脚本,ssh 登陆服务器手动执行,随着业务不断变化,服务器也有10来台了,这样也不是可靠的办法。
于是在2021年就借助 gitlab 生态,全服务全项目都上 gitlab CI/CD,每次打版打一个 tag 即可完成自动发版,根本上解决问题,避免失误,本文分享 Gitlab CI/CD 实现 Go 项目部署。

要讲 gitlab ci/cd 发布之前,我们先梳理一遍手动发布 Go 应用流程,常规流程如下:
使用 CI/CD 流程实现其实也就是将以上手动的操作交给 Gitlab Runner 执行,这里不再讲解有关 Runner 的安装参考官方文档 ,Glitab 的 Runner 通过编写 .gitlab-ci.yml 文件,
stages:
- build
- deploy
build:
image: golang:1.14
only:
refs:
- develop
- tags
stage: build
tags:
- backend
script:
- GOPROXY="https://goproxy.cn" GOOS=linux go build -ldflags "-s -w -X main.build=`date -u '+%Y-%m-%d_%I:%M:%S%p'` -X main.version=$CI_COMMIT_REF_NAME" -o bi server.go
artifacts:
name: "$CI_COMMIT_REF_SLUG"
when: on_success
expire_in: 1 week
paths:
- ./bi
deploy:preview:
image: itbing/rsync:2.1
stage: deploy
tags:
- backend
when: on_success
script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- rsync -rav -e "ssh -p $SSH_PORT -o StrictHostKeyChecking=no" --delete bi "$PREVIEW_SERVER_USER"@"$PREVIEW_SERVER":"$PREVIEW_PROJECT_PATH"
- ssh -p "$SSH_PORT" -o StrictHostKeyChecking=no "$PREVIEW_SERVER_USER"@"$PREVIEW_SERVER" "systemctl restart bi"
only:
refs:
- develop
dependencies:
- build
deploy:production:
image: itbing/rsync:2.1
stage: deploy
tags:
- backend
script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- rsync -rav -e "ssh -p $SSH_PORT -o StrictHostKeyChecking=no" --delete bi "$PRODUCTION_SERVER_USER"@"$PRODUCTION_SERVER":"$PRODUCTION_PROJECT_PATH"
- ssh -p "$SSH_PORT" -o StrictHostKeyChecking=no "$PRODUCTION_SERVER_USER"@"$PRODUCTION_SERVER" "systemctl restart bi"
when: on_success
only:
refs:
- tags
dependencies:
- build
environment:
name: production
url: http://bi.shenjumiaosuan.com下面是对这个配置文件的解释:
stages:定义了流水线中的不同阶段,包括 build 和 deploy。
build 阶段:
image:使用的 Docker 镜像,这里是 golang:1.14。only:指定只有在指定的 refs(引用)下触发构建,这里是 develop 分支和标签。stage:任务所属的阶段,这里是 build。tags:指定运行任务的 runner(执行者),这里是 backend。script:执行的脚本命令,用于构建应用程序。artifacts:指定构建成功后产生的构件,这里是将生成的二进制文件命名为 $CI_COMMIT_REF_SLUG 并保存到路径 ./bi,并设置了过期时间为一周。deploy:preview 阶段(部署测试环境):
image:使用的 Docker 镜像,这里是 itbing/rsync:2.1。stage:任务所属的阶段,这里是 deploy。tags:指定运行任务的 runner,这里是 backend。when:指定任务触发的条件,这里是在 build 阶段成功后触发。script:执行的脚本命令,用于将构建好的二进制文件部署到预览环境,并重启服务。only:指定只有在 develop 分支下触发部署。dependencies:指定任务依赖的其他任务,这里依赖于 build 阶段。deploy:production 阶段(部署生产环境):
image:使用的 Docker 镜像,这里是 itbing/rsync:2.1。stage:任务所属的阶段,这里是 deploy。tags:指定运行任务的 runner,这里是 backend。script:执行的脚本命令,用于将构建好的二进制文件部署到生产环境,并重启服务。when:指定任务触发的条件,这里是在标签推送后触发。only:指定只有在标签推送时触发部署。dependencies:指定任务依赖的其他任务,这里依赖于 build 阶段。environment:指定部署的环境信息,包括环境名称和 URL。这种方式的与上面方式不同的地方在于,Runner 将打包后二进制文件,上传到 docker hub,然后 ssh 登录到远端服务器,拉取 docker image 启动服务,以下是 yml 配置,
gitlab-ci.yml 文件配置参考
# You can copy and paste this template into a new `.gitlab-ci.yml` file.
# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword.
#
# To contribute improvements to CI/CD templates, please follow the Development guide at:
# https://docs.gitlab.com/ee/development/cicd/templates.html
# This specific template is located at:
# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Go.gitlab-ci.yml
variables:
CI_REGISTRY: docker.io
CI_REGISTRY_NAMESPACE: itbing
CI_REGISTRY_BASE_URL: index.docker.io
CI_CONTAINER_REGISTRY: app_client
CI_DOCKER_IMAGE_TAG: $CI_COMMIT_REF_NAME # 使用 Git 分支名称作为镜像标签
stages:
# - test
- build
- deploy
#format:
# image: golang:latest
# stage: test
# tags:
# - backend
# only:
# refs:
# - dev
# - tags
# script:
# - go env -w GOPROXY=https://goproxy.cn,direct
# - go mod tidy
# - go fmt $(go list ./... | grep -v /vendor/)
# - go vet $(go list ./... | grep -v /vendor/)
## - go test -race $(go list ./... | grep -v /vendor/)
compile:
stage: build
tags:
- shell
only:
refs:
- dev
- tags
script:
- docker login -u "$DOCKER_USERNAME" -p "$DOCKER_PASSWORD" $CI_REGISTRY # 登录 Docker Hub
- docker build --pull -t $CI_REGISTRY_NAMESPACE/$CI_CONTAINER_REGISTRY:$CI_DOCKER_IMAGE_TAG -f Dockerfile --build-arg CI_JOB_TOKEN=$PERSONAL_TOKEN .
- docker push $CI_REGISTRY_NAMESPACE/$CI_CONTAINER_REGISTRY:$CI_DOCKER_IMAGE_TAG # 推送 Docker 镜像
- docker image prune -f # 清除未使用的镜像
staging:deploy:
image: itbing/rsync:2.1
stage: deploy
tags:
- backend
only:
refs:
- dev
script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh -o StrictHostKeyChecking=no "$PREVIEW_SERVER_USER"@"$PREVIEW_SERVER" "cd $PREVIEW_PROJECT_PATH && docker-compose down --rmi all && docker-compose up -d"
when: on_success
dependencies:
- compile # 设置依赖关系,确保 compile 作业成功后再执行 staging:deploy
environment:
name: test
url: https://crm.sjmsdev.cn
sh:prod:deploy:
image: itbing/rsync:2.1
stage: deploy
tags:
- backend
only:
refs:
- tags
script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh -o StrictHostKeyChecking=no "$PRODUCTION_SERVER_USER"@"$PROD_SH_HOST_202" "cd $PRODUCTION_PROJECT_PATH && docker-compose down --rmi all && docker-compose up -d"
when: on_success
# dependencies:
# - compile # 设置依赖关系,确保 compile 作业成功后再执行 production:deploy
environment: production
jp:prod:deploy:
image: itbing/rsync:2.1
stage: deploy
tags:
- backend
only:
refs:
- tags
script:
- mkdir -p ~/.ssh
- echo "$SSH_PRIVATE_KEY" >> ~/.ssh/id_rsa
- chmod 600 ~/.ssh/id_rsa
- ssh -o StrictHostKeyChecking=no "$PRODUCTION_SERVER_USER"@"$PROD_JP_HOST_94" "cd $PRODUCTION_PROJECT_PATH && docker-compose down --rmi all && docker-compose up -d"
when: on_success
# dependencies:
# - compile # 设置依赖关系,确保 compile 作业成功后再执行 production:deploy
environment: productiondocker image prune -fFROM itbing/golang:1.21 AS compiler-stage
ARG CI_JOB_TOKEN
RUN echo -e "machine gitlab.shenjumiaosuan.com\nlogin gitlab-ci-token\npassword ${CI_JOB_TOKEN}" > ~/.netrc
WORKDIR /opt
COPY . .
RUN go env -w GOPROXY=https://goproxy.cn,direct
#RUN go fmt $(go list ./... | grep -v /docs/)
#RUN go vet $(go list ./... | grep -v /docs/)
#RUN go test -race $(go list ./... | grep -v -E '(vendor|docs|data)')
RUN go mod tidy && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -o app_client cmd/main.go
FROM ubuntu:latest AS build-stage
RUN apt-get -qq update \
&& apt-get -qq install -y --no-install-recommends ca-certificates curl
COPY --from=compiler-stage /opt/app_client /opt
WORKDIR /opt
CMD [ "/opt/app_client" ]配置解释:
早期因为这个项目引用 Gitlab 私有包,为了项目在构建时能 download 下来 gitlab 上的私有包,所以在使用了自制构建镜像 itbing/golang:1.21,目的是通过 gitlab token 拉取 gitlab 上私有包,如果没有这个需求可以使用 Go 官方 image。
Gitlab 构建 Go 项目私有化包参考这篇文章。
docker-compose.yml 配置文件
version: '3'
services:
go_client:
image: itbing/app_client:dev
container_name: go_client
ports:
- "8089:8089"
volumes:
- ./logs:/opt/storage
- ./config.yml:/opt/config/local.yml