自己紹介
ロジクラでエンジニアをしている高梨です!
前回ロジクラのインフラ構成を紹介 したのに続き、今回もロジクラの開発を支えている環境周りに関して、リリーススピードを数倍にした例を紹介していきます!
以前ロジクラではチーム開発を進めていく中で、機能検証がスタックすることによって開発のスピードが停滞するという課題が発生していました。
具体的な問題としては、検証環境がstagingしか存在しておらず、複数の機能開発が同時に進んでいる場合に、PMなどに仕様を満たしているか確認してもらう順番待ちが発生したり、手直しがあった際に巻き戻しコストがかかりリリースが遅れるということがあります。
結果として、大きめの機能が入ってくるとリリース頻度が週に1度ほどになってしまうことがよくありました。
仕組みの概要
上記の問題を解消するため、変更した機能ごとに開発者以外が簡単に検証することができる stagingDev
という環境を用意することをソリューションとしました。
branchごとにサブドメインの割り振りとECSのタスクを立ち上げ、今まで通りstagingのDBにアクセスできるようにして社内メンバーなら誰でも検証できるというものです。
処理の実行には github actoins
を利用しています。
以下に実際の設定を貼ります。
name: StagingDev作成君 on: pull_request: types: [opened, synchronize] env: ... jobs: build: if: contains(github.event.pull_request.labels.*.name, 'StagingDev') runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: kayac/ecspresso@v1 with: version: latest - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: ... - name: Login to Amazon ECR id: login-ecr uses: aws-actions/amazon-ecr-login@v1 - name: Setup Environment run: ... # 必要な環境変数の準備 - name: Build Image run: | ./deploy/docker_build.sh staging ${{ secrets.IMAGE_REPOSITORY_URI }} $BRANCH_NAME ${{ secrets.SIDEKIQ_CREDENTIAL }} $BRANCH_NAME docker push ${{ secrets.IMAGE_REPOSITORY_URI }}:$BRANCH_NAME - name: Setup if: env.SETUP_COMPLETED == 'false' run: | # priorityが重複するとエラーになるので最大値を取得して重複しないようにする PRIORITY=$(aws elbv2 describe-rules --listener-arn ${arn} | jq -r '.Rules | map(select(.Priority == "default" | not) | .Priority | tonumber) | max + 1') # cloudformationでターゲットグループ, リスナールール, レコードセットの作成 aws cloudformation create-stack \ --stack-name $STACK_NAME \ --template-body file://$(pwd)/deploy/cloudformation/staging_dev.yml \ --parameters ParameterKey=BranchName,ParameterValue=$BRANCH_NAME ParameterKey=ListenerPriority,ParameterValue=$PRIORITY # 完了するまで待つ aws cloudformation wait stack-create-complete --stack-name $STACK_NAME TG_NAME=$STACK_NAME export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --query "TargetGroups[?TargetGroupName == '$TG_NAME'] | [0]" | jq -r .TargetGroupArn) export BRANCH_NAME=$BRANCH_NAME export IMAGE_TAG=$BRANCH_NAME export ENTRY_POINT='"./deploy/init.sh"' # ECS Serviceの作成 ecspresso create --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml ecspresso create --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml - name: Deploy if: env.SETUP_COMPLETED == 'true' run: | TG_NAME=$STACK_NAME export TARGET_GROUP_ARN=$(aws elbv2 describe-target-groups --query "TargetGroups[?TargetGroupName == '$TG_NAME'] | [0]" | jq -r .TargetGroupArn) export BRANCH_NAME=$BRANCH_NAME export IMAGE_TAG=$BRANCH_NAME export ENTRY_POINT='"./deploy/init.sh"' ecspresso deploy --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml ecspresso deploy --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml - ... # issueへのコメントやslack通知処理
上記の処理の流れを簡単に説明します!
- PRに
StagingDev
ラベルを貼り、そのPRに変更をプッシュすることによってActionがトリガ - 該当ブランチで利用する環境変数をセット
- 該当ブランチのイメージを生成
- 用意しているcloudformationの設定を利用し、リソースを構築
- Route53
- LB (target groupの設定など)
- タスクのデプロイ
- 完了したらurlをPRにコメント
最後にslack通知を行います🚀
ちなみに設定は紹介しませんが、PRをクローズすると環境が消されるactionも用意しており、環境が作られっぱなしになることが内容になっています 👌
リソースの構築について
ざっくりとした環境構築までの流れは理解いただけたかなと思うので、ここからさらにstagingDev環境を具体的にどう構築しているかを記載します。
前回の記事でも紹介したようにロジクラはAWS上でECSを利用して運用されています。
実際に検証できる環境を作るためには、branchごとの変更がdeployされたECSの新しいタスクに対して独自のurlからアクセスできるようにリソースを作らないといけません。
CFでTGとRecordSetの作成
まず設定から記載します。
AWSTemplateFormatVersion: "2010-09-09" Description: "" Parameters: BranchName: Type: String VpcId: Type: String Default: AlbArn: Type: String Default: Listener443Arn: Type: String Default: ListenerPriority: Type: Number Resources: TargetGroup: Type: AWS::ElasticLoadBalancingV2::TargetGroup Properties: HealthCheckIntervalSeconds: 10 HealthCheckPath: "/" Port: 80 Protocol: "HTTP" HealthCheckPort: "traffic-port" HealthCheckProtocol: "HTTP" HealthCheckTimeoutSeconds: 5 UnhealthyThresholdCount: 2 TargetType: "ip" Matcher: HttpCode: "200" HealthyThresholdCount: 5 VpcId: Fn::ImportValue: !Sub "${VPCStack}-VPC" Name: !Sub "logikura-tg-ecs-${Environment}" HealthCheckEnabled: true Name: !Sub "staging-dev-${BranchName}" Port: 443 Protocol: HTTP TargetType: ip VpcId: !Ref VpcId ListenerRule: Type: AWS::ElasticLoadBalancingV2::ListenerRule Properties: Actions: - Type: forward TargetGroupArn: !Ref TargetGroup Conditions: - Field: host-header HostHeaderConfig: Values: - !Sub "dev-${BranchName}.com" ListenerArn: !Ref Listener443Arn Priority: !Ref ListenerPriority RecordSet: Type: AWS::Route53::RecordSetGroup Properties: HostedZoneName: logikura.com. RecordSets: - Name: !Sub "dev-${BranchName}.com" Type: CNAME TTL: '60' ResourceRecords: - ***.elb.amazonaws.com
awscliで作っても良いのですが、削除が簡単にできるようにCFを採用しています。 terraformを利用している会社ではterraformで書き換えてもらってもよいかと思います!
最終的にbranch毎に割り振られたurlにアクセスすると、以下のようにbranch毎の変更が反映されたリソースにアクセスできます!
リソースの削除について
削除はCFで作ってるので簡単です。
githubactionsでPRが閉じた際にECSとELB等をまとめて消してます。
ecspresso delete --config ./deploy/ecs/logikura-web-staging-dev/logikura-web-staging-dev.yml --force ecspresso delete --config ./deploy/ecs/logikura-worker-staging-dev/logikura-worker-staging-dev.yml --force aws cloudformation delete-stack --stack-name $STACK_NAME aws cloudformation wait stack-delete-complete --stack-name $STACK_NAME
一つ注意としてはPRの削除のたびに実行するとエラーになるので、stackが存在するかチェックする必要があります。
aws cloudformation describe-stacks --stack-name $STACK_NAME
注意事項
cloudformationのstackNameの制約で、 [a-zA-Z][-a-zA-Z0-9]
にbranch名を限定しないとリソースの作成が失敗してしまうという問題があります。
branch名のルールが決まっていれば問題ないのであえて解決していませんが、もし命名に異なるルールがあるチームの場合は注意してください!
まとめ
ブランチごとに開発者以外が動作検証できる環境を作ったことで、それぞれの環境で仕様確認が同時並行で行うことができ、リリースがスタックすることがなくなりました 🙌
結果的に開発からユーザーに利用してもらうまでのスピードが数倍早くなり、顧客への提供スピードが上がっただけではなく、誰でも自由に検証できる環境があることで事前の社内周知やガイド作成など会社全体とし業務スピードをあげることにも成功しました!
今後も開発がスケールする仕組みを作っていき、さらに顧客へ価値を届けることができるようにしていきたいです 👍
最後に
強引にシェル芸で解決してる部分もあるのですが、もっとスマートにしていきたいので、 ロジクラでは一緒に開発してくれるメンバーを募集しています!
興味がある方はぜひ こちら をご覧ください!