0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Docker Composeのstdin_openとttyの違いを徹底検証してみた

Posted at

はじめに

Docker Composeで開発環境を構築する際、stdin_openttyという2つの設定項目を目にすることがあります。

services:
  app:
    stdin_open: true
    tty: true

「両方trueにしておけば動く」というのは分かるものの、実際にこれらが何をしているのか片方だけでも動くのか、気になったことはありませんか?

本記事では、Haskell開発環境を例に、実際に4パターンの設定を試して動作を検証してみました。

TL;DR(結論)

  • 両方false: ❌ コンテナがすぐ停止、使用不可
  • stdin_open=true, tty=false: ⚠️ 動作するが機能制限あり
  • stdin_open=false, tty=true: ⚠️ 動作するが機能制限あり
  • 両方true: ✅ 完全動作、対話型コンテナには必須

対話型シェル(bash、ghciなど)を使う開発環境では、必ず両方をtrueに設定することを推奨します。

検証環境

  • Docker Compose v2
  • Haskell 9.8コンテナ
  • macOS (Darwin 24.3.0)
Dockerfile
FROM haskell:9.8

WORKDIR /app
RUN ghc --version && stack --version
CMD ["/bin/bash"]

stdin_openttyとは?

stdin_open

標準入力を開いたままにする設定です。

  • 対応するDockerコマンド: docker run -i
  • 役割: コンテナが標準入力からデータを受け取れるようにする
  • 用途: 対話型プログラムで入力を受け付ける

tty

擬似TTY(仮想ターミナル)を割り当てる設定です。

  • 対応するDockerコマンド: docker run -t
  • 役割: ターミナルのような環境を提供
  • 用途: プロンプト表示、色付き出力、カーソル制御など

通常、-i-tは組み合わせて-itとして使われます。

検証方法

4パターンの設定ファイルを用意して、それぞれの動作を確認します。

テスト1: 両方false

docker-compose.test1.yml
services:
  haskell:
    build: .
    container_name: haskell_test1
    volumes:
      - ./app:/app
    stdin_open: false
    tty: false
    working_dir: /app

テスト2: stdin_open=true, tty=false

docker-compose.test2.yml
services:
  haskell:
    build: .
    container_name: haskell_test2
    volumes:
      - ./app:/app
    stdin_open: true
    tty: false
    working_dir: /app

テスト3: stdin_open=false, tty=true

docker-compose.test3.yml
services:
  haskell:
    build: .
    container_name: haskell_test3
    volumes:
      - ./app:/app
    stdin_open: false
    tty: true
    working_dir: /app

テスト4: 両方true

docker-compose.test4.yml
services:
  haskell:
    build: .
    container_name: haskell_test4
    volumes:
      - ./app:/app
    stdin_open: true
    tty: true
    working_dir: /app

検証結果

結果一覧表

テスト stdin_open tty コンテナ起動 docker-compose exec 実際の設定
1 false false ❌ すぐ停止 ❌ 使えない OpenStdin: false, Tty: false
2 true false ✅ 起動維持 ✅ 動作する OpenStdin: true, Tty: false
3 false true ✅ 起動維持 ✅ 動作する OpenStdin: false, Tty: true
4 true true ✅ 起動維持 ✅ 動作する OpenStdin: true, Tty: true

テスト1: 両方false - 完全にNG

$ docker-compose -f docker-compose.test1.yml up -d
$ docker-compose -f docker-compose.test1.yml exec haskell bash
service "haskell" is not running

結果:

  • ❌ コンテナが起動後すぐに停止
  • docker-compose execでエラー
  • ❌ 完全に使用不可

理由:

標準入力もTTYも無効なので、CMD ["/bin/bash"]で起動したbashがすぐに終了してしまいます。入力待ちする理由がないためです。

テスト2: stdin_open=true のみ - 一応動く

$ docker-compose -f docker-compose.test2.yml up -d
$ docker inspect haskell_test2 --format='OpenStdin: {{.Config.OpenStdin}}, Tty: {{.Config.Tty}}'
OpenStdin: true, Tty: false

$ docker-compose -f docker-compose.test2.yml exec haskell bash -c 'echo "Hello"'
Hello  # ✅ 動作する

$ docker-compose -f docker-compose.test2.yml exec haskell bash -c 'tty'
not a tty  # ⚠️ TTYではない

$ docker-compose -f docker-compose.test2.yml exec haskell bash -c 'echo $TERM'
dumb  # ⚠️ 機能制限あり

結果:

  • ✅ コンテナは起動を維持
  • ✅ コマンド実行は可能
  • ⚠️ 完全な端末エミュレーションはない
  • ⚠️ TERM=dumbで色付き出力などが制限される

テスト3: tty=true のみ - 一応動く

$ docker-compose -f docker-compose.test3.yml up -d
$ docker inspect haskell_test3 --format='OpenStdin: {{.Config.OpenStdin}}, Tty: {{.Config.Tty}}'
OpenStdin: false, Tty: true

