web-dev-qa-db-ja.com

S3からcsvを読み取り、AWS Lambdaを使用してMySQLテーブルに挿入する

私はそれがS3バケットに受信されたときに、MySQLテーブルへのcsvのロードを自動化しようとしています。

私の戦略は、S3が指定されたバケットにファイルを受け取ったときにイベントを起動することです(「bucket-file」と呼びましょう)。これは、各行をMySqlテーブルに挿入するファイルをダウンロードして処理するAWS Lambda関数に通知されるイベントです( 'target_table'と呼びましょう)。

RDSがVPCにあることを考慮する必要があります。

バケットの現在の権限構成は次のとおりです。

_{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "PublicReadForGetBucketObjects",
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::bucket-file/*"
        }
    ]
}
_

AWS Lambda関数にアタッチされたAmazonS3FullAccessおよびAWSLambdaVPCAccessExecutionRoleというポリシーを使用してロールを作成しました。

ラムダコードは次のとおりです。

_from __future__ import print_function
import boto3
import logging
import os
import sys
import uuid
import pymysql
import csv
import rds_config


rds_Host  = rds_config.rds_Host
name = rds_config.db_username
password = rds_config.db_password
db_name = rds_config.db_name


logger = logging.getLogger()
logger.setLevel(logging.INFO)

try:
    conn = pymysql.connect(rds_Host, user=name, passwd=password, db=db_name, connect_timeout=5)
except Exception as e:
    logger.error("ERROR: Unexpected error: Could not connect to MySql instance.")
    logger.error(e)
    sys.exit()

logger.info("SUCCESS: Connection to RDS mysql instance succeeded")

s3_client = boto3.client('s3')

def handler(event, context):

    bucket = event['Records'][0]['s3']['bucket']['name']
    key = event['Records'][0]['s3']['object']['key'] 
    download_path = '/tmp/{}{}'.format(uuid.uuid4(), key)

    s3_client.download_file(bucket, key,download_path)

    csv_data = csv.reader(file( download_path))

    with conn.cursor() as cur:
        for idx, row in enumerate(csv_data):

            logger.info(row)
            try:
                cur.execute('INSERT INTO target_table(column1, column2, column3)' \
                                'VALUES("%s", "%s", "%s")'
                                , row)
            except Exception as e:
                logger.error(e)

            if idx % 100 == 0:
                conn.commit()

        conn.commit()

    return 'File loaded into RDS:' + str(download_path)
_

私は関数をテストしており、ファイルがアップロードされるとS3がイベントを送信し、LambdaがRDSインスタンスに接続して通知を受け取ります。バケット名が「bucket-file」でファイル名も正しいことを確認しました。問題は、関数がs3_client.download_file(bucket, key,download_path)の行に達したときであり、lambbaの有効期限に達するまでスタックします。

それが言うログを見て:

_[INFO]  2017-01-24T14:36:52.102Z    SUCCESS: Connection to RDS mysql instance succeeded
[INFO]  2017-01-24T14:36:53.282Z    Starting new HTTPS connection (1): bucket-files.s3.amazonaws.com
[INFO]  2017-01-24T14:37:23.223Z    Starting new HTTPS connection (2): bucket-files.s3.amazonaws.com
2017-01-24T14:37:48.684Z Task timed out after 60.00 seconds
_

また、VPC内で作業している場合、S3バケットにアクセスするには、このサブネットのS3へのアクセスを許可するVPCエンドポイントを作成する必要があることも読みました。私もこの解決策を試しましたが、結果は同じです。

いくつかのアイデアをいただければ幸いです。

前もって感謝します!

11
ainsausti

やっとできた!

問題はVPCの問題でした。先ほど述べたように、VPCエンドポイントを作成してSPCサービスを私のVPCからアクセスできるようにしましたが、ルートテーブルが誤って構成されていました。

したがって、結論として、ラムダを使用するVPCで作業していて、S3にアクセスする場合は、VPCエンドポイントを作成する必要があります。また、VPCの外部にある他のインターネットサービスにアクセスする場合は、NAT Gatewayを構成する必要があります。

3
ainsausti