GitHub Actions から特定のサーバに SSH 接続してデプロイ作業を行う

Cloudflare Pages や Netlify、Vercel などであれば、GitHub 連携で自動的にデプロイができるが、IaaS にデプロイしてあるシステムを GitHub Actions から更新するのはさすがに無理か…?

と思ったら、GitHub Actions から SSH 接続できる Actions があった。

↑ のサンプルコードを見る限り、複数行のスクリプトも実行できるようだ。自分はとりあえず以下のように全て Secrets にしておいた。

name: Deploy SSH
on:
  push:
    branches:
      - main
  workflow_dispatch:
jobs:
  deploy-ssh:
    runs-on: ubuntu-latest
    steps:
      - name: Executing Remote SSH Commands Using SSH Key
        uses: appleboy/ssh-action@v1.0.3
        with:
          host    : ${{ secrets.SSH_HOST }}
          port    : ${{ secrets.SSH_PORT }}
          username: ${{ secrets.SSH_USERNAME }}
          key     : ${{ secrets.SSH_KEY }}
          script  : ${{ secrets.SSH_SCRIPT }}

仮に平文で書いたとしたら、こんな感じになるようにした。

- name: Executing Remote SSH Commands Using SSH Key
  uses: appleboy/ssh-action@v1.0.3
  with:
    host    : 140.0.0.0  # Public IP
    port    : 22
    username: admin
    key     : ${{ secrets.SSH_KEY }}  # ← SSH 秘密鍵の文字列
    script  : |
      whoami
      pwd
      ls -la
      bash ./deploy-script.bash

script の中に具体的なデプロイ用のコマンドを書いていくのではなくて、SSH 先に既に格納してある deploy-script.bash を叩くだけ、という風にしておいた。万が一 Secrets が漏れた時も「なんかスクリプトファイルを実行してるんだな」というところまでしか分からないようにしたかったので。


そんな deploy-script.bash の中では、次のように nohup& コマンドでバックグラウンド起動しておきたいウェブサーバがあった。

nohup node ./dist/main.js &

しかし、前述のように SSH 経由でスクリプトを実行すると、この行でバックグラウンド起動したはずのプロセスの出力が延々と続いてしまい、GitHub Actions が完了しなくなってしまった。

どうも標準出力だけでなく標準エラー出力も決めたファイルに逃がすように明記してあげないと、こういう呼び出し方の場合は詰まってしまうみたい。以下のようにすれば回避できた。

nohup node ./dist/main.js > ./nohup.out 2>&1 &

ついでに、nohup& でバックグラウンド起動したプロセスの ID (PID) は、$ echo $! で取得できることが分かった。つまり、

nohup node ./dist/main.js > ./nohup.out 2>&1 &
echo $! > ./pid.txt

こんな風に pid.txt に PID を書き出しておけば、後でバックグラウンドプロセスを終了したくなった時に $ ps aux | grep node なんてプロセス ID を調べたりせずとも、

kill "$(cat ./pid.txt)"
rm ./pid.txt

で簡単に kill もできるというワケである。


色々と Bash の細かな挙動も知れて楽しかった。