AWS CodePipeline(CodeBuild)からRDSにアクセスする

# はじめに

AWSのCodePipelineから利用するCodeBuildにて、検証および本番環境のスキーマを取得、そのスキーマを元にテストを実行したかったため、RDSに接続する必要がありました。そのため、VPCに配置する必要があり、さらに`mysqldump`コマンド等を使用する必要もあったため、AWS標準で用意されているイメージではなく、カスタムイメージ作成し、利用することにしました。

その際に諸々設定した際の記事となります。

# 全体構成
配置するSubnetはPrivateなため、Proxyを経由してインターネットに接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457889/f8855c87-1fc0-144f-d4b8-5647b2ce1284.png)

大まかな流れは以下

1. CodePipelineを起動
1. CodeCommitからソースダウンロード
1. CodeBuild実行
   1. VPC（PrivateSubnet）に配置
   1. 事前に用意したカスタムイメージ上でBuildSpecを実行（Docker on Docker）
   1. RDS接続・テスト実行など
   1. ECRへPUSH
1. CodeDeployでECSへデプロイ


今回は利用するカスタムイメージの作成や、buildspec/Dockerfileなど各ファイルの設定について記載します。(3-1~3)
※CodePipeline全体的な設定・ECSへのデプロイについては割愛します。

# カスタムイメージの作成
最終的にアプリケーション用イメージが使用するものは以下のため、カスタムイメージではそれをインストールしておきます。
使用するベースイメージは`Ubuntu 20.04`としました。

- mariadb-client(`mysqldump`で必要)
- Docker
- docker-compose
- AWSCLI(v1)

### Dockerfile

```Dockerfile
FROM ubuntu:20.04

RUN apt-get update 
RUN apt-get install software-properties-common -y
RUN add-apt-repository universe

# mysqldumpコマンド実行するためのコマンドインストール
RUN apt-get install -y mariadb-client

# Docker及びdocker-composeを実行するためにインストール
RUN apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common -y
RUN curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
RUN apt-key fingerprint 0EBFCD88
RUN add-apt-repository \
    "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
    $(lsb_release -cs) \
    stable"
RUN apt-get update 
RUN apt-get install -y docker-ce
RUN curl -L "https://github.com/docker/compose/releases/download/1.25.4/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
RUN chmod +x /usr/local/bin/docker-compose

# ECRにPUSHする必要があるためAWSCLIインストール
RUN apt-get install \ 
    unzip \
    python3-venv -y
RUN curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
RUN unzip awscli-bundle.zip
RUN /usr/bin/python3.8 ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
RUN aws --version
```
 
1. `mysqldump`を実行するために`mariadb-client`をインストール
1. Docker及びdocker-composeを起動・実行するためにインストール
    1. [Dockerインストール](https://docs.docker.com/engine/install/ubuntu/#installation-methods)
    1. [docker-composeインストール](https://docs.docker.com/compose/install/#install-compose-on-linux-systems)
    1.  インストールするだけだとDockerデーモンが立ち上がらないので、後述のdocker-composeに`cap_add`で`capability`を追加する
1. AWSCLIを実行するために、[インストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-linux.html)
    1. 既存のbuildspecに記述されてるコマンドでは`v2`だと動かないので、`v1`を明示的に指定

### docker-compose
```docker-compose.yml
version: '3'
services:
  custom_image:
    cap_add:
      - NET_ADMIN
    tty: true
    image: ${IMAGE_URI}
    build:
      context: .
      dockerfile: Dockerfile
```

1. `cap_add`で`NET_ADMIN`を許可しておく（Dockerデーモンを立ち上げるため）

### buildspec
上記の`Dockerfile`と`docker-compose.yml`を利用してbuildspecからECRにPUSHする
※ECRにプッシュするためのbuildspecファイルの設定や、CodePipeline設定についてはここでは割愛します。


# アプリケーション用ビルドプロジェクト設定
## カスタムイメージ指定
該当のビルドプロジェクトから`環境`を編集する
ECRレポジトリから上記で作成した該当イメージを選択。
認証情報プルイメージはプロジェクトサービスロール。
またDockerを操作するので特権を付与する。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457889/e39dd222-2df5-d1b1-b3af-f790f4e15577.png)


## VPC配置
VPC及び、サブネットに配置する（Private配置）
RDSにアクセスする必要があるため、RDS接続用のセキュリティグループを設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/457889/3110e111-8a03-1ee0-a2bb-cdce7c99d482.png)

