いますぐ使うCodeBuild

# CodeBuildとは

AWSのなかでCI/CDを担うCode三兄弟のひとつ、という説明では一切何もわからなかったので、軽く触った理解をメモとして残します。

AWS CodeBuildは、Jenkinsジョブ相当のものを「Build project」と呼び、任意のDockerイメージを起動して、コンテナ内で任意のコマンドを起動できるサービスと理解しました。

私自身はCircleCIあるし時間の無駄でしょと思って敬遠してましたが、CI/CDという色眼鏡を外すと、活用の幅が非常に広い面白い製品です。使わないのはもったいない！

# こんなことに使えそう

* CircleCIやTravisCIなどの CI as a Service を利用されてるなら、料金面や速度など強い不満なければ、わざわざ移設するほどのものではありません
* EC2サーバーにJenkinsを立ててCI/CDに供しているなら、一部のJenkinsジョブをCodeBuildのBuild Projectに移設すると、Dockerコンテナで動かせるフルマネージドサービスなど利点を享受できる可能性があります
* EC2サーバーにSSHして定型作業をしているなら、CodeBuildのBuildProjectとして定型化できる可能性があります
* EC2サーバー1台でcronジョブを動かして、SPOFや性能面のスケールに課題を感じているなら、CodeBuildのBuildProjectに移設し利点を享受できる可能性があります
    * 実用化されていた https://hackerslab.aktsk.jp/codebuild-batch-operation
* ウェブアプリのリリース時のRDBマイグレーションの実行場所に悩んでるなら、CodeBuildのBuildProjectに移設できる可能性があります
* DockerコンテナをLambdaで動かしたいよなあ、という妄想は、CodeBuildで実現できそう

# CodeBuildの制限

私が思いついた使い道の範囲でも、色々と制限が絡んできます。

* [CodeBuild の制限 - AWS CodeBuild](https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/limits.html)
    * 初期値での並列実行数はそんなに多くない
* [Amazon VPC の制限 - Amazon Virtual Private Cloud](https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/amazon-vpc-limits.html)
    * VPCでも動かすなら、起動中はENIとIPを使うことを念頭に。実行が終わったら、CodeBuildがENIは削除、IPは開放してくれる。
* [IAM エンティティおよびオブジェクトの制限 - AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_iam-limits.html)
    * BuildProjectごとにIAM管理ポリシーを勝手に作らせると、気がついたら上限到達してそう。
