0%

基于 gitea + drone + docker 的 CI 流程实践

gitea 安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: "2"
services:
web:
image: gitea/gitea
volumes:
- ./data:/data
ports:
- "3000:3000"
restart: always
db:
image: mariadb:10
restart: always
environment:
- MYSQL_ROOT_PASSWORD=xxxx
- MYSQL_DATABASE=gitea
- MYSQL_USER=gitea
- MYSQL_PASSWORD=xxxxx
volumes:
- ./db/:/var/lib/mysql

drone 安装(server + runner)(请参考官方教程)

官方教程:

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
version: "3"

services:
server:
image: drone/drone:1
container_name: drone_server
restart: always
environment:
DRONE_GITEA_SERVER: https://git.xxxx.cn
DRONE_GITEA_CLIENT_ID: "xxxxx"
DRONE_GITEA_CLIENT_SECRET: "xxxxx"
DRONE_RPC_SECRET: "xxxxx"
DRONE_SERVER_HOST: ci.xxxxx.cn
DRONE_SERVER_PROTO: https
DRONE_USER_CREATE: username:xxxx,admin:true
ports:
- "3001:80"
volumes:
- "./data:/var/lib/drone"

runner:
image: drone/drone-runner-docker
container_name: drone_runner_docker
restart: always
environment:
DRONE_RPC_PROTO: https
DRONE_RPC_HOST: ci.xxxx.cn
DRONE_RPC_SECRET: xxxxx
DRONE_RUNNER_CAPACITY: 5
DRONE_UI_USERNAME: happyxhw
DRONE_UI_PASSWORD: xxxxx
DRONE_RUNNER_NAME: xxxxx
DRONE_RUNNER_LABELS: role:xxxxx
ports:
- "8484:3000"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"

流水线配置(参考示例项目 .drone.yml)

push, pull_request,merge:

  1. linter: 执行 golangci-lint
  2. test
  3. build
  4. notification
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
kind: pipeline
name: drone-golang-example-dev-build

# 指定编译平台
platform:
os: linux
arch: amd64

# 指定代码空间,git代码会被clone到指定的path
workspace:
path: /drone/src

# type: docker, k8s, ssh, exec
type: docker
# 流水线
steps:
- name: linter
image: golang:latest # 编译用的镜像,最好在runner主机上提前下载好,否则会很慢,墙,一生之敌
pull: if-not-exists # 默认always,指定if-not-exists或never可以大幅度提高速度,pull在中国很费时
environment:
GOPROXY: "https://goproxy.cn,direct" # 懂的都懂
volumes: # 缓存 go mod & pkg,可以大幅度提高速度,避免每次都下载
- name: pkgdeps
path: /go/pkg
commands:
- go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.27.0 # golang下好评最高的linter工具,强迫症最爱
- golangci-lint run

- name: test
image: golang:latest
pull: if-not-exists
environment:
GOPROXY: "https://goproxy.cn,direct"
volumes:
- name: pkgdeps
path: /go/pkg
commands:
- go test -v ./...

- name: build
image: golang:latest
pull: if-not-exists
environment:
GOPROXY: "https://goproxy.cn,direct"
volumes:
- name: pkgdeps
path: /go/pkg
commands:
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build # 使用alpine务必要指定 GO_ENABLED=0

- name: notification # 钉钉通知,自行探索
image: lddsb/drone-dingtalk-message
pull: if-not-exists
settings:
token:
from_secret: dingtalk_token # 敏感数据请使用 from_secret
type: markdown
secret:
from_secret: dingtalk_secret
sha_link: true
message_color: true
when: # 即使流水线失败也能通知
status:
- success
- failure

trigger: # 触发条件,并关系
branch: # git 分支
- develop
- master
event: # 事件
- push
- pull_request

volumes: # 挂载,持久化数据
- name: pkgdeps
host:
path: /tmp/pkg

node:
role: xxxx # 指定标签为role:xxxx的runner执行流水线

Drone | Continuous Integration

![](基于 gitea + drone + docker 的 CI 流程实践/v2-f9e22bde7e07201d1568f2d952648a7f_b.jpg)

promote:

  1. linter: 执行 golangci-lint
  2. test
  3. build
  4. publish: 发布镜像
  5. scp: 复制部署文件到目标部署主机
  6. deploy: 拉取镜像,重启容器
  7. notification

注意:

我在docker-compose.yml中将镜像的tag设置为了环境变量${DOCKER_TAG},在开发环境中我喜欢将DRONE_COMMIT作为镜像的tag;在生产环境中我喜欢将DRONE_TAG(tag 事件触发生产环境部署)作为镜像tag,这样的好处是可以快速回滚到指定版本。
此外也可以将部署流程自动化到git事件中(master merge后自动部署生产环境,不需要手动promote)

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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

# 编译、部署开发镜像, 主动触发
kind: pipeline
name: drone-golang-example-dev-deploy

platform:
os: linux
arch: amd64

workspace:
path: /drone/src

type: docker
steps:
- name: linter
image: golang:latest
pull: if-not-exists
environment:
GOPROXY: "https://goproxy.cn,direct"
volumes:
- name: pkgdeps
path: /go/pkg
commands:
- go get github.com/golangci/golangci-lint/cmd/golangci-lint@v1.27.0
- golangci-lint run

- name: test
image: golang:latest
pull: if-not-exists
environment:
GOPROXY: "https://goproxy.cn,direct"
volumes:
- name: pkgdeps
path: /go/pkg
commands:
- go test -v ./...

