Application Auto Scalingのcustom-resourceによるKinesis Data Streamsのオートスケール設定

aws

Application Auto Scalingは、Auto Scaling groupによるEC2のオートスケールのようなことを他のリソースでも行えるようにするサービスで、 DynamoDBのキャパシティやECSのServiceなどコンソール上から設定できるものだけではなく、自前のAPIでハンドリングすることで任意のリソースをこの仕組みに乗せることができる。これを用いてKinesis Data Streamsのオートスケールを行う手法を紹介しているのが次の記事。

Scale Amazon Kinesis Data Streams with AWS Application Auto Scaling | AWS Big Data Blog

サンプルテンプレートの設定項目を見ていく。

ScalableTarget

ServiceNamespacedynamodbecsといった名前空間を、ScalableDimensiondynamodb:table:ReadCapacityUnitsecs:service:DesiredCountといった増減する値を指定する。 ResourceIdにはScalableDimensionのResourceTypeに応じた対象を指定するわけだが、 custom-resourceの場合は状態を確認したり更新するためのAPIのURLを指定するようだ。 ドキュメントにはOutputValueの値と書いてあるが、特に関係ないように見える。

Roleにはcloudwatch:Describe/Put/DeleteAlarmsおよびexecute-api:Invoke*の権限を与える。

KinesisAutoScaling:
  Type: AWS::ApplicationAutoScaling::ScalableTarget
  DependsOn: LambdaScaler
  Properties:
    MaxCapacity: 8
    MinCapacity: 1
    ResourceId: !Sub https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/prod/scalableTargetDimensions/${MyKinesisStream}
    RoleARN: !Sub ${CustomApplicationAutoScalingServiceRole.Arn}
    ScalableDimension: 'custom-resource:ResourceType:Property'
    ServiceNamespace: custom-resource

ScalingPolicy

AdjustmentTypeScalingAdjsutmentの値によって増減値を設定する。 AdjustmentTypeにはChangeInCapacity | ExactCapacity | PercentChangeInCapacityのいずれかを指定する。

AutoScalingPolicyOut:
  Type : "AWS::ApplicationAutoScaling::ScalingPolicy"
  DependsOn: KinesisAutoScaling
  Properties: 
    PolicyName: KinesisScaleOut
    PolicyType: StepScaling
    ResourceId: !Sub https://${MyApi}.execute-api.${AWS::Region}.amazonaws.com/prod/scalableTargetDimensions/${MyKinesisStream}      
    ScalableDimension: "custom-resource:ResourceType:Property"
    ServiceNamespace: custom-resource
    StepScalingPolicyConfiguration: 
      AdjustmentType: ChangeInCapacity
      Cooldown: 600
      MetricAggregationType: Average
      StepAdjustments:
      - MetricIntervalLowerBound: 0
       ScalingAdjustment: 1 

CloudWatch AlarmのActionにこのPolicyを指定して、メトリクスが閾値に達したときに通知されるようにする。 この例ではストリームのIncomingRecordsを見ている。 もしパーティションキーが乱数でなかったり、各シャードに対応するハッシュキーのレンジが均等でなかったりして、 ホットシャードが発生する可能性があるなら、 拡張メトリクスを有効にすると得られるシャードレベルの値を基にしても良いかもしれない。

CWAlarmOut:
  Type: AWS::CloudWatch::Alarm
  DependsOn: MyKinesisStream
  Properties:
    AlarmName: !Ref CloudWatchAlarmNameOut
    AlarmDescription: 'incomingRecord exceeds threshold'
    MetricName: 'IncomingRecords'
    Namespace: 'AWS/Kinesis'
    Dimensions:
    - Name: StreamName
      Value: !Ref MyKinesisStream
    Statistic: 'Sum'
    Period: 60
    EvaluationPeriods: 1
    Threshold: 1000
    ComparisonOperator: 'GreaterThanThreshold'
    AlarmActions:
      - !Ref AutoScalingPolicyOut

API

通知が飛ぶとApplication Auto ScalingがResource IDに指定したAPIをGETで呼び出すので、次のフォーマットで状態を返す。

returningJson = {
  "actualCapacity": float(actualCapacity),
  "desiredCapacity": float(desiredCapacity),
  "dimensionName": resourceName,
  "resourceName": resourceName,
  "scalableTargetDimensionId": resourceName,
  "scalingStatus": scalingStatus,
  "version": "MyVersion"
}

スケールが必要な場合はPATCHdesiredCapacity渡ってくるので、この値になるようにUpdateShardCountし、アラームの閾値を更新する。 UpdateShardCountも内部ではSplit/MergeShardが行われるので倍や半分といった値にすると速く変更できる。

"body": "{\"desiredCapacity\":3.0}"

併せてコンシューマーの数も調整する。 また、KDSのリシャーディング回数や変更幅の制限については別に考慮する必要がある。