アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Elastic Beanstalk 応用編

morishitaです。

アクトインディではいこレポいこーよとりっぷなどいくつかのサービスで稼働環境として AWS Elastic Beanstalkを利用しています。

前回は Elastic Beanstalk で極々簡単な Web アプリをデプロイするして公開するまでを紹介しました。

tech.actindi.net

今回は少し発展的な使い方を紹介します。

もっといろいろ設定したい

前回はほとんど何も設定せず、デフォルトのまま Web アプリをデプロイしました。

Elastic Beanstalk には色々と設定できる項目があり、一旦、「環境」を作ると AWS の Web コンソールから設定できるようになります。
次の様な設定項目があります。

f:id:HeRo:20220125001832p:plain
「環境」の設定

お客様にサービスを提供するシステムを本番運用する場合、デフォルトのままではなく少なくとも次の項目は要件に応じて設定したいのではないでしょうか。

  • VPC
  • オートスケールやインスタンスのストレージの容量
  • ALB
  • デプロイポリシー
  • ログ出力
  • データベースとの接続

Web コンソールから設定しても構わないのですが、これらはすべて設定ファイルでも設定できます。
デプロイする Web アプリのディレクトリの直下に .ebextensions ディレクトリを作り、その中の拡張子 *.config のファイルが設定ファイルとして扱われます。
設定ファイルを利用するとソースコードの一部として変更管理できるのでオススメです。

sample-app/
├── .ebextensions/
│   ├── 01_vpc.config
│   ├── 02_asg.config
│   ├── 03_alb.config
│   └── 04_update.config
├── .elasticbeanstalk/
│   └── config.yml
├── .gitignore
└── docker-compose.yml

設定ファイルは1つにまとめる必要はなく、いくつに分けても構いません。

設定できる項目は「すべての環境に対する汎用オプション」に記されています。

具体的な設定例をいくつか見てみましょう。

デプロイするVPC、サブネットを指定する設定例

次の例はインスタンスを配置する VPC とその中のサブネットを指定する例です。
システムごとに専用の VPC を用意し、ネットワークを分離する場合には設定することになると思います。
設定は option_settings というキーの配下に定義していきます。

option_settings:
  aws:ec2:vpc:
    VPCId: 'vpc-XXXXXXXX'
    AssociatePublicIpAddress: 'false'
    Subnets: 'subnet-11111111, subnet-22222222'
    ELBSubnets: 'subnet-XXXXXXXX, subnet-ZZZZZZZZ'

AssociatePublicIpAddress: 'false' を設定し、Private サブネットにインスタンスを配置するように設定するとインターネットから直接接続できなくなり、よりセキュアにインスタンスを運用できると思います。

オートスケールやインスタンスのストレージの容量の設定例

次の設定はオートスケーリンググループと起動するインスタンスを設定する例です。
オートスケールする条件や起動するインスタンスのストレージなどを指定しています。

option_settings:
  aws:ec2:instances:
    EnableSpot: true # スポットインスタンスを利用するか否か
    InstanceTypes: 't3.small,t2.small'
    SecurityGroups: 'sg-00000000000000000'
    SpotFleetOnDemandBase: '2' # 必ずオンデマンドでプロビジョニングする台数
    SpotFleetOnDemandAboveBasePercentage: '33' # オンデマンドを利用する割合
  aws:autoscaling:asg:
    Cooldown: 300 # オートスケール処理の最低感覚
    MinSize: 2    # 最低サーバ台数
    MaxSize: 10   # 最大サーバ台数
  aws:autoscaling:launchconfiguration:
    RootVolumeType: 'gp3'   # ストレージボリュームの種類
    RootVolumeIOPS: '3000'  # ストレージボリュームの IOPS
    RootVolumeSize: '10'    # ストレージ容量(GB)
  aws:autoscaling:trigger: # オートスケールのトリガー条件
    BreachDuration: 5
    MeasureName: CPUUtilization
    Statistic: Average
    Unit: Percent
    UpperThreshold: 80
    UpperBreachScaleIncrement: 2
    LowerThreshold: 60
    LowerBreachScaleIncrement: -1

インスタンスに適用するセキュリティグループも上記の設定例では設定しています。
例えば RDS を利用する場合には通信を許可するように設定したセキュリティグループを適用する必要があります。
「環境」に紐づく RDS のデータベースも作成できますが、Web アプリを稼働する「環境」とそれが利用する DB はライフサイクルが同じでなかったりするので、別途作って接続するように設定したほうがいいかなと個人的に思います1

ALB の設定例

次の例は ALB のサーバ証明書やヘルスチェックについて設定しています。

