LoginSignup
10
0

AWS EC2環境にCircleCI+CodeDeployを導入した時の記録

Last updated at Posted at 2023-12-18

この記事はウェブクルー Advent Calendar 2023 19日目の記事です。
昨日は@wc-kadowaki さんの「Nuxt3のproxyについて」でした。

概要

AWS EC2で立てているサーバにCircleCIを導入しました。
対象アプリケーションのシステム構成概要↓

サーバ環境:
フロントエンドサーバ: 2台(AWS EC2)
バックエンドサーバ: 2台(AWS EC2)
ソースコード管理:
Github(単一リポジトリ)

各サーバはEC2の内部でDockerコンテナを立てています(ECS on EC2ではない)。
そんな構成なので、CircleCIでCI/CDをトリガーしてその後CodeDeployでサービスをデプロイする流れでCI/CD環境を作成しました。
この構成だとCodeDeployの設定でハマったので、その時に詰まった部分とその対応についての記録。

利用サービス

CircleCI

SaaSのCICDサービスで、GithubなどのVCSと連携可能。pushしただけでビルド~デプロイしてくれるし自動テストもできます。
いろんな環境に対応しやすいように、公式がライブラリ(Orb)を提供してくれています。

CodeDeploy

AWSでもCircleCIと似たようなサービスとしてCodePipelineを提供しています。CodeDeployはそのサービスの一部で、名前の通りAWS環境でデプロイを実施してくれるもので、デプロイ対象はECS、EC2、Lambdaなどです。
https://aws.amazon.com/jp/codepipeline/
CircleCIはAWS環境にデプロイするためにAWSのサービスと連携するOrbをいくつか提供しています。そのうちの一つがCodeDeployを利用するものです。

CircleCIとCodeDeployの詳しい説明についてはすでにたくさん記事があるので割愛。

はまったポイント

1つのリポジトリでフロント・バックエンドのソースコードを管理しており、またそれぞれのサーバが別に管理されている場合、CodeDeployで対応するのが難しかった。
CodeDeployは設定ファイルに動的な分岐を用意できない作りになっており、また環境ごとにファイルを変更したりもできない。

対応方法

結論として、CircleCIのダイナミックコンフィグを使って、採用するappconfig.yml(CodeDeployの設定ファイル)を動的に変更する構成にしました。
CircelCIはjobへ渡すパラメータにstepを指定できるので、その仕様を利用しました。

記述例

構成

  • .circleci/build-deploy.yml
  • .circleci/config.yml
  • appspec.back.yml
  • appspec.front.yml
  • codedeploy/start_front.sh
  • codedeploy/stop_front.sh
  • codedeploy/start_back.sh
  • codedeploy/start_back.sh

以下CircleCIの設定ファイル記述例です。CodeDeploy用のファイルは各サービスの実装構成によってだいぶ変わるので start_front.sh , stop_front.sh , start_back.sh , start_back.shは省略します。
また、 appspec.front.ymlappspec.back.yml とほぼ同じ内容なので割愛します。

.circleci/config.yml
version: 2.1

setup: true

orbs:
  path-filtering: circleci/path-filtering@1.0.0

parameters:
  base-revision:
    type: string
    default: "origin/master"

workflows:
  setup:
    jobs:
      - path-filtering/filter:
          base-revision: <<pipeline.parameters.base-revision>>
          config-path: ./.circleci/build-deploy.yml
          mapping: |
            Application/front/.* build-deploy-front true
            Application/api/.* build-deploy-back true

ほぼcodedeploy用orbのjobのコピペ↓

.circleci/build-deploy.yml
version: 2.1

orbs:
  aws-code-deploy: circleci/aws-code-deploy@3.0.0
  aws-cli: circleci/aws-cli@3.1.4

parameters:
  service-name:
    type: string
    default: service-name
  bucket-name:
    type: string
    default: myS3BucketKey
  build-deploy-app:
    type: boolean
    default: false
  build-deploy-web:
    type: boolean
    default: false


jobs: 
  deploy:  # aws-code-deploy/deployのコピペjob
    docker:
      - image: cimg/aws:2023.03
    parameters:
      application-name:
        type: string
      arguments:
        default: ''
        type: string
# ここだけ追記
      pre-command:
        default: []
        description: >
          実行前に実施してほしいコマンド
        type: steps
