AWS環境にJenkins構築  -スポットインスタンススレーブ編-

# はじめに
前回の [AWS環境にDocker(dokku)でJenkins構築  -masterインスタンス編-](https://qiita.com/gutio/items/715d33eab1cf48572f67) でJenkinsサーバをセットアップしました。ただ、ビルドやテストの時だけ高性能なインスタンスを起動することで、運用コストをできるだけ下げておきたいと思います。EC2の[スポットインスタンス](https://aws.amazon.com/jp/ec2/spot/) を用いてJOB用のスレーブインスタンスを作成する手順をまとめました。

# 環境
[前回作成した環境](https://qiita.com/gutio/items/715d33eab1cf48572f67)をベースにセットアップを行います。
[Amazon EC2 plugin](https://plugins.jenkins.io/ec2) v1.38

# 構築
## 権限設定
### 1. JenkinsのIAMロールにEC2プラグインに必要な権限追加
[Amazon EC2 plugin](https://plugins.jenkins.io/ec2)の設定手順に従って、JenkinsのIAMロールにEC2アクセスなどを許可します。
他のロールに共通して付与する場合はポリシーに作ったほうがいいと思います。ですが、ec2プラグイン専用の設定はそう簡単に複製しないと思うのでインラインポリシーで済ませます。
![スクリーンショット 2017-12-27 17.04.02.png](https://qiita-image-store.s3.amazonaws.com/0/32726/e41f21d6-1cdb-ea54-1bbd-947819e8ed76.png)
![スクリーンショット 2017-12-27 17.09.15.png](https://qiita-image-store.s3.amazonaws.com/0/32726/54d672de-1b6e-35b2-9026-6290a06cddbf.png)

```json:inline-policy.json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1312295543082",
            "Action": [
                "ec2:DescribeSpotInstanceRequests",
                "ec2:CancelSpotInstanceRequests",
                "ec2:GetConsoleOutput",
                "ec2:RequestSpotInstances",
                "ec2:RunInstances",
                "ec2:StartInstances",
                "ec2:StopInstances",
                "ec2:TerminateInstances",
                "ec2:CreateTags",
                "ec2:DeleteTags",
                "ec2:DescribeInstances",
                "ec2:DescribeKeyPairs",
                "ec2:DescribeRegions",
                "ec2:DescribeImages",
                "ec2:DescribeAvailabilityZones",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "iam:PassRole"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
```

### 2. スレーブ用セキュリティグループ
マスターのセキュリティグループからSSH（２２）を受け入れるよう設定したスレーブ用セキュリティグループを作成します。
### (3. slave用IAMロール作成)
作成しなくてもどうにかなります。
ですが、JOBでの成果物をS3やECRに保存する場合に専用のものが存在すると権限を限定できるので、作成をおすすめします。
※:今回のデモでは特に追加権限は必要ありません。

## スレーブAMI作成
### 1. AMIのもとになるインスタンス作成
今回はUbuntu16.04(ami-bec974d8)をベースイメージに利用してEC2インスタンスを作成します。
### 2. Javaのインストール
プラグインでslaveを起動する際にJavaのインストールチェックがあります。自動インストールはプラグインのソースをみるとyumのみになっているため、イメージを作る際にJavaのインストールが必要です。
slave.jarのコピーもするのでJNLPで接続しそうなのですが、その場合には通信用のポート開放がmaster側に必要になるはずですがしなくても動きます。 **詳しい人コメントいただけると幸いです**
※: その他、利用時に実行するJOBに応じてmavenリポジトリのプリフェッチやDockerコマンドのインストールなど、必要応じて追加してください。
### 3. スレーブAMI作成
セットアップの終わったEC2インスタンスを停止させて、AMI(インスタンスイメージ)を作成します。
AMIの作成が終われば元になったEC２インスタンスは削除して構いません。追加の前処理が必要になった場合にはAMIから改めて復元した後に、追加操作をしてイメージを作り直せば大丈夫です。もちろん、停止だけさせておいたりそのまま放置しておいても構いません。

## Jenkins上の設定
### 1. プラグインインストール
「Jenkinsの管理 > プラグインの管理」から、「Amazon EC2 plugin」をインストールしてください。
### 2. 接続設定記載
「Jenkinsの管理 > システムの設定」のクラウド項目で「Amazon EC2」を選択すると設定項目が表示されます。
設定項目については一部をかいつまんで説明します。
![スクリーンショット 2017-12-27 17.15.48.png](https://qiita-image-store.s3.amazonaws.com/0/32726/40b7d70b-7ad0-a22e-f9c8-adb1f1c0aaa0.png)
最初に表示されるものは、EC2での基本的な接続設定です。
![スクリーンショット 2017-12-27 17.19.41.png](https://qiita-image-store.s3.amazonaws.com/0/32726/0412156a-0015-b94f-0e17-1258ff3d9ca9.png)
わかりやすい名前をつけて、秘密鍵を登録してください。ここで登録する秘密鍵は、AWSに登録してあるキーペアの秘密鍵にしてください。
AMIsの追加を押すとslave個別の設定項目が増えます。
![スクリーンショット 2017-12-27 17.28.53.png](https://qiita-image-store.s3.amazonaws.com/0/32726/c379a7e0-c7e3-c3d7-829c-e0437ac91c2f.png)

- AMI ID : スレーブ用に作成したAMIのIDをセットしてください(e.g. ami-abcd1234)
- Instance Type : スレーブを起動させる時のインスタンスタイプを選択してください。
- Use Spot Instance : スポットインスタンスで起動させる場合にはチェックを入れてください
  - Spot Max Bid Price : スポットインスタンスは価格が変動する入札制のため、許容できる最高価格を設定します。「Use Spot Instance」にチェックを入れると表示されます。
- Availability Zone : あとで設定する、subnetに連動するAvailability Zoneに引きずられるので注意してください。
- Remote user : 今回なら「ubuntu」です。ログインするユーザを適宜指定してください。
- Labels : JOBで実行インスタンスを指定する時に使うラベルを登録してください。
- Idle termination time : インスタンスを停止させるまでの時間。0で自動停止しなくなります。正の整数で最後のJOB終了からの経過時間。負の整数で毎時直前N分間にJOBを実行していなければ停止します。

高度な設定を押すと更に設定項目が増えます。

![スクリーンショット 2017-12-27 17.42.59.png](https://qiita-image-store.s3.amazonaws.com/0/32726/10dba93f-c073-020a-dfeb-a9cd2c981d4f.png)

- Number of Executors : インスタンスを起動した時に１つのインスタンスで同時実行可能なJOBの数
- Subnet ID for VPC : スレーブを起動させるsubnetのidを登録してください。(e.g. subnet-absd1234)
- Tags : EC2で付与するTagをセットできます。{Name:Name, Value : EC2でのインスタンス名}は最低でもセットしてください
- IAM Instance Profile : スレーブにセットするIAMロールの「インスタンスプロファイル ARN」項目を登録してください。(e.g. arn:aws:iam::XXXX:instance-profile/XXXX)

これで設定完了です。
Labelsで指定した名前のslaveでのみ実行するJOBを作成して確認してみましょう。

# おわりに
基本的に割安になるスポットインスタンスで高性能なマシンをJOBの実行に利用してはどうでしょうか？
たまに高額になり、入札によってはJOBの途中でAWSに殺されることがあったのでご注意ください。
また、スレーブのイメージ作成についてはansibleなどで手順を記録しておくことで、追加インストールなどイメージを新規に作る時便利だと思います。