$ docker-compose -f docker-compose.test3.yml exec haskell bash -c 'echo "Hello"'
Hello  # ✅ 動作する

$ docker-compose -f docker-compose.test3.yml exec haskell bash -c 'tty'
not a tty  # ⚠️ TTYではない

$ docker-compose -f docker-compose.test3.yml exec haskell bash -c 'echo $TERM'
dumb  # ⚠️ 機能制限あり

結果:

  • テスト2とほぼ同じ
  • ✅ コンテナは起動を維持
  • ✅ コマンド実行は可能
  • ⚠️ 完全な端末機能はない

テスト4: 両方true - 完璧に動作

$ docker-compose -f docker-compose.test4.yml up -d
$ docker inspect haskell_test4 --format='OpenStdin: {{.Config.OpenStdin}}, Tty: {{.Config.Tty}}'
OpenStdin: true, Tty: true

$ docker-compose -f docker-compose.test4.yml exec haskell bash
root@xxx:/app# ghci
GHCi, version 9.8.x: https://www.haskell.org/ghc/  :? for help
Prelude> 1 + 1
2
Prelude> :quit
root@xxx:/app# exit

結果:

  • ✅ 完全な対話型シェルが動作
  • ✅ プロンプトが正常に表示
  • ✅ ghciなどの対話型プログラムが快適に動作
  • ✅ 色付き出力やカーソル制御が正常

重要な発見

1. docker-compose execは自動的に-itを使う

実は、docker-compose execコマンドはデフォルトで-itオプションを付けて実行されます。

# 内部的にはこう実行されている
docker exec -it haskell_test bash

そのため、docker-compose.ymlの設定に関わらず、execコマンド自体はある程度動作します。

これが「片方だけでも動く」理由です。

2. 設定の真の役割は「コンテナ起動時」の動作

stdin_openttyは、コンテナ起動時(docker-compose up)の動作に影響します:

  • stdin_open: true → コンテナが標準入力を保持し、終了しない
  • tty: true → コンテナにTTYを割り当て、終了しない

3. なぜ片方だけでも起動し続けるのか

  • stdin_open=true のみ: 標準入力が開いているのでbashが終了しない
  • tty=true のみ: TTYが割り当てられているのでbashが終了しない
  • 両方 false: 入力もTTYもないので、bashがすぐに終了

つまり、どちらか一方があれば、bashは「何か入力があるかもしれない」と判断して待機し続けるわけです。

ベストプラクティス

対話型開発環境の場合(推奨)

services:
  haskell:
    build: .
    container_name: haskell_dev
    volumes:
      - ./app:/app
    stdin_open: true  # ✅ 必須
    tty: true         # ✅ 必須
    working_dir: /app

理由:

  1. コンテナがバックグラウンドで起動し続ける
  2. docker-compose execで完全な対話型シェルが使える
  3. 色付き出力、カーソル制御などが正常に動作
  4. ghci、python、nodeなどの対話型プログラムが快適
  5. 明示的で意図が分かりやすい

バックグラウンドサービスの場合(不要)

services:
  nginx:
    image: nginx
    # stdin_open: true  ← 不要
    # tty: true         ← 不要
    ports:
      - "80:80"

Webサーバーなどのデーモンプロセスには不要です。むしろリソースの無駄になります。

まとめ

  • stdin_open: 標準入力を開いたままにする(-i相当)
  • tty: 擬似TTYを割り当てる(-t相当)
  • 両方必要な理由: 完全な対話型ターミナル環境を実現するため
  • 片方でも動く理由: docker-compose execが自動的に-itを付与するため
  • 推奨: 対話型コンテナでは必ず両方をtrue

「おまじない」のように書いていた設定の意味が、実験を通じて理解できました!

参考資料

検証用スクリプト

本記事の検証に使用したスクリプトは以下です:

test_runner.sh
#!/bin/bash

echo "stdin_open と tty のテスト"

run_test() {
    local test_num=$1
    local compose_file="docker-compose.test${test_num}.yml"

    echo "テスト ${test_num}"
    docker-compose -f "$compose_file" up -d
    sleep 2

    echo "実際の設定:"
    docker inspect haskell_test${test_num} --format='OpenStdin: {{.Config.OpenStdin}}, Tty: {{.Config.Tty}}'

    echo "コマンド実行テスト:"
    docker-compose -f "$compose_file" exec -T haskell bash -c 'echo "Hello"' 2>&1

    docker-compose -f "$compose_file" down > /dev/null 2>&1
}

run_test 1
run_test 2
run_test 3
run_test 4

実際に試してみたい方は、ぜひ手元で実行してみてください!


この記事が、Docker Composeの設定を理解する助けになれば幸いです。
質問やフィードバックがあれば、コメントでお知らせください!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?