現状のインフラ構成とワーカーのオートスケール
ロジクラでテックリードをしている高梨です!
サービスが公式に公開される前から技術選定だったり、実際にコード書いたり、インフラやSRE的なことまで色々やってます。
最近は愛犬と遊ぶのにはまってます。
概要
ロジクラは今年、AzureからAWSへとインフラの移行を行いました 🎉
当初ロジクラはAzureを利用していました。利用していたAppServiceは十分要件を満たすことができ、Microsoftさんからのサポートも手厚かったことが主な理由で利用していたのですが、会社のフェーズが進み、今後さらに事業拡大し開発組織を大きくしようと考えたとき、リファレンスが充実しており、経験者が多いAWSに移行して採用につなげることを主な目的にインフラ移行を決定しました。 ここでは現状のロジクラのインフラ構成と、移行時に工夫した点などを紹介していきます!
今回移行したものはメインサービスである在庫管理のSaaSのロジクラです。
必要な仕組みとして大まかに説明すると、
- メインのサービスを乗せているサーバー・DB
- 非同期処理を行うためのサーバー
- 非同期処理を行うためキューを貯めるRedis
というシンプルな構成になっています。
管理やスケールの容易さから今回メインのサービスを載せるサーバーについては、ECS・Fargateを利用することにしました。
構成紹介
載せきれない部分もあるのですが、大まかな構成としては以下のようなシンプルな構成になっています。
次にこの構成を作っていった中で、特に工夫していった点を紹介していきます。
デプロイについて
デプロイに関してはgithub actionをトリガーにしています。stagingとproductionでトリガーは異なるのですが、deploy処理に関しては以下のgithub actionの設定ファイル(不要な部分はカットしています)のように、buildしたimageを一度ECRに置き、そのimageを利用してdbのmigrationを行った後にdeployする流れとなります。
... jobs: ... build_image: runs-on: ubuntu-latest steps: - uses: nelonoel/branch-name@v1.0.1 - name: Check out code id: checkout uses: actions/checkout@v2 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} - name: Set environment id: set_environment run: {{環境変数を設定する処理}} - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Build Image run: {{deployするimageをbuild}} deploy: needs: [build_image] runs-on: ubuntu-latest env: IMAGE_TAG: ${{ needs.build_image.outputs.image_tag }} steps: - uses: kayac/ecspresso@v1 with: version: latest - name: Check out code id: checkout uses: actions/checkout@v2 - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: ${{ secrets.AWS_REGION }} - name: Database Migration run: {{DBのmigrate処理}} - name: Quiet Sidekiq Process run: {{Sidekiqプロセスの停止}} - name: deploy run: | ./deploy/ecs_deploy.sh -t web -e staging -i $IMAGE_TAG ./deploy/ecs_deploy.sh -t worker -e staging -i $IMAGE_TAG
実際のdpeloyに何しては、カヤック製のecspressoを利用しており、以下コマンドで $SERVICE_NAME
ごとの設定(productionやstagingなどを入れています)を読み込んでいます。
ecspresso deploy --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml --rollback-events DEPLOYMENT_FAILURE ecspresso wait --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml
詳しくはecspressoのレポジトリをご覧ください!
タスクに関してもgithub actionから実行したいコマンドと環境を指定して実行するよう設定しています。
以下がecspressoでの実行コマンドになります。
ecspresso --config ./deploy/ecs/${SERVICE_NAME}/${SERVICE_NAME}.yml run --overrides="{\"containerOverrides\":[{\"name\":\"logikura-ecs-container-web-${APP_ENV}\", \"command\":[\"./deploy/run_command.sh ${COMMAND}\"]}]}"
最後にdeployとタスク実行ともに、完了するとslack通知が流れてきます。
以上が基本的なdeploy周りの設定となります!
オートスケールについて
構成の中で工夫した点としては、workerのオートスケールの仕組みを導入したことです。
ロジクラは、利用者の性質上、例えば商品登録や出荷データの登録など、大量のデータを一度に処理したいと言う要望が多く、非同期処理を行うことが必要になっています。
ただ、処理が集中した場合jobが詰まってしまうと言うことが発生し得ます。顧客も増えている ⇒ 安定的に全てのお客さんにシステムを使ってもらうために、job数に応じてオートスケールする仕組みを取り入れています。
credentials = Aws::Credentials.new(ENV["AWS_ACCESS_KEY_ID"], ENV["AWS_SECRET_KEY"]) client = Aws::CloudWatch::Client.new(region: EMV["AWS_REGION"], credentials: credentials) client.put_metric_data({ namespace: "SidekiqJobCount", metric_data: [ { metric_name: "job_count", dimensions: [ { name: "app_env", value: ENV["RAILS_ENV"], }, ], value: Sidekiq::Stats.new.enqueued, unit: "Count", }, ], })
落ち着いたらスケールインしたくなりますが、処理の途中で落としてしまうと設計によっては問題が生じる場合があるかと思います。
https://github.com/mperham/sidekiq/wiki/Signals#tstp
SidekiqはTSTPシグナルを送ると新しいジョブの取得を停止し、処理が終わると停止します。
この仕組を利用して、TERMをtrapしてSidekiqを安全に停止するようにしました。
function trap_term() { SIDEKIQ_PID=`ps -ef | grep -v grep | grep sidekiq | awk '{ print $2 }'` kill -TSTP $SIDEKIQ_PID while : do SIDEKIQ_STATUS=`ps axu | grep -v grep | grep sidekiq` echo $SIDEKIQ_STATUS if [[ $SIDEKIQ_STATUS =~ stopping ]]; then echo "stopped sidekiq" exit else sleep 1 fi done } trap trap_term TERM
これで解決したように思えたのですが、処理に時間のかかるjobの場合stopTimeout(最大120秒)を超えるとKILLされてしまう問題が起こりました。
stopTimeoutはFargate環境では現状これ以上伸ばせないので、結局スケールインはできていません。
何か良い方法あれば教えて下さい 🙏
参考
https://aws.amazon.com/jp/blogs/news/graceful-shutdowns-with-ecs/
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_definition_parameters.html
まとめ
toBのサービスだと同じような課題を抱えている会社は多いと思います。ぜひ参考にしてみてください!
ロジクラではこの他にもインフラ面で工夫している点がいくつもあるので、また別の記事でも紹介していきます!
最後に
ロジクラでは一緒に開発してくれるメンバーを募集しています!
興味がある方はぜひ こちら をご覧ください!