ブランチごとにブランチ名をサブドメインとしたgoogle認証付きドメイン環境へデプロイ


目指すところ

ブランチを作成するとgoogle認証付きドメインが自動 or 手動でデプロイされる

feature-1111 ブランチが作成される
ビルドが走って、feature-1111 コンテナが起動する
feature-1111.hoge.example.com ドメインにアクセスすると google 認証でログインして確認できる
追加の要件で開発環境用の設定が3種類あり指定できるようにする (この条件があるのでbotでの手動手順で)

前提条件

  • *.hoge.example.com ドメインの特定albへの関連付け
  • *.hoge.example.com を受け付けるalbでのcognito設定
  • docker入りEC2インスタンスひとつ
  • 環境 aaa, bbb, ccc のどれかを指定してデプロイできるようにする

Dockerの環境について

docker入りEC2インスタンスには同一ネットワークを使い回せるようしたproxyコンテナ準備しておく
ブランチ名.hoge.example.com を ブランチ名コンテナにわたす設定を入れる

version: '3'

services:
  nginx:
    container_name: "proxy-branches"
    hostname: "proxy-branches"
    image: nginx:1.17.4
    environment:
      - TZ=Asia/Tokyo
    ports:
      - 80:80
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - branch_local

networks:
  branch_local:
    external: true
  • default.conf
