
Docker-in-Docker の2つの方法
目次
背景
昨年の終わりくらいからマルチテナントの MCP サーバー管理サービスを作っています。テナント毎に MCP サーバーの実行環境を隔離する方法を検討していて、結論としては以下の通りになりました。
- 無料ユーザー → 無料ユーザー用のコンテナ(複数テナントで共有)内で Bubblewrap 経由でプロセスを起動する
- 有料ユーザー → テナント毎に専用のコンテナを用意し、その中で普通にプロセスを起動する
ただ、検討中に Docker-in-Docker も試したので、それについて簡単に説明します。
Docker-in-Docker (DinD) とは
知らない方も名前から想像がつくと思いますが、Docker コンテナ内で新たな Docker コンテナを起動することです。

DinD の2つの方法
Host Socket Mount あるいは Docker-outside-of-Docker (DooD)
こちらの方法は Docker-outside-of-Docker (DooD) と呼んで DinD と区別する場合もありますが、Docker コンテナ内で Docker コンテナを動かすという意味で DinD と呼ぶ場合も多いので記載しています。
具体的には、ホスト OS で Docker デーモンにアクセスするためのソケット(デフォルトでは /var/run/docker.sock )を Docker コンテナにも共有し、Docker コンテナ内からホスト OS の Docker デーモンにアクセスする方法です。

Docker Compose の場合は、具体的には以下のようにします。
name: "dood-example"
services:
dood:
build: .
tty: true
working_dir: /src
volumes:
# ホスト OS の /var/run/docker.sock をコンテナ内の同じ場所にマウントする
- /var/run/docker.sock:/var/run/docker.sock
Docker デーモンはホスト OS のものを使いますが、docker コマンドはコンテナ内にも必要なので、Dockerfile 内でインストールするようにしてください。
コンテナ内で新たなコンテナを立ち上げる具体的な方法は以下の通りです。
# コンテナ内に入る
$ docker compose exec dood bash
# コンテナ内で新たな Docker コンテナを動かす
$ docker run --rm alpine:3.20 echo dind-ok
専用 Docker デーモン起動
こちらは狭義の DinD です。ホスト OS の Docker デーモンとは別で、新たなコンテナで専用の Docker デーモンを起動する方式です。
構成図を ChatGPT に描いてもらったのですが、イマイチ良いのが出来ませんでした。一応貼っておきます。
Docker Compose の例は以下の通りです。
services:
# Docker デーモン専用コンテナ
docker-dind:
image: docker:27-dind
privileged: true
environment:
- DOCKER_TLS_CERTDIR=
command: ["--host=tcp://0.0.0.0:2375", "--tls=false"]
volumes:
- docker-dind-data:/var/lib/docker
# 通常のコンテナ
dind:
build: .
tty: true
working_dir: /src
environment:
- DOCKER_HOST=tcp://docker-dind:2375
- DOCKER_TLS_CERTDIR=
マルチテナントの場合のセキュリティ的な問題
Host Socket Mount の場合 → ホストへアクセス可能になり得る
ホスト OS の Docker デーモンにコンテナ内からアクセス出来るため、コンテナ内で悪意のあるプログラムを実行されると、以下のような事が起こり得ます。
- ホスト OS の Docker デーモンを使って privileged なコンテナを起動し、ホスト OS とファイルシステムを共有
- ホスト OS の機密情報を読み取る
Docker デーモン専用コンテナの場合 → 他のテナントへアクセス可能になり得る
ホスト OS へのアクセスは防げますが、Docker デーモン専用コンテナへは TCP/IP 経由でアクセス出来るため、他のテナントへのアクセスが可能になり得ます。また、勝手に新たなコンテナを立てたりも出来るようになります。
privileged が必須
通常の Docker コンテナは、一部のシステムコールが使えなかったりデバイスへのアクセスが制限されていたりします。そのままだと DinD が出来ません。そのため privileged が必要です。privileged が何かについては以下のドキュメントを参照してください。
Running containers | Docker Docs
根本的な問題として、Docker 単体はマルチテナントに向かない
そもそも Docker 単体ではマルチテナントに対応した設計になっていません。1つの Docker コンテナを複数のテナントで共有するというのはセキュリティ上問題となりやすいです。
まとめ
Docker コンテナの中で新たに別の Docker コンテナを立てる(Docker-in-Docker / DinD)方法は2つあり、1つはホスト OS の Docker デーモンをソケット経由で共有する方法(Docker outside of Docker / DooD とも呼ばれる)、もう1つは別の Docker デーモンを専用コンテナで起動して、TCP/IP で接続する方法です(狭義の DinD)。
DinD が便利な場合はいくつかありますが、2つのいずれの方法にせよ、マルチテナントアプリで使う場合にはセキュリティ的な問題があるため適していません。

コメントを残す