Dockerを使用してDockerイメージをビルドする
- プラン: Free、Premium、Ultimate
- 提供形態: GitLab.com、GitLab Self-Managed、GitLab Dedicated
DockerとGitLab CI/CDを使用して、コンテナイメージをビルド、テスト、およびプッシュできます。CI/CDジョブでDockerコマンドを実行するには、GitLab Runnerがdockerコマンドをサポートするように設定する必要があります。
選択するアプローチは、インフラストラクチャ、executorタイプ、およびセキュリティ要件によって異なります。一部のアプローチでは、Runnerでprivilegedモードが必要です。privilegedモードを有効にできない場合は、Dockerの代替手段を使用してください。
| アプローチ | executor | 特権モード | OS |
|---|---|---|---|
| Shell executor | Shell | いいえ | Linux |
| Docker-in-Docker | Docker, Kubernetes | はい | Linux |
| Dockerソケットバインディング | Docker, Kubernetes | いいえ | Linux |
| Dockerパイプバインディング | Docker, Kubernetes | いいえ | Windows |
Shell executorを使用する
CI/CDジョブにDockerコマンドを含めるには、shell executorを使用するようにRunnerを設定します。この設定では、gitlab-runnerユーザーがDockerコマンドを実行しますが、そのためには権限が必要です。
GitLab Runnerをインストールします。
Runnerを登録します。
shellexecutorを選択します。例:sudo gitlab-runner register -n \ --url "https://gitlab.com/" \ --registration-token REGISTRATION_TOKEN \ --executor shell \ --description "My Runner"GitLab Runnerがインストールされているサーバーに、Docker Engineをインストールします。サポートされているプラットフォームの一覧を確認してください。
gitlab-runnerユーザーをdockerグループに追加します。sudo usermod -aG docker gitlab-runnergitlab-runnerにDockerへのアクセス権があることを確認します。sudo -u gitlab-runner -H docker infoGitLabで、
docker infoを.gitlab-ci.ymlに追加して、Dockerが動作していることを確認します。default: before_script: - docker info build_image: script: - docker build -t my-docker-image . - docker run my-docker-image /script/to/run/tests
これで、dockerコマンドを使用できるようになります(必要に応じてDocker Composeをインストールします)。
gitlab-runnerをdockerグループに追加すると、事実上gitlab-runnerに完全なroot権限を付与することになります。詳細については、dockerグループのセキュリティを参照してください。
Docker-in-Dockerを使用する
Docker-in-Docker(dind)とは、登録されたRunnerがDocker executorまたはKubernetes executorを使用し、そのexecutorがDockerのコンテナイメージを使用してCI/CDジョブを実行することを意味します。
各ジョブは独自の分離されたDockerデーモンを取得するため、並行するジョブが競合することはありません。これは、Runnerがprivilegedモードをサポートしている場合に推奨されるアプローチです。
設定手順については、Docker-in-Dockerを使用するを参照してください。
Dockerソケットバインディングを使用する
CI/CDジョブでDockerコマンドを使用するには、/var/run/docker.sockをビルドコンテナにバインドマウントします。これにより、イメージのコンテキストでDockerを使用できるようになります。
Dockerソケットをバインドすると、docker:24.0.5-dindをサービスとして使用できません。ボリュームバインディングはサービスにも影響し、互換性が失われます。
Docker executorでDockerソケットバインディングを使用する
Docker executorでDockerソケットをマウントするには、[runners.docker]セクションのボリュームに"/var/run/docker.sock:/var/run/docker.sock"を追加します。
Runnerの登録時に
/var/run/docker.sockをマウントするには、次のオプションを含めます。sudo gitlab-runner register \ --non-interactive \ --url "https://gitlab.com/" \ --registration-token REGISTRATION_TOKEN \ --executor "docker" \ --description "docker-runner" \ --tag-list "socket-binding-docker-runner" \ --docker-image "docker:24.0.5-cli" \ --docker-volumes "/var/run/docker.sock:/var/run/docker.sock"前述のコマンドは、次の例のような
config.tomlエントリを作成します。[[runners]] url = "https://gitlab.com/" token = RUNNER_TOKEN executor = "docker" [runners.docker] tls_verify = false image = "docker:24.0.5-cli" privileged = false disable_cache = false volumes = ["/var/run/docker.sock:/var/run/docker.sock", "/cache"] [runners.cache] Insecure = falseジョブスクリプトでDockerを使用します。
default: image: docker:24.0.5-cli before_script: - docker info build: stage: build tags: - socket-binding-docker-runner script: - docker build -t my-docker-image . - docker run my-docker-image /script/to/run/tests
Kubernetes executorでDockerソケットバインディングを使用する
Kubernetes executorでDockerソケットをマウントするには、[[runners.kubernetes.volumes.host_path]]セクションのボリュームに"/var/run/docker.sock"を追加します。
ボリュームマウントを指定するには、Helmチャートを使用して
values.ymlファイルを更新します。runners: tags: "socket-binding-kubernetes-runner" config: | [[runners]] [runners.kubernetes] image = "ubuntu:20.04" privileged = false [runners.kubernetes] [[runners.kubernetes.volumes.host_path]] host_path = '/var/run/docker.sock' mount_path = '/var/run/docker.sock' name = 'docker-sock' read_only = trueジョブスクリプトでDockerを使用します。
default: image: docker:24.0.5-cli before_script: - docker info build: stage: build tags: - socket-binding-kubernetes-runner script: - docker build -t my-docker-image . - docker run my-docker-image /script/to/run/tests
Dockerソケットバインディングに関する既知の問題
Dockerソケットバインディングを使用すると、特権モードでDockerを実行することを回避できます。ただし、この方法には次の注意点があります。
Dockerデーモンを共有すると、コンテナのセキュリティメカニズムが事実上無効になり、ホストが特権エスカレーションのリスクにさらされます。これにより、コンテナのブレイクアウトが発生する可能性があります。たとえば、プロジェクトで
docker rm -f $(docker ps -a -q)を実行すると、GitLab Runnerコンテナが削除されます。同時ジョブが機能しない可能性があります。テストで特定の名前のコンテナを作成する場合、それらが相互に競合する可能性があります。
Dockerコマンドによって作成されたコンテナは、Runnerの子ではなく、Runnerの兄弟になります。これにより、ワークフローが複雑になる可能性があります。
ソースリポジトリからコンテナへのファイルとディレクトリの共有が、期待どおりに動作しない可能性があります。ボリュームのマウントは、ビルドコンテナではなく、ホストマシンのコンテキストで実行されるためです。例:
docker run --rm -t -i -v $(pwd)/src:/home/app/src test-image:latest run_app_tests
docker:24.0.5-dindサービスを含める必要はありません。Docker-in-Docker executorを使用する場合は、このサービスが必要になります。
default:
image: docker:24.0.5-cli
before_script:
- docker info
build:
stage: build
script:
- docker build -t my-docker-image .
- docker run my-docker-image /script/to/run/testsCodeClimateを使用したCode Qualityスキャンなど、複雑なDocker-in-Dockerセットアップでは、適切に実行するためにホストとコンテナのパスを一致させる必要があります。詳細については、CodeClimateベースのスキャンにプライベートRunnerを使用するを参照してください。
Dockerパイプバインディングを使用する
Windowsコンテナは、Windows Serverカーネルとユーザーランド向けにコンパイルされたWindows実行可能ファイル(windowsservercoreまたはnanoserver)を実行します。Windowsコンテナをビルドして実行するには、コンテナをサポートするWindowsシステムが必要です。詳細については、Windowsコンテナを参照してください。
WindowsコンテナはDocker-in-Dockerアプローチをサポートしていないため、コンテナ内でネストされたDocker Engineを実行することはできません。Windowsコンテナ内からDockerイメージをビルドまたは管理するには、Dockerパイプバインディング(Docker-outside-of-DockerまたはDooDとも呼ばれる)を使用します。
Dockerパイプバインディングにはセキュリティ上の影響があります。\\\\.\\pipe\\docker_engineをバインドマウントすると、コンテナはホストのDockerデーモンに対する完全な管理者アクセス権を持ちます。コンテナ内のプロセスは、他のコンテナの起動や停止、イメージの管理、およびホストシステム上で特権を昇格させる可能性があります。
Dockerパイプバインディングを使用するには、ホストのWindows ServerオペレーティングシステムにDocker Engineをインストールして実行する必要があります。詳細については、Windows ServerへのDocker Community Edition(CE)のインストールに関するページを参照してください。
WindowsベースのコンテナCI/CDジョブでDockerコマンドを使用するには、起動されたexecutorコンテナに\\\\.\\pipe\\docker_engineをバインドマウントします。これにより、イメージのコンテキストでDockerを使用できるようになります。
WindowsにおけるDockerパイプバインディングは、LinuxにおけるDockerソケットバインディングに似ており、Dockerソケットバインディングに関する既知の問題と同様の既知の問題があります。
Dockerパイプバインディングを使用するための必須前提条件は、ホストのWindows ServerオペレーティングシステムにDocker Engineがインストールされ、実行されていることです。参照: Windows ServerへのDocker Community Edition(CE)のインストール
Docker executorでDockerパイプバインディングを使用する
Docker executorを使用して、Windowsベースのコンテナでジョブを実行できます。
Docker executorでDockerパイプをマウントするには、[runners.docker]セクションのボリュームに"\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"を追加します。
Runnerの登録時に
\\\\.\\pipe\\docker_engineをマウントするには、次のオプションを含めます。.\gitlab-runner.exe register \ --non-interactive \ --url "https://gitlab.com/" \ --registration-token REGISTRATION_TOKEN \ --executor "docker-windows" \ --description "docker-windows-runner" --tag-list "docker-windows-runner" \ --docker-image "docker:25-windowsservercore-ltsc2022" \ --docker-volumes "\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"前述のコマンドは、次の例のような
config.tomlエントリを作成します。[[runners]] url = "https://gitlab.com/" token = RUNNER_TOKEN executor = "docker-windows" [runners.docker] tls_verify = false image = "docker:25-windowsservercore-ltsc2022" privileged = false disable_cache = false volumes = ["\\\\.\\pipe\\docker_engine:\\\\.\\pipe\\docker_engine"]ジョブスクリプトでDockerを使用します。
default: image: docker:25-windowsservercore-ltsc2022 before_script: - docker version - docker info build: stage: build tags: - docker-windows-runner script: - docker build -t my-docker-image . - docker run my-docker-image /script/to/run/tests
Kubernetes executorでDockerパイプバインディングを使用する
Kubernetes executorを使用して、Windowsベースのコンテナでジョブを実行できます。
WindowsベースのコンテナにKubernetes executorを使用するには、KubernetesクラスターにWindowsノードを含める必要があります。詳細については、Windows containers in Kubernetes(KubernetesにおけるWindowsコンテナ)を参照してください。
Linux環境で動作し、WindowsノードをターゲットにするRunnerを使用できます。
Kubernetes executorでDockerパイプをマウントするには、[[runners.kubernetes.volumes.host_path]]セクションのボリュームに"\\.\pipe\docker_engine"を追加します。
ボリュームマウントを指定するには、Helmチャートを使用して
values.ymlファイルを更新します。runners: tags: "kubernetes-windows-runner" config: | [[runners]] executor = "kubernetes" # The FF_USE_POWERSHELL_PATH_RESOLVER feature flag has to be enabled for PowerShell # to resolve paths for Windows correctly when Runner is operating in a Linux environment # but targeting Windows nodes. [runners.feature_flags] FF_USE_POWERSHELL_PATH_RESOLVER = true [runners.kubernetes] [[runners.kubernetes.volumes.host_path]] host_path = '\\\\.\\pipe\\docker_engine' mount_path = '\\\\.\\pipe\\docker_engine' name = 'docker-pipe' read_only = true [runners.kubernetes.node_selector] "kubernetes.io/arch" = "amd64" "kubernetes.io/os" = "windows" "node.kubernetes.io/windows-build" = "10.0.20348"ジョブスクリプトでDockerを使用します。
default: image: docker:25-windowsservercore-ltsc2022 before_script: - docker version - docker info build: stage: build tags: - kubernetes-windows-runner script: - docker build -t my-docker-image . - docker run my-docker-image /script/to/run/tests
AWS EKS Kubernetesクラスターに関する既知の問題
dockerdからcontainerdに移行する際、AWS EKSブートストラップスクリプトStart-EKSBootstrap.ps1はDockerサービスを停止して無効にします。この問題を回避するには、Windows ServerにDocker Community Edition(CE)をインストールした後、次のスクリプトを使用してDockerサービスの名前を変更します。
Write-Output "Rename the just installed Docker Engine Service from docker to dockerd"
Write-Output "because the Start-EKSBootstrap.ps1 stops and disables the docker Service as part of migration from dockerd to containerd"
Stop-Service -Name docker
dockerd --register-service --service-name dockerd
Start-Service -Name dockerd
Write-Output "Ready to do Docker pipe binding on Windows EKS Node! :-)"Dockerパイプバインディングに関する既知の問題
Dockerパイプバインディングには、Dockerソケットバインディングに関する既知の問題と同じ一連のセキュリティおよび分離の問題があります。
docker:dindサービスのレジストリミラーを有効にする
サービスコンテナ内でDockerデーモンが起動すると、デフォルト設定が使用されます。パフォーマンスを向上させるため、またDocker Hubのレート制限を超えないようにするため、レジストリミラーを設定することをおすすめします。
.gitlab-ci.ymlファイル内のサービス
dindサービスに追加のCLIフラグを追加して、レジストリミラーを設定できます。
services:
- name: docker:24.0.5-dind
command: ["--registry-mirror", "https://registry-mirror.example.com"] # Specify the registry mirror to useGitLab Runner設定ファイル内のサービス
GitLab Runnerの管理者は、commandを指定して、Dockerデーモンのレジストリミラーを設定できます。DockerまたはKubernetes executorに対してdindサービスを定義する必要があります。
Docker:
[[runners]]
...
executor = "docker"
[runners.docker]
...
privileged = true
[[runners.docker.services]]
name = "docker:24.0.5-dind"
command = ["--registry-mirror", "https://registry-mirror.example.com"]Kubernetes:
[[runners]]
...
name = "kubernetes"
[runners.kubernetes]
...
privileged = true
[[runners.kubernetes.services]]
name = "docker:24.0.5-dind"
command = ["--registry-mirror", "https://registry-mirror.example.com"]GitLab Runner設定ファイル内のDocker executor
GitLab Runnerの管理者は、すべてのdindサービスに対してミラーを使用できます。設定を更新して、ボリュームマウントを指定します。
たとえば、次の内容の/opt/docker/daemon.jsonファイルがあるとします。
{
"registry-mirrors": [
"https://registry-mirror.example.com"
]
}上記のファイルを/etc/docker/daemon.jsonにマウントするためにconfig.tomlファイルを更新します。これにより、GitLab Runnerが作成するすべてのコンテナにこのファイルがマウントされます。dindサービスがこの設定を検出します。
[[runners]]
...
executor = "docker"
[runners.docker]
image = "alpine:3.12"
privileged = true
volumes = ["/opt/docker/daemon.json:/etc/docker/daemon.json:ro"]GitLab Runner設定ファイル内のKubernetes executor
GitLab Runnerの管理者は、すべてのdindサービスに対してミラーを使用できます。設定を更新して、ConfigMapボリュームマウントを指定します。
たとえば、次の内容の/tmp/daemon.jsonファイルがあるとします。
{
"registry-mirrors": [
"https://registry-mirror.example.com"
]
}このファイルの内容でConfigMapを作成します。そのためには、次のようなコマンドを実行します。
kubectl create configmap docker-daemon --namespace gitlab-runner --from-file /tmp/daemon.jsonGitLab RunnerのKubernetes executorがジョブポッドを作成するために使用するネームスペースを使用する必要があります。
ConfigMapが作成されたら、そのファイルを/etc/docker/daemon.jsonにマウントするためにconfig.tomlファイルを更新します。この更新により、GitLab Runnerが作成するすべてのコンテナにこのファイルがマウントされます。dindサービスがこの設定を検出します。
[[runners]]
...
executor = "kubernetes"
[runners.kubernetes]
image = "alpine:3.12"
privileged = true
[[runners.kubernetes.volumes.config_map]]
name = "docker-daemon"
mount_path = "/etc/docker/daemon.json"
sub_path = "daemon.json"Docker-in-Dockerでレジストリに対して認証する
Docker-in-Dockerを使用する場合、サービスによって新しいDockerデーモンが起動されるため、標準の認証方法は機能しません。レジストリに対して認証する必要があります。
Dockerレイヤーのキャッシュ
Dockerレイヤーをキャッシュして、ビルドを高速化できます。詳細については、Docker-in-DockerビルドでのDockerレイヤーのキャッシュを参照してください。
OverlayFSドライバーを使用する
GitLab.com上のインスタンスRunnerは、overlay2ドライバーをデフォルトで使用します。
デフォルトでは、docker:dindを使用する場合、Dockerはvfsストレージドライバーを使用します。これにより、実行のたびにファイルシステムをコピーします。別のドライバー(たとえば、overlay2)を使用すると、ディスクに高い負荷がかかるこの処理を回避できます。
要件
最新のカーネルを使用していることを確認してください(
>= 4.2を推奨)。overlayモジュールが読み込まれているかどうかを確認します。sudo lsmod | grep overlay結果が表示されない場合は、モジュールが読み込まれていません。モジュールを読み込むには、次を使用します。
sudo modprobe overlayモジュールが読み込まれたら、再起動時にもモジュールが読み込まれるようにする必要があります。そのためには、Ubuntuシステムでは
/etc/modulesに次の行を追加します。overlay
OverlayFSドライバーをプロジェクトごとに使用する
.gitlab-ci.ymlでCI/CD変数DOCKER_DRIVERを使用して、プロジェクトごとに個別にドライバーを有効にすることができます。
variables:
DOCKER_DRIVER: overlay2OverlayFSドライバーをすべてのプロジェクトに使用する
独自のRunnerを使用している場合は、config.tomlファイルの[[runners]]セクションでDOCKER_DRIVER環境変数を設定することにより、すべてのプロジェクトでドライバーを有効にできます。
environment = ["DOCKER_DRIVER=overlay2"]複数のRunnerを実行している場合は、すべての設定ファイルを変更する必要があります。
Runnerの設定とOverlayFSストレージドライバーの使用の詳細を参照してください。
Dockerの代替手段
Runnerで特権モードを有効にしなくても、コンテナイメージをビルドできます。
- BuildKit: Dockerデーモンの依存関係をなくすルートレスBuildKitオプションが含まれています。
- Buildah: Dockerデーモンを必要とせず、OCI準拠のイメージをビルドします。
Buildahの例
BuildahをGitLab CI/CDで使用するには、次のいずれかのexecutorを備えたRunnerが必要です。
この例では、Buildahを使用して以下を行います。
- Dockerイメージをビルドする。
- それをGitLabコンテナレジストリにプッシュする。
最後のステップで、BuildahはプロジェクトのルートディレクトリにあるDockerfileを使用してDockerイメージをビルドします。最後に、そのイメージをプロジェクトのコンテナレジストリにプッシュします。
build:
stage: build
image: quay.io/buildah/stable
variables:
# Use vfs with buildah. Docker offers overlayfs as a default, but Buildah
# cannot stack overlayfs on top of another overlayfs filesystem.
STORAGE_DRIVER: vfs
# Write all image metadata in the docker format, not the standard OCI format.
# Newer versions of docker can handle the OCI format, but older versions, like
# the one shipped with Fedora 30, cannot handle the format.
BUILDAH_FORMAT: docker
FQ_IMAGE_NAME: "$CI_REGISTRY_IMAGE/test"
before_script:
# GitLab container registry credentials taken from the
# [predefined CI/CD variables](../variables/_index.md#predefined-cicd-variables)
# to authenticate to the registry.
- echo "$CI_REGISTRY_PASSWORD" | buildah login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
script:
- buildah images
- buildah build -t $FQ_IMAGE_NAME
- buildah images
- buildah push $FQ_IMAGE_NAMEOpenShiftクラスターにデプロイされたGitLab Runner Operatorを使用している場合は、ルートレスコンテナでBuildahを使用してイメージをビルドするチュートリアルを試してください。
GitLabコンテナレジストリを使用する
Dockerイメージをビルドしたら、それをGitLabコンテナレジストリにプッシュできます。
トラブルシューティング
open //./pipe/docker_engine: The system cannot find the file specified
マウントされたDockerパイプにアクセスするためにPowerShellスクリプトでdockerコマンドを実行すると、次のエラーが表示される場合があります。
PS C:\> docker version
Client:
Version: 25.0.5
API version: 1.44
Go version: go1.21.8
Git commit: 5dc9bcc
Built: Tue Mar 19 15:06:12 2024
OS/Arch: windows/amd64
Context: default
error during connect: this error may indicate that the docker daemon is not running: Get "http://%2F%2F.%2Fpipe%2Fdocker_engine/v1.44/version": open //./pipe/docker_engine: The system cannot find the file specified.このエラーは、Windows Amazon EKSノードでDocker Engineが実行されていないため、WindowsベースのexecutorコンテナでDockerパイプバインディングを使用できなかったことを示しています。
この問題を解決するには、Kubernetes executorでDockerパイプバインディングを使用するで説明されている回避策を使用します。