- name: build
image: golang:latest
pull: if-not-exists
environment:
GOPROXY: "https://goproxy.cn,direct"
volumes:
- name: pkgdeps
path: /go/pkg
commands:
- CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build

- name: publish
image: docker/compose
pull: if-not-exists
environment:
USERNAME:
from_secret: aliyun_registry_username
PASSWORD:
from_secret: aliyun_registry_password
volumes:
- name: dockersock
path: /var/run/docker.sock
commands:
- tag=${DRONE_COMMIT} # 使用 DRONE_COMMIT 作为tag
- echo "publish"
- echo dev_$tag
- export DOCKER_TAG=dev_$tag
- docker login --username=$USERNAME registry.cn-hangzhou.aliyuncs.com -p $PASSWORD
- docker-compose build
- docker-compose push
-
- name: scp
image: appleboy/drone-scp
pull: if-not-exists
settings:
host:
from_secret: dev_host
username:
from_secret: dev_user
password:
from_secret: dev_password
port: 22
target: /home/xuhewen/drone-golang-example
source:
- docker-compose.yml

- name: deploy
image: appleboy/drone-ssh
pull: if-not-exists
settings:
host:
from_secret: dev_host
username:
from_secret: dev_user
password:
from_secret: dev_password
port: 22
script:
- tag=${DRONE_COMMIT} # 请预先在目标主机上执行 docker login
- echo "deploy"
- echo dev_$tag
- cd /home/xuhewen/drone-golang-example
- export DOCKER_TAG=dev_$tag
- docker-compose pull
- docker-compose stop
- docker-compose up -d

- name: notification
image: lddsb/drone-dingtalk-message
pull: if-not-exists
settings:
token:
from_secret: dingtalk_token
type: markdown
secret:
from_secret: dingtalk_secret
sha_link: true
message_color: true
when: # 即使流水线失败也能通知
status:
- success
- failure

volumes:
- name: pkgdeps
host:
path: /tmp/pkg
- name: dockersock
host:
path: /var/run/docker.sock

trigger:
event:
- promote
target:
- develop # 指定部署环境: dev,stage,production

when:
branch:
- develop

node:
role: xxxx

触发 promote:

![](基于 gitea + drone + docker 的 CI 流程实践/v2-c4a5ef1e6b0955dde4d9908d71ccd776_b.jpg)

Drone | Continuous Integration

![](基于 gitea + drone + docker 的 CI 流程实践/v2-296c70343b96b87b68340739ce602616_b.jpg)

成功部署:

![](基于 gitea + drone + docker 的 CI 流程实践/v2-88b6e0e8a778d8781224d27c5f66143a_b.jpg)

rollback:根据 DRONE_COMMIT 或 DRONE_TAG 快速回滚

  1. scp: 复制部署文件(clone时git已经切换到了需要回滚的版本)
  2. rollback: 根据 DRONE_COMMIT 或 DRONE_TAG 拉取指定tag的镜像进行回滚
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
 # 根据commit id回滚
kind: pipeline
name: drone-golang-example-dev-rollback

platform:
os: linux
arch: amd64

workspace:
path: /drone/src

type: docker
steps:
- name: scp
image: appleboy/drone-scp
pull: if-not-exists
settings:
host:
from_secret: dev_host
username:
from_secret: dev_user
password:
from_secret: dev_password
port: 22
target: /home/xuhewen/drone-golang-example
source:
- docker-compose.yml

- name: rollback
image: appleboy/drone-ssh
pull: if-not-exists
settings:
host:
from_secret: dev_host
username:
from_secret: dev_user
password:
from_secret: dev_password
port: 22
script:
- tag=${DRONE_COMMIT}
- echo "deploy"
- echo dev_$tag
- cd /home/xuhewen/drone-golang-example
- export DOCKER_TAG=dev_$tag
- docker-compose pull
- docker-compose stop
- docker-compose up -d

- name: notification
image: lddsb/drone-dingtalk-message
pull: if-not-exists
settings:
token:
from_secret: dingtalk_token
type: markdown
secret:
from_secret: dingtalk_secret
sha_link: true
message_color: true
when:
status:
- success
- failure

volumes:
- name: dockersock
host:
path: /var/run/docker.sock

trigger:
event:
- rollback
target:
- develop # 指定部署环境: dev,stage,production

when:
branch:
- develop

node:
role: xxxx

Drone | Continuous Integration

![](基于 gitea + drone + docker 的 CI 流程实践/v2-e7601b0c866cc762b64390f54a1751fb_b.jpg)

成功回滚到指定版本(’hello, to happyxhw’ 回滚到 ‘hi, from happyxhw’):

![](基于 gitea + drone + docker 的 CI 流程实践/v2-b8f3556bccb50089bb41de74bef436a0_b.jpg)

secrets:

![](基于 gitea + drone + docker 的 CI 流程实践/v2-6e17660da65691d41816a75557bd7e64_b.jpg)

镜像:

![](基于 gitea + drone + docker 的 CI 流程实践/v2-50cdbdf6a180f54a2d2b79dca451e781_b.jpg)

补充:

上面我只写出了开发环境中的CI流程,大家可以自行补充,比如加入预发布、生产环境的编译、部署和回滚流程,加入 master 分支、tag 事件的触发流程,具体请参考 drone 官方文档。