🔧

AWS Copilot の Addon 機能を使ってロードバランサをカスタマイズする

2023/12/26に公開

はじめに

こんにちは。レンティオ株式会社でエンジニアをしている松田です。

レンティオでは、2021年10月にAWS Copilotで本番運用を始めてから2年ほど経ちました。
初期からCopilotを使ってきただけあって、当時なかった機能はAWS CDKなどで実現していました。
しかし、Copilotも順調にアップデートを重ね、 便利な機能がどんどん追加されています。
今回はそういった機能の中でも特に便利なAddon機能について、レンティオでの利用例も交えて紹介したいと思います。

AWS Copilot の Addon 機能とは

Addon機能を使うと、Copilot側で作成するリソースの情報(ARN等)を参照しつつ新しいリソースを作ることができます。
これにより、ロードバランサにリスナーを追加してカスタマイズしたり、アプリ用のS3バケットを追加できます。

AddonにはEnvironmentに対して追加するパターンと、ワークロード(JobやServiece)に対して追加するパターンがありますが、書き方やファイルのフォーマットは同じになります。

Addonを使用するには、以下のようなaddonsディレクトリとテンプレートファイル・パラメータファイルを配置します。

.
└── addons/
    ├── template.yml
    └── addons.parameters.yml

テンプレートファイルには、おまじない的にパラメータを設定する必要があります。

Environmentの場合

Parameters:
    App:
        Type: String
    Env:
        Type: String

ワークロードの場合

Parameters:
    App:
        Type: String
    Env:
        Type: String
    Name:
        Type: String

参照するパラメータを追加したい場合は、テンプレートファイルとパラメータファイルにそれぞれ項目を追加します。
たとえば、ワークロードのターゲットグループを参照したい場合は、

template.yml
Parameters:
  ...
  TargetGroup:
    Type: String
addons.parameters.yml
Parameters:
  TargetGroup: !Ref TargetGroup

のように設定することで参照できます。

どのパラメータが参照できるかは、Addonの対象となるEnvironmentまたはワークロードのCloudFormationのテンプレートファイルの中で Resources で定義されているものを確認してみるとわかります。

Addon 機能を使うメリット

Addon機能を使うとメリットをいくつかピックアップしてみました。

メリット1: インフラをCopilot内で管理できるようになる

レンティオでは今まではCDKを使ってCopilotのリソースをカスタマイズしていましたが、リポジトリやファイルの場所も離れているので、どこで管理していたかどうしても忘れがちになります。
Addon機能で追加したファイルは、Copilotのマニフェストファイルの近くにあるので、見つけやすく管理しやすいです。

メリット2: Copilotの作成したリソースを簡単に参照できる

Addon機能を使えば、CloudFormationで定義されているパラメータを簡単に参照できます。
CDKなどの外部のツールを使うと、どうしてもARNなどをハードコーディングするしかないですが、Addon機能であればきれいに参照できます。

メリット3: Copilotのデプロイやアップデートを安心して行えるようになる

Addon機能を使うことで、デプロイやアップデート時に、Addonで追加したリソースも自動で更新されます。
CDKなどの外部ツールを使うと、Copilot側で変更があった際にCDKの方は更新されず、再実行するまで一時的にリソースの状態がおかしなことになるリスクもありますが、Addonを使えば安心してデプロイやアップデートできます。

Addon 機能の利用例

レンティオでAddon機能を使用している例を紹介します。

利用例1: HTTPリスナーで作成したアプリにHTTPSリスナーを追加する

レンティオでは、歴史的な経緯のためHTTPベースでロードバランサを作成しており、HTTPSリスナーはCDKを使って追加していました。
CDKでは主に以下のことをやっていました。

  • Copilotのセキュリティグループを参照して内向きのポート443を許可する
  • Copilotのロードバランサを参照してHTTPSリスナーを追加する
  • HTTPSリスナーに証明書を設定する
  • メンテナンス用に503ステータスのレスポンスを返すリスナールールを追加する

これを実現するためのテンプレートは以下のようになります。

template.yml
Parameters:
  # テンプレートに定義する必要があるパラメータ
  App:
    Type: String
  Env:
    Type: String
  # 追加したパラメータ
  PublicHTTPLoadBalancerSecurityGroup:
    Type: String
  DefaultHTTPTargetGroup:
    Type: String
  PublicLoadBalancer:
    Type: String

Conditions:
  IsProduction: !Equals
    - !Ref Env
    - production
  IsStaging: !Equals
    - !Ref Env
    - staging

