Strapi を開発環境と本番環境のDockerで動かしたい【その4】

【その1】【その2】【その3】の続きです。

前回はデータベースとの接続について考えました。

今回は、Strapi を実際にVPSへデプロイしてみます。

本番環境の構成

本番環境は以下の図のような構成になります。

VPS 上に Docker 環境を構築し、その Docker 環境内に Strapi コンテナと PostgreSQL コンテナの2つを立ち上げます。

実際に Strapi で管理されるデータは、VPS上に直接配置されるようにします。(Docker の volumeを使って外付けする)

デプロイの流れ

GitHub 上のリポジトリにプッシュされたらGitHub Actionsを使って自動でデプロイを行うようにします。

  1. ローカルで更新して GitHub のリポジトリにプッシュする
  2. GitHub Actions を使って自動でビルドを行う
  3. VPS上の既存コンテナを削除して作り直す(2と合わせて自動で行われる)
  4. プラグインの追加等の変更をした時は 1~3を行う

コンテナを作り直しても、実際のデータには影響がないため、誤ってコンテナが停止した場合でもデータは消えません。

ワークフローをつくる

今回は本番環境とほぼ同じ環境でできたステージング環境へのデプロイを行います。

GitHub Actions を使ってデプロイのワークフローを作成していきます。

STEP.1
GitHub のリポジトリを作成する
まずは GitHub のリポジトリを作ります。

# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi

# git 管理をはじめる
$ git init

# git 管理しないファイルを設定する
$ touch .gitignore

.gitignoreを作成して、git 管理しないフォルダを追加します。

デフォルトで作成されている.gitignoredatadistを追加します。

.gitignore
############################
# OS X
############################

.DS_Store
.AppleDouble
.LSOverride
Icon
.Spotlight-V100
.Trashes
._*

############################
# Linux
############################

*~

############################
# Windows
############################

Thumbs.db
ehthumbs.db
Desktop.ini
$RECYCLE.BIN/
*.cab
*.msi
*.msm
*.msp

############################
# Packages
############################

*.7z
*.csv
*.dat
*.dmg
*.gz
*.iso
*.jar
*.rar
*.tar
*.zip
*.com
*.class
*.dll
*.exe
*.o
*.seed
*.so
*.swo
*.swp
*.swn
*.swm
*.out
*.pid


############################
# Logs and databases
############################

.tmp
*.log
*.sql
*.sqlite
*.sqlite3


############################
# Misc.
############################

