【Django x AWS】Codebuildの環境変数を使ってビルドを行う方法

# 概要
AWS Codebuildで設定できる環境変数を使って、ビルド時にDjangoアプリ側へ変数を渡してあげる方法を紹介します。

# 前提
まず、Codebuild関連では環境変数の設定方法が２つあります。

①AWS CodePipelineのBuildステージでの環境変数
```
CodePipeline　-> 編集する -> ステージを編集する ->  鉛筆マーク -> 環境変数
```

②AWS CodeBuildプロジェクトでの環境変数

```
CodeBuild　-> ビルドプロジェクト -> ビルドの詳細 -> 「環境」編集 -> 追加設定 -> 環境変数の追加
```

今回は①の方で設定しています。
※上記設定手順について、以前書いた詳細な記事はこちら：
　[【AWS】CodePipelineからCodeBuildの環境変数を設定した場合の確認方法](https://qiita.com/Ryo-0131/items/d9cf7004567952e69cb9)

AWS CodepipelineのBuildステージの環境変数は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/ea69035b-bf41-3e1f-746c-0614b8ecf691.png)

ちなみにここは入れすぎると以下のようにエラーになります。
環境変数の合計文字数は、1つのアクションに対して1,000文字がMAXみたいです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/4736a10a-84bd-123c-f3cc-936b3acf16a4.png)

```
[Member must have length less than or equal to 1000, Member must have length greater than or equal to 1]
```

②の「Codebuildプロジェクトレベルでの環境変数」でも設定できます。場所は上述の通りですが、画面では以下の箇所になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710373/6cf1690e-97b1-7535-ce96-a4649c5f0465.png)



ビルドプロジェクトレベルで共通するものはそちらにする、とかでも良いかもしれませんが、あまりあちこちに環境変数があるのは後で面倒になりそうだなとも思いました。もし溢れてしまうほどある場合は、AWSのパラメータストアなど他のリソースを使って統一するのもありと思います。

## ディレクトリ構成と`buildspec.yaml`

構成は以下の通り(一部を抜粋)。

```
├── mysite
│   ├── manage.py
│   ├── sample(appディレクトリ)
│   └── mysite(projectディレクトリ)
│  　　　　　　　　　　├── settings.py
│   　　　　　　　　└── constant.py
├──　buildspec.yml
└──　constant.py
```

`mysite/mysite/constant.py`の中身はローカル環境で動くようにするため、以下のようにしています（ローカル用の`docker-compose.yaml`などでマウントしてあげればOK）。AWS環境にデプロイする際はこのファイルが上書きされる形になります。

```constant.py
HOST = 'mysql'
PORT = 3306
USER = 'root'
PASSWORD = 'root'
DATABASE = 'sample_db'
```

つまり、以下の順序ですね。
①`buildspec.yaml`によってCodeBuildの環境変数が`./constant.py`に書き込まれる
②その`constant.py`が`mysite/mysite/`下にコピーされる
③`mysite/mysite/constant.py`のDB変数などに、Codebuildで設定されていた値が入る
(後述：④`settings.py`などで`from mysite.constant import DATABASE...`とすればDB情報がインポートできる)

```buildspec.yaml
version: 0.2
phases:
  pre_build:
    commands:
      - echo DB_HOST=$DB_HOST >> constant.py
      - echo DB_PORT=3306 >> constant.py
      - echo DB_USER=$DB_USER >> constant.py
      - echo DB_PASSWORD=$DB_PASSWORD >> constant.py
      - echo DB_NAME=$DB_NAME >> constant.py
      - cp constant.py mysite/mysite/
      - echo Logging in to Amazon ECR...
      - aws --version
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo Build started on `date`
      - echo Logging in to Docker Hub...
      - docker --version
      - docker login -u $USER_DOCKER -p $DOCKER_KEY
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:latest .
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker images...
      - docker push $REPOSITORY_URI:latest
      - echo Writing image definitions file...
      - printf '{"name:"mysite-web","imageURI":"%s"}' $REPOSITORY_URI:latest > imageDetail.json
      - cat imageDetail.json
artifacts:
  files:
      - imageDetail.json
  discard-paths: yes
```

## どんな使い方があるのか

では、これらの変数は例えばどんな使い方があるのか？
まず、Djangoが接続するデータベース情報を`settings.py`で設定することができます。

以下のようにすれば、AWS環境にのみ重要な情報を保管しておける上、アプリケーションは`mysite`ディレクトリにある`constant.py`の情報を引っ張ってくることでDBに接続できます。

```settings.py
from mysite.constant import DATABASE, USER, PASSWORD, HOST

## 略

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': DATABASE,
        'USER': USER,
        'PASSWORD': PASSWORD,
        'HOST': HOST,
        'PORT': '3306',
        'OPTIONS': {
            'init_command':
                'SET character_set_connection=utf8,collation_connection=utf8_unicode_ci'
        }
    },
}
```

もしくは、以下のように`pymysql`を使って接続することもできます。

```py
import constant
import pymysql

def connect_db():
    """Return connection.

    Returns:
        connection (Connection class): Connection to the mysql
        TypeError: Error when string is not Decimal
    """
    return pymysql.connect(host=constant.DB_HOST,
                           user=constant.DB_USER,
                           passwd=constant.DB_PASSWORD,
                           db=constant.DB_NAME,
                           port=constant.DB_PORT,
                           charset='utf8mb4')
```