Djangoプロジェクトをデプロイするまでの練習 3回目 WebアプリケーションとCircleCIの準備


ここでやること

ここでは、コンテナで動かすWebアプリケーションを作成し、アプリケーションのCIの設定までをおこなう。CIでは、テスト、Dockerイメージのビルド・イメージをECRへプッシュするところまで実施して準備を確認する。

イメージをビルド
docker build -t djangoproject:ver1 .

8000ポートでコンテナを起動
docker run -itd --name djangoproject --mount type=bind,source="$(pwd)"/code/,target=/code/ djangoproject:ver1

djangoプロジェクトをスタート
docker exec djangoproject djang-admin startproject djangoproject /code/project

helloappアプリをスタート
docker exec djangoproject python3 /code/project/manage.py startapp helloapp /code/project/hello

作業用Docker関係のファイル

この作業用Dockerというのは、主にTerraformによるAWSへの環境構築のために使用する。私の環境がWindowsなのでDockerを使用しているだけなので、必要が無ければあえて作成する必要はないかも。

Dockerfile

From python:3.7
ENV PYTHONUNBUFFERED 1
RUN apt-get update && apt-get install -y \
    vim
RUN mkdir /terraform
WORKDIR /terraform
RUN mkdir .aws
ADD .aws .aws
RUN pip install awscli
RUN wget https://releases.hashicorp.com/terraform/0.12.24/terraform_0.12.24_linux_amd64.zip && \
    unzip ./terraform_0.12.24_linux_amd64.zip -d /usr/local/bin/
RUN cp -r .aws ~

イメージをビルド
docker build -t work:ver1 .

コンテナを起動
docker run -itd --name work --mount type=bind,source="$(pwd)"/terraform/,target=/terraform/ work:ver1

GitHubの準備

githubにリポジトリを2つ作成(djangoproject用とterraform用)

途中、.gitignoreにterraformのプラグインが除外されておらず、100MB以上のファイルとしてはねられてしまいました。下記のコマンドで対応できました。参考にさせていただいた記事のリンクはこちら。
https://www.walbrix.com/jp/blog/2013-10-github-large-files.html

$ git filter-branch --force --index-filter \
  'git rm --cached --ignore-unmatch でかいファイル.tar.gz' \
  --prune-empty --tag-name-filter cat -- --all
$ git commit --amend -CHEAD
$ git push

CircleCIでのCIの設定

CircleCIでは、Djangoプロジェクトのビルドと、ビルドしたDockerイメージをECRにpushするまでを行う。

流れとしては、
1. Djangoプロジェクトの作成→GitHubへpush
2. CircleCIでCI実行→CircleCIからGitHubのプロジェクトがcheckoutされてDockerイメージがbuildされる
3. buildが終わったら、DockerイメージがECRにプッシュされる。
という感じです。

なので、以下の準備をしました。
- terraform用とDjangoプロジェクト用のGitリポジトリを作成
- terraformを使ったECRリポジトリの作成
- CircleCIがECRへDockerイメージをpushするためのIAMユーザー作成
- DjangoプロジェクトのCIのためのCircleCIの設定

ECRリポジトリの作成

ECRリポジトリについては、下記のようなディレクトリ構成で作成しました。

terraform/
|-ecr/
 |-.terraform/
 |-aws_ecr_repository.tf
 |-config.tf

aws_ecr_repository.tf
resource "aws_ecr_repository" "sample-image" {
    name = "sample-image"
}
config.tf
terraform {
    backend "s3" {
      bucket    = "保管するS3バケットの名前"
      key   = "ecr/terraform.tfstate"
      region    = "ap-northeast-1"
    }
}

provider "aws" {
    region = "ap-northeast-1"
}

CircleCI用のIAMユーザーの作成

IAM用のterraformのディレクトリ構成はこんな感じ。

terraform/
|-iam/
 |-.terraform/
 |-aws_iam_policies/
  |-ecr_policy.json
 |-aws_iam_policy.tf
 |-aws_iam_user_policy_attachment.tf
 |-aws_iam_user.tf
 |-config.tf

aws_iam_user.tf
resource "aws_iam_user" "deploy-user" {
    name = "deploy-user"
}
aws_iam_policy.tf
resource "aws_iam_policy" "deploy" {
  name      = "deploy"
  path      = "/"
  description   = "deploy policy"
  policy    = file("aws_iam_policies/ecr_policy.json")
}
aws_iam_user_policy_attachment.tf
resource "aws_iam_user_policy_attachment" "deploy-attach" {
  user      = aws_iam_user.deploy-user.name
  policy_arn    = aws_iam_policy.deploy.arn
}
config.tf
terraform {
  backend "s3" {
    bucket = "保管するS3のバケットの名前"
    key    = "iam/terraform.tfstate"
    region = "ap-northeast-1"
  }
}

provider "aws" {
  region = "ap-northeast-1"
}
ecr_policy.json
{
    "Version": "2012-10-17",
    "Statement": [
      {
        "Effect": "Allow",
    "Action": [
      "ecr:GetAuthorizationToken",
      "ecr:BatchCheckLayerAvailability",
      "ecr:GetDownloadUrlForLayer",
      "ecr:GetRepositoryPolicy",
      "ecr:DescribeRepositories",
      "ecr:ListImages",
      "ecr:BatchGetImage",
      "ecr:InitiateLayerUpload",
      "ecr:UploadLayerPart",
      "ecr:CompleteLayerUpload",
      "ecr:PutImage"
    ],
    "Resource": "*"
      }
    ]    
}

上記のtfファイルをもとに、terraform init、terraform plan、terraform applyを実行することでIAMユーザーを作成した。また、作成したユーザーの認証情報からアクセスキー関係の情報を取得しておく。これは、次のステップのCircleCIのセットアップの際に環境変数で設定しておく。

CircleCIのセットアップ

Djangoプロジェクト用にセットアップ。circleci/config.ymlは下記の通り。

circleci/config.yml
version: 2.1

orbs:
  python: circleci/[email protected]

jobs:
  build-and-test:
    executor: python/default
    steps:
      - checkout
      - python/load-cache
      - python/install-deps
      - python/save-cache
      - run: sudo chmod u+x manage.py
      - run:
          command: ./manage.py test
          name: Test
  build-image:
    docker:
      - image: docker:18.09.0
    steps:
      - checkout
      - setup_remote_docker
      - run:
          name: install aws cli
          command: |
            apk add --no-cache --update py-pip
            pip install awscli
      - run:
          name: login ecr
          command: |
            $(aws ecr get-login --no-include-email --region ap-northeast-1)
      - run:
          name: build image
          command: |
            docker build -t ${ECR_DOMAIN}:$CIRCLE_SHA1 -t ${ECR_DOMAIN}:latest .
      - run:
          name: Push docker image
          command: |
            docker push ${ECR_DOMAIN}:$CIRCLE_SHA1
            docker push ${ECR_DOMAIN}:latest

workflows:
  main:
    jobs:
      - build-and-test
      - build-image:
          requires:
            - build-and-test
          filters:
            branches:
              only: master

また、下記の画像の通り、AWSへのアクセスキー関係の情報を環境変数に追加しておく。

この.circleci/config.ymlを含んだDjangoプロジェクトをGitHubにpushすると、
CircleCIのpipelineがRUN状態になり、ECRへDockerイメージがpushされる。