option_settings:
  aws:elbv2:listener:443: # HTTPS を受けるリスナー設定
    ListenerEnabled: 'true'
    Protocol: HTTPS
    SSLCertificateArns: 'arn:aws:acm:ap-northeast-1:999999999999:certificate/00000000-aaaa-xxxx-zzzz-000000000000'
    SSLPolicy: 'ELBSecurityPolicy-2016-08'
  aws:elasticbeanstalk:environment:process:default: # ヘルスチェック設定
    DeregistrationDelay: '20'
    HealthCheckInterval: '30'     # ヘルスチェック間隔
    HealthCheckPath: /healthcheck # ヘルスチェックするパス
    HealthCheckTimeout: '25'
    HealthyThresholdCount: '3'
    UnhealthyThresholdCount: '5'
    Port: '80'
    Protocol: HTTP

デプロイポリシーの設定例

デプロイポリシーは「環境」を更新する場合にどの様にサーバに適用していくかです。
ローリングアップデートするのか? 既存サーバを更新するのか? 新しくインスタンスを起動して入れ替えるのか? などを指定します。

次の設定は全サーバ台数の 20% に当たる数のサーバを新たに起動し、新しいイメージや設定を適用し入れ替えながら更新する設定例です。例えば 10 台で運用している場合、2 台追加しては古いものを 2 台づつ減らしながら更新します。

option_settings:
  aws:elasticbeanstalk:command:
    BatchSize: 20
    BatchSizeType: 'Percentage'
    DeploymentPolicy: 'RollingWithAdditionalBatch'

Elastic Beanstalk のデプロイポリシーは「設定変更」に詳しく説明されているのでよく読んで運用に即した設定をすると良いと思います。

リソースを追加する

Elastic Beanstalk で構築されるリソースは EC インスタンスと ALB などですが、その他のリソースも同時に作りたい場合もあると思います。

Elastic Beanstalk は裏で CloudFormation スタックが動きます。
.ebextensionsconfig ファイルの中で CloudFormation テンプレートの断片を書けばそれに従って標準では作られないリソースを同時に作成可能です。

例えば、次の様な設定を追加すると SNS トピックを通知先とする CloudWatch アラームを追加します。

Parameters:
  AlermSNSTopic:
    Type: String
    Description: "AWS SNS Topic to Notify System Alert"
    Default: "arn:aws:sns:ap-northeast-1:999999999999:system-alert-notifier"

Resources:
  EBALBHealthyHostCountAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName:
        Fn::Join: [ "-", [Ref: AWSEBEnvironmentName, "EB", "ALB", "no", "healthy", "hosts"] ]
      AlarmDescription:
        Fn::Join: [ "", [Ref: AWSEBEnvironmentName, ": ALB no healthy backend hosts."] ]
      Namespace: AWS/ApplicationELB
      MetricName: HealthyHostCount
      Dimensions:
        - Name: LoadBalancer
          Value:
            Fn::GetAtt: [ "AWSEBV2LoadBalancer", "LoadBalancerFullName" ]
        - Name: TargetGroup
          Value:
            Fn::GetAtt: [ "AWSEBV2LoadBalancerTargetGroup", "TargetGroupFullName" ]
      Statistic: Maximum
      Period: 60
      EvaluationPeriods: 3
      Threshold: 1
      ComparisonOperator: LessThanThreshold
      AlarmActions:
        - Ref: AlermSNSTopic
      InsufficientDataActions:
        - Ref: AlermSNSTopic

Elastic Beanstalk が作成するリソースを参照する方法は Elastic Beanstalk が環境向けに作成するリソースを変更する に記載されています。詳しくはそちらを参照ください。

ログの設定

Elastic Beanstalk の設定項目にログがありますが、それらは ALB や Docker などから出力されるもので、Web アプリのアプリケーションログの出力に関するものではないです。

Web アプリのログ出力は docker-compose.yml で設定します。awslogs ドライバーを使って Cloudwatch logs 出力するのが最も簡単だと思います。

設定例を次に示します。

services:
  app:
    # 〜 中略 〜
    logging:
      driver: awslogs
      options:
        awslogs-region: ap-northeast-1
        awslogs-group: /aws/elasticbeanstalk/app
        awslogs-create-group: "true"
        tag: 'app-{{ with split .ImageName ":" }}{{join . "_"}}{{end}}-{{.ID}}'

この設定例は CloudWatch logs に /aws/elasticbeanstalk/app というロググループを作成しログを出力します。 tag の値がログストリームの名前になります。デプロイする Docker イメージ名を含んでいるのでデプロイするイメージ毎にストリームが切り替わる設定となります。

こんな感じで設定により結構柔軟に「環境」をカスタマイズできるのがわかるかと思います。

旧 Docker 環境では移行が促されている

以前より Elastic Beanstalk で Docker コンテナを運用している場合、AWS のコンソールで次の警告が表示されているかと思います。

