Docker-in-Docker の2つの方法

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 デーモンにコンテナ内からアクセス出来るため、コンテナ内で悪意のあるプログラムを実行されると、以下のような事が起こり得ます。

  1. ホスト OS の Docker デーモンを使って privileged なコンテナを起動し、ホスト OS とファイルシステムを共有
  2. ホスト 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つのいずれの方法にせよ、マルチテナントアプリで使う場合にはセキュリティ的な問題があるため適していません。

we are hiring

優秀な技術者と一緒に、好きな場所で働きませんか

株式会社もばらぶでは、優秀で意欲に溢れる方を常に求めています。働く場所は自由、働く時間も柔軟に選択可能です。

現在、以下の職種を募集中です。ご興味のある方は、リンク先をご参照下さい。

コメントを残す