1
0

【AWS】Lambdaを使ってRDSのdumpファイルをS3に保存する

Last updated at Posted at 2024-08-19

以下の手順で、LambdaとRDSを同一VPCに配置し、S3にデータベースのdumpファイルを保存する環境を構築します。

前提条件

  • AWS CLIがインストールされ、適切に設定されていること(バージョン2.0以上推奨)
  • Terraformがインストールされていること(バージョン1.0以上推奨)
  • Python 3.8以上がインストールされていること(Lambda関数の開発用)

手順

VPCの設定

既存のVPCを使用するか、新しいVPCを作成します。VPCには少なくとも2つのサブネット(プライベートサブネット)が必要です。

Terraformを使用してVPCを作成する例:

resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  
  tags = {
    Name = "main-vpc"
  }
}

resource "aws_subnet" "private_1" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.1.0/24"
  availability_zone = "us-west-2a"

  tags = {
    Name = "Private Subnet 1"
  }
}

resource "aws_subnet" "private_2" {
  vpc_id     = aws_vpc.main.id
  cidr_block = "10.0.2.0/24"
  availability_zone = "us-west-2b"

  tags = {
    Name = "Private Subnet 2"
  }
}

S3バケットの作成

データベースダンプを保存するためのS3バケットを作成します。

Terraformを使用してS3バケットを作成する例:

resource "aws_s3_bucket" "db_dump" {
  bucket = "my-db-dump-bucket"

  tags = {
    Name = "DB Dump Bucket"
  }
}

resource "aws_s3_bucket_public_access_block" "db_dump" {
  bucket = aws_s3_bucket.db_dump.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

VPCエンドポイントの設定

S3へのアクセスを可能にするため、VPCエンドポイントを作成します。

resource "aws_vpc_endpoint" "s3" {
  vpc_id       = aws_vpc.main.id
  service_name = "com.amazonaws.us-west-2.s3"
}

resource "aws_vpc_endpoint_route_table_association" "private_s3" {
  route_table_id  = aws_route_table.private.id
  vpc_endpoint_id = aws_vpc_endpoint.s3.id
}

RDSインスタンスの設定

VPC内のプライベートサブネットにRDSインスタンスを作成します。

resource "aws_db_instance" "default" {
  engine               = "mysql"
  engine_version       = "8.0"
  instance_class       = "db.t3.micro"
  allocated_storage    = 20
  storage_type         = "gp2"
  db_name              = "mydb"
  username             = "admin"
  password             = "password" # 注意: 本番環境では安全な方法で管理してください
  parameter_group_name = "default.mysql8.0"

  vpc_security_group_ids = [aws_security_group.rds.id]
  db_subnet_group_name   = aws_db_subnet_group.default.name

  skip_final_snapshot = true
}

resource "aws_db_subnet_group" "default" {
  name       = "main"
  subnet_ids = [aws_subnet.private_1.id, aws_subnet.private_2.id]

  tags = {
    Name = "My DB subnet group"
  }
}

Lambda関数の作成

データベースダンプを実行し、S3にアップロードするLambda関数を作成します。この関数もVPC内のプライベートサブネットに配置します。
※lambdaレイヤーを設定し、pymysqlを使えるようにする必要があります。(手順)

Lambda関数の Python コード例(lambda_function.py):

import boto3
import pymysql
import os
from datetime import datetime

def lambda_handler(event, context):
    # RDS接続情報
    rds_host  = os.environ['RDS_HOST']
    db_name = os.environ['DB_NAME']
    user = os.environ['DB_USER']
    password = os.environ['DB_PASSWORD']
    
    # S3バケット名
    s3_bucket = os.environ['S3_BUCKET']
    
    # 現在の日時を取得
    now = datetime.now()
    date_string = now.strftime("%Y-%m-%d-%H-%M-%S")
    
    # ダンプファイル名
    dump_file = f"/tmp/dump-{date_string}.sql"
    
    # MySQLデータベースに接続
    conn = pymysql.connect(host=rds_host, user=user, passwd=password, db=db_name, connect_timeout=5)
    
    try:
        with conn.cursor() as cur:
            # ダンプコマンドを実行
            cur.execute(f"SELECT * INTO OUTFILE '{dump_file}' FROM your_table")
        
        # S3クライアントを作成
        s3 = boto3.client('s3')
        
        # ダンプファイルをS3にアップロード
        s3.upload_file(dump_file, s3_bucket, f"dumps/dump-{date_string}.sql")
        
        print(f"Database dump uploaded to s3://{s3_bucket}/dumps/dump-{date_string}.sql")
        
    finally:
        conn.close()
        
    return {
        'statusCode': 200,
        'body': 'Database dump completed successfully'
    }

IAMロールの設定

Lambda関数用のIAMロールを作成し、必要な権限(RDSアクセス、S3アクセス、VPC内でのネットワーキング)を付与します。

resource "aws_iam_role" "lambda_role" {
  name = "lambda_db_dump_role"

  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Action = "sts:AssumeRole"
        Effect = "Allow"
        Principal = {
          Service = "lambda.amazonaws.com"
        }
      }
    ]
  })
}

