Docker + Github Actions 自动化部署#

[TOC]

前言#

简明 Docker + GitHub Actions 自动化部署教程 | NX の 博客 (nickxu.me)

本文主要是跟着NX进行操作如何实现自动化部署,gin-ruh-template也是直接抄NX的,对所以这个博客就是记录一下,记录一下自动化部署的时候遇到的问题

以及记录一下自己尝试过这个东西,对、就是这样

运行项目#

要部署想要保证整个项目是可以运行的,对这个操作傻子都会

其实我的copy nx的仓库里面已经是完全版的代码了,不需要干这些事情

1
2
3
4
5
6
7
8
# 复制一份配置文件
cp config/config.example.yaml config/config.yaml

# 运行 MySQL 依赖
docker-compose -f docker-compose-env.yml up -d

# 运行 Go 程序
go run main.go
1
2
3
4
curl http://127.0.0.1:8080/ping
{"message":"pong"}
# 或者直接访问 http://localhost:8080/ping
# 不过你直接访问浏览器会让我觉得你很蠢,好吧我也蠢

手动部署#

大致流程#

  1. 本地把项目编译成镜像
  2. 把镜像推送到docker hub
  3. 在自己的服务器上拉取镜像

安装Docker 与 Docker-compose#

具体怎么安装详见官方文档或者GPT

以下两个命令测试是否安装成功

1
2
docker -v
docker-compose -v

编译Docker Image#

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM golang:latest as go-build-stage

ENV GOPROXY https://goproxy.cn,direct

WORKDIR /go/src/app

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

FROM scratch

WORKDIR /app

COPY --from=go-build-stage /go/src/app/main .

CMD ["./main"]

注意这里的’v1’ 其实是 ‘ tag ‘,可以理解为版本,后面就知道了

1
docker build -t gin-rush-template:v1 .

检查是否编译成功

1
2
3
4
docker images

REPOSITORY TAG IMAGE ID CREATED SIZE
gin-rush-template v1 c21723202467 About a minute ago 21.1MB

推送至Docker Hub#

这里其实跟github差不多,只不过这里放的都是你编译好的镜像

DockerHub 中镜像的名字格式一般为:

1
<username>/<appName>:<tag>

比如我的DockerHub用户名为ccstudyhyc,项目的名称为gin-rush-template,因为是第一次编译一般来说就是第一个版本,写成v1,当然也有最新版,写成latest

因此我的镜像格式就为

1
ccstudyhyc/gin-rush-template:v1

而我们目前的镜像并没有用户名,所以需要使用 docker tag 重新命名

1
docker tag gin-rush-template:v1 ccstudyhyc/gin-rush-template:v1

现在我们就会得到一个镜像,它与之前的镜像指向同一个ID,说明这是同一个镜像,但是名字改了而已,ID都为 c172302467

1
2
3
4
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
gin-rush-template v1 c21723202467 14 minutes ago 21.1MB
nxofficial/gin-rush-template v1 c21723202467 14 minutes ago 21.1MB

然后我们推送镜像(当然前提是我们登录了,本文忽略登录流程)

1
2
3
4
5
docker push nxofficial/gin-rush-template:v1
The push refers to repository [docker.io/nxofficial/gin-rush-template]
c84414e107f0: Pushed
3b8bd6ab22a4: Pushed
v1: digest: sha256:4d1b5f800ba3ec7022fc9dec77ec1e8fd9d2dc103825373ab2dec2c3b6015fb5 size: 734

现在在Docker Hub上就可以看到我们的镜像了

image-20240528222042099

创建服务器#

服务是要在服务器中运行的,自己去阿里云或者其他地方随便买一个,我是白嫖的(试用期)

要做以下准备

  1. 下载安装 dockerdocker-compose

  2. 开放端口,不开放端口外来用户访问不到你(远程连接的时候需要配上ssh)

    哪里可以找到ssh,就自己翻文档吧

image-20240528222658423

  1. 如果你不知道怎么干,那你就问gpt

上传deploy文件#

image-20240528222942890

对就是上面这两个文件,我们就是要上传到服务器

  1. 首先,我们需要能访问服务器

    所以你就需要获得所谓的ssh文件,我的方法是把ssh文件放在根目录下,找起来好找一点,纯粹是。

  2. 其次我们要确保服务器中有对应的路径让我们上传,path自己随便写

    1
    mkdir /home/deploy/gin-rush-template
  3. 用命令上传

