頑張らないために頑張る

ゆるく頑張ります

Dockerfileに書いた'CMD'がVS Code経由だと実行されない

Posted at — Jul 29, 2022

概要

docker-composeのチュートリアルを進めている際に気付いたことです。

「どうも、コマンドラインでdocker-compose upからコンテナをビルドして実行するときと、Visual Studio Codeの拡張機能であるRemoteを使うのでは、同じファイル使っててもコンテナ構築後の挙動が違うぞ・・・?」

環境の情報

上記の環境で実行しています。

何したのよ

docker-composeのチュートリアルでは、Pythonのwebアプリケーション用フレームワークであるFlaskとNoSQLデータベースのRedisを使った、簡単なwebアプリケーションの作成を行います。ここではFlaskが稼働するコンテナと、Redisが稼働するコンテナをそれぞれ構築します。コンテナを構築したあとで、ブラウザでローカルホストに対しアクセスすると「n回アクセスしたよ」と表示されます。

ええ、表示されるはずなんです

image

こんな感じ。なお、表示されている回数はページ更新の際に増加しますので、更新ボタンをポチポチ押せばどんどん数が増えていきます。

チュートリアルでは、ターミナルでdocker compose upを実行してコンテナを構築します。ところがこのとき、たまたまいつものクセでVS CodeのRemote機能を使ってコンテナをビルドしちゃったんですね。「Reopen in container」っていう、VS Codeユーザーにはいつもの「おなじみの処理」です。「まぁ、別にコマンドでビルドしても、VS Code経由でビルドしても結果は変わらないだろ」・・・そんなふうに考えていた時期が俺にもありました

ご想像のとおり、実際は違ったわけです。どういうことだってばよ。

どう違うのよ

上記がコマンドラインによる実行と、VS Code経由による実行の差です。こんな感じで、VS Codeから実行したらFlaskが起動せず、ブラウザからアクセスができませんでした。

FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]

上記は、チュートリアルに記載されているDockerfileの内容です。Flaskは、最終行のCMDで起動するように指定されています。この行が無視されてFlaskが起動されていないか、起動されてもすぐさま終了してしまってアクセスできないのかなー、と思っていました。

原因調査

当初、ttyの永続化が原因かなーと思っていました。つまり、「起動はしたんだけど、すぐさま終了してしまっている」ケースです。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"

上記のとおり、チュートリアルにはのdocker-compose.ymlにはttyの記載がないため、永続化はされていないのです。そのため、「ああなんだ、tty: trueにすればいいんじゃん」と思ってttyの記載を追加しました。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    tty: true  ← ここ
  redis:
    image: "redis:buster"

追加してリビルドしてみます・・・が、結果は変わらずttyが直接の原因ではありませんでした。となると、「CMDが無視されている」という線を考えなければなりません。

けど、そんなことってあるの?という半信半疑のまま、Flaskが起動するような方法を探します。最終的にはdocker-compose.ymlにentrypointでFlaskを起動するコマンドを仕込んでおく、という手段で解決しました。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    entrypoint: "/bin/sh -c 'flask run'"  ← ここ
    tty: true
  redis:
    image: "redis:buster"

docker-compose.yml側でFlaskを起動するため、Dockerfile側のCMDは削除しておきます。

この修正を行うと、コマンドラインからdocker compose upで実行しても、VS Codeから実行してもFlaskが起動するようになりました。

まとめ

正直、VS CodeでCMDが無視される原因はいまだに不明です(参考にしたとあるwebページには、「VS Code経由でdocker runすると--entrypointオプションを自動的に付与するので、CMDが無視される」という記載があったのですが、当方の環境では--entrypointオプションの自動付与を確認できませんでした)。こういう「原因がわからないけどなんか回避できた」っていうケースが一番困る・・・のですが、とりあえず回避できました。

ベストプラクティスかどうかはかなり怪しいですが、とりあえず困ったらこんな回避方法はいかがでしょうか、というところでひとつよろしくお願いしまーす_(┐「ε:)_

参考

  1. Get started with Docker Compose
  2. VS Code Remote Containers で –privileged と /sbin/init を渡したコンテナで開発する方法
  3. Dockerfileによるビルド
  4. docker-compose.ymlの書き方について解説してみた
  5. Docker entrypoint not used
comments powered by Disqus