カスタムイメージを使ってCodebuildでbuildするまで。

マニュアル見ろよ、という話しではあるんだが。
私はなんか突っかかるまでマニュアル読まないタイプの人なので。

# やりたかったこと

- マネージドイメージ内でカスタムイメージをビルド。ビルドしたイメージをECRにPush
- カスタムイメージ内で、golangのpackeageをビルド。ビルドした生成物をS3にアップロード

# カスタムイメージの作成まで

## DOWNLOAD_SOURCEでi/o timeout for primary source
GitHubへのoAuth設定をCodeBuildから行うと、こんなエラーが出る。GitHub側で問題おきていることは考えにくいので、CodeBuild側の問題。

```
CLIENT_ERROR: Get https://github.com/XXXXXXXXXXX/sample.git/info/refs?service=git-upload-pack:
 dial tcp 13.114.40.48:443: i/o timeout for primary source
```

CodeBuildはVPC内に設定してあり、NAT-Gateway経由でGitHubに接続しているのだが、そこが問題だろう。13.114.40.48はGithub.comのIPv4アドレスなので、DNS変換は出来ているが、git cloneで問題が起きているようだ。
### 対応
SecurityGroupの指定に問題あったようで、SGに何らかのインバウンド許可をつけることでNATを通過し通信が届くようになった。
※以下codebuidに付与したSGのインバウントに3389ポートを許可し、ビルドした際のcodebuildのログ
`YAML_FILE_ERROR: YAML file does not exist`
この時点でカスタムイメージを作成するためのbuildspec.yamlはまだ作成してなかったので想定通りなのだが、接続部分で既にエラーが起きるというのは先が思いやられる…。

### 余談
CodeBuildのDOWNLOAD_SOURCEフェーズではログが出ないため、原因を切り分けるのに大変分かりにくい。せめてbashが使えればもっと早く解決できるのに…。

## INSTALLでCOMMAND_EXECUTION_ERROR
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-docker-custom-image.html
を参考にしながら流用しつつそれっぽくyaml作って再実行してみるとまたエラー

```
COMMAND_EXECUTION_ERROR: Error while executing command: timeout 15 sh -c "until docker info; do echo .; sleep 1; done". Reason: exit status 124
```

ログを見るとこんなエラーが出てるなあ。Dockerが動いてない。

```
Server:
ERROR: Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
errors pretty printing info
.
time="2020-06-05T05:53:33.864814126Z" level=info msg="Starting up"
time="2020-06-05T05:53:33.903326219Z" level=warning msg="could not change group /var/run/docker.sock to docker: group docker not found"
time="2020-06-05T05:53:33.903422360Z" level=warning msg="[!] DON'T BIND ON ANY IP ADDRESS WITHOUT setting --tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING [!]"
```

### 対応
「特権付与」が必要。

> このビルドプロジェクトを使用して Docker イメージをビルドするため、[Privileged] を選択します
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-docker.html

よく読めば書いてはあるんだよ。あるんだけど、わかりにくいよねえ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115285/ce9b34e3-578d-6c3a-2732-ede9c2e69fa6.png)

「環境」の箇所から上記をチェック

## PRE_BUILDでCOMMAND_EXECUTION_ERROR
一歩進んで二歩下がる？

```
COMMAND_EXECUTION_ERROR: Error while executing command: $(aws ecr get-login --no-include-email). Reason: exit status 255
```
### 対応

CodeBuildに権限足りてない。
> - デフォルトではECRのポリシーは設定されていない。
> - 自分で設定を追加する必要がある
> - IAMでロールに`AmazonEC2ContainerRegistryPowerUser`(ECRの操作を許可する)ポリシーをアタッチ
https://qiita.com/icck/items/bcf118a38c2a691a837d#2%E3%83%93%E3%83%AB%E3%83%89%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%ABecr%E3%82%92%E6%93%8D%E4%BD%9C%E3%81%99%E3%82%8B%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8B

今度はIAMかよ。

## BUILD/POST BUILDでCOMMAND_EXECUTION_ERROR
ECRにリポジトリも作成し、さてそろそろ完成かな、と思ったらやっぱりエラー。
本当に一歩ずつだな！

```
COMMAND_EXECUTION_ERROR: Error while executing command: docker build -t XXX-build .. Reason: exit status 1
COMMAND_EXECUTION_ERROR: Error while executing command: docker push ${REPOSITORY_URI}:latest. Reason: exit status 1
```
### 対応
buildspec.ymlをどこぞから流用して作ったんだけど、

```buildspec.yml
  build:
    commands:
      - docker build -t XXX-build .
      - docker tag XXX-build:latest ${REPOSITORY_URI}:latest
```
`XXX-build`の部分ってECRのリポジトリの部分と一致していないとダメなのか。そうなのかー。あんまり気づいておらず、適当に修正してしまっていた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/115285/eac5960a-2f88-9627-edfc-6271ea4d48ba.png)
ECRの当該リポジトリのプッシュコマンドをちゃんと見て、それとbuildspec.ymlを合わせないとうまく動作しないね！


