参考
https://blog.bedrock.day/a4d5bc7ab63d6c1ae90e
前提
Docker Desktop for Mac を利用しています。 Linux 環境の利用や他のコンテナランタイムを利用している場合は他の結果になると思います。
Jenkins Controller を docker-compose で立てる
https://www.jenkins.io/doc/book/installing/docker/ https://hub.docker.com/r/jenkins/jenkins
jenkins controller 用の Dockerfile を用意します。 もしなにもカスタマイズしないのであれば、そのまま jenkins/jenkins イメージを使うだけで良いです。 ただし、今回はカスタムプラグインを前もってインストールしておきたいため、下記のように Dockerfile をビルドします。
# ref: https://www.jenkins.io/doc/book/installing/docker/#on-macos-and-linux
FROM jenkins/jenkins:lts-jdk17
USER root
RUN apt-get update && apt-get install -y lsb-release ca-certificates curl && \
install -m 0755 -d /etc/apt/keyrings && \
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && \
chmod a+r /etc/apt/keyrings/docker.asc && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/debian $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null && \
apt-get update && apt-get install -y docker-ce-cli && \
apt-get clean && rm -rf /var/lib/apt/lists/*
USER jenkins
COPY jenkins_plugins.txt /usr/share/jenkins/ref/jenkins_plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/jenkins_plugins.txtcompose から jenkins を起動します。
context をなにも設定しないと、 compose.yml が存在するディレクトリがコンテキストディレクトリになります。
今回は ./jenkins-controller をコンテキストディレクトリとしています。
こうすることで Dockerfile 内の COPY jenkins_plugins.txt 命令において、 ./jenkins-controller から見た相対ディレクトリ = ./jenkins-controller/jenkins_plugins.txt を正しく取ってくれます。
services:
jenkins-controller:
build:
# https://docs.docker.com/reference/compose-file/build/#context
context: ./jenkins-controller
dockerfile: Dockerfile # [context = ./jenkins-controller]/Dockerfile
image: jenkins-controller-image
container_name: jenkins-controller-container
ports:
# 8081 = your pc port, 8080 = jenkins-controller port in container
- 8081:8080
- 50000:50000
volumes:
- ./jenkins-controller-data:/var/jenkins_home
# run container as `jenkins` user
user: jenkinsDocker コマンドを実行できるようにする
https://docs.cloudbees.com/docs/cloudbees-ci/latest/pipelines/docker-workflow https://www.jenkins.io/doc/book/pipeline/docker/
docker pipeline plugin を利用することで
- agent として docker を利用する
- docker image の build push が行える
ことができます。
上記プラグインを動かすには docker がインストールされている必要があります。
しかし、現時点の jenkins-controller はただの jenkins/jenkins image であり、 docker はインストールされていません。
よって、 jenkins-controller に docker を扱えるようにする必要があります。
dood vs dind
https://qiita.com/t_katsumura/items/d5be6afadd6ec6545a9d
- dood
- ホストマシンの docker daemon を利用する
- dind
- ホストマシンに立てたコンテナ A においてさらに Docker をインストールし、 A の Docker Daemon を利用することで
という違いです。
dind で docker pipeline を動かす
https://github.com/ganyariya/jenkins-by-docker-compose/pull/1
docker 公式から提供されている dind image を利用します。 dind image にはすでに docker がインストールされており、かつ DOCKER_TLS_CERTDIR で指定したパスへ証明書を生成を生成するという特徴があります。
あとは jenkins-controller 側で DOCKER_HOST と DOCKER_CERT_PATH を指定します。 こうするとこで、 jenkins-controller 内の jenkins が docker pipeline を実行したとき
- docker daemon への接続として TCP ソケット通信を利用する
- mTLS 通信を行う
ことができ、 docker を agent として利用できます。
services:
jenkins-controller:
build:
# https://docs.docker.com/reference/compose-file/build/#context
context: ./jenkins-controller
dockerfile: Dockerfile # [context = ./jenkins-controller]/Dockerfile
image: jenkins-controller-image
container_name: jenkins-controller-container
ports:
# 8081 = your pc port, 8080 = jenkins-controller port in container
- 8081:8080
# 50000 = jenkins agent node connects to the controller on 50000 port
- 50000:50000
volumes:
- ./jenkins-controller-data:/var/jenkins_home
- jenkins-docker-certs:/certs/client:ro
# run container as `jenkins` user
user: jenkins
environment:
# https://docs.docker.jp/compose/reference/envvars.html#docker-host
# when jenkins controller run docker command, connect to jenkins-docker -runner by `docker` network alias
# use tls connection
- DOCKER_HOST=tcp://docker:2376 # mTLS connection with port 2376 of `docker` service
- DOCKER_CERT_PATH=/certs/client
- DOCKER_TLS_VERIFY=1
depends_on:
- docker
networks:
- jenkins-network
# https://zenn.dev/taku_sid/articles/20250409_docker_patterns
# https://qiita.com/t_katsumura/items/d5be6afadd6ec6545a9d
# docker in docker
docker:
image: docker:dind
container_name: jenkins-docker-container
privileged: true # for run docker command in docker container
expose:
- "2376" # for TLS connection
volumes:
- ./jenkins-controller-data:/var/jenkins_home
- jenkins-docker-certs:/certs/client
environment:
# https://hub.docker.com/_/docker#tls
# dind container creates TLS certificates in DOCKEDR_TLS_CERTDIR
- DOCKER_TLS_CERTDIR=/certs
networks:
- jenkins-network
networks:
jenkins-network:
driver: bridge
volumes:
jenkins-docker-certs:実際に job を動かしたところ下記のようになりました。

pipeline {
agent {
docker { image 'node:24.11.0-alpine3.22' }
}
stages {
stage('Test') {
steps {
sh 'node --eval "console.log(process.platform,process.env.CI)"'
}
}
}
}dood で docker pipeline を動かす
/var/run/docker.sock:/var/run/docker.sock を volume へ追加することで、 jenkins-controller-container がホストマシンへの Docker Daemon へ unix-domain-socket 経由で通信できるようにします。
services:
jenkins-controller:
build:
# https://docs.docker.com/reference/compose-file/build/#context
context: ./jenkins-controller
dockerfile: Dockerfile # [context = ./jenkins-controller]/Dockerfile
image: jenkins-controller-image
container_name: jenkins-controller-container
ports:
# 8081 = your pc port, 8080 = jenkins-controller port in container
- 8081:8080
# 50000 = jenkins agent node connects to the controller on 50000 port
- 50000:50000
volumes:
- ./jenkins-controller-data:/var/jenkins_home
# When the jenkins controller attempts to execute a docker pipeline, use the docker daemon running on the host PC.
- /var/run/docker.sock:/var/run/docker.sock
user: jenkins
networks:
- jenkins-network
networks:
jenkins-network:
driver: bridge
しかし、 job を実行したところ Docker Daemon へアクセスできないというエラーがでました。 どうやら jenkins-controller-container の jenkins user が docker.socket ならびに docker コマンドが実行できないようです。
+ docker pull node:24.11.0-alpine3.22
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "http://%2Fvar%2Frun%2Fdocker.sock/v1.51/images/create?fromImage=docker.io%2Flibrary%2Fnode&tag=24.11.0-alpine3.22": dial unix /var/run/docker.sock: connect: permission deniedそこで、下記記事と同じ対応を取りました。 jenkins user を root グループへ追加しています。
このようにすることで docker.socket へアクセスできるようになり、パイプラインが正常に実行できるようになりました。
https://qiita.com/shiojojo/items/e5ac5a7ca5cfd860c1e7
# ref: https://www.jenkins.io/doc/book/installing/docker/#on-macos-and-linux
FROM jenkins/jenkins:lts-jdk17
USER root
RUN apt-get update && apt-get install -y lsb-release ca-certificates curl && \
install -m 0755 -d /etc/apt/keyrings && \
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc && \
chmod a+r /etc/apt/keyrings/docker.asc && \
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] \
https://download.docker.com/linux/debian $(. /etc/os-release && echo \"$VERSION_CODENAME\") stable" \
| tee /etc/apt/sources.list.d/docker.list > /dev/null && \
apt-get update && apt-get install -y docker-ce-cli && \
apt-get clean && rm -rf /var/lib/apt/lists/*
# add jenkins user to root group
# because jenkins want to access /var/run/docker.sock
RUN gpasswd -a jenkins root
USER jenkins
COPY jenkins_plugins.txt /usr/share/jenkins/ref/jenkins_plugins.txt
RUN jenkins-plugin-cli --plugin-file /usr/share/jenkins/ref/jenkins_plugins.txt
gpasswd -a jenkins docker で docker group に配置する方法もあるのですが
- mac host pc の
/var/run/docker.sockの gid 所有権限 - container の
/var/run/docker.sockの gid 所有権限 が異なり、エラーがでてしまいました。 そのため、 root グループに突っ込むという苦し紛れの対応をとっています。
https://github.com/ganyariya/jenkins-by-docker-compose/pull/2
dind パターンを採用する
https://github.com/ganyariya/jenkins-by-docker-compose/pull/3
Docker Desktop (mac) の場合、 jenkins user を root group へ入れないと dood を実現することが難しそうです。 また、ホストの Docker 領域を汚してしまうというのも辛いところです。 そのため、 dind パターンを採用します。
リファクタする
SSH 接続 agent を作成するまえに、下記のように dind をリファクタしました。
dind-runner で名前解決すれば、 service 名は docker 以外でもよいと考えたためです。
しかし error during connect: Get "[https://dind-runner:2376/v1.51/containers/node:24.11.0-alpine3.22/json](https://dind-runner:2376/v1.51/containers/node:24.11.0-alpine3.22/json)": tls: failed to verify certificate: x509: certificate is valid for 085732f6d3db, docker, localhost, not dind-runner といエラーが発生します。
environment:
# https://docs.docker.jp/compose/reference/envvars.html#docker-host
# when jenkins controller executes docker command, it connects to dind-runner service's 2376 port with mTLS.
- DOCKER_HOST=tcp://dind-runner:2376
- DOCKER_CERT_PATH=/certs/client
- DOCKER_TLS_VERIFY=1
depends_on:
- dind-runner
networks:
- jenkins-network
# https://qiita.com/t_katsumura/items/d5be6afadd6ec6545a9d
# docker in docker
dind-runner:
image: docker:dind
container_name: jenkins-docker-container
privileged: true
expose:
- "2376" # for mTLS connection
volumes:
- ./jenkins-controller-data:/var/jenkins_home
- jenkins-docker-certs:/certs/client
environment:
# https://hub.docker.com/_/docker#tls
# dind image creates TLS certificates in DOCKEDR_TLS_CERTDIR
- DOCKER_TLS_CERTDIR=/certs
networks:
- jenkins-networkerror during connect: Get "[https://dind-runner:2376/v1.51/containers/node:24.11.0-alpine3.22/json](https://dind-runner:2376/v1.51/containers/node:24.11.0-alpine3.22/json)": tls: failed to verify certificate: x509: certificate is valid for 085732f6d3db, docker, localhost, not dind-runner
どうやら dint image が作成する mTLS 用証明書は docker, localhost というホスト名しか許可していないようです。
よって、 network alias を利用して docker という名前でもアクセスできるようにします。
dind パターンの TLS 通信の仕組み
どうやら以下のような仕組みのようです 。
- dind runner で生成した
/certs/clientディレクトリを volume を利用して、 jenkins-controller 側へ渡す - jenkins-controller が docker へアクセスしたいとき、
tcp://docker:2376へアクセスしようとする - TLS 接続を開始する
- jenkins-controller は
/certs/clientの証明書を利用し、 dind-runner は/certs/serverの証明書を利用する - dind-runner の
/certs/caが上位認証局として/certs/serverの正しさを証明する
- jenkins-controller は
大まかな理解ですが、 /certs/client だけ jenkins-controller へ渡し、 dind-runner 側が server として機能することには納得しました。
ssh 接続による agent を利用する
https://hub.docker.com/r/jenkins/ssh-agent
ssh-agent image を利用します。
#yaml-anchor を利用して ssh-agent とするための定義を用意しています。
環境変数として JENKINS_AGENT_SSH_PUBKEY を利用します。
この環境変数は ssh-agent image が利用するものであり、 JENKINS_AGENT_SSH_PUBKEY の内容を自動的に /home/jenkins/.ssh の autorized_keys へ転記します。
x-jenkins-ssh-definition: &jenkins-ssh-definition
image: jenkins/ssh-agent:latest-jdk25
container_name: jenkins-ssh-agent-container
expose:
- "22"
environment:
- JENKINS_AGENT_SSH_PUBKEY=${JENKINS_AGENT_SSH_PUBKEY}
networks:
- jenkins-network
jenkins-ssh-agent1:
*jenkins-ssh-definition
jenkins-ssh-agent2:
*jenkins-ssh-definition
下記のように Jenkins 上でマシンを設定することで、ビルドに利用できます。


pipeline {
agent { label 'jenkins-ssh-agent1-label' }
stages {
stage('Checkout Source') {
steps {
git branch: 'main', url: 'https://github.com/ganyariya/ganyariya.git'
sh 'ls -a'
}
}
stage('docker build and test') {
agent {
docker {
image 'node:lts-alpine'
}
}
steps {
sh 'echo "--- Inside Docker Container ---"'
sh 'ls -a'
sh 'node -v'
}
}
}
}新しい学びとして --user を指定することで該当の user で exec できます。
# root でログインできる
docker compose exec -it --user root jenkins-controller /bin/bashdind パターンだと docker pipeline がうまくいかない
Groovy 上で docker workflow pipeline を利用する場合、 agent となるコンテナに docker がインストールされている必要があります。
そのため、x-jenkins-ssh-agent-definition の Dockerfile で docker をインストールするようにしました。
しかし、 dind コンテナ上でうまくジョブを実行するには dind と jenkins-ssh-agent1 でワークスペースを volume で共有する必要があります。 volume を共有しないと jenkins が job を実行できません。
下記のようにすると、 jenkins-ssh-agent1 を利用して docker pipeline が実行できるようになりました。
しかし、 jenkins-ssh-agent2 も docker-pipeline を実行できるようにさせようとすると、 jenkins-ssh-agent-volume2:/home/jenkins/agent を dind にも設定しないといけません。
すると、 /home/jenkins/agent がコンフリクトしてしまいます。
回避するには dind1, dind2 のように docker in docker を複数コンテナ用意しないといけません。 dind パターンを利用する場合、 dind を使うコンテナは1つに抑えたほうがよさそうです。 複数コンテナから docker を利用したい場合、 dood パターンにしたほうがよいです。
x-dind-accessor-environment: &dind-accessor-environment
# https://docs.docker.jp/compose/reference/envvars.html#docker-host
# when jenkins controller executes docker pipeline, it connects to `docker` 2376 port with mTLS.
DOCKER_HOST: tcp://docker:2376
DOCKER_CERT_PATH: /certs/client
DOCKER_TLS_VERIFY: 1
x-jenkins-ssh-agent-definition: &jenkins-ssh-agent-definition
# https://hub.docker.com/r/jenkins/ssh-agent
build:
context: ./jenkins-ssh-agent
image: jenkins/ssh-agent:latest-jdk25
expose:
- "22"
environment:
<<: *dind-accessor-environment
# jenkins/ssh-agent container automatically registers JENKINS_AGENT_SSH_PUBKEY to `/home/jenkins/.ssh/authorized_keys` on startup
JENKINS_AGENT_SSH_PUBKEY: ${JENKINS_AGENT_SSH_PUBKEY}
depends_on:
- dind-runner
networks:
- jenkins-network
services:
jenkins-controller:
build:
# https://docs.docker.com/reference/compose-file/build/#context
context: ./jenkins-controller
image: jenkins-controller-image
container_name: jenkins-controller-container
ports:
- 8080:8080
- 50000:50000 # 50000 = jenkins agent connects to controller on 50000 port
volumes:
- ./jenkins-controller-data:/var/jenkins_home
- jenkins-docker-certs:/certs/client:ro
user: jenkins
environment:
<<: *dind-accessor-environment
depends_on:
- dind-runner
networks:
- jenkins-network
# docker in docker pattern
dind-runner:
image: docker:dind
container_name: jenkins-docker-container
privileged: true
expose:
- "2376"
volumes:
- ./jenkins-controller-data:/var/jenkins_home
- jenkins-docker-certs:/certs/client
- jenkins-ssh-agent-volume:/home/jenkins/agent
environment:
# https://hub.docker.com/_/docker#tls
# dind image creates TLS certificates in DOCKER_TLS_CERTDIR
- DOCKER_TLS_CERTDIR=/certs
networks:
jenkins-network:
aliases:
- docker # for DOCKER_HOST=tcp://docker:2376 name resolver
jenkins-ssh-agent1:
<<: *jenkins-ssh-agent-definition
volumes:
# ssh-agent1 上で docker pipeline を実行するには
# - dind の docker.socket へ通信する
# - dind と volume を /home/jenkins/agent で共有する
# 必要がある
# その結果 ssh-agent1 と ssh-agent2 両方で docker pipeline を実行しようとすると
# dind 上の volume で jenkins-ssh-agent-volume{1,2}: /home/jenkins/agent とする必要があり、不可能となる
# よって dind を利用して複数の ssh-agent コンテナで docker pipeline は実行できない
- jenkins-ssh-agent-volume:/home/jenkins/agent
- jenkins-docker-certs:/certs/client:ro
jenkins-ssh-agent2:
<<: *jenkins-ssh-agent-definition
volumes:
- jenkins-docker-certs:/certs/client:ro
networks:
jenkins-network:
driver: bridge
volumes:
jenkins-docker-certs:
jenkins-ssh-agent-volume:dood pattern に変更する
https://blog.nijohando.jp/post/docker-in-docker-docker-outside-of-docker/ https://mitomito.hatenablog.jp/entry/2022/06/06/080000
dind pattern において「volume を共有できない」という問題が発生しました。 そのため dood pattern に変更します。 下記コミットのような変更をおこないました。
しかし、この場合 Jenkfins Job で Docker Pipeline を利用しようとするとエラーになってしまいます。
というのも Job を実行する linux の jenkins user は /var/run/docker.sock を読み書きできる権限を持っていないためです。
root user もしくは root group しか読み書きできない設定となっています。
❯ docker compose exec -it jenkins-ssh-agent1 /bin/bash
root@dff89a57a690:/home/jenkins# ls -la /var/run/docker.sock
srw-rw---- 1 root root 0 Nov 15 01:59 /var/run/docker.sock
root@dff89a57a690:/home/jenkins#Running on [Jenkins](http://localhost:8080/computer/\(built-in\)/) in /var/jenkins_home/workspace/Test_Docker
[Pipeline] {[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] isUnix[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] withEnv[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] {[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] sh[](http://localhost:8080/job/Test_Docker/2/console#)
+ docker inspect -f . node:lts-alpine
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get "[http://%2Fvar%2Frun%2Fdocker.sock/v1.51/containers/node:lts-alpine/json](http://%2Fvar%2Frun%2Fdocker.sock/v1.51/containers/node:lts-alpine/json)": dial unix /var/run/docker.sock: connect: permission denied
[Pipeline] isUnix[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] withEnv[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] {[](http://localhost:8080/job/Test_Docker/2/console#)
[Pipeline] sh[](http://localhost:8080/job/Test_Docker/2/console#)
+ docker pull node:lts-alpine
permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Post "[http://%2Fvar%2Frun%2Fdocker.sock/v1.51/images/create?fromImage=docker.io%2Flibrary%2Fnode&tag=lts-alpine](http://%2Fvar%2Frun%2Fdocker.sock/v1.51/images/create?fromImage=docker.io%2Flibrary%2Fnode&tag=lts-alpine)": dial unix /var/run/docker.sock: connect: permission deniedそのため、 dood pattern を実現する場合、 jenkins user を root group に追加するしかありません。
もしくは docker compose up をしたあとで /var/run/docker.sock の権限を変更し docker group 所有にする必要があります。
今回は楽をするために jenkins user を root group へ追加することにしました。
https://github.com/ganyariya/jenkins-by-docker-compose/pull/7