最近写了一些私有的 node.js 小项目,部署在群晖上。然而每次开发、修改完之后,都需要 ssh 到群晖上,然后执行一连串的 cd、git pull、pnpm install、pnpm build、pm2 restart 等命令,非常麻烦,于是想到了使用 Github Actions 来自动化部署。
当然,如果在本地写一个部署脚本,通过 ssh 远程命令的方式来实现也是可以的。但是这样的话,每次修改完代码之后,还需要手动执行一次脚本,这样终归没有直接 push 完之后自动部署来的方便,毕竟 push 这个操作是不可避免的。
由于之前用过 Github 的 self-hosted runner,所以觉得在群晖上操作起来也很容易。结果发现在 ./config.sh 阶段报错了,缺少一些依赖,无奈只能放弃。最后想到可以使用 ssh-action 来部署。
也有想到在群晖的虚拟机套件上安装一个完整的 Ubuntu,这样可以直接使用 self-hosted runner,但我的 Docker 程序都是直接运行在群晖上,感觉为了几个简单的 node.js 项目,另外折腾一个系统有点麻烦了。
前期准备
如果你的目标服务器是在公网上,可以跳过这一步。
由于群晖一般是在家庭局域网中,而 ssh-action 是在 Github 的服务器上执行的,所以需要先将群晖的 ssh 端口映射到公网上,这样 Github 才能访问到群晖。
要实现这个,可以有如下几个选择:
我是使用了 nps,需要借助一台公网服务器,在 nps 上开启一个 tcp 隧道,如此就可以用下面的方式来访问群晖了:
ssh -p 18888 username@your_public_ip
其中 18888
是 nps 服务端开启的端口,username
是群晖的用户名,your_public_ip
是公网服务器的 IP。
然而这里遇到了一个问题,使用 ssh 通过密钥登录时,这个密钥对到底是用公网服务器的还是群晖的呢?经过测试,发现是用的群晖的,也就是说,18888
这个端口仅仅是一个转发,用户名和密钥对都是群晖的,等于是给群晖换了一个端口和 IP 而已。
配置 ssh-action
我的项目用到了 express.js
、typescript
、pnpm
、pm2
等,所以部署过程涉及到了依赖安装、编译、重启服务等步骤。
假设项目在群晖的路径为 /var/services/homes/ovnrain/node-project/my-app
,我们可以这样配置:
name: deploy to synology
on:
push:
branches:
- deploy
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: deploy to synology
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.PRIVATE_KEY }}
port: ${{ secrets.PORT }}
script: |
cd /var/services/homes/ovnrain/node-project/my-app
git fetch
git checkout deploy
git reset --hard origin/deploy
pnpm i --ignore-scripts --frozen-lockfile
pnpm build
pm2 startOrRestart ecosystem.config.json
运行之后发现遇到了一个错误,提示 git、pnpm 等命令不存在,经过研究发现,是因为执行远程命令时,会使用非交互式的 shell,所以 PATH
环境变量不全,需要手动添加(ssh-action 有配置 envs 的选项,但我觉得在 script 中写会更加灵活一些)。
script: |
export PATH=/var/services/homes/ovnrain/.nvm/versions/node/v18.17.1/bin:/usr/local/bin:$PATH
cd /var/services/homes/ovnrain/node-project/my-app
# ...
由于我使用了 nvm 来管理 node.js 版本,所以添加了 /var/services/homes/ovnrain/.nvm/versions/node/v18.17.1/bin
使得在当前 node 版本中安装的 npm、pnpm 等命令可以被找到。/usr/local/bin
是为了可以找到 git 命令。
由于每个人的环境不一样,所以这里的 PATH
可能需要根据自己的情况来修改。你可以通过 which git
、which pnpm
等命令来查看命令的路径。
至此,ssh-action 部署到群晖的配置就可用了,以后每次 push 到 deploy 分支,就会自动部署到群晖上。
然而用了一会儿发现,假如以后升级了 node.js,比如使用了 19、20 版本,那么这里的 PATH
就需要修改,非常不方便。经过一番研究发现,可以直接把 nvm 官方的初始化脚本放到 script 中,这样无论以后使用哪个版本,都不需要再修改 PATH
了。
script: |
export PATH=/usr/local/bin:$PATH
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
cd /var/services/homes/ovnrain/node-project/my-app
# ...
或者更进一步,一般安装了 nvm 之后,.bashrc
已经添加了初始化脚本,所以可以直接使用 source ~/.bashrc
,这样连添加 git PATH 都不需要了。
script: |
source ~/.bashrc
cd /var/services/homes/ovnrain/node-project/my-app
# ...
至此,完整的配置如下:
name: deploy to synology
on:
push:
branches:
- deploy
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: deploy to synology
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.HOST }}
username: ${{ secrets.USERNAME }}
key: ${{ secrets.PRIVATE_KEY }}
port: ${{ secrets.PORT }}
script: |
source ~/.bashrc
cd /var/services/homes/ovnrain/node-project/my-app
git fetch
git checkout deploy
git reset --hard origin/deploy
pnpm i --ignore-scripts --frozen-lockfile
pnpm build
pm2 startOrRestart ecosystem.config.json
体验之后发现,使用 ssh-action 相比于 Github 的 self-hosted runner,有以下几个优点:
- 不需要在群晖上安装任何依赖,只需要开启 ssh 服务即可,不会污染群晖的环境。
- 你可以自由决定使用哪个程序、哪个版本。
- 项目路径可以放置在一个固定的地方。
- 可以使用
.env
文件来管理敏感信息,否则的话需要在 Github 上配置 secret,编辑删除等都不方便。
如果你的程序是一些简单的静态服务,实际运行不涉及 node_modules
、.env
等,或者你的机器配置很低,比如 1 CPU、512M 内存,难以支撑在自己的服务器上运行编译、构建等任务,还可以使用 appleboy/scp-action 或 burnett01/rsync-deployments 等,在 Github 的服务器上安装依赖、编译、打包,然后将最终的输出文件传输到自己的服务器上。
希望这篇文章能对你有所帮助。