GitHub Actionsでポートフォリオサイトのデプロイ自動化した

目次
  1. やりたいこと
  2. ソースコード
  3. 公式ドキュメント
  4. 前提条件
  5. 実装手順
  6. GitHubリポジトリにRepository Secretsを追加する
  7. デプロイ用のスクリプトを追加
  8. 動作チェック
  9. デプロイまでに詰まったところ
  10. serverlessのバージョンによっては正しく動作しない
  11. デプロイに使うIAMユーザーの権限が足りていない
  12. 実行時に「the input device is not a TTY」エラーが発生

前々から触ろう、触ろうと思っていて触れていなかったGitHub Actionsの話です。

先日Dependabotくんがプルリクを出していたので、そろそろ手を出すかと思い試してみました!

やりたいこと

  • GitHub Actionsを使ってmainブランチにpushされたら自動でNext.jsアプリがAWSにデプロイされるようにしたい
  • AWSへのデプロイにはserverless-nextを使う
  • デバッグで使っているコンテナをそのままデプロイにも流用したい(作成済みのDockefile, docker-compose.ymlを流用したい)

ソースコード

下記ソースコードがローカルにcloneされていることが前提で記載をしていますのでご注意ください。

https://github.com/TansanMilMil/my-portfolio

公式ドキュメント

https://docs.github.com/en/actions

前提条件

  • デプロイ対象のNext.jsアプリは既に作成されている
  • Dockerfile, docker-compose.ymlが用意されている
  • AWSへのデプロイ用のIAMユーザー、アクセスキー、シークレットアクセスーは生成されている。またデプロイに必要なポリシーはアタッチ済み。

実装手順

GitHubリポジトリにRepository Secretsを追加する

AWSへデプロイするには認証情報(アクセスキー、シークレットアクセスキー)が必要ですが、gitの管理下にキーをそのまま置いてしまうのはセキュリティ面で大変危険です。

GitHubでは特定のリポジトリのRepository Secretsにキーを定義しておくことで、リモートリポジトリとは別の領域に安全にキーを配置できます。

後でデプロイ時に参照できるように AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY というNameのSecretsを定義しました。ValueにはそれぞれIAMユーザーのキーを指定します。

デプロイ用のスクリプトを追加

ここからはローカルでの作業になります。VSCode等のお好みのエディタでデプロイ用の設定を記述していきます。

まずルートパスに.githubディレクトリ、さらにその内部にworkflowsディレクトリを追加し、空のgithub-actions.ymlを配置します。

$ mkdir .github
$ mkdir .github/workflows
$ touch .github/workflows/github-actions.yml

github-actions.ymlにはGitHub Actionsが実行される条件、実行環境、実行する処理を記述していきます。

.github/workflows/github-actions.yml

name: GitHub Actions
# GitHub Actionsの実行開始条件を指定。mainブランチにpushされた時に実行されるようにしている。
on: 
  push:
    branches:
      - 'main'
env:
  # デプロイ時のホストOSの環境変数を定義。GitHubのRepository Secretsを参照させる。
  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
jobs:
  deploy-aws:
    # ホストOS
    runs-on: ubuntu-latest
    # 実行したい具体的な処理をstepsに記述する。
    steps:
      - uses: actions/checkout@v2
      # docker-compose.ymlがaws-config.env経由でAWSの認証情報を参照する為、ホストOSの環境変数をenvファイルに追記させている。
      - run: echo AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID >> aws-config.env
      - run: echo AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY >> aws-config.env
      - run: docker-compose up -d
      - run: docker-compose exec -T nextjs npm install
      # コンテナ内からデプロイを実施する。
      - run: docker-compose exec -T nextjs serverless

GitHub Actionsではデフォルトでdocker-composeが動作します。これはかなり嬉しいですね!

今回のアプリは元々コンテナを立ち上げて、コンテナ内からデプロイをする構成だったので、元々のソースコードをほぼ弄ることが無く、github-actions.ymlを追加するだけでOKでした。

動作チェック

これで準備は整ったので、適当にmainブランチへpushして動作するか確認してみます。

うまく処理が完了すれば緑のチェックマークが表示されているはずです。(スクリーンショットには試行錯誤の跡がありお見苦しいですが気にしないでください…🤤)

たったこれだけでCI/CD環境が構築できるのはやはり便利ですね。普段よく使うAWS CodeBuildと比べるとかなりお手軽に感じました。

表示されているworkflowの名前をクリックするとデプロイ時の標準出力などの詳細情報も確認できますので、処理に失敗した場合は原因を突き止めるヒントになります。

デプロイまでに詰まったところ

↑までは淡々と書きましたが、実際のところはちゃんと動作させられるまでに数時間はかかりました…。

serverlessのバージョンによっては正しく動作しない

コンテナ内でserverlessを実行した際に Serverless Components CLI v1 is no longer bundled with Serverless Framework CLI というメッセージが表示され、デプロイが実行されない現象が発生。

serverless-nextのIssuesにも同様の報告が挙がっていました。

https://github.com/serverless-nextjs/serverless-next.js/issues/2320

2022/1/29時点ではserverlessのバージョン2.72.2に指定することで回避できました。 Dockerfileにnpm install -g serverless@2.72.2と記述しているのはこの為です。

デプロイに使うIAMユーザーの権限が足りていない

serverless-nextは自動でCloudFront, Lambda, S3, Route53周りの設定を生成、更新してくれるのですが、その辺りの処理が開発者には隠蔽されているので、権限の適用漏れが結構ありました。

例えば、iam:PassRoleの権限が無いIAMユーザーだと下記のようなエラーが標準出力されます。

AccessDeniedException: User: arn:aws:iam::**********:user/github-actions is not authorized to perform: iam:PassRole on resource: arn:aws:iam::**********:role/********** because no identity-based policy allows the iam:PassRole action

権限漏れはかなり具体的に権限名が出力されますので、それを元にIAMユーザーの権限を調整していって対応しました。

実行時に「the input device is not a TTY」エラーが発生

github-actions.ymlのjobsでdocker-compose execを実行する場合、オプションとして-Tを付けないと、実行時にこのエラーが発生します。

docker-compose execはオプション無しだとデフォルトで疑似TTYを割り当てようと試みますが、GitHub Actionsの実行環境ではTTYがそもそも無いためにエラーが発生する模様。

TTYについては正直抽象的な理解しかできていないので、改めて別記事でピックアップしようかと思います。