Dockerfileの内容ミスって3日間ほぼ寝れなかった話 (AWS + Docker + Rails + Node.js)
事の経緯
会社で Docker + Rails(API) + Node.js でアプリ作ってた時のお話。
それをAWSにデプロイすることになったんですよね。
Dockerを使っていたこともあり、Amazon ECSを使うのがいいってことでいろいろ設定してたんだけども、なんか上手くいかない。
そもそもAWSを使うこと自体初めての経験だったのでいろいろ調べながらやってて悪戦苦闘して3日間の睡眠時間が3時間になってしまったポンコツエンジニアのお話です。
何が起こっていたか
Amazon CESを使う際に設定するものの中に ApplicationLoadBalancerってものがあります。そもそもフロントエンジニアなのでインフラ関係には疎いのでロードバランサーについて詳しいことはわからないけど、かいつまんでい言うとクライアントからのリクエストに応じてサービスに到達させるもの。専用のDNSを生成して固定の文字列を使ってアクセスすることができるようになるのでIPアドレスが変わっても実際に入力するURLは変わらないということができます。
これにはヘルスチェックっていうサービスにアクセスしたときにエラーがないかチェックするための機能が備わっていて、事前に設定しておいたパスに自動でアクセスして確認するっていうもの。
しかしこいつがなぜか通らない。ほかの各設定も間違ってないはずだしパスの設定も間違ってない。永遠にヘルスチェックで404吐き続ける動かないロボットが完成してしまいました。
その時のNode.jsのコードとDockerfileの記述がこちら
// app/server/app.js
const fastify = require("fastify");
const path = require("path");
// サーバーの設定
const port = 3000;
const server = fastify({ bodyLimit: 1024 * 1024 * 1024 });
server.register(require("fastify-formbody"));
server.register(require("fastify-qs"));
server.register(require("fastify-static"), {
root: path.join(__dirname, "../public")
});
// サーバー起動
server.listen(port, "0.0.0.0", (error, address) => {
if(error) throw new Error(`Start Application Server Error: ${error}`);
console.log(`Application Server is Listening: address = ${address}`);
});
# app/Dockerfile.prod
# package install layer
FROM node:16-slim as installer
WORKDIR /installer
COPY . ./
RUN npm install
# app runner
FROM gcr.io/distroless/nodejs:16 as runner
WORKDIR /app
COPY --from=installer /installer/package.json ./
COPY --from=installer /installer/node_modules ./node_modules
COPY --from=installer /installer/nodemon.json ./nodemon.json
COPY --from=installer /installer/server ./server
ENV NODE_ENV=production
ENV SERVER_ENV=docker
HTMLとかの静的ファイルを app/public の中に入れておいて fastify-static で静的フォルダを指定しておくという仕組み。開発環境では何の問題もなかっただけに何がおかしいのかわからない。CloudWatchのログを見てもちゃんとサーバー起動したときのログが出てる。なんでやねん。
解決
先ほども言った通り、ALBのヘルスチェックでは事前に設定しておいたパスにアクセスしてエラーがないかチェックします。今回の場合だと app/public/index.html にアクセスするわけです。しかしヘルスチェックで404が返される。開発環境では何も問題なかったのでindex.htmlが存在しないとかはないはずなんです。
勘のいい人は気づいてるかもしれませんが、実は本当に存在しませんでした。
なんでindex.html存在しないの?
実は開発環境では Docker Compose を使っていたので、アプリのファイル構成とローカルのファイル構成が共有されるようにボリュームを設定していました。
version: '3'
services:
app:
container_name: app
tty: true
build: ./app
image: app
env_file: ./dboption.env
environment:
- TZ=Asia/Tokyo
volumes:
- ./app:/app
- /app/node_modules
ports:
- "3000:3000"
(以下略)
前述の Dockerfile.prod は開発用の Dockerfile をコピーして作られたものです。
404エラー解決のためにアプリケーション側の確認をしてたんですがやっぱりどこにも問題がない。と思っていた時期が私にもありました。
何度したかわかりませんが何度目かの Dockerfile チェック。デプロイ作業を始めてから 3日が経とうとしていました。時間は午前11時ごろ。最後に寝てからとっくに24時間以上経過してたのでそろそろもう限界かもって時にふと気づきました。publicフォルダコピーされてないじゃん!!!!
それに気づいてすぐに Dockerfile を修正してもう一度デプロイしなおしました。ちなみに最終的な内容がこちら
# package install layer
FROM node:16-slim as installer
WORKDIR /installer
COPY . ./
RUN npm install
# app runner
FROM gcr.io/distroless/nodejs:16 as runner
WORKDIR /app
COPY --from=installer /installer/package.json ./
COPY --from=installer /installer/node_modules ./node_modules
COPY --from=installer /installer/nodemon.json ./nodemon.json
COPY --from=installer /installer/server ./server
COPY --from=installer /installer/public ./public
ENV NODE_ENV=production
ENV SERVER_ENV=docker
そしたらなんとヘルスチェックが通ったんです!!
ほんとマジで時間返してほしい。ちなみに肝心のアプリ自体まだ完成してないです。トホホ…
Railsで作ったAPIサーバーの方でも…
アプリ本体のヘルスチェックは通ったけど実はRailsアプリの方でもヘルスチェックでエラー吐いてました。
Active Admin を使ってたのでヘルスチェックのパスに /admin を指定してたんですが今度は403エラーが返ってくる始末。前述のこともありマジで気が滅入りそうでしたがこちらは難なく解決。
application.rbにこの1文を記述するだけでよかったしこちらに関してはかなり情報が転がってたので助かりました。
config.host_authorization = { exclude: ->(request) { request.path == '/admin' } }
まとめ
今回の問題が解決したのは最終的に1月11日の12時ごろなんですが実は10日に先方に見せるために一旦デプロイしようということで9日の朝からいろいろ調べてやってたんですね。だけど結局11日までもつれてしまってほんと申し訳ないです。もっと知識つけなきゃね。
こんなPONすること少ないと思うけどまじで情報なくて困ったので記事に残しとておくことにしました。みんなも気をつけような!!!
おやすみなさい…zZZ