EFS/AutoScalingによるJenkinsサーバの構築

Elastic File System

渡辺です。

先日、「AWS における Jenkins(PDF)」というホワイトペーパーが公開されました。

Jenkinsを運用するポイントとして、可用性の確保があります。 Jenkinsは、WordPressなどと同様に、ローカルのファイルシステムに状態を保存するため、冗長構成をとりにくいシステムです。 また、仕組みとして複数のJenkinsを稼動させてしまうと、同じジョブが同時に実行されてしまうという問題もあります。 このため、可用性の高いJenkinsをAWSで構築する場合、2016年9月時点で推奨されるのはAmazon EFS(Amazon Elastic File System)とAutoScalingとの組み合わせです。

早速、試してみましょう。

構成

Jenkinsのデータは、すべてJENKINS_HOMEで定義されるディレクトリに保存されます(ログファイルを除く)。 このJENKINS_HOMEをEFSで構築し、ELB + AutoScalingでJenkinsを動作させるEC2インスタンスの死活監視とオートリカバリを行うという構成です。

HA_Jenkins

EFSの作成

はじめにAmazon EFS(Amazon Elastic File System)を作成します。 EFSはリージョン内のAZを横断して利用できるファイルシステムです。 自動的に拡張されていくことも特徴ですが、HA構成Jenkinsでは共有ファイルシステムとして利用します。 なお、2016年10月時点では東京リージョンでEFSを利用できないため、オレゴンで構築しました。

EFSのメニューを開き、「Create file system」をクリックしてEFSを作成します。 最初の画面で作成するVPCと利用するAZを選択します。

Elastic_File_System_Management_Console

利用するAZについては、単一でも複数でも料金は変わらないようです。 もしかするとパフォーマンスに影響はあるのかもしれませんが、とりあえず全て選択して「Next Step」を選択してください。

Elastic_File_System_Management_Console 2

続けてタグを設定しますが、Nameに「JENKINS_HOME」としました。 識別用の名前なので適当につけていいでしょう。 パフォーマンスモードは推奨の「General Purpose (default)」を選択します。 「Max I/O」は何十何百といったEC2インスタンスが同時にEFSにマウントするようなケースで選択するようです。

以上でEFSの作成は完了です。 容量は自動的に拡張されていくので、指定することはありません。

EC2インスタンスのセットアップ

この後にAuto Scalingで利用するAMIを作成するため、EC2インスタンスを立ち上げます。 今回はAmazonLinuxに予めインストールされているOpenJDK7を利用します。

EFSのマウント

はじめにEFSをEC2インスタンスにマウントします。 作成したEFSの画面に「EC2 mount instructions」というリンクがあるので、これをクリックしてください。

Elastic_File_System_Management_Console 3

マウント用のコマンドがあるのでこれをコピーしておきます。

Elastic_File_System_Management_Console 4

SSHでEC2インスタンスにログインしたならば、ディレクトリ/jenkins_home を作成し、そこにEFSをマウントしましょう。

$ sudo mkdir /jenkins_home
$ sudo mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 $(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone).fs-xxxxxxxx.efs.us-west-2.amazonaws.com:/ /jenkins_home

コマンドは先ほどコピーしたコマンドを元に、最後のefs/jenkins_homeを変えました。

Jenkinsのインストール

Jenkinsのインストール手順を参考に、Jenkinsをインストールします。

$ sudo wget -O /etc/yum.repos.d/jenkins.repo http://pkg.jenkins-ci.org/redhat-stable/jenkins.repo
$ sudo rpm --import https://jenkins-ci.org/redhat/jenkins-ci.org.key
$ sudo yum install jenkins

JENKINS_HOMEの設定

Jenkinsの設定ファイル/etc/sysconfig/jenkinsを編集し、JENKINS_HOMEをEFSでマウントしたディレクトリ/jenkins_homeに変更します。

# Directory where Jenkins store its configuration and working
# files (checkouts, build reports, artifacts, ...).
#
#JENKINS_HOME="/var/lib/jenkins"
JENKINS_HOME="/jenkins_home"

ここで、ディレクトリ/jenkins_homeの所有者をjenkinsに変更しておきます。

$ sudo chown jenkins: /jenkins_home

Jenkinsの起動と動作確認

Jenkinsを起動し、WebUIから動作確認を行いましょう。

$ sudo service jenkins start

動作確認が出来たならば、EC2インスタンスを停止し、AMIを作成してください。

ELBの作成

ELBを作成します。 Jenkinsはポート8080で動作するので、リスナーにインスタンスポートは8080です。 ヘルスチェックも8080ポートで、認証画面である/loginを指定してください。

AutoScalingの設定

先ほど作成したAMIを使ってAutoScalingの起動設定を作成します。

ここでポイントとなるのはEFSのマウントです。 EC2のLunch時に適切なDNS名でマウントできるように、ユーザデータに次のようなスクリプトを追加してください。 mountコマンドへの引数は、EC2インスタンスにEFSをマウントした時のコマンドです。

#!/bin/bash
mount -t nfs4 -o nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2 $(curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone).fs-xxxxxxxx.efs.us-west-2.amazonaws.com:/ /jenkins_home

なお、ホワイトペーパーでは、/etc/fstabを編集してEFSをマウントしています。 しかし、どのAZに起動した場合でもEFSがマウントされるようにするには起動時にエンドポイントを取得しなければなりません。 AutoScalingで動かすインスタンスであるため、reboot時などの対応は不要でしょう。

また、Jenkinsではプラグインインストールなどが行われますので、PublicIPの付与を忘れないようにしてください。

起動設定を作成したならば、AutoScalingグループを作成します。 AutoScalingグループを作成する時、インスタンス数は1を維持するようにします。 また、ELBにアタッチし、ヘルスチェックもELBのヘルスチェックで行うように設定しましょう。

設定が完了したならば、AutoScalingでJenkinsインスタンスが立ち上がり、ELBでIn Serviceになるのを待ちます。 起動が完了したならば、ELBのPublic DNSからJenkinsにアクセスしてください。 問題がなければSSL証明書の設定やドメインの割当てを行ってください。

オートリカバリの確認

以上でAutoScalingによるJenkinsサーバのHA構成が構築できました。

最後に動作確認をしておきます。 ELBのヘルスチェックを失敗させるため、Jenkinsサーバにsshログインし、Jenkinsサービスを停止します。

$ sudo service jenkins stop

しばらくすると、ELBのヘルスチェックに失敗し、AutoScalingがトリガされます。 既存のEC2インスタンスは削除され、新しいEC2インスタンスが起動します。 新しいEC2インスタンスは起動時にEFSをマウントし、Jenkinsの設定はそのまま引き継がれることが確認できます。

まとめ

EFSを利用することでユーザディレクトリを複数のEC2インスタンスで共有できるようになります。 Jenkinsでは、JENKINS_HOMEをEFSで設定する事で簡単にAutoScaling構成をとることができました。

しかし、Jenkinsは冗長構成をとっているわけではないので、障害時に数分ほどのダウンタイムは発生してしまいます。 とはいえ、障害発生時に自動復旧はできるようになるため、可用性は大幅に高くなりました。