正式なドキュメントは英語版であり、この日本語訳はAI支援翻訳により作成された参考用のものです。日本語訳の一部の内容は人間によるレビューがまだ行われていないため、翻訳のタイミングにより英語版との間に差異が生じることがあります。最新かつ正確な情報については、英語版をご参照ください。

Docker-in-Dockerを使用する

  • プラン: Free、Premium、Ultimate
  • 提供形態: GitLab.com、GitLab Self-Managed、GitLab Dedicated

Docker-in-Docker(dind)とは、登録されたRunnerがDocker executorまたはKubernetes executorを使用することを意味します。executorは、Dockerが提供するDockerのコンテナイメージを使用して、CI/CDジョブを実行する。

Dockerイメージには、すべてのdockerツールが含まれており、イメージのコンテキストで、特権モードでジョブスクリプトを実行できます。

常に、docker:24.0.5のように、イメージの特定のバージョンを固定してください。docker:latestのようなタグを使用する場合、どのバージョンが使用されるかを制御できません。この操作は、新しいバージョンがリリースされたときに、互換性の問題を引き起こす可能性があります。

Docker executorでの使用

Docker executorを使用して、Dockerコンテナでジョブを実行できます。

Dockerデーモンは、TLS経由の接続をサポートしています。可能な場合はTLSを使用してください。TLSはDocker 19.03.12以降のデフォルトであり、GitLab.comインスタンスRunnerでサポートされています。

このタスクは--docker-privilegedを有効にします。これにより、コンテナのセキュリティメカニズムが実質的に無効になり、ホストが権限昇格にさらされます。このアクションにより、コンテナのブレイクアウトが発生する可能性があります。詳細については、Runtime privilege and Linux capabilities(ランタイム特権とLinux機能)を参照してください。

次の手順で、TLSを有効にしてDocker-in-Dockerを使用できます。

  1. GitLab Runnerをインストールします。

  2. 次のように、コマンドラインからGitLab Runnerを登録します。dockerおよびprivilegedモードを使用します。

    sudo gitlab-runner register -n \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor docker \
      --description "My Docker Runner" \
      --tag-list "tls-docker-runner" \
      --docker-image "docker:24.0.5-cli" \
      --docker-privileged \
      --docker-volumes "/certs/client"
    • このコマンドは、(ジョブレベルで指定されていない場合)docker:24.0.5-cliイメージを使用するように新しいRunnerを登録します。ビルドコンテナとサービスコンテナを起動するには、privilegedモードを使用します。Docker-in-Dockerを使用する場合は、Dockerコンテナで常にprivileged = trueを使用する必要があります。
    • このコマンドは、/certs/clientをサービスコンテナとビルドコンテナにマウントします。これは、Dockerクライアントがそのディレクトリ内の証明書を使用するために必要です。詳細については、Dockerイメージのドキュメントを参照してください。

    前述のコマンドは、次の例のようなconfig.tomlエントリを作成します。

    [[runners]]
      url = "https://gitlab.com/"
      token = TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:24.0.5-cli"
        privileged = true
        disable_cache = false
        volumes = ["/certs/client", "/cache"]
      [runners.cache]
        [runners.cache.s3]
        [runners.cache.gcs]
  3. これで、ジョブスクリプトでdockerを使用できるようになりました。docker:24.0.5-dindサービスを含めます:

    default:
      image: docker:24.0.5-cli
      services:
        - docker:24.0.5-dind
      before_script:
        - docker info
    
    variables:
      # When you use the dind service, you must instruct Docker to talk with
      # the daemon started inside of the service. The daemon is available
      # with a network connection instead of the default
      # /var/run/docker.sock socket. Docker 19.03 does this automatically
      # by setting the DOCKER_HOST in
      # https://github.com/docker-library/docker/blob/d45051476babc297257df490d22cbd806f1b11e4/19.03/docker-entrypoint.sh#L23-L29
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ci/services/#accessing-the-services.
      #
      # Specify to Docker where to create the certificates. Docker
      # creates them automatically on boot, and creates
      # `/certs/client` to share between the service and job
      # container, thanks to volume mount from config.toml
      DOCKER_TLS_CERTDIR: "/certs"
    
    build:
      stage: build
      tags:
        - tls-docker-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests

Docker executorでTLSが無効になっているDocker-in-Docker