server {

    listen   80;
    server_name _;

    resolver 127.0.0.11;
    real_ip_header X-Forwarded-For;
    set_real_ip_from 0.0.0.0/0;
    real_ip_recursive on;

    client_max_body_size 20M;

    location / {
        set $container $host;
        if ($host ~ ^(.*)\.hoge\.example\.com) { set $container $1; }
        proxy_pass http://$container;

        proxy_set_header Host      $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
}

ブランチごとに作成するコンテナのテンプレート例

version: '3'

services:
  nginx:
    container_name: "{{DIRENV}}"
    hostname: "{{DIRENV}}"
    image: nginx:1.17.4
    environment:
      - TZ=Asia/Tokyo
    volumes:
      - /var/www/{{DIRENV}}:/var/www/html
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d/default.conf:/etc/nginx/conf.d/default.conf
    networks:
      - branch_local
  php-fpm:
    container_name: "{{DIRENV}}-front-php"
    hostname: "{{DIRENV}}-front-php-fpm"
    image: my-front-php-fpm:latest
    volumes:
      - /var/www/{{DIRENV}}:/var/www/html
    networks:
      - branch_local

networks:
  branch_local:
    external: true

環境設定ファイル群は扱いやすい場所においておく

Buildspec参照

cognitoを更新するシェルスクリプト例

ALBでgoogle認証をcognitoで使う方法は以下を参照
https://qiita.com/netebakari/items/bdd6930bec1acc4d647f

環境変数でブランチ名を受け取って、該当ドメインをalbに設定したcognitoユーザプールに追加するシェルスクリプト
(code buildに含める)
CognitoPoolId,CognitoClientId はAWSコンソールを参照

AddURL="https://${BRANCH}.hoge.example.com/oauth2/idpresponse"
AWS_REGION=ap-northeast-1
ExistingCallbackUrls=$(aws cognito-idp describe-user-pool-client --user-pool-id $CognitoPoolId --client-id $CognitoClientId --region $AWS_REGION | jq -r '.UserPoolClient.CallbackURLs|join(" ")' | sed 's/,/ /g')
NewCallbackUrls="$ExistingCallbackUrls $AddURL"
aws cognito-idp update-user-pool-client --user-pool-id $CognitoPoolId --client-id $CognitoClientId --callback-urls $NewCallbackUrls  --supported-identity-providers "Google" --allowed-o-auth-flows "code" --allowed-o-auth-scopes "email" "openid" "profile" --allowed-o-auth-flows-user-pool-client --region $AWS_REGION
echo $SourceVariables

aws cognito-idp がオプションなしupdateを実行すると、すでに入っている設定を維持しないことに注意
オプションを付けないと設定がなくなる.

Buildspec例(code build)

環境変数にDIRENV(ブランチ名->ディレクトリ名)、DEVENV(環境名aaa|bbb|ccc)、DOMAIN(ブランチ名->小文字化したドメイン名)
CognitoPoolId、CognitoClientId を入れる

version: 0.2

phases:
  install:
    runtime-versions:
      nodejs: 10
  build:
    commands:
       - curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.0/install.sh | bash ; export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")" ; [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" ;nvm install v11.11.1
       - npm ci
       - npm start
       - rm bin/build.sh
       - sed -i -e "s/755/777/g" appspec.yml
       - sed -i -e "s@/var/www/html/hoge@/var/www/${DOMAIN}/hoge@g" appspec.yml
       - echo '#!/usr/bin/env bash' > bin/build.sh
       - echo "docker run --rm -v /var/www/${DOMAIN}/hoge:/var/www/html/hoge front-php-fpm-infra sh -c 'cd /var/www/html/hoge && /usr/local/bin/composer install'" >> bin/build.sh 
       - echo "cp -pR /home/hoge/template /home/hoge/dev-branches/${DOMAIN} &&  cd /home/hoge/dev-branches/${DOMAIN} && sed -e \"s/{{DIRENV}}/${DOMAIN}/\" docker-compose.template > docker-compose.yml && sed -i -e \"s/{{DIRENV}}/${DOMAIN}/\" nginx/conf.d/default.conf && docker-compose up -d" >> bin/build.sh
       - echo "docker exec ${DOMAIN}-front-php sh -c 'cd /var/www/html/hoge && php init --env=Development --overwrite=All && chmod -R 777 /var/www/html/hoge'" >> bin/build.sh 
       - echo "cp ../../template/${DEVENV}/hoge/common/config/* /var/www/${DOMAIN}/hoge/common/config/" >> bin/build.sh
       - echo "cp ../../template/${DEVENV}/hoge/frontend/config/* /var/www/${DOMAIN}/hoge/frontend/config/" >> bin/build.sh
       - AddURL="https://${DOMAIN}.hoge.example.com/oauth2/idpresponse"
       - AWS_REGION=ap-northeast-1
       - ExistingCallbackUrls=$(aws cognito-idp describe-user-pool-client --user-pool-id $CognitoPoolId --client-id $CognitoClientId --region $AWS_REGION | jq -r '.UserPoolClient.CallbackURLs|join(" ")' | sed 's/,/ /g')
       - NewCallbackUrls="$ExistingCallbackUrls $AddURL"
       - aws cognito-idp update-user-pool-client --user-pool-id $CognitoPoolId --client-id $CognitoClientId --callback-urls $NewCallbackUrls  --supported-identity-providers "Google" --allowed-o-auth-flows "code" --allowed-o-auth-scopes "email" "openid" "profile" --allowed-o-auth-flows-user-pool-client --region $AWS_REGION
       - echo $SourceVariables

artifacts:
  files:
    - '**/*'

CodePipeline設定jsonの作成

code-pipelineでSource GitHub, Build AWS CodeBuild, Deploy AWS CodeDeploy でとりあえずmasterがデプロイできる環境を設定する

下準備として作成したcode-pipeline設定を読み取ってmetadataを消す
(code-pipelineの設定をjsonで書いてもいいけど大変なのでAWSコンソールで作って読み込んでファイルに出す)

aws codepipeline get-pipeline --name MyFirstPipeline > branches-deploy.json

ブランチが作られるごとに毎回環境が必要ではなかったので
botでのブランチ環境作成としているが
ブランチ毎、プルリク毎の場合はgithubのフック等で叩く
cognitoにドメインを登録するときに大文字が入ると動作しないのでドメインのみ小文字化する

python-slackbot 例

# -*- coding: utf-8 -*
from slackbot.bot import respond_to
from slackbot.bot import listen_to
from slackbot.bot import default_reply

import subprocess
import re

@listen_to('deploy-branch\s*(.*)\s(.*)')
def mention_func1(message, arg1, arg2):
    AWS_COMMAND = "/usr/local/bin/aws"
    BRANCH = arg1
    DEVENV = arg2
    DOMAIN = str.lower(arg1)

    if DEVENV in ["aaa", "bbb", "ccc"]:
        subprocess.run('sed -e "s/{{BRANCH}}/' + BRANCH + '/g" branches-deploy-template.json > branches-deploy.json', shell = True, stdout = subprocess.PIPE, cwd = '/slackbot/plugins')
        subprocess.run('sed -i -e "s/{{DEVENV}}/' + DEVENV + '/g" branches-deploy.json', shell = True, stdout = subprocess.PIPE, cwd = '/slackbot/plugins')
        subprocess.run('sed -i -e "s/{{DOMAIN}}/' + DOMAIN + '/g" branches-deploy.json', shell = True, stdout = subprocess.PIPE, cwd = '/slackbot/plugins')
        subprocess.run('aws codepipeline update-pipeline --cli-input-json file://branches-deploy.json', shell = True, stdout = subprocess.PIPE, cwd = '/slackbot/plugins')
        subprocess.run('aws deploy create-deployment-group --application-name ec2docker-branch --ec2-tag-filters Key=Name,Type=KEY_AND_VALUE,Value=instance-name --deployment-group-name ec2docker-branch-group-' + DOMAIN + ' --service-role-arn arn:aws:iam::XXXXXXXXXX:role/dev-deploy-role', shell = True, stdout = subprocess.PIPE, cwd = '/slackbot/plugins')
        subprocess.run('aws codepipeline start-pipeline-execution --name  branches-deploy --region ap-northeast-1', shell = True, stdout = subprocess.PIPE, cwd = '/slackbot/plugins')
        message.send("https://" + DOMAIN + ".hoge.example.com/ を作成するデプロイを開始しました")
    else:
        message.send("第2引数の環境の指定はaaa,bbb,cccだけです")

使い方

botのいるところで

deploy-branch feature-1111 aaa

説明用