$shibayu36->blog;

株式会社はてなでエンジニアをしています。プログラミングや読書のことなどについて書いています。

Github Actionsをcronとして利用し、notify-issues-to-slackを動かす

以前、レビュータイムや定期的なissueチェックのためにGithubのissueを検索してSlackに投稿するCLIツールを作ったで紹介したnotify-issues-to-slackだが、これまでは

  • 適当に立てたサーバーのcronで動かす
  • Jenkinsなどで実行する

という方法で動かしていた。

しかし、issueをslackに通知するためだけにサーバーを立てたりするのもだるい。そこでGithub Actionsで動かしてみた。

Github Actionsを動かすレポジトリのみを対象とする場合

secrets.GITHUB_TOKENを使えば、そのレポジトリへアクセス可能なので、何も設定せずとも以下のように設定できる。

.github/workflows/notify-issues.yml

name: daily bug report

on:
  schedule:
    - cron: 30 12 * * 1-5 # UTC 12:30 on weekdays

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: daily bug report
        env:
          # You can use secrets.GITHUB_TOKEN if you
          # don't have to access other repositories
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          docker pull shibayu36/notify-issues-to-slack
          docker run --rm shibayu36/notify-issues-to-slack \
            -github-token=${GITHUB_TOKEN} \
            -slack-webhook-url=... \
            -query='repo:shibayu36/notify-issues-to-slack state:open label:"bug"' \
            -text="Please check bug reports @shibayu36" \
            -channel="bug-report-channel"

ポイントとしては、on schedule cronはUTCで設定する必要がある、くらい。

別レポジトリも含めてissue検索し、通知したい場合

Personal access tokenを発行して、secretsに登録すれば良い。

.github/workflows/notify-issues-with-personal-access-token.yml

name: daily bug report

on:
  schedule:
    - cron: 30 12 * * 1-5 # UTC 12:30 on weekdays

jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - name: daily bug report
        env:
          # You have to use personal access token if you
          # want to access other repositories.
          # Create secrets ( named NOTIFY_ISSUES_GITHUB_TOKEN in this example ) in repository settings, and use it.
          GITHUB_TOKEN: ${{ secrets.NOTIFY_ISSUES_GITHUB_TOKEN }}
        run: |
          docker pull shibayu36/notify-issues-to-slack
          docker run --rm shibayu36/notify-issues-to-slack \
            -github-token=${GITHUB_TOKEN} \
            -slack-webhook-url=... \
            -query='repo:shibayu36/notify-issues-to-slack state:open label:"bug"' \
            -text="Please check bug reports @shibayu36" \
            -channel="bug-report-channel"

まとめ

今回はGithub Actionsでnotify-issues-to-slackを動かしてみた。こういうツールはコンテナイメージを作りDocker Hubとかに公開しておけば、シュッとGithub Actionsで利用することが出来て便利だった。

参考

Next.jsアプリケーションを動かす環境をaws-cdkを使って構築する(with CloudFront/S3/Fargate)

Next.jsをproduction環境で使うために外観を掴んでおきたいと思い、Next.jsアプリケーションを動かすAWS環境をaws-cdkを使って構築するサンプルを作ってみた。だいぶ荒削りだけど、参考になる例にはなったと思う。

https://github.com/shibayu36/nextjs-on-ecs

利用した技術

  • AWS
    • CloudFront
    • S3
    • ECR
    • ECS
  • aws-cdk
  • Docker
  • Next.js + TypeScript

今回作ったアーキテクチャ

図を書くとこんな感じ

f:id:shiba_yu36:20200219094922p:plain

初期構築方法と継続的なデプロイ方法

README.md に書いた。

初期構築は以下のようにして行う。誰でも使えるようにECR_BASEがあるせいで複雑になっているが、通常はECRのレポジトリURLはハードコードしたら良さそう。

$ yarn install
$ yarn workspace cdk run cdk bootstrap
$ yarn workspace cdk run cdk deploy VpcStack SecurityGroupStack RepositoryStack
$ ECR_BASE=<your ecr base> ./build-image-and-push.sh
$ yarn workspace cdk run cdk deploy --context application-version=$(git rev-parse --short HEAD) NextServerStack StaticStack
$ ECR_BASE=<your ecr base> ./deploy.sh

