MIXI DEVELOPERS
🈹

みてねにおけるDynamoDBのコスト削減施策

2024/12/13に公開

はじめに

これは、MIXI DEVELOPERS Advent Calendar 2024 の13日目の記事です。

みてねプロダクト開発部 Data Engineeringグループの 鶴田 です。
この記事では、今年実施した 家族アルバム みてね におけるDynamoDBのコスト削減施策について説明します。

DynamoDBとは

DynamoDBはAmazonが提供するフルマネージドなNoSQLデータベースサービスであり、高度なスケーラビリティと可用性、そして低レイテンシを特徴としています。

みてねにおける DynamoDB の利用

「家族アルバム みてね (以下「みてね」)」では、メインのDBとしてAurora (MySQL) を採用していますが、MLモデルの解析結果の保存にはDynamoDBを利用しています。当初、メインのDBの高負荷が問題となっており、その負荷を分散させるためにDynamoDBが導入されました。しかし、導入に伴い従量課金制によるコストが増大し、コスト削減が課題となりました。

コスト削減施策

Provisioned Capacity Modeへの切り替え

DynamoDBには、以下二つのCapacity Modeが用意されています。

  • Provisioned Capacity Mode
    • 必用なRCU (読み取りキャパシティユニット) とWCU (書き込みキャパシティユニット) を事前に指定する。
    • 単価が安価である反面、指定したRCU/WCUを超えて利用するとスロットリングが発生する (処理しきれないリクエストが拒否される)。
  • On-Demand Capacity Mode
    • リクエスト量に応じてキャパシティが自動調整される。
    • 負荷の変動に強い反面、単価はProvisioned Capacity Modeと比べ高価である。

DynamoDBの導入当初は、On-Demand Capacity Modeで運用していました。負荷に応じて自動でスケーリングされ、細かい設定が不要という利点がありますが、リクエストあたりの単価は高くなります。しばらく運用して負荷の傾向が見えてきたため、コスト削減のためにProvisioned Capacity Modeに切り替えました。

オートスケーリングの設定

リクエストが設定値を超えるとスロットリングが発生しますが、常に上限近くのRCU/WCUを設定するのは無駄になります。DynamoDBは標準でオートスケーリング機能が提供されているため、これを利用してコストを削減しました。

ただし、オートスケーリング機能は万能ではなく、急激な負荷増大時にはスロットリングが発生します。[1]
そのため、以下の施策を併せて行いました。

  • k8sのPodスケーリング設定の調整:
    • サービスに影響のない範囲で、DynamoDBを利用するPodがゆっくりスケールアップするよう設定を調整し、急激な負荷増大が起こりにくいよう工夫しました。
    • みてねにおいて、PodのスケーリングはKEDAで管理されています。helm-chartに以下のような設定を記述し、Podがゆっくりスケールアップするようにしました
apiVersion: keda.sh/v1alpha1
kind: ScaledObject
metadata:
  name: {モジュール名}
spec:
  scaleTargetRef:
    apiVersion:    apps/v1
    kind:          Deployment
    name:          {モジュール名}
  pollingInterval: 60
  minReplicaCount: 1
  maxReplicaCount: 50
  advanced:
    horizontalPodAutoscalerConfig:
      behavior:
        scaleUp:
          policies:
          - type: Pods
            value: 1           # <- 1Podづつスケールアップされるようにする
            periodSeconds: 240 # <- ここを長くしてゆっくりスケールアップされるようにする
        scaleDown:
          stabilizationWindowSeconds: 300
          policies:
          - type: Pods
            value: 1
            periodSeconds: 60
  • RCU/WCUのスケジューリング:
    • 予め分かっている負荷増大 (定時バッチなど) に備えて、RCU/WCUをスケジュールに応じてスケールアップするようにしました。
    • みてねのインフラはTerraformで管理されており、以下のような設定を行うことでDynamoDBのリソースをスケジューリングしました。
# 毎日 21 時に定時バッチが走るため、10 分前に Reader の Capacity を引き上げる
resource "aws_appautoscaling_scheduled_action" "{モジュール名}" {
  count              = local.is_production ? 1 : 0
  name               = "{モジュール名}-read-capacity-scaling-scheduled-action"
  resource_id        = aws_appautoscaling_target.{モジュール名}[0].resource_id
  scalable_dimension = aws_appautoscaling_target.{モジュール名}[0].scalable_dimension
  service_namespace  = aws_appautoscaling_target.{モジュール名}[0].service_namespace
  schedule           = "cron(50 11 * * ? *)"
  scalable_target_action {
    min_capacity = 300
    max_capacity = 3000
  }
}

# 3時間後に設定を元に戻す
resource "aws_appautoscaling_scheduled_action" "{モジュール名}_reset" {
  count              = local.is_production ? 1 : 0
  name               = "{モジュール名}-read-capacity-scaling-scheduled-action-reset"
  resource_id        = aws_appautoscaling_target.{モジュール名}[0].resource_id
  scalable_dimension = aws_appautoscaling_target.{モジュール名}[0].scalable_dimension
  service_namespace  = aws_appautoscaling_target.{モジュール名}[0].service_namespace
  schedule           = "cron(0 14 * * ? *)"
  scalable_target_action {
    min_capacity = 100
    max_capacity = 3000
  }
}

スケジューリングを設定すると、このように予定されている処理の前にキャパシティを引き上げ、スロットリングの発生を予防することができます。

削減結果とまとめ

今回のDynamoDBコスト削減では、大きく以下の施策を実施しました。

  1. On-Demand Capacity ModeからProvisioned Capacity Modeへの切り替え
  2. オートスケーリング機能の活用と適切な設定
    a. Podのスケーリング調整
    b. キャパシティのスケジューリング設定

これらの施策を併せて、サービスの安定性を維持しながら、月あたりのコストが最大35%程度削減されました。

なお、2024年11月に、AmazonからDynamoDBの料金の値下げ (特に、On-Demand Capacity Modeの大きな値下げ) が告知されました。

今後Provisioned Capacity Modeへの移行を行う際は、On-Demand Capacity Modeのメリットや損益分岐点を考慮した上で検討を行うとよいでしょう。

脚注
  1. 厳密には バーストキャパシティ という仕組みがあり、キャパシティの未使用分が留保され、スパイクの発生に対応することができます。 ↩︎

MIXI DEVELOPERS
MIXI DEVELOPERS

Discussion