【注意】最后更新于 November 23, 2023,文中内容可能已过时,请谨慎使用。
公司团队较小,没运维、没测试人员,每一次的版本发布都是一次次胆颤,生怕操作失误导致问题,后来为了不出错就将命令整理到了文档,一行一行执行,再后来将命令编写成为了bash
脚本,ssh 登陆服务器手动执行,随着业务不断变化,服务器也有10来台了,这样也不是可靠的办法。
于是在2021年就借助 gitlab
生态,全服务全项目都上 gitlab CI/CD
,每次打版打一个 tag
即可完成自动发版,根本上解决问题,避免失误,本文分享 Gitlab CI/CD 实现 Go 项目部署。
- PHP 项目 Gitlab CI/CD 参见链接:
- 前端 Vue3、React 项目 Gitlab CI/CD 参见链接:
1. Go 项目手动发版
要讲 gitlab ci/cd
发布之前,我们先梳理一遍手动发布 Go
应用流程,常规流程如下:
- 本地开发机打包出服务器二进制文件
- 使用 scp 或 ftp 工具上传至服务器
- 启动新服务替换旧服务
2. Gitlab Runner 构建二进制
使用 CI/CD 流程实现其实也就是将以上手动的操作交给 Gitlab Runner 执行,这里不再讲解有关 Runner 的安装参考官方文档 ,Glitab 的 Runner 通过编写 .gitlab-ci.yml
文件,
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
| 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。
3 Gitlab Runner 构建 Docker 镜像
这种方式的与上面方式不同的地方在于,Runner 将打包后二进制文件,上传到 docker hub,然后 ssh 登录到远端服务器,拉取 docker image 启动服务,以下是 yml 配置,
3.1 Gitlab YML 文件
gitlab-ci.yml
文件配置参考
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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
| # 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: production
|
Runner 所在机器每次构建 Docker 都会产生临时镜像,记得构建完成后及时清理无用镜像,否则磁盘很容易爆满,清理命令:docker image prune -f
3.2 Dockerfile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| FROM 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 项目私有化包[[go-gitlab-private-package]]。
3.3 Docker compose YML
docker-compose.yml
配置文件
1
2
3
4
5
6
7
8
9
10
| 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
|
参考文献