1
scp -i ssh的路径 ./deploy/* root@ip地址:/home/deploy/gin-rush-template
1
2
3
4
5
6
Mysql:
Host: "mysql" // 这里写成 mysql 而不是"127.0.0.1"
Port: "3306"
Username: "root"
Password: "12345678"
DBName: "gin-rush-template"

容器服务发现:在使用 Docker Compose 时,所有定义在 docker-compose.yml 文件中的服务都会自动创建一个内部网络,并且服务名可以作为网络中的别名使用。这意味着,如果你有一个名为 mysql 的服务,你可以在应用配置中使用 "mysql" 作为主机名来连接到这个 MySQL 服务,而不需要使用实际的 IP 地址。

为什么要有两个config,docker-compose

NX的解释:你本地肯定不会在 docker 里跑哇,所以你起了依赖的 compose ,然后自己go run 或者调试之类的,你 config 的依赖肯定是本地的端口,然后你要部署的时候,用 VSCode 连到服务器上,把 deployment 目录复制过去,直接 docker-compose 一起就完了 ~~妙

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
version: '2.1' # 之所以不用3是因为3砍了好用的健康检查而逼你用没人用的 Docker Swarm

networks:
gin-rush-template-net:
driver: bridge

services:
app:
image: nxofficial/gin-rush-template:v1 # 这里后面被改成了 latest,但是先使用 v1 体验一下
container_name: gin-rush-template-app
volumes:
- ./config.yaml:/app/config/config.yaml # 将配置文件映射进来
ports:
- "8080:8080"
depends_on:
mysql:
condition: service_healthy
networks:
- gin-rush-template-net

mysql:
image: mysql:8.0
container_name: gin-rush-template-mysql
environment:
MYSQL_ROOT_PASSWORD: 12345678
MYSQL_DATABASE: gin-rush-template
TZ: Asia/Shanghai
healthcheck:
# MySQL 就绪检测
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
interval: 5s
retries: 10
privileged: true
restart: always
networks:
- gin-rush-template-net

image: nxofficial/gin-rush-template:v1 # 这里后面被改成了 latest,但是先使用 v1 体验一下

注意这句话,后面要用到

服务器中运行#

这里总共就三个命令

注意 在实行 docker-compose 的时候,一定要在 deploy的两个文件所在目录总,要不然无法使用

1
sudo docker-compose up  # 启动
1
sudo docker-compose down # 停止
1
curl localhost:8080/ping # 测试响应
1
2
3
4
5
6
7
8
9
10
11
$ sudo docker-compose up
WARN[0000] /Users/nx/GolandProjects/gin-rush-template/deploy/docker-compose.yaml: `version` is obsolete
[+] Running 2/0
✔ Container gin-rush-template-mysql Created 0.0s
✔ Container gin-rush-template-app Created 0.0s
Attaching to gin-rush-template-app, gin-rush-template-mysql
gin-rush-template-mysql | 2024-03-22 21:21:20+08:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.36-1.el8 started.
gin-rush-template-mysql | 2024-03-22 21:21:20+08:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
gin-rush-template-mysql | 2024-03-22 21:21:20+08:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.36-1.el8 started.
gin-rush-template-mysql | '/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
#...............

在服务器端多开一个实例,运行测试

1
2
curl 127.0.0.1:8080/ping
{"message":"pong"}

外部测试:

1
2
curl http://ip:8080/ping
{"message":"pong"}

实现自动化部署#

主要依靠于 deploy/docker-compose.yml中的 Watchtower

1
2
3
4
5
6
7
8
9
watchtower:
image: containrrr/watchtower
container_name: gin-rush-template-watchtower
command: --interval 5
volumes:
- /var/run/docker.sock:/var/run/docker.sock # 需要将 Docker 的 sock 映射进去以控制 Docker
restart: always
networks:
- gin-rush-template-net

同时,把 deploy/config.yaml 中的 image 改成 (这个很重要)

1
2
- image: nxofficial/gin-rush-template:v1
+ image: nxofficial/gin-rush-template:latest

最后再本地编译 image并推送,记得编译两个,一个为最新版

1
2
3
4
5
# 构建镜像并添加多个标签
docker build -t nxofficial/gin-rush-template:v2 -t nxofficial/gin-rush-template:latest .
# 将两个标签都推送过去
docker push nxofficial/gin-rush-template:latest
docker push nxofficial/gin-rush-template:v2

在不断的迭代中,:latest就会不断更新

🆗,现在并不是NX博客中的重新启动 docker-compose

而是将刚才修改过的编译镜像重新推送到 Github Hub,然后让服务器用新推送的镜像进行运行

因为之前我们运行的镜像中的配置为image: nxofficial/gin-rush-template:v1,但是这个配置并不能给我们自动更新:latest的版本,只会停留在v1版本中无法自拔

最后看到这个 gin-rush-template-app exited with code 2就代表他自动更新了

1
2
3
4
5
6
gin-rush-template-watchtower  | time="2024-03-22T13:57:01Z" level=info msg="Session done" Failed=0 Scanned=3 Updated=0 notify=no
gin-rush-template-watchtower | time="2024-03-22T13:57:21Z" level=info msg="Found new nxofficial/gin-rush-template:latest image (e3283961517d)"
gin-rush-template-watchtower | time="2024-03-22T13:57:36Z" level=info msg="Stopping /gin-rush-template-app (cbfe32e3ed41) with SIGTERM"
gin-rush-template-app exited with code 2
gin-rush-template-watchtower | time="2024-03-22T13:57:37Z" level=info msg="Creating /gin-rush-template-app"
gin-rush-template-watchtower | time="2024-03-22T13:57:38Z" level=info msg="Session done" Failed=0 Scanned=3 Updated=1 notify=no

最后最好再curl一下,这边不演示了,详细见NX的博客

Github Action#

这个主要是实现 在push的时候自动编译上传

具体原理就是 push -> 重新编译成镜像 -> 推送到Docker Hub -> 自动更新

所以我们要创建一个文件夹

image-20240528231030402

下面的东西一个字都不用改,因为我也不会改

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
name: Build and Push Docker Image

on:
push:
branches:
- main # 指定触发事件的分支,这里是 main 分支

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Check out the code
uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

# 记得在 GitHub 仓库的 Secrets 中添加 DOCKER_USERNAME DOCKER_PASSWORD DOCKER_REPOSITORY 三个环境变量
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}

- name: Set short SHA
id: shortsha
run: echo "SHORT_SHA=$(echo ${{ github.sha }} | cut -c1-8)" >> $GITHUB_ENV

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
# 使用 commit hash 作为镜像 tag
tags: |
${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:latest
${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_REPOSITORY }}:${{ env.SHORT_SHA }}
platforms: linux/amd64,linux/arm64 # 为多个架构编译,如果你确定只需要其中一种,可以仅保留一种

同时前往 Github 上配置环境变量

image-20240322102120002 PM

DOCKER_USERNAME = ccstudyhyc

DOCKER_PASSWORD = YOUR_PASSWORD

DOEKR_REPOSITORY = gin-rush-template

参考我的答案,进行修改

最后推送

image-20240528231401624

这就成功了,如果不放心,再去服务器上试验一下就行了

总结#

总的过程其实还是挺坎坷的,从中午做到现在 2024-5-27-23:15,非常长的时间,最大的感受是自己的理解能力以及阅读文档能力,NX的博客写的已经很详细了,但是总是因为某些原因卡住,所以这篇博客其实是夹杂着 自我的错误 + 大致自动化部署的流程 两个过程的

大功告成❤️

常用命令行#

1
scp -i ./echin.pem ./deploy/* root@ip:/home/deploy/gin-rush-template
1
docker push ccstudyhyc/gin-rush-template:v3
1
docker build -t ccstudyhyc/gin-rush-template:v3 -t ccstudyhyc/gin-rush-template:latest .
1
docker tag gin-rush-template:v1 ccstudyhyc/gin-rush-template:v1
1
2
3
docker images
docker -v
docker-compose -v
1
curl http://127.0.0.1:8080/ping
1
sudo docker-compose up 
1
sudo docker-compose down
1
rm -f * # 删除当前文件夹下的所有文件
1
cd /home/deploy/gin-rush-template
1
mkdir -p /home/deploy/gin-rush-template
1
docker pull ccstudyhyc/gin-rush-template:v1
1
# 以及安装	docker 和 docker-compose 的一堆记不住的命令