LambdaからS3やRDSを操作した時の備忘録です。サーバーレスにS3イベントをフックして画像処理を走らせたり、RDSのMySQLを操作するLambda Functionを作りたいと思いました。
画像アップロートのタイミングでサムネイルを生成させたり、画像検索データベースを自動構築してくれたら非常に楽ですよね。
目次
構成図と結論
考えている構成図は以下のようなイメージです。結論を先に言ってしまうと、S3のPutイベントをVPCに置いたLambda functionが受けて、LambdaからRDSに接続、またS3に対してはVPCエンドポイント越しからアクセスする事にしました。
VPCに置くべきか?
ここに至るまでに躓いたのが、LambdaをVPCへ置くか否かでした。VPCの外に配置したLambdaからはRDSにアクセスできなくなり、VPCに置くと今度は逆にS3へアクセスできなくなります。
各サービス | Lambda (No VPC) | Lambda on VPC |
S3 | ◯ | X |
RDS | X | ◯ |
S3にアクセスできないのはLambda on VPCがインターネットにアウトバンドすることができない仕様だからです。また、RDSへセキュアにアクセスするためには、EC2からRDSにアクセスする時と同じように、VPCのサブネットからアクセスしたいところです。
従来の回避策
前述の問題を回避するために、いくつか方法が提案されていました。まず最初に、Lambda VPCがサポートされる以前ではRDSを開放(インバウンド 0.0.0.0/0)する方法が紹介されていました。
AWS LambdaでRDS(MySQL)に接続してみた | Qiita
https://qiita.com/Keisuke69/items/cba4b501e91da95188f8
さらに踏み込んで、RDSに安全にアクセスするために、SecurityGroupにLambdaのIPアドレスが追加し、LambdaからセキュアにRDSへアクセスする方法も紹介されていました。
LambdaからセキュアにRDSに接続する | ナレコムAWSレシピ
https://recipe.kc-cloud.jp/archives/7388
また、Lambda VPCからS3にアクセスする手段としてはNAT InstanceまたはNAT gatwayを介して、外部からからS3にアクセスする方法もあるようです。
VPC Lambdaからs3へアクセスする | Qiita
https://qiita.com/ijin/items/94c0bc4b8f6f5e77a591
いずれにしても、構築の手間や維持費用が多く掛かる事がわかりました。
VPC Endpoint for S3を使う
そこで今回はVPCエンドポイントを使うことにしました。VPCエンドポイントを作っておくとAWS網内でS3へのトラフィックを終端できるようになり、Lmabda VPCからS3に接続。もちろんRDSも使えるようになります。VPCエンドポイントについては以下が参考になりました。
AWSでS3を使う場合は必ずVPCエンドポイントも作成しておくクセをつけましょう。| 株式会社ビットクリア
https://www.bitclear.co.jp/vpcendpoint/
NAT Instanceとの違いも分かりやすかったです。VPCからアクセスできるのはいいですね。
S3 VPCエンドポイントを利用するメリット | Qiita
https://qiita.com/SatoHiroyuki/items/b611485b6ec736e9076f
ここで注意点としてはVPC Endpoint for S3のリージョンはVPCを扱うリージョンに制限されてしまいます(VPCに置いたLambdaが東京リージョンならばS3も東京リージョンのみとなる)。あと、aws-sdk
からS3に接続する際にはS3認証のVersion4を指定する必要があるそうです。具体的にはsignatureVersion
を指定しないとlambdaからのアクセスが拒否されタイムアウトしてしまいます。
VPC Private Network 内の Lambda Function から boto3 で S3 を操作する | Qiita
https://qiita.com/sokutou-metsu/items/47c00bb381e1b103e878#_reference-734313aae611eb61bfe7
ということで
最終的なlambda functionはざっくりと以下のようになりました。
index.js1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| 'use strict';
let im = require('imagemagick'); let fs = require('fs'); let mysql = require('mysql'); let aws = require('aws-sdk'); aws.config.update({ region: 'ap-northeast-1' });
let s3 = new aws.S3({ apiVersion: '2006-03-01', signatureVersion: 'v4' });
exports.handler = (event, context, callback) => { const bucket = event.Records[0].s3.bucket.name; const key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' ')); const params = { Bucket: bucket, Key: key }; s3.getObject(params, (err, data) => { if (err) { console.log(err); callback(err); } else { let conn = mysql.createConnection({ host : 'your-db.***.ap-northeast-1.rds.amazonaws.com', user : 'your-user', password : 'your-password', port : '3306', database : 'your-db' }); conn.connect(); conn.query('**your query**', (err, rows, fields) => { if(err){ console.log('[mysql query error] ' + err); }else{ console.log('[mysql query success] rows:' + JSON.stringify(rows)); } conn.end(function(err){ console.log('[mysql end] ' + err); callback(null,rows); }); }); } }); };
|
リサイズ処理とDB処理の順番を考え中です。リサイズ処理は以下が参考になりました。
AWS Lambdaを使ってS3にアップロードされた画像をリサイズする | Qiita
https://qiita.com/awm-kaeruko/items/00d92cf2484405fb5579
Lambdaのコンソール画面ではnode.js 4.3より古いBlue printが削除されてしまった事情から、従来あったimage-processing-service
が参照できなくなっています。あとQiitaのサンプルをそのまま記述すると無限ループになるので要注意です(^^;)一度タイムアウトまで無限リサイズさせてしまいましたw
最後に
VPC Endpointを活用することで、LambdaからS3やRDSを操作することができるようになりました。roleでアクセスを絞り込んでいけば、安全なものが構築できるかなぁと思っていますが、いかがでしょうか。