RareJob Tech Blog

レアジョブのエンジニア・デザイナーによる技術ブログです

AWS CodePipelineを用いたデプロイ

DevOps チームの うすい と申します。よろしくお願いします。 昨年11月にハーフマラソンに出てから膝に矢を受けてしまった状態が続いています。困っています。

今回は最近作ったシステムにおけるデプロイについてお話したいと思います。

前提

弊社の主だった環境は AWS 上にあります。また、ソースコード管理には GitLab を使用していて、GitLab CI も元気に稼働しています。 本システムは小規模で、リリース後のデプロイは多くないことが想定されています。

デプロイフロー

フローとしては下記のようになっています。

  1. master マージ
  2. GitLab CI で成果物を S3 にアップロード
  3. S3 にファイルが作成されたことをトリガーとして CodePipeline がスタート
  4. ステージング環境にて CodeDeploy でデプロイ
  5. 本番環境デプロイ承認のためのメール送信と Slack 通知
  6. 承認権限を持った人が CodePipeline の中で承認
  7. 本番環境にデプロイ

少し細かく見ていきましょう。

GitLab CI

GitLab CI の中でcomposer installnpm installをしています。各ツールのバージョンを簡単に固定したかったため、今回は PHP 用のイメージの中で npm をインストールしたりするのではなく、バージョンを指定したイメージを使用し作成した成果物を次のジョブに引き継ぐ方式をとりました。 .gitlab-ci.yml の概要です。

stages:
  - npm
  - composer
  - upload

npm:
  stage: npm
  image: node:12.14.1-alpine3.11
  script:
    - npm install
    - npm audit fix
    - npm run production
    - tar czf node_modules.tar.gz node_modules
  artifacts:
    paths:
      - node_modules.tar.gz

composer:
  stage: composer
  image: composer:1.9
  script:
    - composer install
    - zip -r ./${CI_PIPELINE_ID}.zip .
  artifacts:
    paths:
      - ./${CI_PIPELINE_ID}.zip

s3upload:
  stage: upload
  image: alpine:latest
  before_script:
    - apk add --no-cache python3
    - pip3 install awscli
  script:
    - aws s3 cp ./${CI_PIPELINE_ID}.zip s3://${S3BUCKET}/${APP}.zip

実際にはbefore_scriptと呼ばれる文字通りscriptの前に実行されるセクションで環境変数ファイルに秘匿情報を埋め込んだりもしています。それらの秘匿情報は GitLab のSecretに保存しています。 このようにして作成された成果物を最終的に S3 にアップロードしています。

また、これまでは nginx や php-fpm の設定ファイルは DevOps チームの Ansible のリポジトリで管理されていることが多く、開発チームで変更したい場合には

依頼&マージリクエスト作成(開発チーム) → マージ&反映(DevOps チーム) → 確認(開発チーム)

となっていましたが、開発チームで使用しているリポジトリに必要なファイルをすべて含め、CodeDeploy の中で反映を行うようにしました(後述)。

CodePipeline

CodePipeline は S3 にファイルが置かれたことをトリガーとしてスタートします。特に込み入ったことはしておらず、シンプルなパイプラインとなっています。

イメージ図

f:id:goode:20200210174116p:plain
CodePipeline

SNS との連携も上記画像の真ん中の上にあるNotifyから簡単に作成することができて、今回はパイプラインの成功スタート失敗、そして承認時に SNS に通知を送って Lambda を起動し Slack の Incoming Webhooks で通知、といったことをしていますが、AWS Chatbot でも検証中です(下記図)。Lambda 内でメッセージをパースする必要もないので、こだわりがなければ AWS Chatbot で良いかと思います。 承認部分に関してはメールでの通知も行っています。

Chatbot

CodeDeploy

今回は AutoScaling と EC2 を組み合わせたシステム構成になっているので、コンピューティングプラットフォームEC2/オンプレミス、またデプロイ設定はリリース後の修正が多くないこと、また利用される時間があらかじめ把握することができることから、時間の節約のためデプロイタイプインプレースにしました。

CodeDeployはイベントという形で順々に処理を実行していきます。

イベント

こちらのAfterInstallの中で設定ファイルのアップデートや nginx の再起動などを行っています。

また、余談ですがインスタンス起動時のユーザーデータに登録したスクリプトで CloudWatch Alarm の作成を行い、ステージング環境など夜間休日にインスタンスを停止する環境において残り続けてしまう CloudWatch Alarm は AWS Batch で削除しています。 AWS Batch のコンテナイメージはこんな感じで作って ECR に push しています。

delete-insufficient-cloudwatch-alarm.sh

#!/usr/bin/env bash

aws cloudwatch describe-alarms --state-value INSUFFICIENT_DATA --region ap-northeast-1 | grep AlarmName | awk '{print $2}' > ./result.txt

sed -i -e 's/"//g' ./result.txt
sed -i -e 's/,$//g' ./result.txt

for name in $(cat ./result.txt)
do
  aws cloudwatch delete-alarms --alarm-names ${name} --region ap-northeast-1
done

Dockerfile

FROM amazonlinux:latest
 
ENV LANG C.UTF-8
RUN yum -y install unzip aws-cli
ADD delete-insufficient-cloudwatch-alarm.sh /usr/local/bin/delete-insufficient-cloudwatch-alarm.sh
 
ENTRYPOINT ["/usr/local/bin/delete-insufficient-cloudwatch-alarm.sh"]

まとめ

今回は EC2 を選択しましたが、別のプロジェクトでは ECS(Fargate)を使用していたり他にも IaC や CI / CD の強化といったこともしています。DevOps チームのメンバーは現在絶賛募集中ですので、興味を持たれた方はご連絡いただければと思います。また、もっといいやり方や改善点を指摘してくれる方もカジュアル面談でお越しいただければと思います。