初めまして、4月より入社しました新入社員の笹山です。
前職はC#を主に触っていましたが、ギークフィードに入社してからAWSやWebフレームワークなど初めて触れるものが多く、刺激的な体験をさせてもらっています。
さて、初投稿のわりにニッチな課題解決かもしれませんが、今回はタイトルの通り「LambdaをVPC内で実行し、EC2のMySQLにアクセス」したいと思います。
最小構成なプロトタイプを作成する場合などに今回のケースのようなことがあるかもしれないので、備忘録として残しておきたいと思います。
目次
やりたいこと
EC2のMySQL(MariaDB)にLambdaからアクセスし、特定のテーブルをSELECTして結果を取得したいと思います。
セキュリティの観点から、EC2が所属するVPCのセキュリティグループにおいて、MySQLへのアクセス許可は最小限に設定したいと思います。
そのためLambdaはEC2と同一のVPC内で実行する必要があります。
そもそもアンチパターン!?
以前まで、LambdaがEC2やRDSなどのVPC内オブジェクトにアクセスする場合、Lambdaは各々ENI(Elastic Network Interface)を作成、アタッチする必要がありました。
しかしこの方法には以下の問題があり、LambdaをVPC内で実行すること自体がアンチパターンとされていました。
- コールドスタート時の遅延が非常に大きい
- ENIがIPアドレスを消費する
幸い2019年にこれらの問題は解決され、現在ではアンチパターンとはされていません。
※とはいえ、Lambda + RDBMSの構成には同時接続数の問題もあるので、ユースケースに応じて構成は考える必要があります。
なぜAWS LambdaとRDBMSの相性が悪いかを簡単に説明する
構成
今回の構成要素は以下の通りです。
AWS EC2 | Amazon Linux |
AWS Lambda | Node.js |
DB | MariaDB |
EC2の準備
テストデータの作成
EC2のMariaDBで以下のユーザとテーブルをテスト用に作成します。
今回はユーザーとしてlambda_sample、テーブルとしてsample.userを作成しました。
なお、Lambdaは基本的には固定IPを持たないため、接続用のユーザーのホストにはワイルドカードを指定しておきます。(固定IP持たせる方法もあるらしいですが)
1 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 |
MariaDB [(none)]> create user lambda_sample@'%' identified by 'password'; MariaDB [(none)]> grant all on sample.* to lambda_sample@'%' identified by 'password'; MariaDB [(none)]> select host, user, password from mysql.user; +--------------------------------------------------+---------------+-------------------------------------------+ | host | user | password | +--------------------------------------------------+---------------+-------------------------------------------+ | localhost | root | | | % | lambda_sample | *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | +--------------------------------------------------+---------------+-------------------------------------------+ MariaDB [(none)]> create database sample; MariaDB [(none)]> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | mysql | | performance_schema | | sample | +--------------------+ MariaDB [(none)]> create table sample.user (id int, name varchar(10)); MariaDB [(none)]> select * from sample.user; +------+---------------+ | id | name | +------+---------------+ | 1 | Tanaka Taro | | 2 | Tanaka Jiro | | 3 | Tanaka Saburo | +------+---------------+ |
セキュリティグループの設定
EC2インスタンスでMySQLへのアクセスを許可します。
VPCおよびサブネットに関してはEC2作成時のデフォルトをそのまま使用します。
1.EC2インスタンスの概要からセキュリティグループのコンソールを開きます。
2.インバウンドルールのタブを開き、「インバウンドルールの編集」をクリックします。
3.「ルールを追加する」でタイプ[MYSQL/Aurora]のルールを追加します。
このとき、ソースを「0.0.0.0」などにすると不正な場所からのアクセスも許可してしまうので、同一VPCからのアクセスのみ許可するように自身のセキュリティグループIDを指定します。
Lambdaの準備
Node.js用MySQLモジュールのインストール
続いて、Lambda関数で使用するNode.jsのMySQLモジュールをローカル環境でインストールします。
私はWSLのUbuntu 18.04 LTSでNode.jsのインストールからしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// node.js, npmのインストール $ sudo apt update $ sudo apt install nodejs $ sudo apt install npm // 確認 $ node --version $ npm --version // 任意のディレクトリでプロジェクトを初期化 $ cd /home/user/nodetest $ npm init // mysqlモジュールのインストール $ npm install mysql |
完了すると以下のようにMySQL関連のモジュール群ができるので、後でLambdaで読み込むためにこれをzip化しておきましょう
WSLのファイルシステムへは、Windowsエクスプローラーから「\\wsl$」でアクセスできるので、今回はエクスプローラーからzipしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
$ tree -L 2 . ├── node_modules │ ├── bignumber.js │ ├── core-util-is │ ├── inherits │ ├── isarray │ ├── mysql │ ├── process-nextick-args │ ├── readable-stream │ ├── safe-buffer │ ├── sqlstring │ ├── string_decoder │ └── util-deprecate ├── package-lock.json └── package.json |
Lambda関数の作成
まずはLambdaをVPC内で実行するために以下の準備をしておきます。
- Lambdaの実行ロールに「AWSLambdaVPCAccessExecutionRole」というポリシーをアタッチ。
- LambdaのVPCの項目を編集してターゲットとなるEC2のVPC, サブネット、セキュリティグループを設定。
関数には先ほど作成したMySQLモジュール群のzipをアップロードしておきます。
では、index.jsを新規作成して処理を書いていきます。
1 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 |
const mysql = require('mysql'); // 接続プロパティを設定(値は環境変数に保存) const conn = mysql.createConnection({ host : process.env.DB_HOST, // EC2インスタンスのプライベートIPアドレス port : process.env.DB_PORT, // 3306 database : process.env.DB_NAME, // sample user : process.env.DB_USER, // lambda_sample password : process.env.DB_PASS // password }); exports.handler = function(event, context){ // クエリ const myquery = `select * from user where id = ?;`; // バインド変数用の値 var id = '1'; // 接続、クエリの発行(バインド変数の値は第二引数に配列を渡す) conn.query(myquery, [id], function(err, rows, fields) { if(err){ context.done(null, err); } // 結果の出力 context.done(null, rows); }); }; |
実行
できました!
まとめ
「VPC LambdaでEC2のMySQL(MariaDB)にアクセスする」ために大まかに以下のことやってきました。
- EC2のセキュリティグループでMySQL用のポートを開ける
- LambdaにVPC実行用ポリシーのアタッチ、およびVPCの設定をする
今後も使うかどうかはなんとも分かりませんが、AWSもNode.jsも初心者なので色々勉強にもなりました。
- Remix SPAモードで静的WebサイトをGithub Pagesにデプロイする - 2024-11-05
- LangGraph入門!LLMアプリ開発ライブラリのQuick StartをAmazon Bedrockでやってみる - 2024-10-10
- AWS ChatbotでSlackからAmazon Bedrock Agentにアクセスしてみる - 2024-09-18
- Amazon Bedrockの新機能「Prompt flows」を試してみた - 2024-07-11
- Amazon ConnectのZero-ETLがGAされたので試してみた - 2024-06-03