Header image

クラッソーネの開発者がエンジニアリングに関することもそうでないことも綴っています!

インフラ改善しました!第一弾!

インフラ改善しました!第一弾!

こんにちは、クラッソーネでプロジェクトマネージャーをしている田原(@shogocat)です。
オリンピック卓球混合ダブルスの金メダル獲得に大興奮していました。決勝もさることながら、準々決勝の大逆転も感動的でした。

クラッソーネでは、安定したサービス運用を目指してインフラ改善に注力しています。
今回は、今年のはじめに行ったインフラ移行&デプロイ改善についてお話しします。

インフラ移行前の問題

インフラ移行前のクラッソーネでは以下のような問題を抱えていました。

  • ローカル開発環境、ステージング環境、本番環境が大きく異なる
    • コンテナ化されている環境、されていない環境
    • ミドルウェアのバージョンや管理方法がバラバラ
  • デプロイが属人的で、1人のメンバーしかデプロイが行えない
  • デプロイ時に、サーバーメンテナンス期間を設ける必要がある
  • サーバが冗長化されていない

さらっと書いていますが、本当に本当に大きな問題でした。

インフラ移行後

今回のインフラ移行、デプロイ改善の施策により、現状では以下のようになりました!

移行前 移行後
ローカル開発環境、ステージング環境、本番環境が異なっている 構成含め、ほぼ同一に!
デプロイが属人的で、1人のメンバーしかデプロイが行えない 開発者は全員デプロイができるように!
デプロイ時に、サーバーメンテナンス期間を設ける必要がある ダウンタイムゼロに!
サーバが冗長化されていない 冗長構成に!
デプロイは週2度程度 日に3〜5度行うようになった!

やったこと

  • EC2 → ECS(on EC2)への移行
  • CapistranoからCodePipelineへの移行
  • CronからSheduled Tasksへの移行

EC2 → ECS(on EC2)への移行

検討事項

fargate? on EC2? どっちにするの?
→現状ではまだ不具合調査やスクリプト実行でインスタンスにSSHする必要があることがあり、on EC2を選択いたしました。

ローリングアップデート?ブルーグリーンデプロイ?
→開発環境とSTG環境はローリングアップデートにし、本番をブルーグリーンデプロイとしました。

事前準備

すべての環境をDocker化しました。

実はローカルの開発環境はすでにDockerを利用していたため、STGとPRDでも使えるように変更を行いました。
ビルドしたイメージの時点ではローカル環境、STG環境、PRD環境で差異はなく、起動時の環境変数のみで切り替える形としました。

作業工程

  • ALBの作成
  • イメージのビルド
  • イメージのPush
  • タスク定義の作成
  • クラスタの作成
  • サービスの作成

ハマりどころ、工夫したところ

awsvpcを利用する際に、単一のインスタンスに付与できるIPの数に制限があり複数のコンテナを起動することができませんでした。
これはVPC Trunkingをオンにすることで、制限を緩和することができるようになります。

CapistranoからCodePipelineへの移行

Codepipeline(Codebuild+CodeDeploy)を利用することで、比較的容易にブルーグリーンデプロイが実現できました。
仕組みとしては、ECSデプロイ後に、CodeDeployによってALBのターゲットグループのルールが自動的に書き換えられます。

ハマりどころ、工夫したところ

  • ビルド時の利用するDockerのイメージのアーキテクチャとEC2インスタンスのCPUアーキテクチャが異なっている場合、コンテナが正常に起動しないところがありました。これはインスタンス選択時または、ビルド環境選択時にきちんと同じものを選ぶようにすれば問題ありません。
  • アセットをCloudFrontから配信するように変更したのですが、ビルドするイメージに埋め込みたくなかったため、ビルドしたイメージを利用してコンパイルを行い、manifestのみ起動時にS3から取得しています。HASH値を付与しておくことにより、ロールバック等を行った際に、どのコンテナがどのマニュフェストを取りに行くべきかを間違えないようにしています。

COMMIT_HASHを埋め込み

FROM ruby:2.7.3-alpine3.12

ARG COMMIT_HASH
ENV COMMIT_HASH=${COMMIT_HASH}

ビルドしたイメージを利用して、コンパイル

env:
  variables:
    RAILS_ENV: "production"
phases:
  pre_build:
    commands:
      - docker pull ${REPOSITORY_WEB_URI}:latest || true
      - IMAGE_TAG=$CODEBUILD_RESOLVED_SOURCE_VERSION
  build:
    commands:
      - COMMIT_HASH=`echo ${IMAGE_TAG} | cut -c -32`
      - |
        docker build --cache-from ${REPOSITORY_WEB_URI}:latest \
        -t web \
        -f docker/rails/Dockerfile \
        --build-arg COMMIT_HASH=${COMMIT_HASH} \
        .
      - |
        docker run -t --rm \
        -e RAILS_ENV=${RAILS_ENV} \
        web compile.sh

コンパイル後にmanifestにCOMMIT_HASHを付与してアップロード

compile.sh
#!/bin/bash
set -e

bundle exec rails assets:precompile --trace

find ./public/assets/ -type f -name ".sprockets-manifest-*" | head -1 | xargs -i cp {} ./public/assets/.sprockets-manifest-${COMMIT_HASH}.json

aws s3 cp /opt/web/public/assets/.sprockets-manifest-${COMMIT_HASH}.json s3://${FOG_DIRECTORY}/assets/
aws s3 cp /opt/web/public/packs/manifest.json s3://${FOG_DIRECTORY}/packs/

コンテナ起動時にmanifestをダウンロード

entrypoint.sh
#!/bin/bash
set -e

if [ $RAILS_ENV = "production" ]; then
  aws s3 cp s3://${FOG_DIRECTORY}/assets/.sprockets-manifest-${COMMIT_HASH}.json /opt/web/public/assets/
  aws s3 cp s3://${FOG_DIRECTORY}/packs/manifest.json /opt/crs/public/packs/
fi

exec "$@"

CronからScheduled Tasksへの移行

Whenever から ECS Scheduled Tasksへ移行を行いました。
Scheduled Tasksの設定に必要なものは何を(ECSタスク定義)、どのタイミングでという項目のみです。
今回は実際にバックエンドで動かしているDockerイメージをそのまま流用することにしました。

その他

環境変数はすべてコード管理(taskdef.json, buildspec.yml)しています。シークレット情報はパラメータストアを利用しています。

まとめ

CodepipelineやECSの仕様理解に時間がかかり移行は大変でしたが、かけたリソース以上の見返りはありました。
ぜひまだ試せていない方はお試しいただけると良いと思います!

実際に環境を構築していく際には、今考えられる限り最新で最強の完璧な構成を揃えることがベストではないことは多く、
むしろ、速度感であったり、チームの力量に合わせたその瞬間でのベターな選択肢しかないことが多いです。
そういったなかで、ではどのタイミングでリファクタリング等改善を行うかというは判断が非常に難しいです。
今回の場合、デプロイ頻度があがらないと事業上の数字にも十分影響があることをステークホルダーの方々へ納得させることができたため実現できました。
みなさんもステークホルダーの方々とバチバチ意見交換して、エンジニアがより住みやすい世界を作れたら良いのかなと思います。

最後に

クラッソーネではエンジニアを積極採用中です!
Railsが大好きなエンジニアの皆さんもそこそこ好きな皆さんも、興味を持っていただけたらお気軽にご連絡ください。

https://www.crassone.co.jp/recruit/engineer/


起業、エンジニア職、マーケティング職、新規事業立ち上げを経て、現在はクラッソーネでプロダクトマネージャーをしています。ねことスプラトゥーンとNotionが好き。