GitHub ActionsでRailsアプリをロリポップマネージドクラウドにデプロイする
ロリポップ!マネージドクラウド(以下マネクラ)へのRails(Ruby)アプリケーションのデプロイは、Herokuと同じようにgit push
をすることでbundle install
やDBのマイグレーションなどが自動で行われるようになっている。このウェブ日記もRailsで作っていて、そのデプロイをGitHub Actionsで自動化したのでそのやり方や試行錯誤の過程を記録しておく。
2020-05-02時点で利用している完全なYAML
「結論ははじめに」ということで、以下ようなYAMLと2つのsecrets(SSH_PRIVATE_KEYとLOLIPOP_REPOSITORY_URL)を用意すれば、masterへのpushをトリガーにしてマネクラにRailsアプリケーションがデプロイされる。
name: deploy
on:
push:
branches:
- master
jobs:
deploy_to_lolipop_mc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- uses: webfactory/ssh-agent@v0.2.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: add known_hosts
run: cat .github/workflows/known_hosts >> ~/.ssh/known_hosts
- name: git remote add
run: git remote add lolipop ${{ secrets.LOLIPOP_REPOSITORY_URL}}
- name: git remote update
run: git remote update
- name: deploy
run: git push lolipop HEAD:master
各stepを簡単に解説する。
- checkout@v2に
fetch-depth: 0
を設定する。デフォルトでは1となっていてgitの履歴をとってこない設定になっているため、マネクラ側にpushする際にnon-fast-forwardと判定されてしまう。デフォルトのままにしてgit push -f
とする手もあると思う。 - webfactory/ssh-agentを使ってsshの秘密鍵を使えるようにする。(後述)
- webfactory/ssh-agentはknown_hostsの管理はやってくれないので、リポジトリ上にknown_hostsをコミットしておいてそれを書き込む。(後述)
- マネクラのgitリポジトリをremoteに追加する。
- git pushのためには一度fetchしてくる必要があるので
remote update
する。 - マネクラのgitリポジトリにpushする。
今回は、テストの実行などはステップに含めていない。デプロイだけであればこれくらいのステップ簡単にできる。
webfactory/ssh-agent でsshの秘密鍵をメモリ上で管理する
SSHの鍵認証しか許していないサーバにGitHub Actionsの中からアクセスするためには、GitHub Actionsが動作している環境に秘密鍵を置く必要がある。 GitHub Marketplace · Actions to improve your workflowをsshで検索するといくつかsshができるようにするActionsがあることがわかるが今回はwebfactory/ssh-agentを利用した。
sshとその秘密鍵を利用可能にするActionsは大別すると以下の2種類がある。
- ssh-agentを実行する(webfactory/ssh-agent)
~/.ssh
に秘密鍵を書き込む
GitHub Actionsの実行モデルはよく理解できていないが、実行環境のローカルファイルシステム上に秘密鍵を書き込むよりはメモリ内に留められる方法のほうがよかろうと思い、このActionsを使うことにした。なお、公式のactions/checkoutでも秘密鍵を扱う仕組みはあって、ファイル名をランダムにして書き込むということをしている。
Secretsに登録する秘密鍵とURLを準備する
デプロイに使う秘密鍵は、GitHub Actions専用のものを用意したほうが安全だろう。マネクラではキーペアを簡単に作成・登録できる仕組みがあるので、この機能を使ってデプロイ用のキーペアを作っておく。Ed25519の鍵ができるので強度も安心。
ここで作成してダウンロードされた秘密鍵の中身をそのまま、GitHub側に SSH_PRIVATE_KEY
というsecretsに登録すればOK。
また、LOLIPOP_REPOSITORY_URL
という名前で、マネクラのコンパネに書かれているリポジトリ「ssh://xxxxxx@yyyy.lolipop.jp:port/
」を追加しておく。
Actionsの実行に必要な秘匿情報の追加はこれでおわり。
known_hostsをリポジトリに入れる
sshで初めて接続しようとしたときにプロンプトが出てくるアレのこと。何らかの方法でマネクラサーバのホストキーをknown_hostsにいれないとgit push
ができないのだ。webfactory/ssh-agentのREADMEでは、actionsの中でssh-keyscan
を使って書き込むか、この内容は秘匿情報ではないのでリポジトリにコミットするとよいと書かれている。このファイルの目的は、READMEにもあるとおり接続先のサーバが正しいものか、MITM攻撃にあっていないか、を検証するためのものなので、Actionsの中で毎回取得するよりは同じものを使ったほうが良いだろうと思い、コミットすることにした。
マネクラのコンパネからリモートリポジトリの情報を元に、以下のコマンドで実行することでホストキーを取得することができる。しかし、マネクラ側のSSHコンテナが起動していないと鍵がうまく取れないことがあるので、事前に別のターミナルなどでSSHをつないでおいたほうがいい。
ssh-keyscan -p <your port> -H <your ssh host>
上のコマンドを実行したときに、stderrに以下のような文字列が出てきたら、一度SSHしてから試してみるとうまくいく。
xxxx.mc.lolipop.jp: Connection closed by remote host
この問題のせいで、Actionsの中でssh-keyscan
するのはひと手間掛かりそう。
おわりに
GitHub Actionsを使ったマネクラへのデプロイは、sshの秘密鍵をどう管理したらいいかわからずに二の足を踏んでいたのだけど、改めて学習し直して自分なりに安全な方法を考えてみた。マネクラでもこれで継続的なデプロイをGitHubだけで完結させることができるので、かなり便利ではないかと思う。
この方法でも危険な部分などあったら、是非コメントやツイッターなどで教えてください。
おまけ
テックブログでのロリポ関連の記事を読んだことで、この記事を書くモチベーションがあがりました。@antipopと@linyowsに感謝!