バックエンド側 node.js AWS環境デプロイ(Docker化確認)

パクリ22発目!
パクリ元
内容
以下一覧を完了させて ECS リリースさせることでバックエンド側を終わりとする。順序は適宜入れ替える。
- 開発環境構築
- プロジェクト作成・最適なフォルダ構成
- 認証チェック(含めた前処理)
- ログ処理
- APIによってはIPチェックやHeaderチェック
- バリデーション
- 環境ファイルによる振り分け
- DB参照・更新
- 外部API呼び出し
- 外部API呼び出し待ち合わせ
- 認証・認可
- SPA における CSRF CORS
- 長時間処理を非同期処理で
- メール送信
- 画像アップロード
- 画像ダウンロード
EXCEL/PDF作成- テスト
- (AWS環境デプロイ) ←←← やっと
- Docker 化確認
- CodePipeline
- RDS 対応
- CloudFront
AWS環境へデプロイ(Docker 化確認)
いやーやっと最終工程まできたけどまだまだ作業目白押し。一つの項目の中にいろいろありすぎるんだよなー
ビルドされた js ファイルを Docker 化
AWS に上げてもいきなり動かないものだから、ローカルでコンテナが動くことを確認する。Docker 環境が整えてある wsl を立ち上げ、パクリ元1つ目を参考に Dockerfile 作成。
FROM node:16-alpine3.15 as builder
WORKDIR /app
ENV TINI_VERSION v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini-static /tini
RUN chmod +x /tini
COPY package.json yarn.lock ./
RUN yarn install --prod --frozen-lockfile
FROM gcr.io/distroless/nodejs:16
ENV NODE_ENV production
WORKDIR /app
COPY --from=builder --chown=nonroot:nonroot /tini /tini
COPY --from=builder --chown=nonroot:nonroot /app/node_modules ./node_modules
COPY --chown=nonroot:nonroot . .
USER nonroot
EXPOSE 3000
ENTRYPOINT [ "/tini", "--", "/nodejs/bin/node" ]
CMD ["/app/index.js"]
この中で yarn というのに気づく。パッケージ管理 npm と同じ階層のものらしいが、使ってないので npm に直す。パクリ元2つ目から npm ci –production で対応することに。合ってるか知らんけど。yarn.lock も package-lock.json に。さらに開発はポート3001で進めていたので合わせる。
・・・
#COPY package.json yarn.lock ./
COPY package.json package-lock.json ./
#RUN yarn install --prod --frozen-lockfile
RUN npm ci --production
・・・
#EXPOSE 3000
EXPOSE 3001
・・・
次に開発環境のファイルを Docker 環境に移動。
nodetest
┣━ ・・・
┣━ dist
┃ ┗━ src
┃ ┗━ ・・・
┣━ ・・・
┣━ package-lock.json
┗━ package.json
上記 dist に出来た src 内の js、package.json と package-lock.json を Dockerfile と同じ場所に。
docker/node
┣━ controllers
┃ ┗━ ・・・.js
┣━ middleware
┃ ┗━ ・・・.js
┣━ models
┃ ┗━ ・・・.js
┣━ routes
┃ ┗━ ・・・.js
┣━ types
┃ ┗━ ・・・.js
┣━ validators
┃ ┗━ ・・・.js
┣━ Dockerfile
┣━ index.js
┣━ package-lock.json
┗━ package.json
ビルドして立ち上げる。
~/docker/node$ docker build -t node_container .
~/docker/node$ docker run -p 3001:3001 node_container
・・・
Error: Problem reading config from file "./config/log4js.json". Error was ENOENT: no such file or directory, open './config/log4js.json'
・・・
オーよく考えたら log4js の設定ファイルは src の外に置いたんだった。そりゃ dist に入らんわ。さらに dist/prisma には seed.js しか入ってない。migration てどうすんだ?テストもそうだ!とりあえず 開発環境:nodetest/config/log4js.json の中身を app.js の log4js.configure の引数として直書きして再度ビルドしてみた。
~/docker/node$ docker rmi node_container -f
~/docker/node$ docker builder prune
WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
・・・
~/docker/node$ docker build -t node_container .
~/docker/node$ docker run -p 3001:3001 node_container
・・・
Error: EACCES: permission denied, mkdir './log'
・・・
オー今度は権限の話か。コンテナ内にフォルダなんて作成することは通常しないから log4js の カテゴリ全てのアペンダーを console だけに変更して再度。
~/docker/node$ docker rmi node_container -f
~/docker/node$ docker builder prune
WARNING! This will remove all dangling build cache. Are you sure you want to continue? [y/N] y
・・・
~/docker/node$ docker build -t node_container .
~/docker/node$ docker run -p 3001:3001 node_container
environment:LOCAL / listening on port:3001!
エラーなく起動。
~$ curl localhost:3001/user/list
{"result":"failure","message":"authentication error"}
とりあえず動いた、、がテストとか考えると TypeScript のままで docker 化を考えた方が良さそう。
TypeScript 環境を Docker 化
ということでパクリ元3つ目を参考に。開発環境に Docker を入れるのが早いかもしれないけどとりあえず別々で。ポートは3001、js ファイルの作成場所は dist 、index.js は server.js に自分環境に合わせて、パクリ元2つ目により npm install は npm ci に修正。
##########################################################
#### ビルドステージ
FROM node:18.7.0-alpine3.15 as builder
WORKDIR /work
# ビルド用の依存パッケージをインストール
COPY package*.json ./
RUN npm ci
# TypeScript コードをコピーしてビルド
COPY src tsconfig.json ./
RUN npm run build
##########################################################
#### 実行用イメージの作成
FROM node:18.7.0-alpine3.15 as runner
WORKDIR /work
ENV NODE_ENV production
ENV PORT 3001
EXPOSE 3001
# 本番環境用のパッケージをインストール
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
# builder からビルド結果だけコピー
COPY --from=builder /work/dist ./dist
# Node.js アプリを起動
CMD ["node", "./dist/server.js"]
node_modules
npm-debug.log
・・・
"scripts": {
・・・
"build": "tsc"
},
・・・
なるほどね、docker 化にもいくつかの段階に分けて対応すると。開発環境から dist / node_modeles 以外のフォルダを全コピーしてビルド。
~/docker/node$ docker image build -t myapp .
・・・
=> ERROR [builder 6/6] RUN npm run build ・・・
3.222 config/localStrategyWithSessionAuthenticate.ts(4,10): error TS2305: Module '"@prisma/client"' has no exported member 'User'.
3.224 config/localStrategyWithTokenAuthenticate.ts(9,10): error TS2305: Module '"@prisma/client"' has no exported member 'User'.
3.224 models/userModel.ts(1,32): error TS2305: Module '"@prisma/client"' has no exported member 'User'.
・・・
パクリ元4つ目5つ目を参考に Dockerfile 修正。builder 側にも runner 側にも追加。
・・・
+COPY prisma ./prisma
+RUN npx prisma generate
・・・
~/docker/node$ docker builder prune
~/docker/node$ docker image build -t myapp .
[+] Building 40.0s (17/17) FINISHED
~/docker/node$ docker container run --rm -p 3001:3001 --init --name myapp myapp
・・・
Error: Cannot find module '@aws-sdk/s3-request-presigner'
・・・
まだか。調査したら @aws-sdk は package.json の devDependencies 側にしかない。前回の記事みたら「npm install –save-dev @aws-sdk/s3-request-presigner」としっかり書いてある。@がついてるから間違えたんだな、、開発環境でインストールしなおして package.json、package-lock.json を再度 Docker 環境へ移動させてビルド。元記事も直しとこ。
~/nodetest$ npm uninstall --save-dev npm @aws-sdk/s3-request-presigner @aws-sdk/client-s3
~/nodetest$ npm install --save npm @aws-sdk/s3-request-presigner @aws-sdk/client-s3
~/docker/node$ docker rmi myapp
~/docker/node$ docker builder prune
~/docker/node$ docker image build -t myapp .
[+] Building 40.8s (17/17) FINISHED
~/docker/node$ docker container run --rm -p 3001:3001 --init --name myapp myapp
Warning: connect.session() MemoryStore is not
designed for a production environment, as it will leak
memory, and will not scale past a single process.
environment:production / listening on port:3001!
セッションストアを設定してないので警告でているけどOK。
テストを実行
テストも Dockerfile にねじ込んでみる。
#### テストステージ
FROM node:18.7.0-alpine3.15 as test
# ビルド用の依存パッケージをインストール
WORKDIR /work
COPY package*.json ./
RUN npm ci
# TypeScript コードをコピー
WORKDIR /work/src
COPY src ./
# TypeScript コードをコピー
WORKDIR /work
COPY tsconfig.json ./
# TypeScript テストコードをコピー
WORKDIR /work/__tests__
COPY __tests__ ./
# prisma
WORKDIR /work/prisma
COPY prisma ./
RUN npx prisma generate
WORKDIR /work
RUN npm run test
・・・
テストソースから本体ソース見てるからフォルダ構造を同じようにしないとダメだった。
~/docker/node$ docker image build -t myapp --target test .
テストがダメな場合はエラーが出て止まる。OK。
DB マイグレーションを実行
マイグレーションもやっておきたい。Docker で MySql を用意する。
version: '2'
services:
mysql:
image: mysql
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: pass
MYSQL_DATABASE: db
MYSQL_USER: user
MYSQL_PASSWORD: password
MYSQL_ROOT_HOST: '%'
restart: always
~/docker/mysql$ docker-compose up -d
schema.prisma に provider = “sqlite" とあるので provider = “mysql" としておき、DATABASE_URL を Dockerfile に環境変数として設定する(provider は env で渡せない?様子)。Dockerfile は以下。
##########################################################
#### マイグレーション用イメージの作成
FROM node:18.7.0-alpine3.15 as migration
WORKDIR /work
ENV NODE_ENV production
ENV PORT 3001
ENV DATABASE_URL "mysql://root:pass@localhost:3306/db"
EXPOSE 3001
# 本番環境用のパッケージをインストール
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
# builder からビルド結果だけコピー
COPY --from=builder /work/dist ./dist
# prisma
COPY prisma ./prisma
RUN npx prisma generate
RUN npx prisma migrate dev --name init
でもどうしても Error: P1001: Can’t reach database server at mysql docker が出てprisma からつながらない。QA サイトあさって出てきた、DB も docker だから DATABASE_URL を mysql://root:pass@mysql(サービス名):3306/db とやっても全くダメ。ハマりまくり。こちらからDBコンテナが見えないようなので、Docker ネットワーク関係と当たりをつけ、ネットワークいろいろ作ったりしたが、こちらはコンテナではなくコンテナ作成中だからやはり見えない。その後 DB コンテナに固定IP を割り当てようとしたりどうにか見ようとしていたが、そのうちパクリ元6つ目を参考に、「ホスト側のネットワークを使ってビルド」するようにしたら見えるようになって少し進んだ(ホスト側からなので DBコンテナは localhost:3306 で見える)!今回のハマりポイント。実際には RDS 等の DB がみれればいいだけなんで気にする必要はないと思うけど、
~/docker/node$ docker image build -t myapp --target migration . --network host
=> ERROR [migration 8/8] RUN npx prisma migrate dev ・・・
1.409 The datasource provider `mysql` specified in your schema does not match the one specified in the migration_lock.toml, `sqlite`. Please remove your current migration directory and start a new migration history with prisma migrate dev.
・・・
sqlite から mysql に変えたから migration_lock とか全部消せだと?そりゃそうだ。消して再度実行。
~/docker/node$ docker image build -t myapp --target migration . --network host
[+] Building 40.1s (18/18) FINISHED
おー。DB確認したらテーブルが作成されていた。やっと Docker 化確認が終わり。進みはまるで遅いが、テスト/ Typescriptビルド/コンテナ化/マイグレーション のタスクを作成できたので後は CI ツールから呼ぶだけでいいと思う。
雑感
環境側が入ってくることで使う筋肉が違ってくるので疲れる。お願いマッスル!
ディスカッション
コメント一覧
まだ、コメントがありません