## BUILDでCOMMAND_EXECUTION_ERROR - take2
ちょっと進んだけどやっぱりエラー。しかもExitStatusは一緒…。わけわからん。

```
COMMAND_EXECUTION_ERROR: Error while executing command: docker build -t custom-image-build .. Reason: exit status 1
```

### 対応
これ元々こんな感じのシェルをDockerfileから呼び出してRedisをインストールするようにしていたのだけど（これもどこぞから流用した）、`redis-5.0.4-1.el7`このパッケージがもうないんだよね。他にもこういうのに悩まされた。

```install.sh
#!/bin/sh

# 必要なリポジトリの有効化
yum -y install epel-release
yum -y install wget
wget http://rpms.famillecollet.com/enterprise/remi-release-7.rpm
rpm -Uvh remi-release-7.rpm
yum-config-manager --enable epel epel-release remi remi-release

# Redisのインストール
yum -y install --enablerepo=epel,remi redis-5.0.4-1.el7.remi.x86_64
```

CodeBuildにこんなログ出てはいるんだけど、完全に見落としてて、ずっと他の部分を調査してた。

```
Loaded plugins: fastestmirror, ovl
Loading mirror speeds from cached hostfile
 * base: d36uatko69830t.cloudfront.net
 * epel: d2lzkl7pfhq30w.cloudfront.net
 * extras: d36uatko69830t.cloudfront.net
 * remi: ftp.riken.jp
 * remi-safe: ftp.riken.jp
 * updates: d36uatko69830t.cloudfront.net
No package redis-5.0.4-1.el7.remi.x86_64 available.
```

**No package**とか**Not Found**とかを見逃さないこと。ErrorとかWarning表記になってないので、ちゃんとログを見ないとわからない。

### 余談
CodebuildのExit status一覧どこにあるのよマジで。ちらっと探しただけだと見つからないし。

# カスタムイメージを用いたビルドまで。

## pull access denied 
buildspec.ymlを適当にかいてCodebuildで動作させてみると、PROVISIONINGでエラーが出る。

```
BUILD_CONTAINER_UNABLE_TO_PULL_IMAGE: Unable to pull customer's container image. 
CannotPullContainerError: Error response from daemon: pull access denied for 
XXXXXXXXXXXXX.dkr.ecr.ap-northeast-1.amazonaws.com/custom-docker-image, repository does not exist or may require 'docker login'
```
docker login？特権権限がないから？、と思って再度やってみてもやっぱりエラーが出る。そこまで安易じゃなかった。

### 対応
 > [アクション] で、プル専用アクションとして [ecr:GetDownloadUrlForLayer]、[ecr:BatchGetImage]、および [ecr:BatchCheckLayerAvailability] を選択します。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/sample-ecr.html

ECRにCodebuildからpullできる権限付与が必要なのである。CodeBuildからECRにアクセスする権限も必要だし、なんか色々めんどいな。

## PRE_BUILDでCOMMAND_EXECUTION_ERROR

まあ上記を修正しても当然のようにエラー。

```
COMMAND_EXECUTION_ERROR: Error while executing command: make setup. Reason: exit status 127
```

ログは…(流石にログの見方も段々慣れてきた。)

```
[Container] 2020/06/09 02:43:58 Running command make setup
/codebuild/output/tmp/script.sh: line 4: make: command not found
```

はあ。make入ってないですか。そうですか。そうですね。インストールしなきゃね。

### 対応

カスタムイメージ側に以下の対処。BuildしてECRに再PUSH

```
yum -y install gcc
yum -y install make
```

## PRE_BUILDでCOMMAND_EXECUTION_ERROR - take2

またかよ！Status変わってるし。

```
COMMAND_EXECUTION_ERROR: Error while executing command: make setup. Reason: exit status 2
```

ログを見ると

```
golint -set_exit_status ./...
make: golint: Command not found
make: *** [lint] Error 127
```

あーもう。PATHが通ってない。
というわけでhttps://github.com/golang/lint を参考にどこにインストールされているか調べてみると、

```
go list -f {{.Target}} golang.org/x/lint/golint
/codebuild/output/src813842990/bin/golint
```

ここである。$GOPATHが`/codebuild/output/src813842990`を指していたので、その下にPATHを通せばよいことが判明。
### 対応
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/build-spec-ref.html
じゃあ上記を参考にbuildspec.ymlのenvシーケンスに書けばいいのね？と思ったら、envシーケンス内はリテラルになるため変数展開できないことが判明。
マニュアルに書いとけよ。読んでも分かんないよ。

つまり、

```buildspec.yml
env:
  variables:
    PATH: ${GOPATH}/bin:${PATH}
```

は駄目で、↓のようにする必要がある。

```buildspec.yml
  pre_build:
    commands:
      - export PATH=${GOPATH}/bin:${PATH} && echo ${PATH}
```

これで完了！やったね！
ってS3アップロードまで出来てない気がする。もう疲れた。

# 結論
FAQに入れとけよと思うよマジで。
https://docs.aws.amazon.com/ja_jp/codebuild/latest/userguide/troubleshooting.html