# 追記ここまで
      auth:
        description: >
          The authentication method used to access your AWS account. Import the
          aws-cli orb in your config and

          provide the aws-cli/setup command to authenticate with your preferred
          method. View examples for more information.
        type: steps
      bundle-bucket:
        type: string
      bundle-key:
        type: string
      bundle-source:
        default: .
        type: string
      bundle-type:
        default: zip
        type: string
      deploy-bundle-arguments:
        default: ''
        type: string
      deployment-config:
        default: CodeDeployDefault.OneAtATime
        enum:
          - CodeDeployDefault.OneAtATime
          - CodeDeployDefault.HalfAtATime
          - CodeDeployDefault.AllAtOnce
        type: enum
      deployment-group:
        type: string
      get-deployment-group-arguments:
        default: ''
        type: string
      profile-name:
        default: default
        type: string
      region:
        default: ${AWS_DEFAULT_REGION}
        description: >
          AWS region of CodeDeploy App. Defaults to environment variable
          ${AWS_DEFAULT_REGION}.
        type: string
      service-role-arn:
        description: The service role for a deployment group.
        type: string
    steps:
      - checkout
      - steps: << parameters.command >>
      - steps: << parameters.auth >>
      - aws-code-deploy/create-application:
          application-name: << parameters.application-name >>
          arguments: << parameters.arguments >>
          profile-name: << parameters.profile-name >>
          region: << parameters.region >>
      - aws-code-deploy/create-deployment-group:
          application-name: << parameters.application-name >>
          arguments: << parameters.arguments >>
          deployment-config: << parameters.deployment-config >>
          deployment-group: << parameters.deployment-group >>
          get-deployment-group-arguments: << parameters.get-deployment-group-arguments >>
          profile-name: << parameters.profile-name >>
          region: << parameters.region >>
          service-role-arn: << parameters.service-role-arn >>
      - aws-code-deploy/push-bundle:
          application-name: << parameters.application-name >>
          arguments: << parameters.arguments >>
          bundle-bucket: << parameters.bundle-bucket >>
          bundle-key: << parameters.bundle-key >>
          bundle-source: << parameters.bundle-source >>
          bundle-type: << parameters.bundle-type >>
          profile-name: << parameters.profile-name >>
          region: << parameters.region >>
      - aws-code-deploy/deploy-bundle:
          application-name: << parameters.application-name >>
          bundle-bucket: << parameters.bundle-bucket >>
          bundle-key: << parameters.bundle-key >>
          bundle-type: << parameters.bundle-type >>
          deploy-bundle-arguments: << parameters.deploy-bundle-arguments >>
          deployment-config: << parameters.deployment-config >>
          deployment-group: << parameters.deployment-group >>
          get-deployment-group-arguments: << parameters.get-deployment-group-arguments >>
          profile-name: << parameters.profile-name >>
          region: << parameters.region >>

workflows:
  deploy_back_application:
    when: <<pipeline.parameters.build-deploy-back>>
    jobs:
      - aws-code-deploy/deploy:
          context: aws-context
          pre-command:
            - run: cp appspec.back.yml appspec.yml
          auth:
            - aws-cli/setup:
                profile-name: CircleCI IDENTITY PROFILE
                role-arn: $ASSUME_ROLE_ARN    # aws-contextに登録している環境変数
                role-session-name: pdm-codedeploy-session
          name: <<pipeline.parameters.service-name>>-Back-App
          application-name: <<pipeline.parameters.service-name>>-Back-App
          bundle-bucket: <<pipeline.parameters.bucket-name>>
          service-role-arn: $SERVICE_ROLE_ARN
          profile-name: CircleCI IDENTITY PROFILE
          bundle-key: <<pipeline.parameters.service-name>>-Back
          deployment-group: <<pipeline.parameters.service-name>>>-Back-DepGrp
          filters:
            branches:
              only:
                - master
  deploy_front_application:
    when: <<pipeline.parameters.build-deploy-front>>
    jobs:
      - deploy:
          context: aws-context
          pre-command:
            - run: cp appspec.front.yml appspec.yml
          auth:
            - aws-cli/setup:
                profile-name: CircleCI IDENTITY PROFILE
                role-arn: $ASSUME_ROLE_ARN
                role-session-name: pdm-codedeploy-session
          name: <<pipeline.parameters.service-name>>-Front-App
          application-name: <<pipeline.parameters.service-name>>-Front-App
          bundle-bucket: <<pipeline.parameters.bucket-name>>
          service-role-arn: $SERVICE_ROLE_ARN
          profile-name: CircleCI IDENTITY PROFILE
          bundle-key: <<pipeline.parameters.service-name>>-Front
          deployment-group: <<pipeline.parameters.service-name>>-Front-DepGrp
          requires:
            - approval-deploy
          filters:
            branches:
              only:
                - master
appspec.back.yml
version: 0.0
os: linux
files:
  - source: /Application/api
    destination: /home/app/api
file_exists_behavior: OVERWRITE
hooks:
  ApplicationStop:
    - location: odedeploy/stop_back.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: codedeploy/start_back.sh
      timeout: 300
      runas: root

所感

せっかくorbで簡潔にかけるはずが、今回の構成のためにjobを定義することになってしまった点が残念でした。IaaCがデファクトスタンダードになってきている現状では、バックエンドとフロントエンドのリポジトリを分けるべきですね。

参考

https://circleci.com/docs/ja/contexts/
https://circleci.com/developer/ja/orbs/orb/circleci/aws-code-deploy
https://circleci.com/docs/ja/dynamic-config/
https://docs.aws.amazon.com/codedeploy/latest/userguide/tutorials-wordpress.html
https://docs.aws.amazon.com/codedeploy/latest/userguide/reference-appspec-file-example.html#appspec-file-example-server

明日は @wc-fukuda さんの投稿です。

10
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
0