初期構築した後に、継続的にデプロイするのは以下のコマンド一発でできる。

ECR_BASE=<your ecr base> ./deploy.sh

技術的なポイント

今回構築のメインのポイントは以下の通り。

  • Next.jsアプリケーションのコンテナイメージを作る
  • Next.jsのnext buildで生成した静的ファイルをどう配信するか
  • aws cdkを使って、CloudFront + S3 + ECSの構成を作る
  • どうやってECSへ継続的にデプロイするか

Next.jsアプリケーションのコンテナイメージを作る

Next.jsアプリケーション用のDockerイメージを作る - $shibayu36->blog; に書いてあるとおり。基本はデプロイするコンテナにはpackage.json、yarn.lock、.next/を入れておき、yarn installでproductionに必要なものにみ入れておくと良い。またnext.config.jsを使っている場合はそれもコンテナに入れておくと良さそうだった。

Next.jsのnext buildで生成した静的ファイルをどう配信するか

ユーザーがブラウザでアクセスした時にダウンロードする静的ファイルはCloudFront + S3で配信したい。next buildで作った成果物の中には、SSR用サーバのためのファイル群と、ユーザーがブラウザからアクセスする際に使われる静的ファイル群が存在するので、後者をS3に配置すれば良い。

ブラウザでアクセスした時には/_next/static/*のURLで静的ファイルを取得していて(例: _next/static/8VBMV8AYTxMJOTzvzTfZj/pages/index.js)、これは.next/static/以下のファイルに対応するので

  • aws s3 sync ./.next/static s3://nextjs-on-ecs-static-bucket/_next/static のようにして、S3に静的ファイルをアップロードしておく
  • CloudFrontの設定で、/_next/static/*のpathは、S3のnextjs-on-ecs-static-bucketへ転送する

のようにしたら、CloudFront + S3配信が出来た。

aws cdkを使って、CloudFront + S3 + ECSの構成を作る

これはコードを見るのが早そう。

どうやってECSへ継続的にデプロイするか

ecspressoなどのECSへのデプロイツールを別に用意する必要もあるが、今回はaws-cdkをデプロイにも用いることにした。デプロイ時はaws-cdkのcontext機能を使って、どのイメージタグをデプロイするか決定する。

流れとしては以下のようになる。deploy.sh一発で全て動くようにしている。

  • gitのrevisionをイメージタグとして、build & ECRへpushする
  • buildしたイメージから.next/ディレクトリを取り出し、静的ファイル部分をS3にアップロードする
  • aws-cdkのcontextのapplication-versionにビルドしたイメージタグを渡して、NextServerStackを更新
  • cdk.context.jsonにapplication-versionを記録しておきたいので、sedを使って置換する
    • 今はcdk.context.jsonをいい感じに上書きする手法がなさそう?

build-image-and-push.sh

#!/bin/bash

set -ex

export DOCKER_BUILDKIT=1

: ${REVISION:="$(git rev-parse --short HEAD)"}
: ${ECR_BASE:=648803025740.dkr.ecr.ap-northeast-1.amazonaws.com}

docker build --file Dockerfile.server --target nextjs-on-ecs-server --tag "nextjs-on-ecs-server:$REVISION" --progress plain .

$(aws ecr get-login --no-include-email --region ap-northeast-1)
docker tag "nextjs-on-ecs-server:$REVISION" "$ECR_BASE/nextjs-on-ecs-server:$REVISION"
docker push "$ECR_BASE/nextjs-on-ecs-server:$REVISION"
echo "$ECR_BASE/nextjs-on-ecs-server:$REVISION"

deploy.sh

#!/bin/bash

set -ex

: ${REVISION:="$(git rev-parse --short HEAD)"}
export REVISION
export ECR_BASE

./build-image-and-push.sh

# Next.jsで作った静的ファイルをS3に上げる
rm -rf ./dist/
mkdir ./dist/
CONTAINER_ID="$(docker create "nextjs-on-ecs-server:$REVISION")"
docker cp ${CONTAINER_ID}:/app/.next ./dist/
docker rm -v ${CONTAINER_ID}
aws s3 sync ./dist/.next/static s3://nextjs-on-ecs-static-bucket/_next/static

# Nextのサーバが動くECSを更新
yarn workspace cdk run cdk deploy --context application-version=$REVISION NextServerStack

# cdk.context.jsonをデプロイしたrevisionに更新しておく
sed -i '' -E "s/\"application-version\": \"[^\"]+\"/\"application-version\": \"$REVISION\"/" ./cdk/cdk.context.json

まとめ

今回はNext.jsアプリケーションを動かすAWS環境をaws-cdkを使って構築するサンプルを作ってみた。Next.jsのデプロイ、AWSの要素技術、Dockerのマルチステージビルドやキャッシュの仕組み、aws-cdkによる構築の勉強になってかなり良かった。

感想としては

  • aws cdkによる構築はだいぶ体験が良い
    • CloudFormationで構築するのかなり辛かったので...
  • ECSのデプロイ方法は悩む。ベストプラクティスが知りたい
  • DockerのBuildKitによるキャッシュの仕組みは便利
  • Nextのデプロイに関するドキュメントがあんまりないので、勘でやってる...

参考

Next.jsアプリケーション用のDockerイメージを作る

Next.jsアプリケーションをECSで動かしたいと思い、Dockerfileを作ってみたのでメモ。

設定の用意

Dockerfileと.dockerignoreを用意し、ビルドコマンド用にMakefileも用意した。

Dockerfile

# syntax = docker/dockerfile:experimental

FROM node:12-stretch-slim AS nextjs-on-ecs-server-builder

USER node

WORKDIR /app

COPY --chown=node:node package.json yarn.lock ./

RUN mkdir -p /home/node/.cache
RUN --mount=type=cache,target=/home/node/.cache,id=yarn-cache,sharing=private,uid=1000 yarn install --pure-lockfile

COPY --chown=node:node . ./

RUN npm run build

FROM node:12-stretch-slim AS nextjs-on-ecs-server

WORKDIR /app

COPY --from=nextjs-on-ecs-server-builder /app/package.json /app/yarn.lock ./
RUN --mount=type=cache,target=/home/node/.cache,id=yarn-cache,sharing=private,uid=1000 yarn install --pure-lockfile --production
COPY --from=nextjs-on-ecs-server-builder /app/.next ./.next

CMD ["npm", "run", "start"]
  • 本番用に無駄なデータが入らないように、next buildを実行するビルド用イメージ(nextjs-on-ecs-server-builder)と、実際にに実行するためのイメージ(nextjs-on-ecs-server)を、マルチステージビルドで分離した
  • # syntax = docker/dockerfile:experimental を書いておき、yarn installでは高速化のため --mount=type=cache 付きで実行して、イメージ更新時にキャッシュが使われるようにする
  • https://nextjs.org/docs/deployment を参考にし、nextjs-on-ecs-serverには
    • package.json、yarn.lock、.nextだけ入れる
      • yarn.lockは実行するだけならいらないかも?
    • yarn installは--production付きでdependenciesだけを入れる

.dockerignore

.git
node_modules
.next

Makefile

export DOCKER_BUILDKIT=1

.PHONY: build
build:
    docker build --file Dockerfile.server --target nextjs-on-ecs-server --tag nextjs-on-ecs-server:latest --progress plain .

ビルドして実行する

ビルドする

$ make build

これでnextjs-on-ecs-serverというイメージが出来たので、実行する。

$ docker run --rm -ti -p 3000:3000 nextjs-on-ecs-server

> with-typescript@1.0.0 start /app
> next start

> Ready on http://localhost:3000

http://localhost:3000/ にアクセスすると、以下のように表示される。 f:id:shiba_yu36:20200202215412p:plain

まとめ

これでNext.jsアプリケーションをコンテナで動かすことができた。あとはECSなり何でも良いのでデプロイしたら良い。