* [AWS サービスの制限 - アマゾン ウェブ サービス](https://docs.aws.amazon.com/ja_jp/general/latest/gr/aws_service_limits.html#limits_ssm)
    * 環境変数の値をSSMパラメータストアから取ってくるなら念頭に。最近緩和されましたが同時接続数なども。
        * [パラメーターストアが大幅にアップデートしました！ ｜ DevelopersIO](https://dev.classmethod.jp/cloud/aws/ssm-parameter-store-advanced-parameter/)

# サンプル: CodeBuildでGithubリポジトリを取ってきてRDS MySQLに接続する

妄想を並べるのもどうかと思うので、動きそうなサンプルを組んでみました。

- GitHubリポジトリを持ってくる
- CodeBuild提供ではないDockerイメージで動かす
- VPCで動かす
- MySQLに接続する
- MySQLの接続情報はSSMパラメータストア(暗号化)から取ってくる
- 定期実行させる

これだけ要素を押さえれば、cronジョブ実行に使えそうだとか、RDBマイグレにも使えそうだとかわかりそうなので。

## 作業するあなたは IAMFullAccess 権限を有している必要があります

CodeBuildのBuildProjectを作成する過程で、IAM権限を構成させるためです。

## VPCを準備する

VPCで動作させるなら、以下の準備が必要です。

* Publicサブネットを用意する
* PublicサブネットにNATゲートウェイを立てる
* Privateサブネットを用意する
* Privateサブネットのルートテーブルに、0.0.0.0/0がNATゲートウェイを経由するよう記載

## RDSを立てる

VPC Privateサブネットに立てておきます。VPCで動くCodeBuildから接続できるよう、RDS側セキュリティグループに穴を開けておきます。

## SSMパラメータストアに、RDS接続パスワードをテキトーな名前で入れておく

* 名前は「sasasin-aurora-serverless-admin-passwd」
* 利用枠は標準
* タイプは「安全な文字列」
* KMS主要なソースは「現在のアカウント」
* KMSキーIDは「alias/aws/ssm」

## GitHubリポジトリを準備する

とりあえず作るだけなので、README.mdだけ転がってるリポジトリでよいです。

## CodeBuild に Build Project を作る

CodeBuild管理画面を開く。Create Build Project。

- 名前は hello-codebuild-vpc-mysql
- SourceProviderは GitHub
- Repository in my GitHub Account で準備したGitHubリポジトリを指定
    - 初回はOAuth認証を要求されるので、Authしてあげて
- webhook
    - 設定組むと「特定のブランチでpushされたら」などが実現できる。今回は割愛
        - [AWS CodeBuildでGitHub Webhookイベントをフィルタリングする - あとらすの備忘録](https://kitigai.hatenablog.com/entry/2019/04/13/220607)
- Envirionment
    - Environment Image: Custom image
    - Environment Type: Linux
    - External Image URL: library/amazonlinux:2
    - Service Role: New
    - Role Name: hello-codebuild-vpc-mysql
    - 追加設定
        - VPC, Subnet, Security Group: 用意したPrivateサブネットを選択。セキュリティグープは、RDSに接続可能なものを選択。
        - Environment variables:
            - RDS_USER: (RDSに接続す
るユーザーを)、Plaintext
            - RDS_ENDPOINT: (RDSのエンドポイントURLを)、Plaintext
            - RDS_PASSWD: (SSMパラメータストアの名前を)、Parameter
                - こいつだけ Parameterなことに注意
                - 私は「sasasin-aurora-serverless-admin-passwd」としていました
- BuildSpec
    - insert
    - switch to editor 押して、内容は以下

```
version: 0.2

phases:
  install:
    commands:
      - yum install -y mariadb-server
  build:
    commands:
      - env | sort
      - mysql -u${RDS_USER} -p${RDS_PASSWD} -h${RDS_ENDPOINT} -e "show databases;"
```

amazonlinux:2では、mysqlコマンドはmariadb-serverに含まれている。mysqlではなくmariadbなのでこのように。

envを見ているのは、まあ、環境変数で指定した値や、SSMパラメータストアから取ることにした環境変数は、このように取れますよというご確認で。

## できあがり

こんな。

<img width="1067" alt="スクリーンショット 2019-05-06 17.56.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22947/d47873b9-6bf7-b544-ffec-8a81231548b6.png">

## Build Projectを実行してみる

Start Buildボタン押したら、しばらくして実行されます。

今回の作例ではyum installが時間食って2分くらいかかってますね。実行頻度や料金が気になってきたら、予めyum installしたコンテナイメージをECRかDockerhubに入れて使うと良いでしょう。

## 定期実行させる

なんも悩む要素ないです。build projectを選んで、Build triggers、create triggerで実行周期を設定するだけです。書いた時刻はGMTで解釈されるようです。

<img width="1079" alt="スクリーンショット 2019-05-06 18.07.49.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/22947/b18e9c8d-575b-af75-60e9-b909236ad8b9.png">

## 他の方法で実行させる

* webhookで実行する
    - 設定組むと「特定のブランチでpushされたら」などが実現できる。今回は割愛
        - [AWS CodeBuildでGitHub Webhookイベントをフィルタリングする - あとらすの備忘録](https://kitigai.hatenablog.com/entry/2019/04/13/220607)
* 任意のタイミングで、AWS SDKのCodeBuild.Client.start_build()で実行する
    * https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/codebuild.html#CodeBuild.Client.start_build
* AWSイベントをLambdaで受けて、CodeBuildのBuild Projectを実行する
    * https://github.com/tdmalone/codebuild-trigger/blob/master/lambda_function.py
* 任意のイベントをZapier経由でLambdaで受けて、CodeBuildのBuild Projectを実行する
    * https://zapier.com/apps/categories/aws

などなど

# ここまでをコード化する

TerraformかCloudFormationでしょう。

* https://www.terraform.io/docs/providers/aws/r/codebuild_project.html
* https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-resource-codebuild-project.html

buildspec.ymlをどこで管理するのが正解か。私は今のところ、TerraformかCloudFormationと一緒に置いておくのが良いのではないかなあという気がしています。正直、buildspec.ymlだけ見たところで何もわからない。ジョブ定義の全体を見なければわからない。

自前のDockerfileがあるなら、同じくTerraformかCloudFormationと一緒に置いておくと、見通しが良いように感じてます。

# ハマりどころ

* VPCで実行さすと10~20秒、余分にかかる。VPCで実行する確かな理由がないなら、VPC指定無しで実行するのが総合的に正しい
* ca-certificateが入ってないDockerイメージを使い、かつ、SSMパラメータストアの暗号化した値を取り出そうとするとハマる。buildspec.ymlで入れようとしても、それより前の段階でSSMパラメータストアから復号化してるため間に合わない。具体的にはalpineでDOWNLOAD_SOURCEという段階で、こんなエラーログが出ます。今回はAmazonLinux2のイメージを利用し回避しました。

```
[Container] 2019/05/06 07:53:44 Phase complete: DOWNLOAD_SOURCE State: FAILED 
[Container] 2019/05/06 07:53:44 Phase context status code: Decrypted Variables Error Message: RequestError: send request failed 
caused by: Post https://ssm.us-east-2.amazonaws.com/: x509: certificate signed by unknown authority 
```