本篇是一个 GitHub Actions 使用示例,实现在每次提交代码时自动更新你的远端服务器正在运行的项目
但是我并不会从头开始向你介绍 GitHub Actions 是什么,而要求你事先对它有所了解
你可以去看官方文档,去 B 站找视频看,网上有很多高质量的参考资料
简单地说就是 GitHub 可以在你更新代码或合并分支的时候开一个虚拟机帮你完成一些事情
又或者,你也可以去问 ChatGPT,哈哈😄
那为什么说是“简单的”呢?因为本篇只会举一个很简单的例子,不会涉及 docker ,也没有 k3s 集群
本篇的例子可以在我的仓库找到:https://github.com/NX-Official/github-actions-test
为了将重点放在部署上,我以下面的 main.go
为例,它只显示一个简单的 hello world
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package mainimport ( "github.com/gin-gonic/gin" ) func main () { r := gin.Default() r.GET("/" , func (c *gin.Context) { c.String(200 , "Hello World" ) }) r.Run(":8080" ) }
目前的项目结构如下:
1 2 3 4 ./ ├── go.mod ├── go.sum └── main.go
我该如何部署?
现在来思考一个问题,我该部署到远程服务器
看了一圈助手里面的项目,大概有两种方案:
不在虚拟机上编译,直接 ssh 连接服务器,执行命令或一个脚本文件来自动拉取最新代码,编译文件并完成替换
直接在 GitHub 的虚拟机上完成编译,得到二进制文件,然后把文件送到我们的服务器上(直接转送可能很慢,我看有的项目是先放到 OSS 上,然后用一个 webhook 通知守护进程,去拉取文件并完成替换)
我两种都会演示一下,先来第一种,使用 ssh 直接连接服务器,并执行下面的步骤:
使用 git pull
拉取最新代码
运行一个自动编译与重新启动的脚本
服务器端的准备
现在来动手实践,首先我们先将项目克隆下来(这里当然是我自己的项目)
1 git clone https://github.com/NX-Official/github-actions-test.git
然后尝试编译并执行(当然啦,你要先安装 golang)
1 2 3 4 cd github-actions-testgo mod tidy go build main.go ./main
看来是没问题的
现在,为了更方便地运行项目,我们可以将我们的程序注册为一个服务
1 2 cd /etc/systemd/system/nano github-actions-test.service
然后写入下面的代码(你需要根据自己的项目来修改名称与路径)
1 2 3 4 5 6 7 8 9 [Unit] Description=github-actions-test [Service] ExecStart=/root/github-actions-test/main Restart=always [Install] WantedBy=multi-user.target
然后使用下面的命令运行与查询状态
1 2 systemctl start github-actions-test systemctl status github-actions-tes
没有问题,继续下面的步骤
定义 Actions secrets
要连接到服务器,必定需要用户名和密码(当然你也可以使用私钥之类的,但这里为了简单我就使用密码)
但是密码这种东西你肯定不能直接写在 build.yml
中,你肯定不想让别人翻你的项目的时候翻到你服务器的密码吧~
所以 GitHub 就有了 Actions secrets 这个东西,你可以在这里定义运行时的环境变量,并只有项目参与者才能修改
在这里定义好三个变量,就可以在下面的脚本中使用了
编写脚本
为了方便,现在来为这个项目写一个重启服务的脚本,保存为 run.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #!/bin/sh serviceName="github-actions-test" systemctl stop $serviceName rm main -fexport PATH=$PATH :/usr/local/go/bingo mod tidy go build main.go systemctl start $serviceName systemctl status $serviceName
现在在项目中创建 .github/workflow
目录 ,再在里面建一个 build.yml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 name: Build on: push: branches: - main jobs: build: name: Build runs-on: ubuntu-latest steps: - name: 直接在服务器执行命令 uses: appleboy/ssh-action@master with: host: ${{ secrets.DEPLOY_HOST }} username: ${{ secrets.DEPLOY_USER }} password: ${{ secrets.DEPLOY_SECRET }} script: cd ~/github-actions-test && git pull && bash run.sh
这意味着将在 push
到 main
分支的时候,自动连接服务器并执行最下面的命令
现在来对 main.go
做一些修改,测试一下自动化部署对效果
1 2 3 r.GET("/" , func (c *gin.Context) { c.String(200 , "Hello World, I'm a Gin server" ) })
然后提交并推送,观察发现脚本已经成功运行
刷新页面,看见已经成功重新编译并启动服务
另一种方案
下面来试一下另一种方案,为了区分开来,我新建了一个 v2
分支
然后修改脚本
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 name: Build on: push: branch: - v2 jobs: build: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: Setup Golang uses: actions/setup-go@v3 with: go-version: '1.19' cache: true - name: Build run: go build main.go - name: Deploy env: SSH_USERNAME: ${{ secrets.DEPLOY_USER }} SSH_PASSWORD: ${{ secrets.DEPLOY_SECRET }} SSH_PORT: ${{ secrets.SSH_PORT }} DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }} DEPLOY_PATH: ${{ secrets.DEPLOY_PATH }} run: | # Stop the service sshpass -p $SSH_PASSWORD ssh -o StrictHostKeyChecking=no $SSH_USERNAME@$DEPLOY_HOST -p $SSH_PORT "systemctl stop github-actions-test && rm -rf $DEPLOY_PATH/main" sshpass -p $SSH_PASSWORD scp -r -o StrictHostKeyChecking=no -P 47 main [email protected] :/root/github-actions-test sshpass -p $SSH_PASSWORD ssh -o StrictHostKeyChecking=no $SSH_USERNAME@$DEPLOY_HOST -p $SSH_PORT "systemctl start github-actions-test"
Checkout
指的是拉取你的代码,然后后面设置 go 版本,编译,部署
同时修改一下 main.go
,这样可以对比出不同
然后提交,看看结果
十分顺利
这里我的例子是将编译和部署写在一个文件中的,其实你可以拆分成两个,这样更加规整,这个就留给你来折腾了😄