【Amazon Neptune】簡単節約術!Lambda を使って自動起動・停止してみた

記事タイトルとURLをコピーする

こんにちは。ディベロップメントサービス1課の山本です。

今回はコスト削減のため AWS Lambda (以降、Lambda) を使って Amazon Neptune(以降、Neptune) を自動起動・停止してみました。

この記事の対象者は?

  • Neptune を利用されている方
  • Neptune のコストを抑えたい方

Neptune のコストについて

コンソール上で Neptune を作成する際に表示される、本番稼働用テンプレートのデフォルト値で計算してみます。条件は以下の通りです。

項目
タイプ Provisioned
クラスターストレージ設定 Neptune I/O 最適化
インスタンスクラス db.r6g.xlarge
マルチ AZ 配置 有(アクティブ/スタンバイ)

db.r6g.xlarge の時間単価は以下の通りです。

インスタンスあたりの料金 (USD/時)
1.071

料金 - Amazon Neptune | AWS

1ヶ月の Neptune のコストはこちらです。(2025年4月8日 時点 東京リージョン)

  • 1.071 (USD/時) * 24 (時) * 30(日) * 2 (アクティブ/スタンバイ) * 148 (円/USD) = 約 228,000 円
  • 注)今回の取り組みで節約できるインスタンス料金のみ抜粋
  • 注)為替レートにより変動します

1ヶ月起動しているだけで約 23 万円とめちゃめちゃ高い。
起動時間を半分に減らすことができれば、月 10 万円以上の節約になります。

構成図

作成する AWS リソースの構成図は以下の通りです。

構成図

定時起動する Amazon EventBridge (以降、EventBridge)で Lambda 関数を呼び出して、関数内で Neptune の起動・停止処理を行います。

ファイル構成

AWS Serverless Application Model(以降、SAM)を利用して Lambda 関数をデプロイします。

.
├── samconfig.toml
├── src
│   └── app.py
└── template.yaml

samconfig.toml

SAM の設定ファイルとなります。
起動は平日の朝8時。停止は毎日20時(夜)に設定するようにします。
いずれも JST(日本時間)の設定です。

version = 0.1

[default.deploy.parameters]
stack_name = "blog-auto-neptune-start-stop"
capabilities = "CAPABILITY_NAMED_IAM"
region = "ap-northeast-1"
s3_bucket = "{リソースを格納するS3バケット名}"
parameter_overrides = [
    "SystemName=blog", # システム名
    "NeptuneClusterId=blog-neptune", # Neptune クラスターID(識別子)
    'StartCronExpression="cron(0 8 ? * MON-FRI *)"',    # Neptune 起動時間(JST)
    'StopCronExpression="cron(0 20 ? * * *)"'    # Neptune 終了時間(JST)
]

template.yaml

SAM のテンプレートファイルとなります。

以下のリソースを作成します。

  • Lambda × 1
  • EventBridge スケジュール × 2
  • IAM Role × 1
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM Template for Neptune Auto Start/Stop

Parameters:
  SystemName:
    Type: String
    Description: The name of the system
  NeptuneClusterId:
    Type: String
    Description: The ID of the Neptune cluster to start/stop
  StartCronExpression:
    Type: String
    Default: "cron(0 8 ? * MON-FRI *)"
    Description: "Cron expression for starting the Neptune cluster"
  StopCronExpression:
    Type: String
    Default: "cron(0 20 ? * * *)"
    Description: "Cron expression for stopping the Neptune cluster"

Resources:
  NeptuneStartStopFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${SystemName}-neptune-start-stop
      Description: "Neptune の自動起動・停止 Lambda"
      Handler: app.handler
      Runtime: python3.13
      CodeUri: ./src
      Environment:
        Variables:
          NEPTUNE_CLUSTER_ID: !Ref NeptuneClusterId
      Policies:
        - Version: "2012-10-17"
          Statement:
            - Effect: Allow
              Action:
                - rds:StartDBCluster
                - rds:StopDBCluster
              Resource: !Sub arn:aws:rds:${AWS::Region}:${AWS::AccountId}:cluster:${NeptuneClusterId}

  StartScheduler:
    Type: AWS::Scheduler::Schedule
    Properties:
      Name: !Sub ${SystemName}-start-neptune
      Description: Neptune 起動スケジュール
      ScheduleExpression: !Ref StartCronExpression
      ScheduleExpressionTimezone: "Asia/Tokyo"
      FlexibleTimeWindow:
        Mode: "OFF"
      Target:
        Arn: !GetAtt NeptuneStartStopFunction.Arn
        RoleArn: !GetAtt LambdaInvokeRole.Arn
        Input: '{"action": "start"}'

  StopScheduler:
    Type: AWS::Scheduler::Schedule
    Properties:
      Name: !Sub ${SystemName}-stop-neptune
      Description: Neptune 停止スケジュール
      ScheduleExpression: !Ref StopCronExpression
      ScheduleExpressionTimezone: "Asia/Tokyo"
      FlexibleTimeWindow:
        Mode: "OFF"
      Target:
        Arn: !GetAtt NeptuneStartStopFunction.Arn
        RoleArn: !GetAtt LambdaInvokeRole.Arn
        Input: '{"action": "stop"}'

  LambdaInvokeRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub ${SystemName}-scheduler-lambda-invoke-role
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: "Allow"
            Principal:
              Service: "scheduler.amazonaws.com"
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: !Sub ${SystemName}-scheduler-lambda-invoke-policy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: "Allow"
                Action:
                  - "lambda:InvokeFunction"
                Resource:
                  - !GetAtt NeptuneStartStopFunction.Arn

app.py

Lambda 関数で実行されるプログラムファイルとなります。
boto3 を用いて、開始と停止を行うだけです。

import os
import boto3

neptune_client = boto3.client('neptune')


def handler(event, _context):
    cluster_id = os.environ['NEPTUNE_CLUSTER_ID']
    action = event.get('action')

    if action == 'start':
        neptune_client.start_db_cluster(DBClusterIdentifier=cluster_id)
    elif action == 'stop':
        neptune_client.stop_db_cluster(DBClusterIdentifier=cluster_id)
    else:
        raise ValueError("Invalid action. Use 'start' or 'stop'.")

実行結果

自動起動

無事、自動起動できてます。

自動起動結果

自動停止

無事、自動停止できてます。

自動停止結果

まとめ

  • Neptune は性能・可用性を求めるとコストがかなり高くなる(約23万円/月)
  • Lambda を使って、自動起動・停止してコストを削減しよう
  • Neptune の起動には時間がかかる(体感 20分程度)ため、早めのスケジュール設定が必要

さいごに

自動停止後に残業で再起動することのないように。

本ブログがどなたかのお役に立てれば幸いです。

山本 真大(執筆記事の一覧)

アプリケーションサービス部 ディベロップメントサービス1課

2023年8月入社。カピバラさんが好き。