f:id:HeRo:20220126180613p:plain
Amazon Linux 2 に移行を促す警告

以前はElastic Beanstalk で Docker コンテナを動かす環境は次の 2 種類でした。

  • Multi-container Docker running on 64bit Amazon Linux
  • Docker running on 64bit Amazon Linux

いずれも Deprecated となっており、これらで作られた環境では先の警告が出ています。
要は、Multi-container Docker running on 64bit Amazon Linux は非推奨になったので Docker running on 64bit Amazon Linux 2 に乗り換えてねってことです。
移行については次のページにまとまっています。

既存の「環境」の種類を変更できないので、新たな「環境」を Docker running on 64bit Amazon Linux 2 で作って入れ替えることになります。

移行の際には Dockerrun.aws.json という独自の JSON 形式で定義していたコンテナ構成を docker-compose.yml に変更する必要があります。 内容的には両フォーマットで大きな違いはないのでほとんどフォーマット変換だけだと思います。
一点、ログ設定だけはログストリームの指定方法が異なるので前述の設定例を参考にしてもらえればと思います。

docker-compose.yml に書き直したら次のコマンドで「環境」を作成します。

$ eb create <作成する環境名> -p docker

既存「アプリケーション」に「環境」を追加する場合、旧環境の種類がデフォルト設定されていると思うので -p docker オプションで指定します。
ひょっとすると ALB を使いたいのに CLB で「環境」が作られてしまうかもしれません。その場合は --elb-type application オプションも追加して指定すると良いです。

まだ、EOS の予定は示されてないですが、「非推奨ブランチには、予定されたリタイア日がある場合があります」とあるので放置しておくとそのうちいつまでに移行しなさいって事になると思います。早めに移行したほうが無難でしょう。

EB CLI の注意点

EB CLI を使っていて、いくつか気になる動きがあったので最後に記述します。
利用しているのは EB CLI 3.20.2 (Python 3.10.)です。

eb init は AWS_PROFILE が効かない

EB CLI は AWS CLI のクレデンシャルを利用します。
複数のプロファイルを使い分けていて、デフォルトプロファイル以外でコマンドを実行したい場合にはそのプロファイルを指定する必要があります。

コマンドを連続して実行する場合にはいちいち --profile オプションで指定するのは面倒なので AWS CLI では環境変数 AWS_PROFILE で設定しておくことができます。
EB CLI でも環境変数 AWS_PROFILE が基本的に使え、eb createeb deploy では AWS_PROFILE の設定が効きます。 しかし、eb init では効かないので --profile オプションで指定する必要があります。バグなのかなと思います。

eb コマンドはコミットしている状態をデプロイする

docker-compose.yml.ebextensions に置いた設定ファイルを Git で管理している場合、EB CLI はコミットしている状態をデプロイします。
最初、これに気づかずハマりました。多分、以前はそうじゃなかったと思うのですが…。

eb deploy コマンドには --staged オプションがあり、コミットしなくてもステージングしただけの変更をデプロイできるのですが、eb create コマンドにはこのオプションがないので実行前に予めコミットしておく必要があります。
これが割と不便なので、EB CLI に Git のコミット状態を無視して保存した状態の設定ファイルをそのままデプロイするオプションがあればいいのになと思いました。

ちなみに Git で管理されていない場合には保存されている設定ファイルがそのままデプロイされます。

まとめ

Elastic Beanstalk を .ebextensions の設定ファイルで設定する方法について紹介しました。 各種設定により要件に即した動作に調整できるだけでなく、リソースを追加も可能です。

Elastic Beanstalk の使い所ですが、結局クラスターを構成するすべてのインスタンスで同じコンテナ構成が起動します。 そのため様々な種類のコンテナを動かして、それぞれ稼働コンテナ数を調整したいような複雑なシステムの運用には向かないと思います。
しかし、モノリスな Web アプリを動かすようなシンプルなシステムを運用する環境としては構築が簡単で便利だと思います。

Docker の稼働環境としては今は過渡期で、Docker running on 64bit Amazon Linux 2 に移行が促されています。 それに伴いちょっと癖のある独自形式を学習する必要がなくなり docker-compose.yml になってより使いやすくなったと思います。

Elastic Beanstalk 自体に料金はかかりません。利用する EC2 インスタンスなどの AWS リソースに課金されるだけです。Heroku を検討するのだったら Elastic Beanstalk と比較してみてもいいかなと思います。

参考

最後に

アクトインディではエンジニアを募集しています。

actindi.net


  1. 「環境」に紐づけて DB を作成しても後で切り離すこともできるようです。ただ、Elastic Beanstalk のコンソールから RDS の DB を作成する画面では Aurora を選択できないようです。