場合によっては、TLSを無効にする正当な理由があります。たとえば、使用しているGitLab Runnerの設定を制御できない場合などです。

  1. 次のように、コマンドラインからGitLab Runnerを登録します。dockerおよびprivilegedモードを使用します。

    sudo gitlab-runner register -n \
      --url "https://gitlab.com/" \
      --registration-token REGISTRATION_TOKEN \
      --executor docker \
      --description "My Docker Runner" \
      --tag-list "no-tls-docker-runner" \
      --docker-image "docker:24.0.5-cli" \
      --docker-privileged

    前述のコマンドは、次の例のようなconfig.tomlエントリを作成します。

    [[runners]]
      url = "https://gitlab.com/"
      token = TOKEN
      executor = "docker"
      [runners.docker]
        tls_verify = false
        image = "docker:24.0.5-cli"
        privileged = true
        disable_cache = false
        volumes = ["/cache"]
      [runners.cache]
        [runners.cache.s3]
        [runners.cache.gcs]
  2. ジョブスクリプトにdocker:24.0.5-dindサービスを含めます。

    default:
      image: docker:24.0.5-cli
      services:
        - docker:24.0.5-dind
      before_script:
        - docker info
    
    variables:
      # When using dind service, you must instruct docker to talk with the
      # daemon started inside of the service. The daemon is available with
      # a network connection instead of the default /var/run/docker.sock socket.
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ci/services/#accessing-the-services
      #
      DOCKER_HOST: tcp://docker:2375
      #
      # This instructs Docker not to start over TLS.
      DOCKER_TLS_CERTDIR: ""
    
    build:
      stage: build
      tags:
        - no-tls-docker-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests

Docker-in-Dockerとビルドコンテナ間で共有するボリューム上でUnixソケットを使用する

Docker executorでTLSを有効にしたDocker-in-Dockerのアプローチでは、volumes = ["/certs/client", "/cache"]で定義されたディレクトリは、ビルド間で永続します。Docker executor Runnerを使用する複数のCI/CDジョブでDocker-in-Dockerサービスが有効になっている場合、各ジョブが同じディレクトリパスに書き込みます。このアプローチでは、競合が発生する可能性があります。

この競合に対処するには、Docker-in-Dockerサービスとビルドコンテナの間で共有されるボリューム上でUnixソケットを使用します。このアプローチは、パフォーマンスを向上させ、サービスとクライアント間の安全な接続を確立します。

以下は、ビルドコンテナとサービスコンテナ間で共有される一時ボリュームを設定したconfig.tomlのサンプルです。

[[runners]]
  url = "https://gitlab.com/"
  token = TOKEN
  executor = "docker"
  [runners.docker]
    image = "docker:24.0.5-cli"
    privileged = true
    volumes = ["/runner/services/docker"] # Temporary volume shared between build and service containers.

Docker-in-Dockerサービスはdocker.sockを作成します。Dockerクライアントは、このDocker Unixソケットボリュームを介してdocker.sockに接続します。

job:
  variables:
    # This variable is shared by both the DinD service and Docker client.
    # For the service, it will instruct DinD to create `docker.sock` here.
    # For the client, it tells the Docker client which Docker Unix socket to connect to.
    DOCKER_HOST: "unix:///runner/services/docker/docker.sock"
  services:
    - docker:24.0.5-dind
  image: docker:24.0.5-cli
  script:
    - docker version

Docker executorでプロキシが有効になっているDocker-in-Docker

docker pushコマンドを使用するには、プロキシの設定が必要になる場合があります。

詳細については、dindサービスの使用時のプロキシ設定を参照してください。

Kubernetes executorでの使用

Kubernetes executorを使用して、Dockerコンテナでジョブを実行できます。

