AWS

【AWS】EC2のDockerでJenkins(Master+Slave)導入

【AWS】EC2のDockerでJenkins(Master+Slave)導入
エンジニア
EC2のDockerに、Docker ComposeでJenkinsのMasterとSlaveをインストールする手順を教えてください。

AWS経験3年以上の僕が詳しくご説明します。

本記事ではまず正しい手順でJenkinsを導入し、次につまずきポイントと対処法についても紹介します。

 

本記事の内容

  • EC2 (Amazon Linux 2) のDocker + Docker ComposeでJenkinsのMasterをインストール
  • JenkinsのSlaveを1台追加
  • Jenkinsインストール時のつまずきポイントについて

構成イメージ:はじめに

EC2のDockerにおけるJenkins(Master+Slave)構成イメージ

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をインストール

事前準備として、EC2にDockerとDocker Composeをインストールしておきます。

次に、Jenkins管理画面に接続するために必要なセキュリティグループを設定します。

 

EC2にDockerとDocker Composeをインストール

EC2はAmazon Linux 2をt2.microで作成、パブリックサブネットに配置しパブリックIPアドレスを付与します。

EC2作成方法については、こちらの記事をご覧ください。↓

関連記事
【AWS】EC2でAmazon Linux 2を構築しSSH接続してみよう
【AWS】EC2でAmazon Linux 2を構築しSSH接続してみよう

続きを見る

 

EC2にDockerとDocker Composeをインストールする方法は、こちらの記事をご覧ください。↓

関連記事
【AWS】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に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を作成します。ホストのjenkins_homeディレクトリとコンテナの/var/jenkins_homeディレクトリを共有するように、volumes属性を定義します。

$ vi docker-compose.yml


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] をクリックします。

Jenkins初期パスワード入力画面

 

左の [Install suggested plugins] をクリックします。

Jenkinsプラグインインストール選択

 

プラグインのインストールが完了するまで数分待ちます。

Jenkinsプラグインインストール中

 

[Create First Admin User] 画面でJenkins管理画面で使用するアカウント情報を設定します。

以下を参考に情報を入力し、[Save and Continue] をクリックします。(これらの情報はあとから変更できます)

項目 説明 入力例
ユーザー名 管理画面のログインユーザー名 jenkins
パスワード 上記ユーザーのパスワード 任意
パスワードの確認 パスワードの確認 任意
フルネーム 上記ユーザーのフルネーム jenkins
メールアドレス 上記ユーザーのメールアドレス jenkins@example.com

Jenkins管理ユーザー作成

 

Jenkins管理画面のURLが表示されますが、先程のcurlで確認したURLと同じなので特にメモする必要はありません。[Save and Finish] をクリックします。

Jenkins管理画面のURL

 

[Start using Jenkins] をクリックします。

Jenkins準備完了

 

Jenkins管理画面にログインできました。これでJenkinsのMasterインストールは完了です。

Jenkins管理画面

 

JenkinsのSlaveを追加する

EC2のDockerに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.ymlDockerfileを元に、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の管理] をクリックします。

EC2のDockerにJenkins(Slave)を追加1

 

[System Configuration] の [ノードの管理] をクリックします。

EC2のDockerにJenkins(Slave)を追加2

 

左側の [新規ノード作成] をクリックします。

EC2のDockerにJenkins(Slave)を追加3

 

今回はSlaveのノード名を [slave01] とします。以下のとおり入力し、[OK] をクリックします。

  • ノード名: slave01
  • Permanent Agent:チェックを入れる

EC2のDockerにJenkins(Slave)を追加4

 

以下のとおり入力、設定します。記載していないパラメータは、入力不要(デフォルトのまま)です。

(入力する箇所が分かりづらいので、自分の画面と以下の画像をよく照らし合わせながら設定してください。)

  • リモートFSルート:/home/jenkins
  • 起動方法:SSH経由でUnixマシンのスレーブエージェントを起動
  • ホスト:slave01
  • 認証情報: [追加] -> [Jenkins] をクリック

EC2のDockerにJenkins(Slave)を追加5

 

[認証情報の追加] で以下のとおり入力し、[OK] をクリックします。記載していないパラメータは入力不要(デフォルトのまま)です。

  • 種類:SSHユーザー名と秘密鍵
  • ユーザー名:jenkins
  • 秘密鍵:[直接入力] にチェックを入れる -> [Add] をクリック

EC2のDockerにJenkins(Slave)を追加6

 

続いて、Slaveが使用するSSH秘密鍵を設定します。

以下コマンドでSSH秘密鍵を全て(-----BEGIN OPENSSH PRIVATE KEY----- から-----END OPENSSH PRIVATE KEY-----まで)コピーし、[鍵] の入力フィールドにペースト、[追加] をクリックします。


$ cat jenkins_home/.ssh/id_rsa

 

EC2のDockerにJenkins(Slave)を追加7

 

最後に以下のとおり設定し、[保存] をクリックします。

  • [認証情報] をクリックし、[jenkins] を設定する
  • Host Key Verification Strategy:[Manually trusted key Verification Strategy]

EC2のDockerにJenkins(Slave)を追加8

 

Slaveを追加した直後は赤いマークが表示されています。

EC2のDockerにJenkins(Slave)を追加9

 

[ステータス更新] を何回かクリックし、MasterがSlaveを認識すると赤いマークが消えます。これでSlaveを追加できました。

EC2のDockerにJenkins(Slave)追加完了

 

DockerにJenkinsを導入する際のつまずき(エラー)ポイント

DockerにJenkinsを導入する際のつまずき(エラー)ポイントを3つ紹介します。

 

1. ERROR: manifest for jenkins:latest not found: manifest unknown: manifest unknown

Jenkins(Master)のDockerコンテナを起動する際、docker-compose.ymlimage属性で、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.ymlvolumes属性で、ホストとコンテナで共有するディレクトリを定義します。

試しに、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の赤いマークが消えません。

Jenkins管理画面で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) をインストールする手順と、つまずきポイントについてご紹介しました。

当記事が参考になれば幸いです。

-AWS

© 2022 ふにノート