**環境変数は状況に応じて各種設定を行う**

# アプリケーション設定
上記で設定したビルドプロジェクトで動かすアプリケーション側の各ファイル（buildspec・Dockerfile・docker-compose）設定

## buildspec

```buildspec.yml
version: 0.2
proxy:
  upload-artifacts: yes
  logs: yes
phases:
  install:
    runtime-versions:
      docker: 18
  pre_build:
    commands:
      # Dockerデーモン起動+PROXY設定
      - export HTTP_PROXY=${HTTP_PROXY}
      - grep "#export http_proxy" /etc/default/docker
      - sed -i -e "/^#export http_proxy/c\export http_proxy=\"${HTTP_PROXY}\"" /etc/default/docker
      - grep "export http_proxy" /etc/default/docker
      - service docker start
      - service docker status
      # DBスキーマを取得
      - mysqldump --user=${DB_USER_NAME} --password=${DB_PASS} --quick --single-transaction -h readreplica-xxx.com targetdb -d > db/schema.sql
  build:
    commands:
      # テスト実行
      - docker-compose -f docker-compose.testing.yml build
      - docker-compose -f docker-compose.testing.yml run tester
      - $(aws ecr get-login --region ${AWS_DEFAULT_REGION} --no-include-email)
      省略…
  post_build:
    commands:
      省略…
artifacts:
  files: imagedefinitions.json
```

ポイントは以下
DockerfileでのイメージダウンロードなどProxyを経由するため`/etc/default/docker`の`http_proxy`を設定
(コメントアウトを外して、HTTP_PROXYを設定しなおす)

```
- export HTTP_PROXY=${HTTP_PROXY}
- grep "#export http_proxy" /etc/default/docker
- sed -i -e "/^#export http_proxy/c\export http_proxy=\"${HTTP_PROXY}\"" /etc/default/docker
- grep "export http_proxy" /etc/default/docker
```

Dockerのデーモンを起動(前述のNET_ADMINを設定していないと、ここでデーモンが起動しない)

```
- service docker start
```

リードレプリカに接続して、schemaを取得する

```
- mysqldump --user=${DB_USER_NAME} --password=${DB_PASS} --quick --single-transaction -h readreplica-xxx.com targetdb -d > db/schema.sql
```

テスト実行

```
- docker-compose -f docker-compose.testing.yml build
- docker-compose -f docker-compose.testing.yml run tester
```

## docker-compose

同様にProxyを経由するように各種を変更
ここでは、buildspecから渡ってくる`HTTP_PROXY`を`args`でDockerfileに渡す

```docker-compose.testing.yml
version: '3'
services:
  tester:
    build:
      context: .
      dockerfile: Dockerfile.testing
      args:
        - HTTP_PROXY=${HTTP_PROXY}
    links:
      - db_test
    environment:
      - UNIT_TEST_MYSQL_CONNECTION_STRING=root:password@tcp(db_test:3307)/db_test?parseTime=true&loc=Local&readTimeout=30s&writeTimeout=30s&timeout=30s
    command: go test ./... -v

  db_test:
    image: mysql:5.7.12
    environment:
      MYSQL_ROOT_PASSWORD: 'password'
      MYSQL_DATABASE: 'db_test'
    ports:
      - "3307:3307"
    command: mysqld --port 3307
```

## Dockerfile

`ARG`で`HTTP_PROXY`を設定する
今回はGoを用いているため、`go mod download`する際にProxyを経由するように設定

```Dockerfile.testing
FROM golang:1.14 as builder

ARG HTTP_PROXY
省略…
RUN http_proxy=$HTTP_PROXY go mod download

省略…
```

# まとめ
RDSに接続するために、PrivateSubnetへの配置を前提としている構成になっているため、Dockerや各言語が使用するコマンドを動かすために各所Proxyを設定していく必要があり、その辺が多少手間だと感じました。
とはいえ、動的にSchemaを取得し、テストの実行ができるようになったので、今後メンテナンスも含めて多少楽になるかと思っています。