CIパイプライン上でDockerイメージをビルドしようとすると、この壁に必ずぶつかります。
コンテナの内側でDockerを動かす代表的な方法は2つです。「Docker in Docker(DinD)」と「docker.sockマウント」。どちらを選ぶかで、パイプラインの設計方針・セキュリティリスク・日々のトラブルの種類がまったく変わります。
この記事では、docker in docker 設計の仕組みを実際の設定ファイルと実行出力例を使って比較し、どちらを選ぶべきかの判断基準を示します。
動作確認環境:Docker 26.1.4 / GitLab Runner 17.2(Ubuntu 24.04 LTS)
この記事のポイント
・DinDはコンテナ内に独立したDockerデーモンを起動する方式で --privileged が必須になる
・docker.sockマウントはホストのデーモンを共有する方式で、実質rootと同等の権限になる
・セルフホスト型RunnerではDinD推奨、Kubernetes上ではKanikoへの移行を検討すること
・どちらも権限は広く、本番Kubernetesクラスターへの直接適用は避けること
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
CIパイプラインでDockerビルドが難しい理由
GitLab CIやGitHub Actionsのジョブは、それ自体がコンテナとして実行されます。GitLab Runnerは指定したDockerイメージ(例:`docker:26.1`や`ubuntu:24.04`)を起動し、その中でスクリプトを実行します。ここで問題が生じます。コンテナの中でさらに`docker build`コマンドを実行するには、Dockerデーモン(dockerd)が必要です。しかし通常のコンテナの中にはdockerdは存在しません。ホストのdockerdを使ってコンテナが起動しているため、コンテナ内から`docker`コマンドを叩いても「デーモンが見つからない」エラーになります。
この問題を解決するのが「DinD」と「docker.sockマウント」の2方式です。
Docker in Docker(DinD)方式の仕組みと設定
DinDは、CIジョブのコンテナ内に独立したDockerデーモンを起動する方式です。Docker公式が提供する`docker:dind`イメージをサービスコンテナとして追加し、そのデーモンに対してビルドジョブからDockerコマンドを実行します。1. DinDの基本アーキテクチャ
DinD方式では、2つのコンテナが協調して動作します。・ジョブコンテナ:`docker:26.1`イメージで起動し、`docker build`コマンドを実行する
・DinDサービスコンテナ:`docker:26.1-dind`イメージで起動し、独立したdockerdを提供する
ジョブコンテナは環境変数`DOCKER_HOST`でDinDコンテナのエンドポイントを指定し、そのデーモンに対してAPIリクエストを送ります。デフォルトではTLS通信(ポート2376)ですが、テスト用途ではTLSを無効にしてポート2375(平文)でも使えます。
実際の通信イメージは以下のとおりです。
# CIジョブ側(docker:26.1 コンテナ内)から見たデーモン接続確認 $ docker info 2>&1 | head -5 Client: Docker Engine - Community Version: 26.1.4 Context: default Debug Mode: false Server: Containers: 0 Running: 0 Paused: 0 Stopped: 0 Images: 0 Server Version: 26.1.4 Storage Driver: overlay2
2. --privilegedが必要な理由とセキュリティリスク
DinDのサービスコンテナ(docker:dind)は、内部でdockerdを起動するために Linux カーネルの名前空間(namespace)とcgroupsを直接操作します。そのため、`--privileged`フラグが必須になります。`--privileged`はコンテナとホストの間のほぼすべてのセキュリティ境界を取り除くフラグです。具体的には次の権限が付与されます。
・すべての Linux Capabilities(CAP_SYS_ADMIN 含む)が有効になる
・ホストのデバイスすべてにアクセス可能になる
・AppArmor / SELinux のプロファイルが無効化される
つまり DinD サービスコンテナが侵害された場合、攻撃者はホストのカーネルに対してほぼ自由なアクセスを得られます。「コンテナによる隔離」が実質的になくなると考えてください。
GitLab Runnerの設定でDinDを有効にする方法は、`/etc/gitlab-runner/config.toml`の該当エグゼキューターセクションに`privileged = true`を追加することです。
# /etc/gitlab-runner/config.toml(抜粋) [[runners]] name = "docker-runner-01" url = "https://gitlab.example.com" executor = "docker" [runners.docker] image = "docker:26.1" privileged = true # DinDサービスに --privileged を渡すために必要 volumes = ["/cache"]
3. GitLab CIでのDinD設定例(.gitlab-ci.yml)
DinD方式の`.gitlab-ci.yml`設定例を示します。TLSを無効にしてシンプルに動作させるパターンです。# .gitlab-ci.yml(DinD方式・TLS無効) stages: - build build-image: stage: build image: docker:26.1 services: - name: docker:26.1-dind alias: docker variables: DOCKER_HOST: tcp://docker:2375 DOCKER_TLS_CERTDIR: "" # TLS無効化 DOCKER_DRIVER: overlay2 before_script: - docker info script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
DinDの大きなメリットはジョブ間の完全な隔離です。各ジョブはそれぞれ独立したdockerdを持つため、あるジョブのビルドキャッシュが他のジョブに漏洩しません。一方でデメリットは、ジョブが終了するとdockerdごとすべてのキャッシュが消えるため、毎回フルビルドになりやすいことです。
docker.sockマウント方式の仕組みと設定
docker.sockマウント方式は、ホストのDockerデーモンが公開しているUnixソケット(`/var/run/docker.sock`)をCIコンテナ内にマウントし、ホストのデーモンを直接使う方式です。1. docker.sockマウントの基本アーキテクチャ
この方式ではdockerdは1つ(ホストのもの)だけです。CIコンテナはホストのUnixソケットを通じてホストデーモンにDockerコマンドを送ります。・ジョブコンテナ:`/var/run/docker.sock`がマウントされた`docker:26.1`イメージで起動する
・使用するデーモン:ホストのdockerd(追加の起動プロセスなし)
コンテナから見ると`docker info`の出力がホストのdockerdのものになります。
# docker.sockマウント方式でdocker infoを実行した例 $ docker info 2>&1 | grep -E "Server Version|Containers|Images" Containers: 12 # ホストで動いている他のコンテナ数が見える Running: 5 Paused: 0 Stopped: 7 Images: 47 # ホスト上のすべてのイメージが見える Server Version: 26.1.4
2. docker.sockマウントのセキュリティリスク
docker.sockへのアクセスは、実質的なrootアクセスとほぼ同等です。これはDockerの仕様に起因します。Dockerクライアントが`/var/run/docker.sock`に接続できると、次のことが可能になります。
・ホスト上のすべてのコンテナの起動・停止・削除
・`--privileged`コンテナをホスト上で新たに起動し、ホストのファイルシステムをマウントして任意のコードを実行
・ホスト上のすべてのDockerイメージの参照・削除
DinDの`--privileged`と異なるのは、侵害されたコンテナがそのジョブのスコープを超えてホスト全体を操作できる点です。CI/CDパイプラインのビルドスクリプトが外部から注入されたコマンドを実行する脆弱性(依存パッケージのsupply chain攻撃など)があった場合、ホスト全体が危険にさらされます。
また、docker.sock方式ではビルドしたイメージがホストのデーモンに蓄積されるため、長期間運用するとディスクを圧迫します。定期的な`docker system prune`が必要です。
3. GitLab CIでのdocker.sockマウント設定例
docker.sockマウント方式を使う場合、GitLab Runnerの設定でソケットをマウントします。# /etc/gitlab-runner/config.toml(docker.sockマウント方式) [[runners]] name = "docker-runner-02" url = "https://gitlab.example.com" executor = "docker" [runners.docker] image = "docker:26.1" privileged = false # --privilegedは不要 volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"]
# .gitlab-ci.yml(docker.sockマウント方式) stages: - build build-image: stage: build image: docker:26.1 # servicesセクションは不要 before_script: - docker info script: - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA . - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
2方式の比較と選定基準
2方式の主要な違いをまとめます。| 項目 | Docker in Docker(DinD) | docker.sockマウント |
|---|---|---|
| 権限要件 | --privileged が必須 | ソケットの読み書き権限のみ |
| デーモンの独立性 | ジョブごとに独立(高隔離) | ホストのデーモンを共有(低隔離) |
| セキュリティリスク | --privileged によるホストカーネル露出 | docker.sockによるroot相当アクセス |
| ビルドキャッシュ | ジョブ終了で消える(毎回フルビルド) | ホストで蓄積(再利用可能) |
| Kubernetes上での使用 | PSS/PSSで制限あり(要設定) | 通常は禁止(ホストパス依存) |
| 他ジョブへの影響 | 基本的になし | ホストのイメージ・コンテナを共有 |
| 設定の複雑さ | やや複雑(TLS設定・サービス定義) | シンプル(ソケットマウントのみ) |
選定の判断基準を示します。
・セルフホスト型GitLab Runnerで専用サーバーを使う場合:DinDが推奨です。`--privileged`のリスクはありますが、ジョブ間の隔離が保たれます。Runner専用サーバーであればホストが侵害されてもインフラ全体への被害を最小化できます。
・ビルドの高速化が最優先でセキュリティを許容できる場合:docker.sockマウントが有効です。ただし、Runner専用の分離されたサーバー上でのみ使用してください。
・共有インフラ・マルチテナント環境:どちらも推奨しません。後述のKanikoやBuildahを使ってください。
・Kubernetesクラスター上でCIを実行する場合:どちらも本番クラスターへの直接適用は避けてください。KanikoがKubernetes環境向けのデファクトスタンダードになっています。
よりセキュアな代替手段:Kaniko
DinDとdocker.sockマウントの両方に共通する問題は「権限が広すぎる」ことです。この課題を解決するために登場したのがKanikoです。KanikoはGoogleが開発したDockerイメージビルドツールで、Dockerデーモンなしでコンテナイメージをビルドできます。ユーザー空間でDockerfileの各命令を実行するため、`--privileged`も`/var/run/docker.sock`へのアクセスも不要です。
GitLab CIでKanikoを使う設定例を示します。
# .gitlab-ci.yml(Kaniko方式) stages: - build build-image: stage: build image: name: gcr.io/kaniko-project/executor:v1.23.0-debug entrypoint: [""] script: - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
# GitLab CI ジョブログ(抜粋) INFO[0001] Retrieving image manifest ubuntu:24.04 INFO[0003] Unpacking rootfs as cmd COPY requires it. INFO[0008] Taking snapshot of files... INFO[0009] COPY app /app INFO[0009] Taking snapshot of files... INFO[0010] RUN pip install -r /app/requirements.txt INFO[0045] Taking snapshot of files... INFO[0046] Pushing image to registry.example.com/mygroup/myapp:a1b2c3d4 INFO[0052] Pushed registry.example.com/mygroup/myapp:a1b2c3d4@sha256:...
よくあるトラブルと対処法
1. 「Cannot connect to the Docker daemon」エラー(DinD方式)
DinDサービスの起動タイミングとジョブコンテナの起動タイミングにズレがあると発生します。# エラー例 error during connect: Get "http://docker:2375/v1.47/info": dial tcp: lookup docker on 192.168.1.1:53: no such host # 対処:before_scriptでデーモン起動を待つ before_script: - until docker info; do echo "Waiting for docker..."; sleep 1; done
2. 「permission denied」エラー(docker.sockマウント方式)
ホストの`/var/run/docker.sock`がroot所有の場合、コンテナ内の非rootユーザーから接続できません。# エラー例 permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Head "http://%2Fvar%2Frun%2Fdocker.sock/..." # 確認コマンド(Runner実行ホストで実行) $ ls -la /var/run/docker.sock srw-rw---- 1 root docker 0 Sep 4 09:00 /var/run/docker.sock # 対処1:Runnerの実行ユーザーをdockerグループに追加 $ sudo usermod -aG docker gitlab-runner $ sudo systemctl restart gitlab-runner # 対処2:config.tomlでDockerイメージをroot実行にする(非推奨)
3. DinDで「overlay2: driver not supported」エラー
ホストのカーネルがDinDのネストされたoverlayfsをサポートしていない環境で発生します。# .gitlab-ci.yml側の変数に以下を追加して回避 variables: DOCKER_DRIVER: vfs # overlayfsが使えない環境向け(速度は低下する)
本記事のまとめ
CIパイプライン上でDockerイメージをビルドする2方式を比較しました。| やりたいこと | 推奨方式 |
|---|---|
| セルフホストRunner専用サーバーでジョブ隔離を重視する | Docker in Docker(DinD) |
| ビルドキャッシュを再利用して高速化したい(専用サーバー限定) | docker.sockマウント |
| Kubernetesクラスター上でビルドしたい | Kaniko |
| 最小権限でRHEL9/Rocky系環境でビルドしたい | Buildah(rootless) |
「DinDにすれば安全」「docker.sockにすれば楽」という単純な話ではありません。どちらも権限が広く、使う環境(専用サーバーか共有インフラか)と守るべき境界(ジョブ間か、ホストか)によって選択が変わります。本番Kubernetes環境ではKanikoへの移行を検討することをおすすめします。
次に読む記事:
・Dockerfileの書き方入門|自分のアプリをイメージ化する基礎と注意点
・docker execコマンドでコンテナ内を調査・デバッグする方法|docker logs・docker inspectの実践例も
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、DinDとdocker.sock方式の設計選定からKanikoを使ったセキュアなCIパイプライン構築まで、実践的なDocker講座を用意しています。
→ Dockerマスター講座の詳細はこちら >>
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:DockerのコンテナランタイムをOCI仕様から理解する|containerd・runc・dockerdの役割と連携フロー
- この記事の属するカテゴリ:Dockerへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます