docker + docker composeでJenkinsを使ってみる
はじめに
今更ですが、試しにJenkinsを使う環境を構築したので、備忘録として残します。
(GitHub Actionsの方が良いのかもしれませんが。。。)
agent(node)側のdocker imageについて
ネットで調べるとjenkinsci/ssh-slave
の記事が結構ヒットしたので、を使おうとしていたのですが、Deprecatedようですね。
それに気づくのに時間を使ってしまいました。。。
現在はjenkinsciではなく、jenkinsにあるagentのイメージを使えば良さそうです。
せっかくなので、本記事はjenkins/inbound-agent
とjenkins/ssh-agent
を使ってみます。
前提環境
- Docker: 26.1.3
- Docker Compose: v2.27.0
事前準備:フォルダとファイル構成の作成
作業用フォルダを作成します。(今回はjenkins)
その配下は下図のように構成します。
jenkins/
├── compose.yml
├── .env
├── controller/
│ ├── Dockerfile
│ └── jenkins_home/
├── ssh-agent/
│ └── Dockerfile
├── inbound-agent/
│ └── Dockerfile
└── secrets/
ssh鍵の作成
ssh-agentとの接続で使用するssh鍵を作成します。
(-N
はパスフレーズを指定するオプションです。)
# ssh-keygen -t ed25519 -b 4096 -C "" -f secrets/jenkins_agent_key -N ""
# chmod 600 secrets/jenkins_agent_key
# chmod 644 secrets/jenkins_agent_key.pub
作成されたpubキーの中身を.envファイルに記載します。
(ssh-agentが起動したときにauthorized_keysに自動で登録される。)
JENKINS_AGENT_SSH_PUBKEY=<pubキーの内容>
ディレクトリ、ファイルの所有者の変更
jenkinsイメージ上でjenkinsユーザーがアクセスするディレクトリやファイルはオーナーを変更しておかないとエラーとなります。
jenkinsユーザーは(UID 1000)のようです。
# chown -R 1000:1000 controller/jenkins_home
# chown 1000:1000 secrets/jenkins_agent_key
dockerとdocker composeファイル作成
Controler
FROM jenkins/jenkins:lts
# 推奨:Docker-in-DockerやSSHを使う場合は、以下を許可
USER root
# 必要に応じてツールを追加
RUN apt-get update && \
apt-get install -y ssh && \
apt-get install -y openssh-client
# Jenkins用SSHディレクトリ作成(実行ユーザーに権限を与える)
RUN mkdir -p /var/jenkins_home/.ssh && \
chown -R jenkins:jenkins /var/jenkins_home/.ssh && \
chmod 700 /var/jenkins_home/.ssh
#COPY --chown=jenkins:jenkins secrets/jenkins_agent_key /var/jenkins_home/.ssh/id_rsa
#RUN chmod 600 /var/jenkins_home/.ssh/id_rsa
# 言語を日本語に設定
#RUN localedef -i ja_JP -f UTF-8 ja_JP.UTF-8 && \
# echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
RUN echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
ENV LANG ja_JP.UTF-8
# 日付を日本語に設定
#RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock && \
# rm -f /etc/localtime && \
# ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
USER jenkins
Node(ssh-agent)
FROM jenkins/ssh-agent
# 必要であれば、Node.jsやJava、Docker CLIなどを追加
USER root
RUN apt-get update && \
apt-get install -y curl
# 言語を日本語に設定
#RUN localedef -i ja_JP -f UTF-8 ja_JP.UTF-8 && \
# echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
RUN echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
ENV LANG ja_JP.UTF-8
# 日付を日本語に設定
#RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock && \
# rm -f /etc/localtime && \
# ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
#USER jenkins
Node(inbound-agent)
FROM jenkins/inbound-agent
# 必要であれば、Node.jsやJava、Docker CLIなどを追加
USER root
RUN apt-get update && \
apt-get install -y curl
# 言語を日本語に設定
#RUN localedef -i ja_JP -f UTF-8 ja_JP.UTF-8 && \
# echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
RUN echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
ENV LANG ja_JP.UTF-8
# 日付を日本語に設定
#RUN echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock && \
# rm -f /etc/localtime && \
# ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
USER jenkins
compose
services:
jenkins-controller:
build:
context: ./controller
container_name: jenkins-controller
hostname: jenkins-controller
ports:
- "8080:8080"
- "50000:50000" # エージェントとの接続に使用
tty: true
volumes:
- ./controller/jenkins_home:/var/jenkins_home
- ./secrets/jenkins_agent_key:/var/jenkins_home/.ssh/id_rsa:ro
networks:
jenkins-net:
ipv4_address: 192.168.1.2
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/login"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
inbound-agent:
build:
context: ./inbound-agent
container_name: inbound-agent
hostname: inbound-agent
depends_on:
jenkins-controller:
condition: service_healthy
environment:
- JENKINS_AGENT_WORKDIR=${JENKINS_AGENT_WORKDIR}
- JENKINS_URL=${JENKINS_URL}
- JENKINS_SECRET=${JENKINS_SECRET}
- JENKINS_NAME=${JENKINS_NAME}
tty: true
networks:
jenkins-net:
ipv4_address: 192.168.1.3
ssh-agent:
build:
context: ./ssh-agent
container_name: ssh-agent
hostname: ssh-agent
depends_on:
jenkins-controller:
condition: service_healthy
environment:
- JENKINS_AGENT_SSH_PUBKEY=${JENKINS_AGENT_SSH_PUBKEY}
tty: true
networks:
jenkins-net:
ipv4_address: 192.168.1.4
networks:
jenkins-net:
ipam:
driver: default
config:
- subnet: 192.168.1.0/24
初回起動
コンテナ起動
docker compose up -d
コマンドでコンテナ起動をします。
-d
オプションを付けないとコンテナ内のログはコンソールへ出力され続けます。
今回は別コンソールでdocker compose logs -f
コマンドで確認しています。
# docker compose up -d
[+] Building 1.6s (25/25) FINISHED docker:default
=> [jenkins-controller internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 1.08kB 0.0s
...
[+] Running 4/4
✔ Network jenkins_jenkins-net Created 0.2s
✔ Container jenkins-controller Healthy 22.9s
✔ Container inbound-agent Started 23.6s
✔ Container ssh-agent Started 23.5s
#
docker ps
コマンドで全てのコンテナが起動していることを確認します。
jenkins-controller内のknow_hostsにssh-agentを追加するため、下記コマンドを実行しておきます。
コンテナーを新しくするたびにこの作業は必要となります。(毎回新しい鍵が作成されるため)
# docker exec jenkins-controller bash -c "ssh-keyscan -t rsa ssh-agent >> /var/jenkins_home/.ssh/known_hosts"
(上記はcompose.yml内でssh-agent -> jenkins-controllerという順に起動するように設定すれば、Dockerfile内などで自動設定することが可能そうです。)
Webアクセス
次に、ControllerへWebアクセスして、Nodeの設定と変数の確認をしていきます。
その前に管理者アカウントのパスワードを下記コマンドで確認しておきます。
# cat controller/jenkins_home/secrets/initialAdminPassword
http://<dockerマシンのipアドレスまたはhostname:8080/
へアクセスすると下図のような画面が表示されるので、上記で確認したパスワードを入力して「Continue」をクリックします。
Customize Jenkins画面が表示されます。今回は「Install suggested plugins」を選択します。
インストールされるまで待ちます。
Create First Admin User画面が表示されますが、今回はskipします。
Instance COnfiguration画面が表示されるので、URLを確認して「Save and Finish」をクリックします。
するとJenkinsダッシュボードへアクセスできるようなります。
Node設定
ssh-agent
Jenkinsダッシュボード画面で「Jenkinsの管理」>「Nodes」と遷移します。
「New Node」をクリックします。
New node画面で下記の値を設定して「Create」をクリックします。
- ノード名:ssh-agent
- Type:Permanent Agentにチェック
ノードの詳細を設定する画面で下記の値を設定して、「保存」をクリックします。
- リモートFSルート:/home/jenkins
- ラベル:ssh-agent
- 起動方法:SSH経由でUnixマシンのスレーブエージェントを起動
- ホスト:ssh-agent
- 認証情報:「追加」>「Jenkins」とクリック。
- 認証情報:
- 種類:SSHユーザー名と秘密鍵
- スコープ:グローバル
- ID:jenkins
- ユーザー名:jenkins
- 秘密鍵:作成した秘密鍵を直接コピペ
(Host Key Verification Strategyは「Non verifying Verification Strategy」の方がknown_hostsをチェックしないからラク?)
- 認証情報:
上記の設定でノードとしてssh-agentが追加され、同期中になっていれば大丈夫です。
もし、下記のようなエラーで同期されなかった場合は、jenkins-controller内の.sshやknown_hostsのオーナーおよびパーミッションを確認してみてください。
/var/jenkins_home/.ssh/known_hosts [SSH] No Known Hosts file was found at /var/jenkins_home/.ssh/known_hosts. Please ensure one is created at this path and that Jenkins can read it.
Key exchange was not finished, connection is closed.
他に、下記のようなエラーメッセージが出力される場合は、ssh-agentコンテナー内のjenkinsユーザーの.ssh/authorized_keysへpubキーが登録されているか確認してください。
ERROR: Server rejected the 1 private key(s) for jenkins (credentialId:jenkins/method:publickey)
inbound-agent
ssh-agentと同様に追加していきます。
Jenkinsダッシュボード画面で「Jenkinsの管理」>「Nodes」と遷移します。
「New Node」をクリックします。
New node画面で下記の値を設定して「Create」をクリックします。
- ノード名:inbound-agent
- Type:Permanent Agentにチェック
ノードの詳細を設定する画面で下記の値を設定して、「保存」をクリックします。
- リモートFSルート:/home/jenkins
- ラベル:inbound-agent
- 起動方法:Launch agent by connecting it to the controller
ノードが作成されますが、同期されていないと思います。
ノード名のリンクをクリックし、Agent inbound-agent画面へ進み、Run from agent command lineの情報をコピーしておきます。
curl -sO http://<ip or hostname>:8080/jnlpJars/agent.jar
java -jar agent.jar -url http://<ip or hostname>:8080/ -secret <secret value> -name "inbound-agent" -webSocket -workDir "/home/jenkins"
上記の値を.envファイル内に記載します。
JENKINS_AGENT_SSH_PUBKEY=<pubキーの内容>
JENKINS_URL=http://jenkins-controller:8080/
JENKINS_AGENT_WORKDIR=/home/jenkins
JENKINS_SECRET=<secret value>
JENKINS_NAME=inbound-agent
上記の設定をして、コンテナーを再起動または再作成(known_hosts関連の再設定が必要)します。
# docker compose stop
# docker compose up -d
# docker compose down
# docker compose up -d
pipelineを試しに動かしてみる
試しにジョブを作成して動かしてみます。
Jenkinsダッシュボード上で「新規ジョブ作成」をクリックします。
- ジョブ名:pipeline-test
- Select an item type:パイプライン
- General
- 定義:Pipeline script
- Script:下記のJenkinsfileの内容
Jenkinsfile
pipeline { // Declarative pipelineであることを宣言する
//agent any // 環境の指定(anyなので指定なし)
//agent { label 'jenkins-controller'} //実行する環境を指定
agent { label 'ssh-agent'}
stages{
stage('Hello') {
steps {
sh '''
TEST_FILE_NAME=file_$(date +"%Y%m%d%I%M%S").txt
touch ${TEST_FILE_NAME}
echo 'Hello World' > ${TEST_FILE_NAME}
echo "hostname: $(hostname)" >> ${TEST_FILE_NAME}
echo "user: $(whoami)" >> ${TEST_FILE_NAME}
echo "time: $(date)" >> ${TEST_FILE_NAME}
echo "printenv: $(printenv)" >> ${TEST_FILE_NAME}
'''
}
post { //ステップ終了処理
always { //常に実行
sh '''
echo "end Hello stage"
'''
}
success { //成功時
sh '''
echo "success Hello stage"
'''
}
failure{ //失敗時
echo "========Fail……========"
}
}
}
stage("check"){
steps{
echo 'checking'
}
}
}
post{
always{
echo "========Finish========"
}
}
}
その他
今回のcompose.ymlファイルではcontroller->agentの順で起動するように指定しましたが、
agent->controllerの方が良いのかな?agentからcontrollerへの接続は試行してくれないということもどっかの記事で見た気がするが。。。
あと、それぞれのコンテナで日本語と日本時間の設定をするようにしたのですが、うまく動作していない。。。
参考
- dockerhub Jenkins
- docker-composeを用いたローカルJenkins環境構築方法 メモ
- docker-composeを使って開発環境でJenkinsを動かせるようにした時のトラシュー
- docker-composeを使用したJenkinsのセットアップで詰まったところ
- jenkins管理サーバにターゲットノードを追加して操作できるようにする
- 【Jenkins運用基本】Jenkinsの管理ノードに外部サーバを追加する手順
- [01] declarative pipeline の使用例 -- Slave 8台から空いている Slave を使う(4パターン)
- Jenkinsfileの書き方
- Jenkinsfile入門
- Jenkinsを勉強する - Pipelineを使ってみる
- Jenkinsfileの書き方 (Jenkins Pipeline)
- 使ってみよう!Jenkins 「Pipeline」
- dockerでjenkins構築(2020年05月時点)
- 【初心者】いまさらJenkinsに入門する (1)インストールと基本操作
- AmazonLinux2023にJenkinsをインストールする
Discussion