Jibを使用したCircleCI上でのテストフロー構築

###TL;DR

- Googleが公開しているJibの簡単な紹介
    - https://github.com/GoogleContainerTools/jib
- JibとCircleCIを使用し、ブラックボックステスト済みのDockerイメージをECR(Amazon EC2 Container Registry)にpushするまでのフローを構築します。

#Jibとは
DockerでJavaアプリケーションをDockerコンテナ化するには、JavaのビルドはもちろんのことDockerfileの準備やDockerのインストール等の工程を踏むことになります。
Jibはそういった煩わしい工程をコマンド一つで全て行ってくれるOSSです。
MavenおよびGradleのプラグインとして公開されています。
　※今回はGradleのプラグインを使用します。
それだけではなく、Dockerのビルド設定を読み込み、変更のあったレイヤーだけを操作するので、DockerからコンテナをビルドするよりもJibは早く動作する点も魅力です。


###マニュアル通りにJibを使ってみる。
build.gradleに以下の記述を追加

```
plugins {
  id 'com.google.cloud.tools.jib' version '0.10.1'
}
```

記述ができたら、マニュアル通り以下の通りにコマンドを実行
※Gradleのバージョンを4.6以降が必要になります。

```
gradle jib --image=生成したいイメージ名
```

と、これだけを行うことでDockerイメージを生成し、Dockerhubへpushしてくれます。
Dockerデーモンも不要なため、**実行する環境にDockerを必要としません。**Gradleがあれば動きます。
ただし、Dockerがない環境で実行した場合、上記の記述だけではDockerhubへの認証でこけます。
ローカルにDockerがない状態で実行するには、buid.gradleに認証を設定する必要があります。
ローカルにDockerがある場合は、事前にdocker loginを実行して認証を通しておけばbuild.gradleに設定を記載しなくてもpushできます。

また、ローカルにDockerイメージを作成したいときもあります。もし、そういった場合は

```
gradle jibDockerBuild --image=生成したいImage名
```
とすることでローカルのDocker上に生成することも可能です。



#やりたかったこと
JibはDockerから手作業でコンテナイメージを生成するより簡単に、イメージをDockerリポジトリにpushまで行ってくれます。
Dockerfile等を用意せずに手軽にコンテナ化を行えるメリットを生かし、CIのフローに組み込みこんでみようと思います。
Jibが生成したDockerイメージに対してブラックボックステストをかけ、問題なければECRへPush、問題があればECRへ送らずにアラートをあげる仕組みを構築します。

###自動化の流れ

1. コードを変更し、GitHubにpush。CircleCIが走る
2. CircleCI上でホワイトボックステストを実行
3. Jibを実行し、CircleCI上のDockerにDockerイメージを生成
4. 生成したイメージからコンテナを立ち上げ、ブラックボックステストを実行
5. 上記全てに問題がなければ、Jibを使用しECRへpush。問題があればSlackへ通知



