AWS Copilot の Addon 機能を使ってロードバランサをカスタマイズする
はじめに
こんにちは。レンティオ株式会社でエンジニアをしている松田です。
レンティオでは、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
参照するパラメータを追加したい場合は、テンプレートファイルとパラメータファイルにそれぞれ項目を追加します。
たとえば、ワークロードのターゲットグループを参照したい場合は、
Parameters:
...
TargetGroup:
Type: String
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ステータスのレスポンスを返すリスナールールを追加する
これを実現するためのテンプレートは以下のようになります。
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'
Parameters:
PublicHTTPLoadBalancerSecurityGroup: !Ref PublicHTTPLoadBalancerSecurityGroup
DefaultHTTPTargetGroup: !Ref DefaultHTTPTargetGroup
PublicLoadBalancer: !Ref PublicLoadBalancer
ロードバランサのARNやセキュリティグループを参照する必要があるので、Environmentに対してAddonを設定しています。
レンティオでは、1つのロードバランサでユーザー向け・管理者向け・企業向けのアプリへのリクエストをまとめて受け付けているため、証明書も複数登録しています。
証明書のARNは伏せ字にしています。
Conditions
を定義して Env
を参照することで、環境ごとにリソースを作成できます。
レンティオでは本番環境とstaging環境で証明書が異なるので、IsProduction
や IsStaging
を使って切り替えています。
Outputs
で出力している AddonHTTPSListenerArn
については、次に説明するワークロードへのAddonで参照するために使用します。
利用例2: リスナールールを追加して複数のアプリを扱えるようにする
レンティオでは、ユーザー向け・管理者向け・企業向けのアプリが同時に稼働していて、ロードバランサはリスナールールを使ってリクエストを振り分けています。
それぞれのアプリでリスナールールを設定する必要があります。
どのアプリのテンプレートファイルも構成はほとんど同じなので、ユーザー向けアプリだけ紹介します。
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
Parameters:
TargetGroup: !Ref TargetGroup
リスナールールはHTTPSリスナーに対して設定するので、Fn::ImportValue
関数を使って、 Outputs
で出力した AddonHTTPSListenerArn
を参照します。
Conditions
のパスパターンなどを使って、パスごとにアプリへのリクエストを振り分けられます。
サービスの作成するターゲットグループは TargetGroup
を使って参照できます。
まとめ
以上になります。
Addon 機能の便利さがお分かりいただけたかと思います。
非常に強力な機能なので、うまく使えばインフラをより柔軟かつ堅牢に管理できるようになると思います。
また、今回紹介したロードバランサを使った例以外にも、ストレージコマンドを使ってS3バケットやDynamoDBやAuroraをAddonとして追加する方法などもあるので、気になった方はぜひ調べてみてください。
採用情報
レンティオでは絶賛、エンジニアを募集しています!
Discussion