Resources:
  AddonHTTPSSecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      CidrIp: '0.0.0.0/0'
      Description: 'Allow from anyone on port 443'
      FromPort: 443
      GroupId: !Ref PublicHTTPLoadBalancerSecurityGroup
      IpProtocol: 'tcp'
      ToPort: 443
  AddonHTTPSListener:
    Type: AWS::ElasticLoadBalancingV2::Listener
    Properties:
      Certificates:
        - CertificateArn: 'xxx' # *.example.com
      DefaultActions:
        - TargetGroupArn: !Ref DefaultHTTPTargetGroup
          Type: forward
      LoadBalancerArn: !Ref PublicLoadBalancer
      Port: 443
      Protocol: HTTPS
  AddonALBListenerHttpsDefaultCertificatesAdmin:
    Type: AWS::ElasticLoadBalancingV2::ListenerCertificate
    Properties:
      Certificates:
        - CertificateArn: 'xxx' # *.example.org
      ListenerArn: !Ref AddonHTTPSListener
  AddonALBListenerHttpsDefaultCertificatesExampleProduction:
    Type: AWS::ElasticLoadBalancingV2::ListenerCertificate
    Condition: IsProduction
    Properties:
      Certificates:
        - CertificateArn: 'xxx' # *.api.example.com
      ListenerArn: !Ref AddonHTTPSListener
  AddonALBListenerHttpsDefaultCertificatesExampleStaging:
    Type: AWS::ElasticLoadBalancingV2::ListenerCertificate
    Condition: IsStaging
    Properties:
      Certificates:
        - CertificateArn: 'xxx' # *.api.example.org
      ListenerArn: !Ref AddonHTTPSListener
  # 固定503レスポンスを返す
  AddonListenerRuleMaintenance:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - FixedResponseConfig:
            StatusCode: '503'
          Type: fixed-response
      Conditions:
        - Field: 'path-pattern'
          PathPatternConfig:
            Values:
              - '/*'
      ListenerArn: !Ref AddonHTTPSListener
      Priority: 50


Outputs:
  AddonHTTPSListenerArn:
    Value: !Ref AddonHTTPSListener
    Export:
      Name: !Sub '${App}-${Env}-AddonHTTPSListenerArn'
addons.parameters.yml
Parameters:
  PublicHTTPLoadBalancerSecurityGroup: !Ref PublicHTTPLoadBalancerSecurityGroup
  DefaultHTTPTargetGroup: !Ref DefaultHTTPTargetGroup
  PublicLoadBalancer: !Ref PublicLoadBalancer

ロードバランサのARNやセキュリティグループを参照する必要があるので、Environmentに対してAddonを設定しています。

レンティオでは、1つのロードバランサでユーザー向け・管理者向け・企業向けのアプリへのリクエストをまとめて受け付けているため、証明書も複数登録しています。
証明書のARNは伏せ字にしています。

Conditions を定義して Env を参照することで、環境ごとにリソースを作成できます。
レンティオでは本番環境とstaging環境で証明書が異なるので、IsProductionIsStaging を使って切り替えています。
Outputs で出力している AddonHTTPSListenerArn については、次に説明するワークロードへのAddonで参照するために使用します。

利用例2: リスナールールを追加して複数のアプリを扱えるようにする

レンティオでは、ユーザー向け・管理者向け・企業向けのアプリが同時に稼働していて、ロードバランサはリスナールールを使ってリクエストを振り分けています。
それぞれのアプリでリスナールールを設定する必要があります。
どのアプリのテンプレートファイルも構成はほとんど同じなので、ユーザー向けアプリだけ紹介します。

template.yml
Parameters:
  # テンプレートに定義する必要があるパラメータ
  App:
    Type: String
  Env:
    Type: String
  Name:
    Type: String
  # 追加したパラメータ
  TargetGroup:
    Type: String

Resources:
  AddonListenerRuleEc:
    Type: AWS::ElasticLoadBalancingV2::ListenerRule
    Properties:
      Actions:
        - TargetGroupArn: !Ref TargetGroup
          Type: forward
      Conditions:
        - Field: 'path-pattern'
          PathPatternConfig:
            Values:
              - '/*'
      ListenerArn:
        Fn::ImportValue: !Sub '${App}-${Env}-AddonHTTPSListenerArn'
      Priority: 20
addons.parameters.yml
Parameters:
  TargetGroup: !Ref TargetGroup

リスナールールはHTTPSリスナーに対して設定するので、Fn::ImportValue 関数を使って、 Outputs で出力した AddonHTTPSListenerArn を参照します。
Conditions のパスパターンなどを使って、パスごとにアプリへのリクエストを振り分けられます。
サービスの作成するターゲットグループは TargetGroup を使って参照できます。

まとめ

以上になります。
Addon 機能の便利さがお分かりいただけたかと思います。
非常に強力な機能なので、うまく使えばインフラをより柔軟かつ堅牢に管理できるようになると思います。
また、今回紹介したロードバランサを使った例以外にも、ストレージコマンドを使ってS3バケットやDynamoDBやAuroraをAddonとして追加する方法などもあるので、気になった方はぜひ調べてみてください。

採用情報

レンティオでは絶賛、エンジニアを募集しています!
https://www.wantedly.com/companies/rentio

Discussion