##1.コードを変更し、GitHubにpush。CircleCIが走る
まずは自分のGitHubリポジトリをCircleCIと連携させましょう。
[GitHubとCircleCIを連携させる
](https://qiita.com/kooohei/items/4806fdb6b92982e47f81)
などを参考に連携させれば準備OK

※前準備
AMIからECRへの許可を与えたユーザを作成し、EnvironmentVariablesに作成したユーザのアクセスキーをAWS_ACCESS_KEY_IDへ、シークレットキーをAWS_SECRET_ACCESS_KEYを登録しましょう。

##2.ホワイトボックステストを実行
ここは基本的な部分なので割愛。

##3. Jibを実行し、CircleCI上のDockerにDockerイメージを生成
以下の記述をbuild.gradleに記載し、ローカルのDockerにイメージを作るコマンドを実行します。

```build.gradle
jib {
  from {
    //ベースイメージ
    image = 'circleci/java'
  }
  to {
    image = 'push先のECRリポジトリ'
  }
}
```

```
gradle jibDockerBuild
```

gradle上でjibのtoの設定を加えているので--imageは不要です。

##4. 生成したイメージからコンテナを立ち上げ、ブラックボックステストを実行
Dockerイメージからコンテナを立ち上げ、ブラックボックステストを行いますが、注意点があります。
CircleCI上のDockerはCircleCIのホスト上にはいないため、立ち上げたホストからアクセスを行おうと思ってもできません。

CircleCI上でNginxのコンテナを立ち上げた場合を例に挙げます。

```bash:Nginxでの例
docker run -d -p 8080:80 nginx
curl localhost:8080
curl: (7) Failed to connect to localhost port 8080: Connection refused
```

そのため、Dockerコンテナに対してテストを行うDockerイメージを用意し、テストにかけます。
要は特定のIP(今回はテスト対象のコンテナのIP)に対してリクエストを送ってテストするだけのイメージです。
これもjibでサクッと作って実行しましょう。

```yml:.circle/config.yml
      - run:
          name: create test image
          command: cd ./test ; ../gradlew jibDockerBuild
      - run:
          name: blackbox test
          command: docker run テストを実行するイメージ
```

##5. 5. 上記全てに問題がなければ、Jibを使用しECRへpush。問題があればSlackへ通知
JibからECRへpushする場合には、[amazon-ecr-credential-helper](https://github.com/awslabs/amazon-ecr-credential-helper)を使用します。
そのため、amazon-ecr-credential-helperをPATHに加えてから実行しなければなりません。

```yml:.circle/config.yml
      - run:
          name: download go
          command: wget -O - "https://redirector.gvt1.com/edgedl/go/go1.9.2.linux-amd64.tar.gz" | tar zxvf - -C /home/circleci/project
      - run:
          name: get ecr-helper
          command: ./go/bin/go get -u github.com/awslabs/amazon-ecr-credential-helper/ecr-login/cli/docker-credential-ecr-login
      - run:
          name: push ecr
          command: export PATH=$PATH:/home/circleci/go/bin/ ; ./gradlew jib
```

上記の記述はGo言語ごと落としてくるのを毎回行なっているので効率的とは言えません。
 ※今回はDocker関連のファイルを使わない方向で実現させているので。
CircleCIのベースイメージを自前で用意する方が毎回DLを行わずにスムーズに実行してくれます。

GCP、Dockerhubへの認証の通したい場合は、[公式のドキュメント](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#configuration)を確認してください。

Slackへの通知は[CircleCIの結果をSlackに通知する](https://qiita.com/su-kun1899/items/640f6fa8b48749396c16)等を参考に設定。

##サンプルコード
https://github.com/supecura/JibSample

#どの辺りが良いのか
- Dockerfile等のDocker関連のファイル管理が不要
    - が、ベースに使用するイメージを自前で用意した場合はそちらの管理が必要になります
    - 上記例のようにCircleCIに全部任せるという手もあります。任せた分だけビルドの時間が伸びるので一長一短といったところでしょうか
- 問題の発見が早くなる
 - ブラックボックステストは本番環境を用意し、BlueGreenデプロイ行う前の段階でする印象がありますが、上記のフローの方が問題の発見は早いです
 - また大量にデプロイするサービスの場合にも、わざわざ本番環境を用意した後に見つかると、巻き戻すのは結構手間になりますが、そういった状況を防ぐのにも役立ちます


#おまけ
####実際に導入しようとしたときに浮かんだ疑問とその機能の有無
- Javaの実行クラスや引数の設定はできるの？
- JVM引数の変更はできるの？
- 環境変数の設定はできるの？

これらはJibのcontainerプロパティから設定可能です。

```gradle:sample
jib {
  container {
    jvmFlags = ['-Xms512m']
    mainClass = '実行クラス'
    args = ['hoge','huga']
    environment = [ENV:'develop', LANG:'ja_JP.UTF-8']
  }
}
```
その他、細かい設定は[公式のドキュメント](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin#example)へ