はじめに
Actions Runner Controller (ARC) は、Kubernetesクラスタ上でGitHub ActionsのSelf-hosted runnerの環境を構築するためのツールです。自動スケーリングにも対応しています。
ARCで構築したSelf-hosted runner上でDockerコマンドが実行できる仕組みについて少し調べる機会があったので、その共有です。
本記事では、内部でDinD (Docker in Docker)を利用するレガシー版ARCを扱います。新しいGitHub公式版については公式ドキュメントをご参照ください。
アーキテクチャ概要
ARCでは、各runnerのPod内に以下の2つのコンテナが起動します:
- runnerコンテナ: CI/CDジョブを実行する環境
- dindコンテナ: Docker Daemonが動作する環境
各コンテナで使用されるデフォルトのイメージは以下の通りです:
- runner: summerwind/actions-runner:latest
- dind: docker:dind
動作の仕組み
- dindコンテナ内でDocker Daemonが起動
- dindコンテナの
/var/run/docker.sockを共有ストレージを通してrunnerコンテナにマウント - runnerコンテナにインストールされたDocker CLIが、マウントされたソケット経由でDocker Daemonと通信
これにより、runnerコンテナからDockerコマンドを実行すると、実際の処理はdindコンテナ内のDocker Daemonで行われます。
なぜDinD(2コンテナ構成)を採用しているのか
「1つのコンテナ内でDocker Daemonも起動すれば、構成がシンプルになるのでは?」と思われるかもしれません。確かに技術的には可能ですが、セキュリティ上の重大なリスクがあるため推奨されません。
特権モードのリスク
コンテナ内でDocker Daemonを起動するには、--privileged オプションによる特権モードが必要です。これは、Docker Daemonがホストのcgroupなどのリソースにアクセスする必要があるためです。
しかし、特権モードではコンテナに以下の権限が付与されます:
- ホストのLinuxカーネルへのフルアクセス
- ホストの全てのデバイスへのアクセス
- ホストで実行可能なほぼ全ての操作
ARCにおけるセキュリティリスク
ARCの特性上、Self-hosted runnerは複数のユーザーやワークフローから利用されます。特権モードでrunnerを起動すると、全てのユーザーにホストへの完全な権限を与えることになり、これは重大なセキュリティホールとなります。
DinDによる権限分離
そこでARCでは、Docker APIを介した通信のみを許可することで、権限を最小限に抑えています:
- runnerコンテナ → Docker API経由でのみdindと通信
- dindコンテナ → 特権モードで起動(分離された環境)
- 直接的なホストアクセスを防止
カスタムイメージの設定方法
runnerとdindのバージョン互換性を保つため、カスタムイメージを指定したい場合があります。
デフォルトでは、dindコンテナのDocker Server(Daemon)は最新版ですが、runnerコンテナのDocker Client(CLI)が古いバージョンの場合、互換性の問題が発生する可能性があります。ここでは、両方のイメージを明示的に指定する方法を解説します。
前提条件
本設定では、ARCが提供するCRD(Custom Resource Definitions)のactions.summerwind.dev/v1alpha1 APIを使用します。
設定項目の確認
RunnerDeploymentのAPI仕様から、主要な設定項目は以下の通りです:
runnerコンテナのイメージ
- 設定項目:
RunnerSpec.Image
dindコンテナの設定
- 自動設定:
RunnerSpec.DockerEnabled: trueで最新のdindイメージを自動使用 - 手動設定:
RunnerSpec.SidecarContainersでカスタムイメージを指定
今回はイメージのバージョンを明示的に指定するため、RunnerSpec.DockerEnabledをfalseに設定し、RunnerSpec.SidecarContainersで手動設定を行います。
SidecarContainersはContainer型の配列で、Container.Imageフィールドでdindコンテナのイメージを指定できます。
必要な追加設定
SidecarContainersでイメージを指定するだけでは不十分です。以下の設定も必要になります:
-
特権モードの有効化: dindコンテナに
privileged: trueを設定 -
Unixソケットの共有:
emptyDirボリュームで/var/run/docker.sockを共有 -
環境変数の設定:
DOCKER_HOSTを明示的に指定
完成したマニフェスト
上記を踏まえた設定例は以下の通りです:
apiVersion: actions.summerwind.dev/v1alpha1
kind: RunnerDeployment
metadata:
name: example-runnerdeploy
spec:
replicas: 1
template:
spec:
# ...(省略)...
image: <runnerのDockerイメージ>
dockerEnabled: false
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
volumes:
- name: docker-sock
emptyDir: {}
env:
- name: DOCKER_HOST
value: unix:///var/run/docker.sock
sidecarContainers:
- name: docker
image: <dindのDockerイメージ>
securityContext:
privileged: true
volumeMounts:
- name: docker-sock
mountPath: /var/run/docker.sock
env:
- name: DOCKER_HOST
value: unix:///var/run/docker.sock
まとめ
今回はActions Runner Controllerで構築したSelf-hosted runnerでDockerコマンドが使える仕組みとカスタムイメージの設定について解説しました。
ポイント
- ARCはDinD(2コンテナ構成)を採用し、セキュリティリスクを低減
- runnerコンテナはDocker API経由でのみdindコンテナと通信
- カスタムイメージを使う場合は、バージョン互換性と必要な設定に注意
普段何気なく使っているCI/CD環境も、裏側ではセキュリティとユーザビリティを両立させるための工夫が凝らされています。こうした仕組みを理解することで、より適切な運用や設定が可能になります。
