うさぎでもわかるdindとdood
うさぎでもわかるdindとdood
1. はじめに
みなさん、こんにちは!今日はDockerの世界で少し混乱しやすい概念である、dind (docker-in-docker) と dood (docker-outside-of-docker) について、うさぎさんでも理解できるように解説していきます🐰
Dockerはコンテナ技術として広く普及し、多くの開発者や運用担当者の日常ツールとなっています。その便利さから「Dockerコンテナの中からさらにDockerを使いたい!」というニーズも増えてきました。例えば、CI/CDパイプラインをコンテナ化したい場合や、開発環境をコンテナで統一しつつDockerビルドも行いたい場合などです。
しかし、「コンテナ内からDockerを使う」というのは、単純に思えて実は複雑な課題です。これを解決するためのアプローチとして、dind と dood という2つの方法が広く知られています。
本記事では、以下のことを目指します:
- dindとdoodの基本概念を理解する
- それぞれの仕組みと特徴を図解で把握する
- 実装方法とユースケースを具体的に学ぶ
- どのような場面でどちらを選ぶべきかの判断基準を得る
この記事を読めば、「コンテナ内からDockerを使う」という一見複雑な課題も、うさぎさんのようにピョンピョン軽やかに乗り越えられるようになるでしょう!
それでは、Dockerの世界に飛び込んでいきましょう!
2. dindとdoodの基本概念
まずは、dindとdoodの違いを理解するために、Dockerの基本構造を確認しておきましょう。
Dockerの基本構造
Dockerは主に2つのコンポーネントから成り立っています:
- Docker Daemon (dockerd) - コンテナの作成・実行・管理などを行うサーバー側のコンポーネント
- Docker CLI - ユーザーがコマンドを入力し、Docker Daemonに指示を出すクライアント側のコンポーネント
通常、私たちがターミナルで docker
コマンドを実行すると、Docker CLIがDocker Daemonに命令を送り、処理が実行されます。両者はSocket(多くの場合は /var/run/docker.sock
)を通じて通信しています。
さて、ここで「コンテナ内からDockerを使いたい」という要件が出てきたとき、どのように実現するかで大きく2つのアプローチに分かれます。それが dind と dood です。
dind (Docker in Docker) とは
dindは、Docker in Docker の略で、その名の通り「Dockerコンテナ内に別のDocker環境を構築する」方法です。
コンテナ内で独立したDockerデーモンを起動し、コンテナ内に閉じた完全なDocker環境を構築します。つまり、ホストのDocker環境とは完全に分離された、独立したDocker世界を作るイメージです。
dood (Docker outside of Docker) とは
doodは、Docker outside of Docker の略で、「コンテナ内からホスト側のDockerデーモンを利用する」方法です。
コンテナ内にはDocker CLIのみをインストールし、ホスト側のDocker Daemonに命令を送ることで、実質的にホスト側のDocker環境を操作します。つまり、コンテナ内からホストのDocker環境を共有して使うイメージです。
基本的な違い
dindとdoodの基本的な違いは、どこでDockerデーモンが動いているかという点にあります。
DinD(Docker in Docker)の概念図
dindでは、コンテナ内に独自のDockerデーモンを起動し、完全に分離された環境でDockerを実行します。コンテナ内で作成されたイメージやコンテナは、ホスト側からは見えません。この方式を実現するには、コンテナを特権モード(--privileged
)で実行する必要があります。
DooD(Docker outside of Docker)の概念図
doodでは、ホスト側のDockerデーモンのソケットファイル(/var/run/docker.sock
)をコンテナ内にマウントし、コンテナからホスト側のDocker環境を操作します。コンテナ内から作成されたイメージやコンテナは、実際にはホスト側に作成され、ホスト側から見ることができます。
ここから、それぞれの方式について詳しく見ていきましょう。
3. Docker in Docker (DinD)の詳細
仕組みと構造
DinDは、その名の通り「Dockerコンテナの中でDockerを動かす」アプローチです。コンテナ内に完全な Docker デーモンをインストールして起動し、独立したDocker環境を構築します。
通常のDockerではコンテナ内からホストカーネルの一部機能にアクセスすることに制限がありますが、DinDでは特権モードを使うことでこの制限を回避し、コンテナ内でDockerデーモンを起動できるようにします。
DinDの特徴は以下の通りです:
- コンテナ内に完全な Docker 環境が構築される
- ホスト側の Docker 環境とは完全に分離されている
- コンテナ内で作成されたイメージ・コンテナはコンテナ内にのみ存在する
- コンテナが停止すると、その中で作成されたイメージ・コンテナも消える
実装方法
公式Docker-in-Dockerイメージを使用する方法
最も簡単な実装方法は、Dockerが公式に提供している docker:dind
イメージを使用することです。
# DinDコンテナを起動する
docker run --name dind-container --privileged -d docker:dind
# コンテナ内に入る
docker exec -it dind-container sh
# コンテナ内でDockerコマンドを実行
docker ps
docker images
ここで重要なのは、--privileged
フラグです。このフラグを付けることで、コンテナに特権モードでの実行を許可し、ホストのデバイスに直接アクセスできるようになります。
一般的なイメージでDinDを構築する方法
docker:dind
以外のイメージ(例:Ubuntu, CentOSなど)でDinDを実現したい場合は、コンテナ内でDockerをインストールする必要があります。
# 例:CentOSコンテナを特権モードで起動
docker run --name centos-dind --privileged -d centos:7
# コンテナに入る
docker exec -it centos-dind bash
# Docker インストール用のリポジトリを追加
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# Docker をインストール
yum install -y docker-ce docker-ce-cli containerd.io
# Docker を起動
systemctl start docker
# Docker の動作確認
docker ps
利点と欠点
利点
- 完全な隔離環境: ホスト側のDocker環境と完全に分離されているため、コンテナ同士の影響を心配する必要がありません。
- クリーンな環境: 毎回クリーンな状態から始められるため、テストや実験に適しています。
- 独立した設定: Dockerデーモンの設定をホストと別に行えるため、特定の設定が必要なテスト環境などに適しています。
- サンドボックス環境: プロジェクトごとに独立したDocker環境を提供できるため、異なるプロジェクト間での影響を防げます。
欠点
-
特権モードの必要性:
--privileged
フラグが必要なため、セキュリティリスクが高まります。 - リソース消費: コンテナ内でDockerデーモンを動かすためリソース(メモリ、CPU)を多く消費します。
- ストレージの二重利用: イメージやコンテナがホストとコンテナ内の両方で保存領域を消費します。
- パフォーマンス: 入れ子のコンテナ化によるオーバーヘッドで、パフォーマンスが低下する場合があります。
- 制限付き機能: 一部のDockerの機能が正しく動作しないことがあります(例:ボリュームマウント)。
実際のユースケース
DinDが特に役立つシナリオは以下の通りです:
- CI/CD環境でのDocker実行: ジョブごとに独立したDocker環境が必要な場合。
- マルチテナント環境: 複数のユーザーに独立したDocker環境を提供したい場合。
- Docker開発自体: Docker自体の開発やテスト(実際にDinDはこの目的で作られました)。
- 隔離された実験環境: Docker関連の実験を安全に行いたい場合。
- 教育用環境: ユーザーごとに独立したDocker環境を提供する教育プラットフォーム。
代表的な例としては、GitLab CI/CDのRunnerをDockerコンテナで実行しつつ、その中でDockerビルドを行うケースがあります。
4. Docker outside of Docker (DooD)の詳細
仕組みと構造
DooD(Docker outside of Docker)は、「コンテナ内からホスト側のDockerデーモンを使用する」アプローチです。コンテナ内にはDocker CLIのみをインストールし、ホスト側のDocker Daemonに命令を送って操作します。
具体的には、ホスト側の Docker デーモンとの通信に使われるソケットファイル(多くの場合は /var/run/docker.sock
)をコンテナ内にマウントすることで実現します。これにより、コンテナ内のDocker CLIコマンドは、すべてホスト側のDocker Daemonに転送されて実行されます。
DooDの特徴は以下の通りです:
- コンテナ内にはDocker CLIのみがインストールされる
- ホスト側の Docker 環境を共有して利用する
- コンテナ内から作成されたイメージ・コンテナはホスト側に作成され、ホストから見える
- コンテナが停止しても、その操作で作成されたイメージ・コンテナはホスト側に残る
実装方法
Docker CLIイメージを使用する方法
最も簡単な実装方法は、Docker CLIが含まれているイメージを使用し、ホストのDocker socketをマウントする方法です。
# DooDコンテナを起動する(docker.sockをマウント)
docker run --name dood-container -v /var/run/docker.sock:/var/run/docker.sock -d docker:cli
# コンテナ内に入る
docker exec -it dood-container sh
# コンテナ内でDockerコマンドを実行
# これらのコマンドはホスト側のDocker環境に対して実行される
docker ps
docker images
ここで重要なのは、-v /var/run/docker.sock:/var/run/docker.sock
オプションです。このオプションによって、ホスト側のDocker socketをコンテナ内にマウントし、コンテナからホスト側のDockerデーモンにアクセスできるようになります。
一般的なイメージでDooDを構築する方法
任意のベースイメージ(Ubuntu, CentOSなど)でDooDを実現するには、Docker CLIのみをインストールし、ホストのDocker socketをマウントします。
# 例:Ubuntuコンテナを起動し、docker.sockをマウント
docker run --name ubuntu-dood -v /var/run/docker.sock:/var/run/docker.sock -d ubuntu:20.04
# コンテナに入る
docker exec -it ubuntu-dood bash
# 必要なパッケージをインストール
apt-get update
apt-get install -y apt-transport-https ca-certificates curl gnupg lsb-release
# Dockerのリポジトリ追加
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
# Docker CLIのみをインストール(デーモンは不要)
apt-get update
apt-get install -y docker-ce-cli
# Docker の動作確認(ホスト側のDockerデーモンが使われる)
docker ps
利点と欠点
利点
- シンプルな実装: 特権モードが不要で、単純にソケットファイルをマウントするだけで実現できます。
- リソース効率: Docker Daemonはホスト側で1つだけ動くため、リソース(メモリ、CPU)の消費が少なくて済みます。
- ストレージ効率: イメージはホスト側だけに保存されるため、ストレージの二重使用が発生しません。
- 永続性: コンテナを停止・削除しても、作成したコンテナやイメージはホスト側に残ります。
- すべての機能: ホスト側のDockerの機能をすべて利用できます。
欠点
- セキュリティリスク: Docker socketをマウントすることは、コンテナに実質的にホストへの高い権限を与えることになり、セキュリティリスクがあります。
- 環境の共有: 環境が分離されないため、複数のコンテナが同じDockerデーモンを使うとコンテナ間で干渉する可能性があります。
- クリーンな環境の欠如: ホスト側のDockerを使うため、毎回クリーンな環境から始められません。
- ホスト側への影響: コンテナ内の操作がホスト側のDocker環境に直接影響します。
- ユーザー権限の問題: コンテナ内のユーザーがdockerグループに属していない場合、権限の問題が発生する可能性があります。
実際のユースケース
DooDが特に役立つシナリオは以下の通りです:
- ローカル開発環境: 開発者のマシン上で、コンテナ化された開発ツールからDockerを使用したい場合。
- CI/CDツール: CI/CDツール(Jenkins, Gitlabなど)をコンテナで動かしつつ、ビルドもDocker上で行う場合。
- 運用自動化ツール: Ansible, Terraform, Puppetなどの自動化ツールをコンテナで実行しつつ、Dockerを操作したい場合。
- 管理ツール: Docker管理用のUIやツールをコンテナで実行する場合。
- モニタリングツール: Dockerコンテナを監視するツールをコンテナ内から実行する場合。
代表的な例としては、Jenkinsを含むCIツールをコンテナ内で実行し、ビルドプロセスでDockerを使用するケースがあります。また、開発環境をコンテナ化しつつ、そこからDockerビルドを行う場合にもよく使われます。
5. dindとdoodの比較
ここでは、DinDとDooDを様々な観点から比較し、どちらを選ぶべきかの判断材料を提供します。
機能面の比較
- 隔離性: DinDはコンテナごとに独立したDocker環境を提供するため、隔離性が高い。DooDは共有環境なので隔離性が低い。
- データの永続性: DinDではコンテナが削除されるとすべてのデータが失われるが、DooDではホスト側に残る。
- ボリュームマウント: DinDではボリュームマウントが複雑になる場合がある。特にホストパスのマウントは二重のマッピングが必要になることも。DooDではホスト側と同じようにボリュームマウントができる。
- ネットワーク: DinDでは入れ子のコンテナネットワークとなり、複雑になる場合がある。DooDではホスト側のネットワークをそのまま利用できる。
パフォーマンスの比較
- 起動時間: DinDはデーモンプロセスの起動が必要なため、起動に時間がかかる。DooDは起動が速い。
- メモリ使用量: DinDは別のDockerデーモンを実行するため、メモリ使用量が多い。DooDは基本的にCLIのみなので軽量。
- ディスク使用量: DinDではイメージが二重に保存される可能性があるため、ディスク使用量が増加する。DooDではイメージはホスト側の1か所だけに保存される。
- CPU使用率: DinDはデーモンプロセスが動くため、CPU使用率が高くなる傾向がある。DooDはCLIのリクエストのみなので比較的軽い。
セキュリティ面の比較
両方とも、それぞれの理由でセキュリティ上の懸念があります:
-
DinD:
--privileged
フラグを使うため、コンテナがホストのリソースに高い権限でアクセスできてしまう。 -
DooD:
docker.sock
をマウントすることで、コンテナからホスト環境を完全に制御できる状態になる。実質的に特権モードと同等のリスクがある。
どちらの場合も、信頼できないコードをこれらのコンテナ内で実行することは危険です。セキュリティが最も重要な環境では、別の方法を検討した方が良いでしょう。
利用シーンによる選択基準
以下のような場合にはDinDを選ぶと良いでしょう:
- 完全に隔離されたDocker環境が必要な場合
- マルチテナント環境でユーザーごとに分離したい場合
- クリーンな環境から始めたいテスト環境
- Docker自体の開発やテストを行う場合
以下のような場合にはDooDを選ぶと良いでしょう:
- リソース効率が重要な場合
- ホスト側と同じイメージやコンテナを扱いたい場合
- シンプルな実装で済ませたい場合
- 開発環境でローカルマシンのDockerを操作したい場合
6. 実装例
CI/CDパイプラインでのDinDの実装例
GitLab CI/CDでDinDを使用する例を見てみましょう。以下は.gitlab-ci.yml
の例です。
# GitLab CI/CDでのDinD実装例
build_image:
image: docker:dind
stage: build
services:
- docker:dind
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: "/certs"
DOCKER_TLS_VERIFY: 1
DOCKER_CERT_PATH: "/certs/client"
before_script:
- docker info
script:
- docker build -t my-app:$CI_COMMIT_SHORT_SHA .
- docker push my-app:$CI_COMMIT_SHORT_SHA
この設定では、GitLab CIのランナーがDinDサービスを起動し、その中でDockerビルドを実行します。DOCKER_HOST
変数を設定することで、DinDサービスに接続するようにしています。
CI/CDパイプラインでのDooDの実装例
以下は、JenkinsでDooDを使用するDockerfile
と設定例です。
# JenkinsでDooDを使用するためのDockerfile
FROM jenkins/jenkins:lts
USER root
# Docker CLIのインストール
RUN apt-get update && \
apt-get -y install apt-transport-https ca-certificates curl gnupg2 software-properties-common && \
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add - && \
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable" && \
apt-get update && \
apt-get -y install docker-ce-cli
USER jenkins
そして、このイメージを使ってJenkinsコンテナを起動する際には、以下のようにdocker.sockをマウントします。
docker run -p 8080:8080 -p 50000:50000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v jenkins_home:/var/jenkins_home \
jenkins-with-docker-cli
これで、Jenkins内のジョブからホスト側のDockerを操作できるようになります。
よくあるトラブルと解決法
DinDでのよくある問題
-
ボリュームマウントの問題
問題: コンテナ内のDockerからホストのディレクトリをマウントしようとしても、パスが見つからない。
解決策: DinDでホストのディレクトリをマウントするには、まずホストからDinDコンテナにディレクトリをマウントし、その後DinDコンテナから内部コンテナにマウントする必要があります。
-
パフォーマンスの問題
問題: DinDが遅く、リソースを多く消費する。
解決策: キャッシュボリュームを使用して、イメージレイヤーを保存する場所を永続化することでパフォーマンスを向上させることができます。
docker run --privileged --name dind \ -v dind-cache:/var/lib/docker \ -d docker:dind
DooDでのよくある問題
-
権限の問題
問題:
docker.sock
にアクセスする権限がない。解決策: コンテナ内のユーザーをホスト側の
docker
グループに追加するか、コンテナをroot
で実行する必要があります。ただし、root
での実行はセキュリティリスクが高くなります。FROM ubuntu:20.04 RUN apt-get update && apt-get install -y docker-ce-cli # コンテナ内のdockerグループIDをホスト側と合わせる RUN groupadd -g 999 docker && usermod -aG docker myuser USER myuser
-
パス解決の問題
問題: コンテナ内とホスト側でパスが異なるため、ボリュームマウントが意図通りに動作しない。
解決策: 絶対パスではなく相対パスを使用するか、環境変数を使ってパスをマッピングします。
7. CI/CDパイプラインでの活用例
Jenkinsでの利用
Jenkinsをコンテナで実行し、そこからDockerビルドを行うシナリオは非常に一般的です。
DinDでのJenkins設定
version: '3'
services:
jenkins:
image: jenkins/jenkins:lts
ports:
- "8080:8080"
volumes:
- jenkins_home:/var/jenkins_home
docker:
image: docker:dind
privileged: true
environment:
- DOCKER_TLS_CERTDIR=/certs
volumes:
- jenkins-docker-certs:/certs/client
- jenkins-docker-data:/var/lib/docker
expose:
- "2376"
volumes:
jenkins_home:
jenkins-docker-certs:
jenkins-docker-data:
Jenkins内のジョブでは、DOCKER_HOST
環境変数を設定して、DinDサービスに接続します。
DooDでのJenkins設定
version: '3'
services:
jenkins:
image: jenkins-with-docker-cli
ports:
- "8080:8080"
volumes:
- jenkins_home:/var/jenkins_home
- /var/run/docker.sock:/var/run/docker.sock
volumes:
jenkins_home:
GitHub Actionsでの利用
GitHub Actionsでもdindとdoodを利用できます。
DinDでのGitHub Actions設定
name: Build with DinD
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Docker
uses: docker/setup-buildx-action@v1
with:
driver-opts: image=moby/buildkit:master
- name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: false
tags: myapp:latest
DooDでのGitHub Actions設定
name: Build with DooD
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Build image
run: |
docker build -t myapp:latest .
docker run --rm myapp:latest
その他のCI/CDツールでの活用
TravisCI、CircleCI、GitLab CI/CDなど、ほとんどの主要なCI/CDツールはdindとdoodの両方をサポートしています。選択する際の一般的なガイドラインは:
- 完全な隔離が必要な場合はDinD
- シンプルさとパフォーマンスが重要な場合はDooD
を選ぶとよいでしょう。
8. まとめ
本記事では、Docker in Docker (DinD) と Docker outside of Docker (DooD) という、コンテナ内からDockerを使用するための2つのアプローチについて解説しました。
dindとdoodの使いどころ
-
DinDは、完全に分離されたDocker環境が必要な場合に適しています。特に複数のユーザーが独立した環境を必要とするマルチテナント環境や、クリーンな状態からのテスト実行に向いています。ただし、特権モードが必要でリソース消費が多いという欠点があります。
-
DooDは、シンプルな実装と効率的なリソース利用が求められる場合に適しています。ローカル開発環境や、ホスト側のDockerリソースを共有したいケースに向いています。ただし、セキュリティリスクや環境の共有による影響に注意が必要です。
最後に
どちらのアプローチも、それぞれに長所と短所があります。あなたのユースケースに合った方法を選ぶことが大切です。セキュリティが最重要の環境では、どちらの方法も慎重に使用する必要があります。
これからDockerコンテナからDockerを操作する際に、この記事がうさぎさんのようにピョンピョン軽やかな選択の手助けになれば幸いです🐰
Discussion