次の手順で、KubernetesでTLSを有効にしてDocker-in-Dockerを使用できます。

  1. Helmチャートを使用して、values.ymlファイルを更新し、ボリュームマウントを指定します。

    runners:
      tags: "tls-dind-kubernetes-runner"
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
          [[runners.kubernetes.volumes.empty_dir]]
            name = "docker-certs"
            mount_path = "/certs/client"
            medium = "Memory"
  2. ジョブにdocker:24.0.5-dindサービスを含めます。

    default:
      image: docker:24.0.5-cli
      services:
        - name: docker:24.0.5-dind
          variables:
            HEALTHCHECK_TCP_PORT: "2376"
      before_script:
        - docker info
    
    variables:
      # When using dind service, you must instruct Docker to talk with
      # the daemon started inside of the service. The daemon is available
      # with a network connection instead of the default
      # /var/run/docker.sock socket.
      DOCKER_HOST: tcp://docker:2376
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ci/services/#accessing-the-services.
      #
      # Specify to Docker where to create the certificates. Docker
      # creates them automatically on boot, and creates
      # `/certs/client` to share between the service and job
      # container, thanks to volume mount from config.toml
      DOCKER_TLS_CERTDIR: "/certs"
      # These are usually specified by the entrypoint, however the
      # Kubernetes executor doesn't run entrypoints
      # https://gitlab.com/gitlab-org/gitlab-runner/-/issues/4125
      DOCKER_TLS_VERIFY: 1
      DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"
    
    build:
      stage: build
      tags:
        - tls-dind-kubernetes-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests

KubernetesでTLSが無効になっているDocker-in-Docker

KubernetesでTLSを無効にしてDocker-in-Dockerを使用するには、前述の例を次のように変更する必要があります。

  • values.ymlファイルから[[runners.kubernetes.volumes.empty_dir]]セクションを削除する。
  • DOCKER_HOST: tcp://docker:2375を指定し、ポートを2376から2375に変更する。
  • DOCKER_TLS_CERTDIR: ""を指定し、TLSを無効にしてDockerを起動するように指示する。

例:

  1. Helmチャートを使用して、values.ymlファイルを更新します。

    runners:
      tags: "no-tls-dind-kubernetes-runner"
      config: |
        [[runners]]
          [runners.kubernetes]
            image = "ubuntu:20.04"
            privileged = true
  2. これで、ジョブスクリプトでdockerを使用できるようになりました。docker:24.0.5-dindサービスを含めます:

    default:
      image: docker:24.0.5-cli
      services:
        - name: docker:24.0.5-dind
          variables:
            HEALTHCHECK_TCP_PORT: "2375"
      before_script:
        - docker info
    
    variables:
      # When using dind service, you must instruct Docker to talk with
      # the daemon started inside of the service. The daemon is available
      # with a network connection instead of the default
      # /var/run/docker.sock socket.
      DOCKER_HOST: tcp://docker:2375
      #
      # The 'docker' hostname is the alias of the service container as described at
      # https://docs.gitlab.com/ci/services/#accessing-the-services.
      #
      # This instructs Docker not to start over TLS.
      DOCKER_TLS_CERTDIR: ""
    build:
      stage: build
      tags:
        - no-tls-dind-kubernetes-runner
      script:
        - docker build -t my-docker-image .
        - docker run my-docker-image /script/to/run/tests

Docker-in-Dockerに関する既知の問題

Docker-in-Dockerは推奨される設定ですが、次の問題に注意してください。

  • docker-composeコマンド: この設定において、デフォルトではこのコマンドは使用できません。ジョブスクリプトでdocker-composeを使用するには、Docker Composeのインストール手順に従ってください。

  • キャッシュ: 各ジョブは新しい環境で実行されます。各ビルドが独自のDockerエンジンインスタンスを取得するため、同時ジョブが競合を引き起こすことはありません。ただし、レイヤーがキャッシュされないため、ジョブが遅くなる可能性があります。Dockerレイヤーキャッシュを参照してください。

  • ストレージドライバー: デフォルトでは、以前のバージョンのDockerではvfsストレージドライバーを使用し、ジョブごとにファイルシステムをコピーします。Docker 17.09以降では--storage-driver overlay2を使用し、これが推奨されるストレージドライバーです。詳細については、OverlayFSドライバーを使用するを参照してください。

  • ルートファイルシステム: docker:24.0.5-dindコンテナとRunnerコンテナはルートファイルシステムを共有しないため、ジョブの作業ディレクトリを子コンテナのマウントポイントとして使用できます。たとえば、子コンテナと共有するファイルがある場合は、/builds/$CI_PROJECT_PATHの下にサブディレクトリを作成し、それをマウントポイントとして使用できます。詳細については、イシュー41227を参照してください。

    variables:
      MOUNT_POINT: /builds/$CI_PROJECT_PATH/mnt
    script:
      - mkdir -p "$MOUNT_POINT"
      - docker run -v "$MOUNT_POINT:/mnt" my-docker-image