上一篇 『CI/CD』使用GitHub Actions实现简单的自动化部署 中实现了简单的自动化部署,本篇来介绍一下使用 docker 的做法 (NX 整整折腾了两天呢)
基本流程
首先要明确一下最终的运行流程,一共有4个步骤
更新代码,提交至GitHub
GitHub 触发 Actions,自动 build 镜像,并推送至镜像仓库
服务器拉取最新镜像
重新启动容器
因为今天时间有点紧我就不画图了(明天赶回家,要收拾东西)
本地原生运行测试
首先第一步要介绍一下我们的项目,还是上次的仓库:https://github.com/NX-Official/github-actions-test ,当然啦,现在是 v3
分支
为了方便我本次使用一个 go-zero 的 demo,因为它来生成 Dockerfile 比较方便
首先和文档一样创建一个 hello 服务
为了模拟可能的业务场景,我现在给这个 demo 增加两个要求:
我这个项目需要使用 mysql 和 redis 等服务,但是 docker 中的 mysql 会对性能造成影响,我可能需要直接使用宿主机上的端口与服务
本地测试环境和实际环境有不同,我希望到时候能从宿主机的某个目录读取配置文件,而不是打包在一起
所以我在这个 dome 中连接了 127.0.0.1
的 mysql 和 redis 服务,并从文件中读取配置,你可以在项目中看见
现在,我在 hello
目录下原生运行本项目,没有问题
本地打包&运行测试
接下来,用 docker 打包试试
使用 go-zero 的 goctl 工具可以方便地将当前项目打包(如果你不是 go-zero 你就得自己写 Dockerfile
了,有关教程烂大街,不属于本节内容)
在 hello
目录下一键生成 Dockerfile
1 goctl docker -go hello.go
为了方便与整洁,我选择使用 docker-compose 来构建运行,而不是在命令行手动折腾一堆参数
在项目根目录新建 docker-compose.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 version: '3.5' services: hello-api: build: context: . dockerfile: hello/Dockerfile environment: - TZ=Asia/Shanghai privileged: true ports: - "8888:8888" stdin_open: true tty: true
然后运行,你就会发现
在容器里是不能使用 127.0.0.1
来访问宿主机的本地服务的
这个问题可以看我的另一篇 『Docker』Docker内程序如何访问宿主机的端口 ,结论就是要把 127.0.0.1
换成 host.docker.internal
但是观察自动生成的 Dockerfile ,他是直接把配置文件打包进去了
所以要改一下,把那两行注释一下
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 FROM golang:alpine AS builderLABEL stage=gobuilder ENV CGO_ENABLED 0 ENV GOPROXY https://goproxy.cn,directRUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories RUN apk update --no-cache && apk add --no-cache tzdata WORKDIR /build ADD go.mod . ADD go.sum . RUN go mod download COPY . . RUN go build -ldflags="-s -w" -o /app/hello hello/hello.go FROM scratchCOPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt COPY --from=builder /usr/share/zoneinfo/Asia/Shanghai /usr/share/zoneinfo/Asia/Shanghai ENV TZ Asia/ShanghaiWORKDIR /app COPY --from=builder /app/hello /app/hello CMD ["./hello" , "-f" , "etc/hello-api.yaml" ]
然后运行时挂一下自己的配置文件,在根目录新建一个 etc
目录,在里面也弄一个 hello-api.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Name: hello-api Host: 0.0 .0 .0 Port: 8888 DBList: Mysql: Address: host.docker.internal:3306 Username: root Password: "12345678" DBName: "test" Redis: Address: host.docker.internal:6379 Password:
然后修改 docker-compose.yml
,加上两行,把目录映射进去
1 2 volumes: - ./etc:/app/etc
再运行,没有问题
打包上传&本机重新拉取测试
现在尝试一下将这个 image 上传到托管仓库,再在服务器拉取看看能不能跑
综合考虑,打算使用阿里云的容器镜像服务
前往 https://cr.console.aliyun.com/cn-hangzhou/instances 创建一个个人实例
然后随便创个命名空间
再在这个空间里创一个仓库
进入仓库可以看见一些提示
我们现在需要将镜像推送至阿里云,所以需要按照下面的步骤
1 2 3 $ docker login --username=***** registry.cn-hangzhou.aliyuncs.com $ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/****/github-actions-test:[镜像版本号] $ docker push registry.cn-hangzhou.aliyuncs.com/*****/github-actions-test:[镜像版本号]
然后我在这个测试项目中新建一个 assume_server_dir
目录,假设这里就是服务器上的目录
1 2 3 4 5 6 nx@NXsMacBook-Pro assume_server_dir % tree . . ├── docker-compose.yml └── etc └── hello-api.yaml
这里的 docker-compose.yml
就不能这样写了,因为这里不是构建,而是拉取,所以指定镜像地址即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14 version: '3.5' services: hello-api: image: registry.cn-hangzhou.aliyuncs.com/****/github-actions-test:latest environment: - TZ=Asia/Shanghai privileged: true volumes: - ./etc:/app/etc ports: - "8888:8888" stdin_open: true tty: true
之后把本地的容器、镜像全删掉,再运行这个 docker-compose
发现可以正常拉取镜像并运行
服务器拉取测试
好了,下一步就是在服务器上拉取并测试了
但是有个问题,我用的 M1 的 MacBook ,虽然 docker 是跨平台的,但是我这里构建的镜像是 arm 架构的,服务器上拿到运行会这样
所以我需要使用 docker buildx
重新编译并推送(参考文章:跨平台构建 Docker 镜像新姿势,x86、arm 一把梭 )
1 2 3 4 docker buildx create --use --name mybuilder docker buildx inspect mybuilder --bootstrap docker buildx ls docker buildx build -t registry.cn-hangzhou.aliyuncs.com/*****/github-actions-test:latest -f hello/Dockerfile --platform=linux/amd64 . --push
然后再在服务器拉取运行,但是又遇到了新的问题
然后我去谷歌,算是解决了问题
现在终于跑起来了
GitHub Actions打包&服务器拉取运行
好了,现在手动把环境都打通了,轮到使用 GitHub Actions 来自动化了
先来一个只推送到阿里云的版本,测试一下效果
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 name: Build on: push: branch: - v3 jobs: build: runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v3 - name: Docker meta id: meta uses: docker/metadata-action@v4 with: images: ${{ secrets.IMAGE_URL }} tags: | type=schedule type=ref,event=branch type=ref,event=pr type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=sha - name: Set up QEMU uses: docker/setup-qemu-action@v1 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 - name: Login to Aliyun Registry uses: docker/login-action@v1 with: registry: registry.cn-hangzhou.aliyuncs.com username: ${{ secrets.ALIYUN_REGISTRY_USERNAME }} password: ${{ secrets.ALIYUN_REGISTRY_PASSWORD }} - name: Build and push uses: docker/build-push-action@v2 with: context: . file: hello/Dockerfile push: true tags: ${{ steps.meta.outputs.tags }}
可以看见已经成功推送,就是 tag 打的不是 latest,这个随便了,我们在服务器也改成 v3 的版本就可以了
现在随便改动一下项目
1 2 3 4 5 func (l *HelloLogic) Hello(req *types.Request) (resp *types.Response, err error ) { return &types.Response{ Message: "If you can see this message, it means that the service is running successfully." , }, nil }
然后增加重启的命令
1 2 3 4 5 6 7 8 - name: Pull and run uses: appleboy/ssh-action@master with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} password: ${{ secrets.DEPLOY_SECRET }} port: ${{ secrets.SSH_PORT }} script: sudo docker login --username=${{ secrets.ALIYUN_REGISTRY_USERNAME }} -p ${{ secrets.ALIYUN_REGISTRY_PASSWORD }} registry.cn-hangzhou.aliyuncs.com && cd /home/admin/projects/github-actions-test && docker-compose pull && docker-compose up -d
当然啦,除了直接执行命令之外,你还可以使用 webhook 来通知服务器拉取最新镜像
至此,完成自动化部署