AWS経験5年以上の僕が詳しくご説明します。
本記事ではまず正しい手順でJenkinsを導入し、次につまずきポイントと対処法についても紹介します。
本記事の内容
- EC2 (Amazon Linux 2) のDocker + Docker ComposeでJenkinsのMasterをインストール
- JenkinsのSlaveを1台追加
- Jenkinsインストール時のつまずきポイントについて
もくじ
構成イメージ:はじめに
EC2のDocker + Docker ComposeでJenkinsのMasterを導入し、次にSlaveを追加します。
AWSマネジメントコンソールとSSHクライアントを使用します。
Jenkinsを導入するEC2環境
Jenkinsを導入するEC2環境は以下のとおりです。
- OS:EC2 (Amazon Linux 2) t2.micro
- Docker:ver 20.10.7
- Docker Compose:ver 1.29.2
- Jenkins:ver 2.303.1
EC2のホスト側に作成するファイル・ディレクトリ構成
本記事の手順の中で、EC2 ホスト側のec2-userホームディレクトリに、以下のファイルとディレクトリを作成していきます。
.
├── docker-compose.yml
├── Dockerfile
└── jenkins_home/
EC2にJenkinsをインストールする前の準備
事前準備として、EC2にDockerとDocker Composeをインストールしておきます。
次に、Jenkins管理画面に接続するために必要なセキュリティグループを設定します。
EC2にDockerとDocker Composeをインストール
EC2はAmazon Linux 2をt2.microで作成、パブリックサブネットに配置しパブリックIPアドレスを付与します。
EC2作成方法については、こちらの記事をご覧ください。↓
-
【AWS】EC2でAmazon Linux 2を構築しSSH接続してみよう
続きを見る
EC2にDockerとDocker Composeをインストールする方法は、こちらの記事をご覧ください。↓
-
【AWS】EC2にDockerとDocker Composeをインストール
続きを見る
セキュリティグループの設定
EC2のセキュリティグループで、インバウンドルールに以下が設定されていることを確認します。もし未設定の場合は設定しておきましょう。
- タイプ:SSH
- ソース:マイIP
続いて、同じEC2のセキュリティグループに、以下のインバウンドルールを追加します。
本記事では、クライアントPCからJenkins管理画面に接続する際に、ポート番号に28080/tcpを使用するためです。
- タイプ:カスタム TCP
- ポート範囲:28080
- ソース:マイIP
EC2にSSHで接続する
SSHクライアントを使用して、作成したEC2にec2-userでSSH接続します。
EC2のDockerにJenkins(Master)をインストールする
最初に、EC2のDockerに対して、Docker ComposeでJenkinsのMasterをインストールします。
docker-compose.ymlを作成
はじめに任意のディレクトリを作成します。今回はec2-userのホームディレクトリにjenkins
ディレクトリを作成して、cd
で移動します。
$ mkdir ~/jenkins && cd ~/jenkins
ホストからコンテナへマウントするディレクトリを作成しておきます。今回はjenkins_home
ディレクトリを作成します。
$ mkdir jenkins_home
コンテナ内のjenkinsユーザは、uid:gidが1000:1000
となります。jenkinsユーザがファイルを書き込めるように、jenkins_home
ディレクトリのオーナーをjenkinsユーザの1000:1000
に変更しておきます。
$ sudo chown 1000:1000 jenkins_home
EC2において実際はchown
は必須ではありません。Amazon Linux 2におけるec2-userのuid:gidは、たまたまjenkinsユーザーと同じ1000:1000
であるためです。
しかし、Dockerを使用したJenkinsインストール手順に共通性をもたせるためchown
する手順も入れています。
docker-compose.yml
を作成します。
$ vi docker-compose.yml
ホストのjenkins_home
ディレクトリとコンテナの/var/jenkins_home
ディレクトリを共有するように、volumes
属性を定義します。
version: "3.8"
services:
master:
container_name: master
image: jenkins/jenkins:lts
ports:
- 28080:8080
volumes:
- ./jenkins_home:/var/jenkins_home
Jenkins(Master)のDockerコンテナ起動
docker-compose.yml
を元に、Jenkins(Master)のDockerコンテナを起動します。
docker-compose up
コマンドで-dオプションをつけてバックグラウンドでコンテナを起動します。
$ docker-compose up -d
Creating network "jenkins_default" with the default driver
Pulling master (jenkins/jenkins:lts)...
lts: Pulling from jenkins/jenkins
略
Status: Downloaded newer image for jenkins/jenkins:lts
Creating master ... done
docker-compose ps
コマンドでプロセスが起動していることを確認します。
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------
master /sbin/tini -- /usr/local/b ... Up 50000/tcp, 0.0.0.0:28080->8080/tcp,:::28080->8080/tcp
以下ファイルからJenkins管理画面の初期管理パスワードを確認できるので、テキストエディタなどにコピペしておきます。
$ cat jenkins_home/secrets/initialAdminPassword
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上記ファイルは、このあと行う [Jenkins管理画面における初期設定] の完了後は自動的に削除されます。
初期設定でのみ使う一時的なパスワードだからですね。
Jenkins管理画面における初期設定
EC2のパブリックDNSを、EC2のメタデータから取得します。(AWSマネジメントコンソールの対象EC2における「詳細」タブからも確認可能)
$ curl -s http://169.254.169.254/latest/meta-data/public-hostname | \
sed -e "1s/^/http:\/\//" \
-e "1s/$/:28080\//" | \
awk 1
コマンドの説明
- curlの -s オプションで出力の進捗を非表示にする
- [sed -e "1s/^/http:\/\//"] で先頭に [http://] を付与
- [-e "1s/$/:8080\//"] で末尾に「:28080/] を付与
- [awk 1] で末尾に改行を付与
上記の出力結果より、Webブラウザから http://EC2のパブリックDNS:28080
にアクセスします。
[Jenkinsの準備ができるまで、お待ちください。] が表示されていた場合は少し待ちます。
[Administrator password] に先程メモした初期パスワードをコピペし、右下の [Continue] をクリックします。
左の [Install suggested plugins] をクリックします。
プラグインのインストールが完了するまで数分待ちます。
[Create First Admin User] 画面でJenkins管理画面で使用するアカウント情報を設定します。
以下を参考に情報を入力し、[Save and Continue] をクリックします。(これらの情報はあとから変更できます)
項目 | 説明 | 入力例 |
ユーザー名 | 管理画面のログインユーザー名 | jenkins |
パスワード | 上記ユーザーのパスワード | 任意 |
パスワードの確認 | パスワードの確認 | 任意 |
フルネーム | 上記ユーザーのフルネーム | jenkins |
メールアドレス | 上記ユーザーのメールアドレス | jenkins@example.com |
Jenkins管理画面のURLが表示されますが、先程のcurl
で確認したURLと同じなので特にメモする必要はありません。[Save and Finish] をクリックします。
[Start using Jenkins] をクリックします。
Jenkins管理画面にログインできました。これでJenkinsのMasterインストールは完了です。
JenkinsのSlaveを追加する
つぎに作成したJenkinsのMasterに対して、Slaveを追加します。
Jenkins(Master) のSSHキーペアを作成
Jenkins(Master)でSSHキーペアを作成します。Slaveを追加する時にこのSSHキーペアを使用します。
docker container exec
でホストから直接コンテナに対しssh-keygen
を実行。
対話は全て何も入力せずEnterキーを押下します。今回、パスフレーズは設定しません。
$ docker container exec -it master ssh-keygen -t rsa -C ""
Generating public/private rsa key pair.
Enter file in which to save the key (/var/jenkins_home/.ssh/id_rsa):
Created directory '/var/jenkins_home/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /var/jenkins_home/.ssh/id_rsa
Your public key has been saved in /var/jenkins_home/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
The key's randomart image is:
+---[RSA 3072]----+
略
+----[SHA256]-----+
以下を実行しテキストエディタなどにSSH公開鍵の中身をコピペしておきます。
$ cat jenkins_home/.ssh/id_rsa.pub
ssh-rsa AAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx略
以下のとおりコンテナにbashでログインし、SSH公開鍵を確認することもできます。
$ docker container exec -it master bash
$ cat /var/jenkins_home/.ssh/id_rsa.pub
$ exit
docker-compose.ymlにSlaveの設定を追加
docker-compose.yml
にJenkins(Slave)の設定を追加します。
$ vi docker-compose.yml
以下の10行目以降を追加。末尾のJENKINS_SLAVE_SSH_PUBKEY
の値は、先程メモしたSSH公開鍵を設定します。
version: "3.8"
services:
master:
container_name: master
image: jenkins/jenkins:lts
ports:
- 28080:8080
volumes:
- ./jenkins_home:/var/jenkins_home
slave01:
container_name: slave01
build: .
environment:
- JENKINS_SLAVE_SSH_PUBKEY=ssh-rsa AAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx略
Slave用にDockerfile
を作成します。
$ vi Dockerfile
FROM jenkinsci/ssh-slave
RUN ln -s /usr/local/openjdk-8/bin/java /usr/local/bin/java
Dockerfileを作成する理由
Slaveとして起動するコンテナでは、デフォルトではOSのjenkinsユーザのPATHにJavaが含まれていません。
このため、Slaveのコンテナ起動時に/usr/local/openjdk-8/bin/java
へのシンボリックリンク/usr/local/bin/java
を作成し、SlaveのjenkinsユーザがJavaを使用できるようにします。
Jenkins(Slave)のコンテナをbuild
Slave起動前のプロセスを確認します。この時点ではmasterしか起動していません。
$ docker-compose ps
Name Command State Ports
-------------------------------------------------------------------------------------------------------
master /sbin/tini -- /usr/local/b ... Up 50000/tcp, 0.0.0.0:28080->8080/tcp,:::28080->8080/tcp
docker-compose.yml
とDockerfile
を元に、SlaveのDockerコンテナを起動します。
-d オプションをつけてバックグラウンドでコンテナを起動します。(出力結果は見やすいように改行しています)
$ docker-compose up -d
Building slave01
Sending build context to Docker daemon 315.1MB
Step 1/2 : FROM jenkinsci/ssh-slave
latest: Pulling from jenkinsci/ssh-slave
略
Successfully built b6d822f82c97
Successfully tagged jenkins_slave01:latest
WARNING: Image for service slave01 was built because it did not already exist.
To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.
master is up-to-date
Creating slave01 ... done
Slave起動後のプロセスを確認します。masterに加えて、slave01も起動していることが確認できますね。
$ docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------------------------------
master /sbin/tini -- /usr/local/b ... Up 50000/tcp, 0.0.0.0:28080->8080/tcp,:::28080->8080/tcp
slave01 setup-sshd Up 22/tcp
Jenkins管理画面からSlaveを追加
左側の [Jenkinsの管理] をクリックします。
[System Configuration] の [ノードの管理] をクリックします。
左側の [新規ノード作成] をクリックします。
今回はSlaveのノード名を [slave01] とします。以下のとおり入力し、[OK] をクリックします。
- ノード名: slave01
- Permanent Agent:チェックを入れる
以下のとおり入力、設定します。記載していないパラメータは、入力不要(デフォルトのまま)です。
(入力する箇所が分かりづらいので、自分の画面と以下の画像をよく照らし合わせながら設定してください。)
- リモートFSルート:/home/jenkins
- 起動方法:SSH経由でUnixマシンのスレーブエージェントを起動
- ホスト:slave01
- 認証情報: [追加] -> [Jenkins] をクリック
[認証情報の追加] で以下のとおり入力し、[OK] をクリックします。記載していないパラメータは入力不要(デフォルトのまま)です。
- 種類:SSHユーザー名と秘密鍵
- ユーザー名:jenkins
- 秘密鍵:[直接入力] にチェックを入れる -> [Add] をクリック
続いて、Slaveが使用するSSH秘密鍵を設定します。
以下コマンドでSSH秘密鍵を全て(-----BEGIN OPENSSH PRIVATE KEY----- から-----END OPENSSH PRIVATE KEY-----まで)コピーし、[鍵] の入力フィールドにペースト、[追加] をクリックします。
$ cat jenkins_home/.ssh/id_rsa
最後に以下のとおり設定し、[保存] をクリックします。
- [認証情報] をクリックし、[jenkins] を設定する
- Host Key Verification Strategy:[Manually trusted key Verification Strategy]
Slaveを追加した直後は赤いマークが表示されています。
[ステータス更新] を何回かクリックし、MasterがSlaveを認識すると赤いマークが消えます。これでSlaveを追加できました。
DockerにJenkinsを導入する際のつまずき(エラー)ポイント
DockerにJenkinsを導入する際のつまずき(エラー)ポイントを3つ紹介します。
1. ERROR: manifest for jenkins:latest not found: manifest unknown: manifest unknown
Jenkins(Master)のDockerコンテナを起動する際、docker-compose.yml
のimage
属性で、Docker HubからpullするJenkinsのDockerイメージ名を定義します。
試しに、イメージ名をjenkins
と記述し、コンテナを起動しようとするとタイトルのエラーが発生します。
$ vi docker-compose.yml
version: "3.8"
services:
master:
container_name: master
image: jenkins
ports:
- 28080:8080
volumes:
- ./jenkins_home:/var/jenkins_home
docker-compose up
コマンドを実行すると以下エラーになります。ちなみに、イメージ名をjenkins:latest
と記述しても同様のエラーになります。
$ docker-compose up -d
Creating network "test_default" with the default driver
Pulling master (jenkins:)...
ERROR: manifest for jenkins:latest not found: manifest unknown: manifest unknown
理由は jenkins
またはjenkins:latest
はDocker Hubからイメージをpullできないためです。本記事では代わりにjenkins/jenkins:lts
を使用しています。
ちなみにdocker search
で「jenkins
ではなくjenkins/jenkins:lts
を指定してください」と確認できます。
$ docker search --limit 5 jenkins
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
jenkins DEPRECATED; use "jenkins/jenkins:lts" instead 5341 [OK]
jenkins/jenkins The leading open source automation server 2725
jenkinsci/jenkins Jenkins Continuous Integration and Delivery … 391
jenkins/jnlp-slave a Jenkins agent which can connect to Jenkins… 148 [OK]
jenkinsci/jnlp-slave A Jenkins slave using JNLP to establish conn… 134 [OK]
2. Can not write to .. copy_reference_file.log. Wrong volume permissions .. Permission denied
Jenkins(Master)のDockerコンテナを起動する際、docker-compose.yml
のvolumes
属性で、ホストとコンテナで共有するディレクトリを定義します。
試しに、volumes
属性で指定したディレクトリを、ホストで作成せずにコンテナを起動しようとするとタイトルのエラーが発生します。
$ ls
docker-compose.yml
エラーが標準出力で確認できるようにdocker-compose up
コマンドで-dオプションをつけずに、フォアグラウンドでコンテナを起動します。すると以下エラーになります。
$ docker-compose up
Creating network "test_default" with the default driver
Pulling master (jenkins/jenkins:lts)...
lts: Pulling from jenkins/jenkins
略
Creating master ... done
Attaching to master
master | Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions?
master | touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied
master exited with code 1
ホストにvolumes
属性で指定したディレクトリが存在しない場合、コンテナ起動時にDocker Composeが自動でディレクトリを作成してくれます。ここで重要なのが、デフォルトではそのディレクトリのオーナーがrootになる点です。
以下はDocker Composeが作成したディレクトリですが、オーナーがrootになっています。
$ ls -l
total 4
-rw-rw-r-- 1 ec2-user ec2-user 177 Oct 23 00:31 docker-compose.yml
drwxr-xr-x 2 root root 6 Oct 23 00:37 jenkins_home
この状態だとコンテナのjenkinsユーザーからファイルが書き込めないため、タイトルのPermission deniedが発生します。
本エラーを回避するため、本記事では事前にホスト側でディレクトリを作成し、オーナーをjenkinsユーザに変更してからコンテナを起動します。
3. Java is not in the PATH nor configured with the javaPath setting, Jenkins will try to guess where is Java
Slaveとして起動するJenkinsは、OSのjenkinsユーザにおけるPATHにJavaが含まれていません。
試しに、Slave用のDockerfile
を使用せず、上記のPATHを意識しないでSlaveのコンテナを起動しようとするとタイトルのエラーが発生します。
Slaveを追加する際のdocker-compose.yml
を以下のとおりに修正します。
$ vi docker-compose.yml
以下の10行目以降を追加。image
属性に直接Slaveのコンテナイメージを指定します。
version: "3.8"
services:
master:
container_name: master
image: jenkins/jenkins:lts
ports:
- 28080:8080
volumes:
- ./jenkins_home:/var/jenkins_home
slave01:
container_name: slave01
image: jenkinsci/ssh-slave
environment:
- JENKINS_SLAVE_SSH_PUBKEY=ssh-rsa AAAAxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx略
SlaveのDockerコンテナを起動します。(出力結果は見やすいように改行しています)
$ docker-compose up -d
Pulling slave01 (jenkinsci/ssh-slave:)...
latest: Pulling from jenkinsci/ssh-slave
略
Status: Downloaded newer image for jenkinsci/ssh-slave:latest
master is up-to-date
Creating slave01 ... done
Slave起動後のプロセスを確認すると、Slaveのコンテナ自体は正常に起動しています。
$ docker-compose ps
Name Command State Ports
--------------------------------------------------------------------------------------------------------
master /sbin/tini -- /usr/local/b ... Up 50000/tcp, 0.0.0.0:28080->8080/tcp,:::28080->8080/tcp
slave01 setup-sshd Up 22/tcp
しかし、Jenkins管理画面からSlaveを追加した後、[ステータス更新] を何度クリックしても、Slaveの赤いマークが消えません。
Slaveのログを確認してみると、java
コマンドが見つからないというエラーが出力されています。(出力結果は見やすいように改行しています)
$ cat jenkins_home/logs/slaves/slave01/slave.log
略
Checking Java version in the PATH
bash: java: command not found
Java is not in the PATH nor configured with the javaPath setting, Jenkins will try to guess where is Java,
this guess will be removed in the future. :Launch agents via SSH
略
java.io.IOException: Java not found on hudson.slaves.SlaveComputer@568f1157. Install Java 8 or Java 11 on the Agent.
at hudson.plugins.sshslaves.JavaVersionChecker.resolveJava(JavaVersionChecker.java:84)
at hudson.plugins.sshslaves.SSHLauncher$1.call(SSHLauncher.java:453)
at hudson.plugins.sshslaves.SSHLauncher$1.call(SSHLauncher.java:421)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:829)
略
Slaveのコンテナにbashでログインし、jenkinsユーザーにスイッチしてみます。
/usr/local/openjdk-8/bin/java
へのPATHがないため、java
コマンドが使用できていないことが分かりました。
$ docker container exec -it slave01 bash
# su - jenkins
$ java -version
-su: java: command not found
$ ls /usr/local/openjdk-8/bin/java
/usr/local/openjdk-8/bin/java
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/local/games:/usr/games
Jenkinsの実行環境としてjava
コマンドが使用できる必要があります。
本エラーを回避するため、本記事ではSlave用のDockerfile
を作成し、Slaveのコンテナ起動時に/usr/local/openjdk-8/bin/java
へのシンボリックリンク/usr/local/bin/java
を作成しています。
EC2にDocker ComposeでJenkins導入:まとめ
EC2のDockerにDocker Composeを使用してJenkins (Master / Slave) をインストールする手順と、つまずきポイントについてご紹介しました。
当記事が参考になれば幸いです。