レンタルサーバーにGitHub Actionsで自動デプロイ(rsyncするだけのはずが…)


動機

GitHubで管理、開発したコードを自動でレンタルサーバーにアップロードすると、かっこいい!(あと便利)
rsync叩くだけだから簡単やろ!w
 → 結果:数時間の沼にハマる(´;ω;`)

コピペできるような、ピンとくる記事がなかったので知見を共有します。

環境

レンタルサーバー:ColorfulBox(プランはBox1)
GitHub:適当なプライベートリポジトリ

免責

「日曜プログラマー」を自称している素人がゆるりと勉強した内容です。
内容を理解したうえで、自己責任でご使用ください。

記事中のコードはパブリックドメインとしてご自由にお使いください。

TL;DR

rsync --rsync-path=...

セットアップ

仕様

developブランチにpushされたら、実行する
 → developブランチの内容を取得する
 → サーバーへssh接続経由でrsyncコマンドを使い、/path/to/www-root/の内容を更新

事前準備

今回使用したColorfulBoxにはrsyncコマンドがインストールされていなかったので、インストールします。
しかし、root権限は与えられていないのでsudo apt install rsyncなど到底できません。かなしい。

そこで、

  1. サーバー上にソースコードを用意
  2. サーバー上でコンパイル
  3. コンパイル先にパスを通す

という作業を踏む必要があります。
依存関係の準備に手間取った気がしますが、基本的にはコマンドをたたくだけです。

こちらのサイトが新しくかつ詳しく、非常に参考になりました!
ColorfulBox に Rsync をインストールする - Atuweb

workflowを作成

以下のファイルをリポジトリの/.github/workflows/に追加します。

deploy2dev.yml
name: deploy to development env.

on:
  push:
    branches:
      - develop

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
        with:
          ref: develop

      - name: prepare ssh
        run: |
          touch /tmp/key && echo "$SSH_KEY" > /tmp/key
          chmod 700 /tmp/key
          touch /tmp/known_hosts && echo "$KNOWN_HOSTS" > /tmp/known_hosts
        env:
          SSH_KEY: ${{ secrets.SSH_KEY }}
          KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}

      - name: rsync deploy
        run: |
          rsync -rptgDvz --delete \
          -e "ssh -i /tmp/key -o UserKnownHostsFile=/tmp/known_hosts" \
          --rsync-path=~/.local/bin/rsync \
          --exclude='/.git/' --exclude='/.github/' \
          $GITHUB_WORKSPACE/ $USERNAME@$SERVER_ADDRESS:$DEVELOP_SERVER_DESTINATION
        env:
          DEVELOP_SERVER_DESTINATION: ${{ secrets.DEVELOP_SERVER_DESTINATION }}
          SERVER_ADDRESS: ${{ secrets.SERVER_ADDRESS }}
          USERNAME: ${{ secrets.USERNAME }}

こちらにも上げています。
https://gist.github.com/kokko-san/836bd530dc17c886446d5b2f85714b9f

secretを用意

https://github.com/[user or org name]/[repo name]/settings/secrets/actions

SSH_KEY
SSH用の秘密鍵
KNOWN_HOSTS
接続先の、想定されるknown host情報(詳しくは後述)
DEVELOP_SERVER_DESTINATION
デプロイ先のパス。e.g. /path/to/www-root/
SERVER_ADDRESS
SSH接続先のアドレス。e.g. example.com
USERNAME
SSHでログインするユーザー名

を用意します。

このような感じになります↓

完了!

これで、あとはdevelopブランチにpushすれば自動的にアップロードが始まるはずです。
mainブランチで動かしたいなどの応用も簡単に効くでしょう。


沼ポイントの解説

checkout

Git弱勢なのでcheckoutって何に使うねんという感じでしたが、これは「指定のリポジトリ(の指定のブランチ、コミットetc.)をcheckout(≒clone)してくれるaction」みたいです。

actionを実行し始めた段階では、新しくコンテナが展開されただけの状態なので、actionを登録しているリポジトリのファイルすら持っていません。
そこで、checkoutしてリポジトリの情報をとってきます。
(応用編として、他のリポジトリの情報もとってこれるようですが)

actionを登録しているリポジトリがプライベートリポジトリでも、checkoutするときに認可が必要ないのが今回の使用方法でのポイントでしょうか。

known_hosts

rsyncするときの-e引数を、

-e "ssh -i /tmp/key -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no"

という風に-o StrictHostKeyChecking=noを指定すれば、known_hostsを用意せずとも接続はできます。
ただ、デフォルトでaskなものをわざわざnoにするのは少し気持ちが悪いので、きちんと用意することにしました。

手元のbash環境で(今回はWSLのUbuntuを用いた)以下ようにssh-keyscanをして、

username@COMPUTER-NAME:~# ssh-keyscan -H example.com
# example.com:22 SSH-*.*-OpenSSH_*.*
|1|***...

出てきた出力すべてをリポジトリのSecretとしてコピペすれば多分よいでしょう。

非常に参考になりました↓

--rsync-path

こいつです。今回の主役 兼 悪役 兼 ヒーローです。

そもそもですよ、世の中には煩雑なrsyncのオプションやssh鍵の準備を自動化してくれるactionのリポジトリがあります。
e.g. Rsync Deployments Action

これを使えばカンタンじゃん!!!
と思ったはいいものの、実際に使ってみるとなぜか

bash: rsync: command not found

とログに残して死んでいきます。

おっかしいなぁと、自分でrsyncをたたくworkflowを用意してもcommand not found

手元でサーバーにsshしてwhich rsyncはちゃんと動くのに!なんで!!
と悩み続け、きちんとrsyncのお勉強をしたら出てきました。--rsync-pathオプション。

このオプションは、 「rsyncへのパスがローカルとリモートで異なる場合、リモートでのパスを指定するオプション」 です。

サーバーでwhich rsyncをたたいて出たパスを、--rsync-path=で指定するだけです。

正直、このオプションの存在を知らしめたいがためにこの記事を書いています。

改良できそうなポイント

--excludeというか、--exclude-fromというか

rsyncの--excludeに現在はどのリポジトリでもいらないであろう、/.git//.github/ディレクトリを指定していますが、実際にはさらにREADME.mdなどもっといらないファイルがあると思います。

今回のようにwebサーバー上に置く場合は.htaccessを使ってdeny from allもいい気がしますが、リポジトリにrsync-excludes.txtを用意してrsyncに--exclude-fromで指定するのもきれいな気がします。

--rsync-pathの自動設定

これってssh接続してwhich rsyncした内容を自動で設定すればもっとスマートなのでは?
未検証なので試してみたいと思います。


記事は以上です!
LGTMやコメント、編集リクエストなどお待ちしています!