Upgrade to Pro — share decks privately, control downloads, hide ads and more …

CMD について語りたい : その Dockerfile、30 秒無駄にしてるかも?

Avatar for mu_zaru mu_zaru
September 22, 2025

CMD について語りたい : その Dockerfile、30 秒無駄にしてるかも?

このスライドはYouTubeムーザルちゃんねる動画の資料です。
動画はこちらから見れます。
https://youtu.be/zbyMdaSGctg

Dockerで「CMD node server.js って普通に動くし、問題ないでしょ?」…そう思っていた時期が、僕にもありました。実はこの書き方、コンテナがうまく終了しない原因になっているかもしれません。

この動画では、
・CMD の シェル形式 vs exec形式 の違い
・SIGTERM が届かない・反応しないコンテナの仕組み
・PID 1 問題とその対処法
・npm run の罠と OS によるシグナル伝搬の違い
などについて解説します!

Avatar for mu_zaru

mu_zaru

September 22, 2025
Tweet

More Decks by mu_zaru

Other Decks in Programming

Transcript

  1. CMD の書き方は 2 種類ある シェル形式と exec 形式 # シェル形式 CMD

    node idle.js # exec形式 CMD ["node", "idle.js"] exec形式ではなくJSON形式とも呼ぶことがありますが、 ここではexec形式で統一します
  2. 同じくコマンド実行するが プロセスの作られ方が違う シェル形式は sh が PID 1 で、 指定コマンドは子プロセスの親子関係 /bin/sh

    -c "指定コマンド" が実行される exec 形式は指定コマンドが PID 1 の単体プロセス PID 1 とは、 OS が最初に起動するプロセスのこと(SystemInit) 何が違うのか?
  3. 動くけど CMD は exec 形式で必ず書いて! # シェル形式 CMD node idle.js

    # exec形式 CMD ["node", "idle.js"] 今日はこれだけ覚えて帰ってもらえたら OK です 違いはあれど、 どちらも動く
  4. Node.js が動く Docker コンテナ、 シェル形式と exec 形式の 2 つある。 SIGTERM

    で、 それぞれのコンテナを 終了させることはできる? SIGTERMとは終了してね、 という依頼を送ること (要は、 コンテナを止めること) 問題 問題
  5. // idle.js ずっとスリープして待機するだけのコード setInterval(() => {}, 1 << 30); #

    シェル形式 CMD node idle.js # exec形式 CMD ["node", "idle.js"] 2 つのコンテナを終了させてみる (docker kill --signal SIGTERM ) その結果は?
  6. ECS でデプロイした時、 新しいコンテナを起動させつつ、 古いコンテナ を SIGTERM で終了させるが、SIGTERM に反応しないと一定時間待 機してから SIGKILL

    で強制終了させる デフォルトだと、 30 秒間待機するので無駄、 嬉しくない / ECS Fargatgeだと秒単位で課金 ローカルのDocker終了も10秒待機するので、 やはり嬉しくはない デプロイ時間が無駄に長くなる
  7. 実は Node.js も、PID 1 で動く場合に限り、 デフォ ルトではシグナルを受け取っても何もしないから 正確には Node.js が内部で依存しているイベントループライブラリの

    libuv の挙動。 つまり 実質的にはNode.jsはshと同じ挙動になる。 ちなみにNode.js以外のRubyなどではデフォル トでも終了するので、 ランタイムによって挙動は違う ただ、 libuvに依存してないDenoやBunも同様にPID 1だとシグナルを無視する挙動。 これが 意図的かどうかは不明
  8. PID 1 でも、 スクリプト側でシグナルを受け取る処理を書けばプロセ スを終了させることができる process.on("SIGTERM", () => { //

    SIGTERMが来たら、 自分自身を終了させる console.log("SIGTERM received, exiting..."); process.exit(0); }); 大丈夫、 対応方法はあります
  9. 参考:Node.js の Web サーバで Graceful Shutdown // 必要な箇所だけ抜粋 import http

    from "http"; function shutdown() { // 新しい接続を受け付けず、 処理中の接続が終わったら実行される server.close((error) => { if (error) { process.exit(1); } process.exit(0); }); } process.on("SIGTERM", shutdown);
  10. docker run --init とオプションを指定すると、 docker-init が PID 1 で動き、 いい感じにシグナルをハンドリング(転送)

    してくれる。 こうすれば node は PID 1 ではないので普段通りの挙動になる docker-init は Tini というライブラリを内部で使っているので、 直接 Tini をインストールす ることでも対応が可能 別解 : --init を使う
  11. シェル形式は sh のプロセスが増える PID1 で動く sh と node は特殊 exec

    形式にして、 安全に終了させる対応をしよう まとめ
  12. FROM node:22-slim # Debian CMD ["npm", "run", "start"] "scripts": {

    "start": "node idle.js" } // SIGTERM 対応コードを定義済み process.on("SIGTERM", () => { console.log("SIGTERM received, exiting..."); process.exit(0); });
  13. FROM node:22-alipne # Alpine CMD ["npm", "run", "start"] "scripts": {

    "start": "node idle.js" } // SIGTERM 対応コードもちゃんと定義済み process.on("SIGTERM", () => { console.log("SIGTERM received, exiting..."); process.exit(0); });
  14. 比較してみよう 終了するが SIGTERM 対応は実行されない FROM node:22-slim # Debian CMD ["npm",

    "run", "start"] "scripts": { "start": "node idle.js" } // SIGTERM 対応コードもちゃんと定義済み process.on("SIGTERM", () => { console.log("SIGTERM received, exiting..."); process.exit(0); }); 終了し、 SIGTERM 対応も実行される FROM node:22-alipne # Alpine CMD ["npm", "run", "start"] "scripts": { "start": "node idle.js" } // SIGTERM 対応コードもちゃんと定義済み process.on("SIGTERM", () => { console.log("SIGTERM received, exiting..."); process.exit(0); });
  15. 終了するが SIGTERM 対応は実行されない FROM node:22-slim # Debian 終了し、 SIGTERM 対応も実行される

    FROM node:22-alipne # Alpine 片方は Debian で、 もう片方は Alpine になっている OS が違う!
  16. /bin/sh は他のシェルへのエイリアスになっている Debian 系は sh は dash のエイリアス Alpine は

    sh は busybox sh のエイリアス RHEL 系は sh は bash のエイリアス
  17. npm run xxx を使わず node xxx.js を使う もしくは、 自前で exec

    を使ってプロセス置換する… 環境依存になりやすいのでオススメはしない 対応方法
  18. CMD は exec 形式 ["node", "server.js"] で書こう OS(シェル)によって挙動が異なるので注意しよう ランタイムによって PID

    1 の挙動が異なるので注意しよう もし SIGTERM 対応が必須なら npm run は避けよう 自分のコンテナが強制終了されたときのリスクを把握しよう まとめ