docker-composeでUbuntuのイメージのみを管理しようとしてハマったので、ruby-jpで質問した

環境

$ docker --version
Docker version 19.03.1

本題

Ubuntu18.04をベースイメージとしたDockerfileを作成し、docker-composeでコンテナの管理をしようとすると、イメージからコンテナを作成されていることが標準出力から確認できるのですが、attachをする過程でdocker-isucon-go-local_app_1 exited with code 0となります。docker-compose psで確認すると下記のように表示されます。docker-compose exec appを実行することでコンテナ内で操作をしたかったのですが、ERROR: No container found for app_1というエラーになってしまいます。

            Name                Command    State    Ports
---------------------------------------------------------
docker-isucon-go-local_app_1   /bin/bash   Exit 0        

該当のファイル群

[docker-compose.yml]
version: "3.7"
services:
  app:
    build:
      context: ./containers/app/
[containers/app/Dockerfile]
FROM ubuntu:18.04

公式ドキュメントよりdocker-composeは、サービスを管理するものであるということがわかりました。systemctlコマンドなどで管理するサービスのことを指すのではないかと思ったので、DockerfileのベースイメージをMySQLに変更すると、docker-compose psのStateがUpとなっていることを確認できました。

Compose is a tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application’s services. Then, with a single command, you create and start all the services from your configuration.

[containers/app/Dockerfile]
FROM mysql:5.7
ENV MYSQL_ALLOW_EMPTY_PASSWORD=yes
            Name                         Command             State          Ports       
----------------------------------------------------------------------------------------
docker-isucon-go-local_app_1   docker-entrypoint.sh mysqld   Up      3306/tcp, 33060/tcp

最終的にDockerfileは、golangをインストールしたサービスを動かすため問題ないのですが、デバック過程でdocker exec app bashでコンテナの状況を確認できないのは、辛いと思ったのでruby-jpで質問しました。

ruby-jp.github.io

そして、

  • docker rundocker execの違いを調べる
  • Ubuntuをベースイメージとした時の、CMDはbin/bashとなっているので、コンテナが終了してしまう

というアドバイスを頂きました。

そこで、runとexecのhelpを確認するとexecは動いてるコンテナに対してのコマンドであることがわかりました。

$ docker run --help
> Run a command in a new container
$ docker exec --help
> Run a command in a running container

ベースイメージのDockerfileCMD ["/bin/bash"]を実行しているためコンテナがrun状態でないということが問題でした。

Dockerfileでdocker run -itでビルドしたコンテナを操作できることを思い出したので、docker-composeでも同様のことができるのではないかと考えて調査してみると、公式ドキュメントに記載されていました。

Each of these is a single value, analogous to its docker run counterpart. Note that mac_address is a legacy option.

user: postgresql
working_dir: /code

domainname: foo.com
hostname: foo
ipc: host
mac_address: 02:42:ac:11:65:43

privileged: true


read_only: true
shm_size: 64M
stdin_open: true
tty: true

ここで、-itが何を行なっているか調べるために再度docker run --helpを実行すると、疑似端末を割り当てattachされていないコンテナに対しても標準入力を待ち続けることがわかりました。

-i, --interactive Keep STDIN open even if not attached
-t, --tty  Allocate a pseudo-TTY

これらより、tty: trueを追加すればよさそうなので追加すると無事docker-compose exec appでコンテナで操作を行うことができました。

version: "3.7"
services:
  app:
    build:
      context: ./containers/app/
    tty: true

知見

ruby-jpで質問した際に出た知見をまとめてます。

tty: true以外の方法で実現する場合

sleepコマンド

一定時間処理を待つ

使い方

sleep 7d

sleep inf

FROM ubuntu:18.04
CMD ["/bin/sh", "-c", "while true; sleep 1; done"]

stressコマンド

負荷をかけるツール

使い方

stress --vm-bytes 1B -m 1

やらない方がよい

tailコマンド

最終行から数行を表示するコマンド

使い方

tail -f /dev/null

readコマンド

標準入力から受け取った内容を1行単位で変数に入れるコマンド

使い方

read hoge

その他

sleep inifinitytail -f /dev/nullはSIGTERだとタイムアウト後、遅れて終了するので時間がかかるので、今回のケースでは利用しないほうが良いかもという意見もでました。これは、Linux関連の知識がないので現状調査方法がわからないためレベルが上がってから挑戦したいです。

参考文献

What is a TTY on Linux? (and How to Use the tty Command)

まとめ

  • 質問に答えてくれた方々、ありがとうございました
  • ruby-jpはいいぞ
  • ruby-jpで質問、ログを追ってアウトプットする最強の学習メソッドがあるらしい

Docker実践ガイド 第2版 impress top gearシリーズ

Docker実践ガイド 第2版 impress top gearシリーズ