※ この記事は K-Ruby #25 のLT資料として書かれた記事です。
こんにちは!
先日、GMOペパボの東証一部上場が決まったことで**「東証一部上場の Web 系企業に未経験転職した29歳」**になって怪しさに磨きがかかりましたよしこ @k2_yoshikouki です。そろそろエンジニアになれる石売ります。
最近 yoshikouki.net という個人サイトを作っている最中で、勉強も兼ねて以下の要件で作っています
- Infrastructure as Code で環境構築
- Chef(ホスト内の実装について定義)
- Terraform(各ホストの関係について定義)
- AWS を使用
- EC2 に nginx (リバースプロキシ)を載せる
- ECS, EKSなどのコンテナサービスは使わない
- フレームワークは Next.js (React) を使用
- バックエンドは node.js
(しばらくは Next.js のルーティングを使用したベタ書きかなあと考えている) - CI/CD は GitHub Actions を利用
- バックエンドは node.js
苦節一週間の結果、GitHub Actions で自動デプロイ(Continuous Delivery)を実装できたのでその内容を紹介いたします。
Ruby? 知らない子ですね...
ゴール
GitHub Actions を使って AWS EC2 への自動デプロイを導入します
- テストがないのでCIの優先順位が低い
課題
-
AWS へのデプロイでいい感じにしてくれるサービスはECS, EKS関係が潮流で情報が豊富
(しかし、今回のコンテナ不使用という仕様には合わない) -
候補のデプロイサービスは AWS CodeDeploy や Capistrano がある
- CodeDeploy はAWS専用サービスかつ情報が少ない
- Capistrano は Ruby環境が必要なので、node.js な今回の仕様でスマートじゃない
-
コードの移設は rsync なりscp なりでできるが、プロセスの起動
next start
がややこしい- 色々試行しているとき、ワークフローで
ssh ~~~ "npm run start"
を実行してCI/CDが一生終わらないバグを埋め込んだ
(プロセスが待機状態になるため) -
nohup npm run start &
などで裏プロセスとして動かせたが、2度目以降のデプロイではプロセスが生きているのでエラーになる
-> 前回のプロセスを kill しないと行けない
- 色々試行しているとき、ワークフローで
対応策
- コード移設には rsync コマンドで対応
- Next.js のプロセス管理に PM2 を採用
GitHub Actions ワークフロー
name: Deploy to yoshiko.net
on:
push:
env:
ssh_key_path: ~/.ssh/yoshikouki.net.pem
app_path: /var/www/
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Cache multiple paths
uses: actions/cache@v2
with:
path: |
~/.npm
**/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- uses: actions/setup-node@v1
with:
node-version: '12.x'
- run: npm install
- run: npm build
deploy:
if: github.ref == 'refs/heads/main'
needs: build
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Generate SSH key
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ${{ env.ssh_key_path }}
chmod 400 ${{ env.ssh_key_path }}
eval "$(ssh-agent -s)"
ssh-add ${{ env.ssh_key_path }}
- name: Deploy with "rsync" command
run: |
rsync -avL --progress --exclude ".git/" \
-e "ssh -i ${{ env.ssh_key_path }} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no" \
./ \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }}:${{ env.app_path }}
- name: Build and Start Next.js
run: |
ssh -i ${{ env.ssh_key_path }} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no \
${{ secrets.DEPLOY_USER }}@${{ secrets.DEPLOY_HOST }} \
'cd ${{ env.app_path }} \
&& sudo npm install \
&& sudo npm install pm2 -g\
&& sudo npm run build \
&& pm2 startOrReload app.json --env production'
PM2 のプロセス設定ファイル
{
"name" : "app",
"script" : "./node_modules/next/dist/bin/next",
"env" : {
"NODE_ENV" : "development"
},
"env_production" : {
"NODE_ENV" : "production"
}
}
PM2 is 何
-
ADVANCED, PRODUCTION PROCESS MANAGER FOR NODE.JS
PM2 is a daemon process manager that will help you manage and keep your application online 24/7- "deamon process manager" が分からない方向けの良教材
Linux リテラシ - 第4回 デーモン
- "deamon process manager" が分からない方向けの良教材
-
node.js 向けのプロセス管理ツール
-
色々できる
-
今回は「Next のプロセスの起動か再読み込みをする」という動きをしてもらっている
-
-
Deployment Workflow 機能もあるので、rsync でやっているコード移設や npm 処理周りも PM2 で管理できるがまだやっていない
- PM2 deployment ドキュメント
https://pm2.keymetrics.io/docs/usage/deployment/ - このPullRequestで実装中だが、発表に間に合わなかった
- PM2 deployment ドキュメント
まとめ
GitHub Actions の便利アクションやコンテナ技術を使用することで、実際の処理やデプロイ先の環境を意識することなく簡単にCI/CDできることができますが、今回のこの実装で**「便利なラッパーが実際どういう処理をしているのか」**ということが学べました。
Capistrano のデフォルト挙動では、アップロードしたディレクトリへのシンボリックリンクを公開パスに配置するという頭良すぎる工夫を知ることもできて(なのでロールバックする場合は以前アップロードしたディレクトリにシンボリックリンクを張り直すだけ)、**「便利なライブラリをただ利用するだけでなく、その仕組がどうなっているのかまで理解すると応用できる」**というエンジニアとしての重要な気付きも得ることができたのでした。
P.S.
**Ruby on Rails で最速CDする記事**も上げましたので、そちらもよろしくお願いします。本来このLTでやろうと思っていた内容でした(やれよ)