resource "aws_iam_role_policy_attachment" "lambda_vpc_access" {
  role       = aws_iam_role.lambda_role.name
  policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}

resource "aws_iam_role_policy" "lambda_s3_access" {
  name = "lambda_s3_access"
  role = aws_iam_role.lambda_role.id

  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [
      {
        Effect = "Allow"
        Action = [
          "s3:PutObject",
          "s3:GetObject",
          "s3:ListBucket"
        ]
        Resource = [
          aws_s3_bucket.db_dump.arn,
          "${aws_s3_bucket.db_dump.arn}/*"
        ]
      }
    ]
  })
}

セキュリティグループの設定

LambdaとRDS用のセキュリティグループを作成し、必要な通信を許可します。

resource "aws_security_group" "lambda" {
  name        = "lambda_sg"
  description = "Security group for Lambda function"
  vpc_id      = aws_vpc.main.id

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "rds" {
  name        = "rds_sg"
  description = "Security group for RDS instance"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port       = 3306
    to_port         = 3306
    protocol        = "tcp"
    security_groups = [aws_security_group.lambda.id]
  }
}

Lambda関数のデプロイ

作成したLambda関数をデプロイします。

data "archive_file" "lambda_zip" {
  type        = "zip"
  source_file = "lambda_function.py"
  output_path = "lambda_function.zip"
}

resource "aws_lambda_function" "db_dump" {
  filename      = "lambda_function.zip"
  function_name = "db_dump_function"
  role          = aws_iam_role.lambda_role.arn
  handler       = "lambda_function.lambda_handler"
  runtime       = "python3.8"

  vpc_config {
    subnet_ids         = [aws_subnet.private_1.id, aws_subnet.private_2.id]
    security_group_ids = [aws_security_group.lambda.id]
  }

  environment {
    variables = {
      RDS_HOST     = aws_db_instance.default.endpoint
      DB_NAME      = aws_db_instance.default.db_name
      DB_USER      = aws_db_instance.default.username
      DB_PASSWORD  = aws_db_instance.default.password
      S3_BUCKET    = aws_s3_bucket.db_dump.id
    }
  }
}

スケジューリングの設定(オプション)

定期的なダンプ実行のため、Amazon EventBridgeを使用してLambda関数をスケジュールします。

resource "aws_cloudwatch_event_rule" "daily_db_dump" {
  name                = "daily-db-dump"
  description         = "Trigger DB dump Lambda function daily"
  schedule_expression = "cron(0 1 * * ? *)"  # 毎日午前1時(UTC)に実行
}

resource "aws_cloudwatch_event_target" "db_dump_lambda" {
  rule      = aws_cloudwatch_event_rule.daily_db_dump.name
  target_id = "TriggerLambdaFunction"
  arn       = aws_lambda_function.db_dump.arn
}

resource "aws_lambda_permission" "allow_cloudwatch" {
  statement_id  = "AllowExecutionFromCloudWatch"
  action        = "lambda:InvokeFunction"
  function_name = aws_lambda_function.db_dump.function_name
  principal     = "events.amazonaws.com"
  source_arn    = aws_cloudwatch_event_rule.daily_db_dump.arn
}

最後に

  • VPC内でのリソース配置により、インターネットアクセスが制限されます。必要に応じてNATゲートウェイを設定してください。
  • RDSインスタンスのバックアップ方法として、このアプローチを使用する場合は、整合性とパフォーマンスに注意してください。大規模なデータベースの場合、ダンプ中にデータベースがロックされる可能性があります。
  • 大規模なデータベースの場合、Lambda関数のタイムアウト設定(デフォルトは3秒)と割り当てメモリ(最小128MB、最大10,240MB)を調整する必要があります。
  • ダンプファイルの保管期間や、S3のライフサイクルポリシーの設定を検討してください。長期保存が不要な場合は、古いダンプファイルを自動的に削除するポリシーを設定することをお勧めします。
  • 本番環境では、データベースのパスワードなどの機密情報をAWS Secrets Managerなどのサービスを使用して安全に管理することを強く推奨します。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0