*#
ssl
.idea
nbproject
public/uploads/*
!public/uploads/.gitkeep

############################
# Node.js
############################

lib-cov
lcov.info
pids
logs
results
node_modules
.node_history

############################
# Tests
############################

testApp
coverage

############################
# Strapi
############################

.env
license.txt
exports
*.cache
build
.strapi-updater.json

# 追加
data
dist

.gitignoreを編集後、コミットをして GitHub リポジトリにプッシュします。

# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi

# git ステージング
$ git add .

# git コミット
$ git commit -m "Initial commit"

# ブランチ名を master から main に変更
$ git branch -M main

# GitHub のブランチと紐づける
$ git remote add origin git@github.com:ユーザ名/Donuts-Strapi.git

# GitHub にプッシュする
$ git push -u origin main
STEP.2
ワークフローを作成する

ワークフローの作成は「Next.js のプロジェクトを VPS 上の Docker にデプロイする【その4】」の記事で戦ったので、それを元に書きます。

今回はステージング環境へのデプロイ用のワークフローを作成します。

# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi

# ワークフローを配置するディレクトリを作成
$ mkdir -p .github/workflows/

# ワークフローファイルを作成する
$ touch .github/workflows/deploy.stg.yml

deploy.stg.ymlを書きます。

deploy.stg.yml
name: Deploy to Sakura VPS Staging

# staging ブランチにプッシュされた時に実行する
on:
  push:
    branches: [staging]
  # 手動実行時のログを設定
  workflow_dispatch:
    inputs:
      logLevel:
        description: 'Log level'
        required: true
        default: 'warning'
jobs:
  deploy:
    runs-on: ubuntu-latest
    container: node:16
    steps:
      # チェックアウト
      - uses: actions/checkout@v3
      # Github Container Registry へビルドしてイメージを格納
      - name: Build and Publish to Github Container Registry
        uses: elgohr/Publish-Docker-Github-Action@master
        env:
          HOST: ${{ secrets.STRAPI_HOST }}
          PORT: ${{ secrets.STRAPI_PORT }}
          APP_KEYS: ${{ secrets.APP_KEYS }}
          API_TOKEN_SALT: ${{ secrets.API_TOKEN_SALT }}
          ADMIN_JWT_SECRET: ${{ secrets.ADMIN_JWT_SECRET }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
          DATABASE_HOST: ${{ secrets.DATABASE_HOST }}
          DATABASE_PORT: ${{ secrets.DATABASE_PORT }}
          DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
          DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
          DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
        with:
          name: GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
          dockerfile: stg.Dockerfile
          buildargs: HOST, PORT, APP_KEYS, API_TOKEN_SALT, ADMIN_JWT_SECRET, JWT_SECRET, DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD
          tags: latest
      # VPSへのデプロイ
      - name: Deploy to Sakura VPS
        uses: appleboy/ssh-action@master
        env:
          GITHUB_USERNAME: ${{ github.actor }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          STRAPI_HOST: ${{ secrets.STRAPI_HOST }}
          STRAPI_PORT: ${{ secrets.STRAPI_PORT }}
          APP_KEYS: ${{ secrets.APP_KEYS }}
          API_TOKEN_SALT: ${{ secrets.API_TOKEN_SALT }}
          ADMIN_JWT_SECRET: ${{ secrets.ADMIN_JWT_SECRET }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
          DATABASE_HOST: ${{ secrets.DATABASE_HOST }}
          DATABASE_PORT: ${{ secrets.DATABASE_PORT }}
          DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
          DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
          DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
        with:
          host: ${{ secrets.DEPLOY_STG_HOST }}
          port: ${{ secrets.DEPLOY_STG_PORT }}
          username: ${{ secrets.DEPLOY_STG_USER }}
          key: ${{ secrets.DEPLOY_STG_KEY }}
          envs: GITHUB_USERNAME, GITHUB_TOKEN, STRAPI_HOST, STRAPI_PORT, APP_KEYS, API_TOKEN_SALT, ADMIN_JWT_SECRET, JWT_SECRET, DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD
          script: |
            docker login ghcr.io -u $GITHUB_USERNAME -p $GITHUB_TOKEN
            docker image pull ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest
            docker container rm -f donuts-strapi-staging
            docker container rm -f donuts-strapi-postgres
            docker system prune -f
            docker network create donuts-strapi-staging
            docker container run --name donuts-strapi-postgres -dit --net=donuts-strapi-staging -p $DATABASE_PORT:5432 -v /srv/donuts-strapi/data:/var/lib/postgresql/data -e POSTGRES_DB=$DATABASE_NAME -e POSTGRES_USER=$DATABASE_USERNAME -e POSTGRES_PASSWORD=$DATABASE_PASSWORD postgres
            docker container run --name donuts-strapi-staging -dit -p $STRAPI_PORT:1337 --net=donuts-strapi-staging -v /srv/donuts-strapi/public:/opt/app/public --restart=always ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest

STEP.3
環境変数を設定する
deploy.stg.ymlで利用する環境変数をGitHubのリポジトリ>Secrets>Actionsより追加します。

それぞれ適当な値を入力します。

HOST=0.0.0.0
PORT=1337
APP_KEYS=ランダムの値
API_TOKEN_SALT=ランダムの値
ADMIN_JWT_SECRET=ランダムの値
JWT_SECRET=ランダムの値
DATABASE_HOST=donuts-strapi-postgres(PostgreSQLのコンテナ名)
DATABASE_PORT=5432
DATABASE_NAME=donuts-strapi
DATABASE_USERNAME=donuts-strapi
DATABASE_PASSWORD=パスワード
DEPLOY_STG_HOST=さくらVPSのIPアドレス
DEPLOY_STG_PORT=SSH接続用のポート
DEPLOY_STG_USER=SSH接続するユーザ
DEPLOY_STG_KEY=SSH接続の秘密鍵

DATABASE_HOSTはPostgreSQLのコンテナ名を指定します。

ワークフローが起動することを確認します。

# 作業ディレクトリに移動する
$ cd ~/StrapiProjects/donuts-strapi

# staging ブランチを作成
$ git branch staging

# staging ブランチに移動
$ git checkout staging

# staging ブランチを push する
$ git push origin staging 
エラーの解消

ワークフロー実行時にエラーが出ました。詳細を見てみます。

.envがないと言われています。.env.gitignoreされているのでリポジトリに含まれていません。

環境変数は全て GitHub 上の Secrets で設定したので、stg.Dockerfile上のCOPY --from=builder --chown=strapi:nodejs /opt/app/.env ./.envの行を削除して、再度stagingにプッシュしておきます。

エラーの解消②

VPS のデプロイで転けてしまいました。

2022/09/28 15:30:01 dial tcp ***:***: connect: connection refused

jobs.deploy.stepsDeploy to Sakura VPSの方のenvHOST, PORTとしていたのをリネームしたところ解決しました。予約語的なやつかな。。

deploy.stg.yml
      - name: Deploy to Sakura VPS
        uses: appleboy/ssh-action@master
        env:
          # これだとエラーが起きる
          # HOST: ${{ secrets.STRAPI_HOST }}
          # PORT: ${{ secrets.STRAPI_PORT }}
          STRAPI_HOST: ${{ secrets.STRAPI_HOST }}
          STRAPI_PORT: ${{ secrets.STRAPI_PORT }}

参照: https://github.com/appleboy/ssh-action/issues/41#issuecomment-621693365

STEP.4
稼働状況の確認をする

deploy.stg.ymlの設定がうまくいっていれば、デプロイに成功します。
うまくいかない場合には上記メモやアクションのエラーを参考に修正が必要です。

サイトの確認

まずは、http://IPアドレス:STRAPIのポート番号 でアクセスしてみます。

表示できません。😢

コンテナの確認

コンテナの確認をします。

# VPSにログインする
$ ssh -p ポート番号 ユーザ名@IPアドレス

# docker コンテナの確認をする
% docker container ls

ea099807d5a4   ghcr.io/ユーザ名/donuts-strapi/donuts-strapi-staging-image:latest           "docker-entrypoint.s…"   45 seconds ago   Restarting (1) 5 seconds ago                                                 donuts-strapi-staging
0ee0657a592b   postgres                                                                 "docker-entrypoint.s…"   46 seconds ago   Up 45 seconds                  0.0.0.0:5432->5432/tcp

# docker イメージの確認もする
% docker image ls

REPOSITORY                                                        TAG             IMAGE ID       CREATED         SIZE
ghcr.io/ユーザ名/donuts-strapi/donuts-strapi-staging-image           latest          a69ca3767d97   3 minutes ago   1.25GB

PostgreSQL のコンテナは正しく起動していますが、Strapi のコンテナは起動せず、リスタートを繰り返しているようです。

データ保管用のフォルダの確認

次にVPS上にデータ保管用フォルダを確認します。

# VPSにログインする
$ ssh -p ポート番号 ユーザ名@IPアドレス

# ボリュームを格納するフォルダに移動
% cd /srv/donuts-strapi/

# donuts-strapi フォルダの中身を表示
% ls

data  public

# data の中を確認
% cd data

cd: permission denied: data

# public の中を確認
% cd public
% ls

なし
手動で実行してみる

どうしてコンテナがうまく起動できていないのかを確認するために手動でコンテナを起動してみます。

# VPSにログインする
$ ssh -p ポート番号 ユーザ名@IPアドレス

% コンテナを確認
% docker container ls

ea099807d5a4   ghcr.io/ユーザ名/donuts-strapi/donuts-strapi-staging-image:latest           "docker-entrypoint.s…"   45 seconds ago   Restarting (1) 5 seconds ago                                                 donuts-strapi-staging
0ee0657a592b   postgres                                                                 "docker-entrypoint.s…"   46 seconds ago   Up 45 seconds                  0.0.0.0:5432->5432/tcp

# すでに起動しているStrapiコンテナを止めて削除する(リスタートを繰り返している)
% docker container rm -f donuts-strapi-staging

% コンテナを確認
% docker container ls

0ee0657a592b   postgres                                                                 "docker-entrypoint.s…"   9 minutes ago   Up 9 minutes   0.0.0.0:5432->5432/tcp                        donuts-strapi-postgres

# Strapiコンテナを立ち上げる(-dit オプションと --restart オプションを外す)
% docker container run --name donuts-strapi-staging -p ポート番号:1337 --net=donuts-strapi-staging -v /srv/donuts-strapi/public:/opt/app/public ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest

error: The upload folder (/opt/app/public/uploads) doesn't exist or is not accessible. Please make sure it exists.

uploads フォルダがないのが問題なようです。

STEP.4
uploadsフォルダの作成
# VPSにログインする
$ ssh -p ポート番号 ユーザ名@IPアドレス

# ボリュームを格納するフォルダに移動
% cd /srv/donuts-strapi/

# donuts-strapi フォルダの中身を表示
% ls

data  public

# public ディレクトリに移動
% cd public

# uploads フォルダの作成
% sudo mkdir uploads
次のエラー

/srv/public/uploadsフォルダを作ることにより、上記エラーは解消しましたが、次のエラーが発生しました。

# Strapiコンテナを手動で立ち上げる(-dit オプションと --restart オプションを外す)
% docker container run --name donuts-strapi-staging -p ポート番号:1337 --net=donuts-strapi-staging -v /srv/donuts-strapi/public:/opt/app/public ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest

yarn run v1.22.19
$ strapi start
[2022-09-29 06:28:59.965] debug: ⛔️ Server wasn't able to start properly.
[2022-09-29 06:28:59.968] error: connect ECONNREFUSED 127.0.0.1:5432
Error: connect ECONNREFUSED 127.0.0.1:5432

Strapi と PostgreSQLデータベースがうまく紐づいていません…。

そもそもenvで設定しているデータベースの値等が、特に Strapi コンテナ起動時には使われていないので、デフォルトの値が使われてしまっています。

次はそこを変えてみましょう。

STEP.5
Dockerfile で環境変数を渡す

stg.Dockerfileを修正して、deploy.stg.ymlで設定している環境変数を使えるようにします。

.envサーバ側に配置するのも手軽ですが、今回はDockerfileに設定します。

stg.Dockerfile
### deps ステージ ###
FROM node:16 AS deps
RUN apt-get update && apt-get install libvips-dev -y
ENV NODE_ENV=production
WORKDIR /opt/
COPY ./package.json ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g && yarn install

### builder ステージ ###
FROM node:16 AS builder

ENV NODE_ENV=production

WORKDIR /opt/app

ARG HOST
ARG PORT
ARG APP_KEYS
ARG API_TOKEN_SALT
ARG ADMIN_JWT_SECRET
ARG JWT_SECRET
ARG DATABASE_HOST
ARG DATABASE_PORT
ARG DATABASE_NAME
ARG DATABASE_USERNAME
ARG DATABASE_PASSWORD

ENV HOST=$HOST
ENV PORT=$PORT
ENV APP_KEYS=$APP_KEYS
ENV API_TOKEN_SALT=$API_TOKEN_SALT
ENV ADMIN_JWT_SECRET=$ADMIN_JWT_SECRET
ENV JWT_SECRET=$JWT_SECRET
ENV DATABASE_HOST=$DATABASE_HOST
ENV DATABASE_PORT=$DATABASE_PORT
ENV DATABASE_NAME=$DATABASE_NAME
ENV DATABASE_USERNAME=$DATABASE_USERNAME
ENV DATABASE_PASSWORD=$DATABASE_PASSWORD

# deps ステージでインストールしたライブラリをコピーする
COPY --from=deps /opt/node_modules ./node_modules
COPY . .
RUN yarn build

### runner ステージ ###
FROM node:16 AS runner

WORKDIR /opt/app

ARG HOST
ARG PORT
ARG APP_KEYS
ARG API_TOKEN_SALT
ARG ADMIN_JWT_SECRET
ARG JWT_SECRET
ARG DATABASE_HOST
ARG DATABASE_PORT
ARG DATABASE_NAME
ARG DATABASE_USERNAME
ARG DATABASE_PASSWORD

ENV HOST=$HOST
ENV PORT=$PORT
ENV APP_KEYS=$APP_KEYS
ENV API_TOKEN_SALT=$API_TOKEN_SALT
ENV ADMIN_JWT_SECRET=$ADMIN_JWT_SECRET
ENV JWT_SECRET=$JWT_SECRET
ENV DATABASE_HOST=$DATABASE_HOST
ENV DATABASE_PORT=$DATABASE_PORT
ENV DATABASE_NAME=$DATABASE_NAME
ENV DATABASE_USERNAME=$DATABASE_USERNAME
ENV DATABASE_PASSWORD=$DATABASE_PASSWORD

ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 strapi

COPY --from=builder /opt/app/public ./public
COPY --from=builder /opt/app/package.json ./package.json
COPY --from=builder /opt/app/favicon.ico ./favicon.ico

COPY --from=builder --chown=strapi:nodejs /opt/app/dist ./
COPY --from=builder --chown=strapi:nodejs /opt/app/node_modules ./node_modules

EXPOSE 1337

ENV PORT 1337

CMD ["yarn", "start"]

これで再度 staging ブランチにプッシュし、ワークフローを実行してみます。

次のエラー

さて、またエラーが出ました。

# Strapiコンテナを手動で立ち上げる(-dit オプションと --restart オプションを外す)
% docker container run --name donuts-strapi-staging -p ポート番号:1337 --net=donuts-strapi-staging -v /srv/donuts-strapi/public:/opt/app/public ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest

yarn run v1.22.19
$ strapi start
[2022-09-29 07:32:35.478] debug: ⛔️ Server wasn't able to start properly.
[2022-09-29 07:32:35.480] error: password authentication failed for user "donuts-strapi"
error: password authentication failed for user "donuts-strapi"

これは Docker コマンドに渡す環境変数名を間違えているだけでした😢
$DATABASE_USER$DATABASE_USERNAMEに修正して解決しました。

deploy.stg.yml
# 修正前
docker container run --name donuts-strapi-postgres -dit --net=donuts-strapi-staging -p $DATABASE_PORT:5432 -v /srv/donuts-strapi/data:/var/lib/postgresql/data -e POSTGRES_DB=$DATABASE_NAME -e POSTGRES_USER=$DATABASE_USER -e POSTGRES_PASSWORD=$DATABASE_PASSWORD postgres

# 修正後
docker container run --name donuts-strapi-postgres -dit --net=donuts-strapi-staging -p $DATABASE_PORT:5432 -v /srv/donuts-strapi/data:/var/lib/postgresql/data -e POSTGRES_DB=$DATABASE_NAME -e POSTGRES_USER=$DATABASE_USERNAME -e POSTGRES_PASSWORD=$DATABASE_PASSWORD postgres

また、外付けされている/srv/donuts-strapi/dataの方でユーザ名とパスワードがすでに設定されてしまっている状態なので、一度/srv/donuts-strapi/dataを削除してやり直す必要がありました。

STEP.6
再度稼働状況の確認をする

各環境変数が正しく設定されている状態で、再度確認をしていきます。

サイトの確認

まずは、http://IPアドレス:STRAPIのポート番号 でアクセスしてみます。

表示できました!

コンテナの確認

コンテナの確認をします。

# VPSにログインする
$ ssh -p ポート番号 ユーザ名@IPアドレス

# docker コンテナの確認をする
$ docker container ls

CONTAINER ID   IMAGE                                                                    COMMAND                  CREATED          STATUS          PORTS                                         NAMES
91c475f5f910   ghcr.io/ユーザ名/donuts-strapi/donuts-strapi-staging-image:latest           "docker-entrypoint.s…"   30 seconds ago   Up 29 seconds   0.0.0.0:Strapiポート番号->1337/tcp                       donuts-strapi-staging
5f63e0439908   postgres                                                                 "docker-entrypoint.s…"   31 seconds ago   Up 30 seconds   0.0.0.0:データベースポート番号->5432/tcp                        donuts-strapi-postgres

# docker イメージの確認もする
$ docker image ls

REPOSITORY                                                        TAG             IMAGE ID       CREATED         SIZE
ghcr.io/ユーザ名/donuts-strapi/donuts-strapi-staging-image           latest          c4fecc0023e9   2 minutes ago   1.25GB

稼働中になっていまね!

データ保存の確認

実際に Strapi にデータを追加してみます。

http://IPアドレス:ポート番号/admin/auth/register-adminにアクセスしてユーザ登録を行います。

ユーザ登録が完了したら User を追加してみます。

登録後、登録内容が正しく表示されていることを確認します。

次にメディアも置いてみます。

無事に置くことができました。

最後に、GitHub Actions より、Re-run all jobsから再度ワークフローを実行してみます。
ワークフローの実行によりコンテナは一度廃棄され、別のコンテナが作成されます。
その前後でデータが消滅しないことを確認します。

詳しくは、前回の記事で書いています。

ビルドが完了したら再度アクセスして、追加した User と メディア が正しく表示されることを確認します。

確認できたのでこれでデプロイは完了です。

最終的なステージング環境のファイル

無事に Strapi のコンテナが起動した ワークフロー と Dockerfile を最後に貼っておきます。

stg.Dockerfile
### deps ステージ ###
FROM node:16 AS deps
RUN apt-get update && apt-get install libvips-dev -y
ENV NODE_ENV=production
WORKDIR /opt/
COPY ./package.json ./yarn.lock ./
ENV PATH /opt/node_modules/.bin:$PATH
RUN yarn config set network-timeout 600000 -g && yarn install

### builder ステージ ###
FROM node:16 AS builder

ENV NODE_ENV=production

WORKDIR /opt/app

ARG HOST
ARG PORT
ARG APP_KEYS
ARG API_TOKEN_SALT
ARG ADMIN_JWT_SECRET
ARG JWT_SECRET
ARG DATABASE_HOST
ARG DATABASE_PORT
ARG DATABASE_NAME
ARG DATABASE_USERNAME
ARG DATABASE_PASSWORD

ENV HOST=$HOST
ENV PORT=$PORT
ENV APP_KEYS=$APP_KEYS
ENV API_TOKEN_SALT=$API_TOKEN_SALT
ENV ADMIN_JWT_SECRET=$ADMIN_JWT_SECRET
ENV JWT_SECRET=$JWT_SECRET
ENV DATABASE_HOST=$DATABASE_HOST
ENV DATABASE_PORT=$DATABASE_PORT
ENV DATABASE_NAME=$DATABASE_NAME
ENV DATABASE_USERNAME=$DATABASE_USERNAME
ENV DATABASE_PASSWORD=$DATABASE_PASSWORD

# deps ステージでインストールしたライブラリをコピーする
COPY --from=deps /opt/node_modules ./node_modules
COPY . .
RUN yarn build

### runner ステージ ###
FROM node:16 AS runner

WORKDIR /opt/app

ARG HOST
ARG PORT
ARG APP_KEYS
ARG API_TOKEN_SALT
ARG ADMIN_JWT_SECRET
ARG JWT_SECRET
ARG DATABASE_HOST
ARG DATABASE_PORT
ARG DATABASE_NAME
ARG DATABASE_USERNAME
ARG DATABASE_PASSWORD

ENV HOST=$HOST
ENV PORT=$PORT
ENV APP_KEYS=$APP_KEYS
ENV API_TOKEN_SALT=$API_TOKEN_SALT
ENV ADMIN_JWT_SECRET=$ADMIN_JWT_SECRET
ENV JWT_SECRET=$JWT_SECRET
ENV DATABASE_HOST=$DATABASE_HOST
ENV DATABASE_PORT=$DATABASE_PORT
ENV DATABASE_NAME=$DATABASE_NAME
ENV DATABASE_USERNAME=$DATABASE_USERNAME
ENV DATABASE_PASSWORD=$DATABASE_PASSWORD

ENV NODE_ENV=production

RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 strapi

COPY --from=builder /opt/app/public ./public
COPY --from=builder /opt/app/package.json ./package.json
COPY --from=builder /opt/app/favicon.ico ./favicon.ico

COPY --from=builder --chown=strapi:nodejs /opt/app/dist ./
COPY --from=builder --chown=strapi:nodejs /opt/app/node_modules ./node_modules

EXPOSE 1337

ENV PORT 1337

CMD ["yarn", "start"]
.github/workflows/deploy.stg.yml
name: Deploy to Sakura VPS Staging

# staging ブランチにプッシュされた時に実行する
on:
  push:
    branches: [staging]
  # 手動実行時のログを設定
  workflow_dispatch:
    inputs:
      logLevel:
        description: 'Log level'
        required: true
        default: 'warning'
jobs:
  deploy:
    runs-on: ubuntu-latest
    container: node:16
    steps:
      # チェックアウト
      - uses: actions/checkout@v3
      # Github Container Registry へビルドしてイメージを格納
      - name: Build and Publish to Github Container Registry
        uses: elgohr/Publish-Docker-Github-Action@master
        env:
          HOST: ${{ secrets.STRAPI_HOST }}
          PORT: ${{ secrets.STRAPI_PORT }}
          APP_KEYS: ${{ secrets.APP_KEYS }}
          API_TOKEN_SALT: ${{ secrets.API_TOKEN_SALT }}
          ADMIN_JWT_SECRET: ${{ secrets.ADMIN_JWT_SECRET }}
          JWT_SECRET: ${{ secrets.JWT_SECRET }}
          DATABASE_HOST: ${{ secrets.DATABASE_HOST }}
          DATABASE_PORT: ${{ secrets.DATABASE_PORT }}
          DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
          DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
          DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
        with:
          name: GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
          dockerfile: stg.Dockerfile
          buildargs: HOST, PORT, APP_KEYS, API_TOKEN_SALT, ADMIN_JWT_SECRET, JWT_SECRET, DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD
          tags: latest
      # VPSへのデプロイ
      - name: Deploy to Sakura VPS
        uses: appleboy/ssh-action@master
        env:
          GITHUB_USERNAME: ${{ github.actor }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          STRAPI_PORT: ${{ secrets.STRAPI_PORT }}
          DATABASE_HOST: ${{ secrets.DATABASE_HOST }}
          DATABASE_PORT: ${{ secrets.DATABASE_PORT }}
          DATABASE_NAME: ${{ secrets.DATABASE_NAME }}
          DATABASE_USERNAME: ${{ secrets.DATABASE_USERNAME }}
          DATABASE_PASSWORD: ${{ secrets.DATABASE_PASSWORD }}
        with:
          host: ${{ secrets.DEPLOY_STG_HOST }}
          port: ${{ secrets.DEPLOY_STG_PORT }}
          username: ${{ secrets.DEPLOY_STG_USER }}
          key: ${{ secrets.DEPLOY_STG_KEY }}
          envs: GITHUB_USERNAME, GITHUB_TOKEN, STRAPI_HOST, STRAPI_PORT, APP_KEYS, API_TOKEN_SALT, ADMIN_JWT_SECRET, JWT_SECRET, DATABASE_HOST, DATABASE_PORT, DATABASE_NAME, DATABASE_USERNAME, DATABASE_PASSWORD
          script: |
            docker login ghcr.io -u $GITHUB_USERNAME -p $GITHUB_TOKEN
            docker image pull ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest
            docker container rm -f donuts-strapi-staging
            docker container rm -f donuts-strapi-postgres
            docker system prune -f
            docker network create donuts-strapi-staging
            docker container run --name donuts-strapi-postgres -dit --net=donuts-strapi-staging -p $DATABASE_PORT:5432 -v /srv/donuts-strapi/data:/var/lib/postgresql/data -e POSTGRES_DB=$DATABASE_NAME -e POSTGRES_USER=$DATABASE_USERNAME -e POSTGRES_PASSWORD=$DATABASE_PASSWORD postgres
            docker container run --name donuts-strapi-staging -dit -p $STRAPI_PORT:1337 --net=donuts-strapi-staging -v /srv/donuts-strapi/public:/opt/app/public --restart=always ghcr.io/GitHubのユーザ名/donuts-strapi/donuts-strapi-staging-image:latest

おわりに

変数名のミスなどですごくハマりましたが、無事にデプロイすることができました。

本番環境でも同様にprod.Dockerfileを作成し、deploy.prod.ymlにワークフローを定義します。本番環境では別のブランチ(main)へのマージでワークフローを実行します。また、ホストやポートなどの環境変数も本番環境用に定義をして利用します。

何か問題が起こるとすれば、ほとんどが環境変数の設定ミスのはずということがわかったので、その辺に留意しながら本番環境も設定していきたいです。

次回は、独自ドメインの設定を行なっていこうと思います。