やりたいこと
AWSのLambdaを使って、EC2上のMySQLのdumpを取得後、S3にアップロードする。
ポイント
VPC内にあるEC2とVPCの外にあるS3のこの両方にアクセスが必要
というのがハマりポイントでした。
(これまでLambdaを使ったことなかったのであまりわかってなく、簡単にいけるだろうと思って、何も考えずにやってそれ以外の箇所でも結構ハマりました)
構成図
最終的にはこのようになります。
手順
1.Lambda Functionを作る
コンソールからLambdaを選択肢、Create Functionで作る。
- Runtimeは、今回Node.jsを選択します。(ここはお好みで)
- Roleは今回新しくLambda用に作成する形にしています。
2.Lambda スクリプトを実装する
2-1.スクリプト実装
'use strict';
const aws = require('aws-sdk');
const mysqldump = require('mysqldump');
const fs = require('fs');
const s3 = new aws.S3({ apiVersion: '2006-03-01' });
require("date-utils");
var date = new Date();
var timeStamp = date.toFormat('YYYYMMDDHH24MISS');
const dumpFileName = 'dump_' + timeStamp + '.sql';
const dumpPath = '/tmp/' + dumpFileName;
exports.handler = async (event, context) => {
console.log("start mysqldump");
console.log('dumpFileName:' + dumpFileName);
const resultDump = await mysqldump({
connection: {
host: process.env.DBHOST,
user: process.env.DBUSER,
password: process.env.DBPASS,
database: process.env.DBNAME,
},
dumpToFile: dumpPath, // Lambda上のサーバにdumpファイルを一時的に置く場所
});
console.log("start s3 upload");
var params = {
Bucket: process.env.BUCKETNAME,
Key: process.env.BUCKETKEY + dumpFileName,
Body: fs.readFileSync(dumpPath)
};
var resultPutS3 = await s3.putObject(params).promise();
console.log(resultPutS3);
}
2-2.環境変数設定
上記スクリプト内で process.env.xxxとなっていた箇所です。
Configuration → Enviroment variablesから設定できます。
※Dateのタイムゾーンは日本にしておきたいため、TZでセットしています。(ここは自身の環境に合わせてTZをセットすると良いと思います)
実行してみる
Codeから、Deployを実施したのち、Testを選択すると実行できます。
結果
Runtime.ImportModuleError
が発生します
原因
awsのライブラリ以外は、ローカルからアップロードする必要があるため
(Lambda初心者の私はこれを知らなかったため、最初はなぜエラーなのかわかりませんでした。。)
2-3. スクリプトに必要なライブラリをアップロード
2-3-1.ローカルでスクリプトとライブラリをZIPで固めたものを作成する
Lambda上のファイルのコピーをlocalに作成します。
-rw-rw-r--@ 1 yukiyoshimura admin 1090 9 12 14:19 index.js
[yukiyoshimura sample]$
aws以外のライブラリをnpmでダウンロードします。
npm install mysqldump
npm install date-utils
zipで固める
[yukiyoshimura sample]$ zip -r ../sample.zip ./
2-3-2.lambdaへアップロードする
以下からアップロードします
アップロード後
node_modulesディレクトリに必要なライブラリが入った状態でlambdaコンソール上で確認できればOK⬇︎
再び実行してみる
Time out するけど、 Runtime.ImportModuleError
が解消されていれば一旦OKです。
3.LambdaがEC2にアクセスできるようにする
3-1. Lambdaに紐づけているroleに権限を付与する
以下をセットしておきます。
AWSLambdaVPCAccessExecutionRole
AmazonS3FullAccess
3-2. lambdaをVPCに登録する
- VPCは、アクセスしたいEC2のVPCと同じものをセットします。
- Subnetは、Lambda用にPrivate Subnetを作ったのでそれをセットしました。
- Security groupsは、デフォルトのものをそのままセットします。(インバウンドなし、アウトバウンドAll)
4.EC2に対してmysqldumpができるようにする
EC2に関連付けしているSecurity groupのインバウンドに以下を追加する。
Sourceのところは、今回、LambdaでセットしたSecurity Groupと同じものをセットしました。
5.VPCに登録したlambdaからS3にアクセスできるようにする
S3アクセス用のVPC Endpointの作成
VPC → Endpoints → Create Endpoint
S3のエンドポイントを作成します。今回は、Gatewayで作成しています。
VPCは、EC2と同じもの。
routetablesは、Lambdaでセットしたsubnetが紐づいているものを指定する。
https://docs.aws.amazon.com/ja_jp/vpc/latest/privatelink/vpce-gateway.html
endpointを作成すると、指定したroutetablesにS3へのendpointが追加されます。
その他
lambdaのタイムアウトはデフォルト3秒なので10秒くらいにしておくと良い
ネットワークのエラーでタイムアウトなのか、処理時間でのタイムアウトなのか見分けがつかない時がありました。
今回のdumpしてからS3へのアップロードだと3秒では終わらなかったので10秒にしました。
Configuration → General configuraton から変更できます。
MySQLに外から繋ぐため、外部からの接続を許可しておく必要があります
この辺りを参考に。
自分はlambda接続用のユーザを作成しました。
https://qiita.com/yoshiokaCB/items/df4ae185be7cbc4f03ac
まとめ
VPC内へのアクセスと外のアクセスが必要だったため、色々設定が必要でしたが、何とかできました。
VPC Endopointを利用する以外にもNATゲートウェイを使う方法もあるようです。
参考にしたページ
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/configuration-vpc.html
https://devlog.arksystems.co.jp/2018/04/04/4807/