LambdaのCustom Runtime のPHPを使って ALB + VPC Lambdaの構成でRDSに接続してみた

西田@大阪です。

この記事は AWS Lambda Custom Runtimes芸人 Advent Calendar 2018 の 6日目 です。

今回は Custom Runtime のLambda (PHP) を使ってALB + VPC Lambdaの構成でRDSのデータをブラウザに表示するところまでやってみました

(本当はWordPressを動かすところまでやりたかったのですが時間が足りず断念しました><)

構成図

前提

RDS, VPC, ALB はすでに作成済みで、RDSに接続できるSubnet, Security Groupがあるものとします

Lambda

ALBのHeath Checkを通すために提供されているPHP Custom Runtimeを修正

STACKERYより提供されているこちらのLayerを使って作成します

そのままですとALBのHealth Checkに失敗するのでリポジトリをCloneし、以下のファイルを修正をします

bootstrap

修正する箇所

  • response['statusDescription']にHTTP のReason-Phraseまで含めた値を設定します
  • response['isBase64Encoded']にfalseを設定します

差分

   curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$response) {
-    if (preg_match('/HTTP\/1.1 (\d+) .*/', $header, $matches)) {
-      $response['statusCode'] = intval($matches[1]);
+    if (preg_match('/HTTP\/1.1 ((\d+) .*)/', $header, $matches)) {
+      $response['statusCode'] = intval($matches[2]);
+      $response['statusDescription'] = trim($matches[1]);
+      $response['isBase64Encoded'] = false;
       return strlen($header);
     }

修正後のコード

curl_setopt($ch, CURLOPT_HEADERFUNCTION, function ($ch, $header) use (&$response) {
  if (preg_match('/HTTP\/1.1 ((\d+) .*)/', $header, $matches)) {
    $response['statusCode'] = intval($matches[2]);
    $response['statusDescription'] = trim($matches[1]);
    $response['isBase64Encoded'] = false;
    return strlen($header);

修正したら make コマンドを実行し作成されたzipファイルを使い以下の記事を参考にLambdaのLayerを作成します

[検証]LambdaのLayer機能を早速試してみた #reinvent

DB接続に必要なPDOを追加するLambdaのLayerを作成

本番環境のLambdaをステップ実行!! Lambdaのカスタムランタイム環境(PHP用)にExtensionを組み込んでみた #reinvent

こちらを参考にすすめていきます

以下のシェルをbuild.shという名前で作成します

#!/bin/bash
yum install -y php71-mysqlnd

mkdir -p /tmp/layer/
cd /tmp/layer

mkdir -p lib/php/7.1/modules

for lib in mysqli.so mysqlnd.so pdo_mysql.so pdo.so; do
  cp "/usr/lib64/php/7.1/modules/${lib}" lib/php/7.1/modules
done

zip -r /opt/layer/php71_pdo.zip .

作成したシェルを使ってLayerにするzipファイルを作成します

docker run --rm -v $(ROOT_DIR):/opt/layer lambci/lambda:build-nodejs8.10 /opt/layer/build.sh

作成されたzipファイルを使って新しくLambdaのLayerを作成します

VPC Lambdaに必要な IAM Roleを作成

AWSLambdaVPCAccessExecutionRole がアタッチされた IAM Roleを作成します

Lambdaを作成

全体のファイル構成です

├── src
│   └── php
│       ├── php.ini
│       └── index.php
└── template.yaml

src/php/php.ini

mysql操作に必要なライブラリを有効にします

extension=mysqlnd.so
extension=mysqli.so
extension=pdo.so
extension=pdo_mysql.so

src/php/index.php

動作確認用のコードです。testという名前のテーブルで、id, nameという列がありすでにデータが登録されている想定です

※ ALBのHealth Checkを通すためにContent-Typetext/html; charset=utf-8と設定する必要がありました

<?php
header('Content-Type: text/html; charset=utf-8');
$pdo = new PDO('${RDSのホスト名};dbname=${DB名};charset=utf8', '${ユーザー}', '${パスワード}',
        array(PDO::ATTR_EMULATE_PREPARES => false));
$stmt = $pdo->query("SELECT * FROM test");
?>

<html>
    <ul>
    <?php while($row = $stmt->fetch()): ?>
        <li><?php echo 'id:' . $row['id'] . ', name: ' . $row['name'] ?></li>
    <?php endwhile; ?>
    </ul>
</html>

template.yaml

SAMのテンプレートです

AWSTemplateFormatVersion: 2010-09-09
Description: My PHP Application
Transform: AWS::Serverless-2016-10-31
Resources:
  phpserver:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: !Sub ${AWS::StackName}-phpserver
      Description: PHP Webserver
      CodeUri: src/php
      Runtime: provided
      Handler: index.php
      MemorySize: 3008
      Timeout: 30
      Tracing: Active
      Role: ${LambdaにアタッチするIAM Role のarn}
      Layers:
        - ${PHP Layerのarn}
        - ${PHP PDO Layerのarn}
      VpcConfig:
        SecurityGroupIds:
          - ${RDSに接続できるSecurity Group}
        SubnetIds:
          - ${RDSに接続できるSubnet}
      Events:
        api:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

デプロイ用のS3に作成しSAMを実行しLambdaをデプロイします

sam package --template-file template.yaml --output-template-file serverless-output.yaml --s3-bucket ${S3バケット}
sam deploy --template-file serverless-output.yaml --stack-name my-first-serverless-php-service --capabilities CAPABILITY_IAM

ALB

ALBのバックエンドにLambdaを選択してみた! #reinvent

こちらを参考ALBを作成し、ターゲットグループに作成したLambdaを登録します

注意点として、STACKERYより提供されているこちらのLayerはmultiValueHeadersを返すようになってるのでALB側も複数値のヘッダーを有効にしないと Health Check に失敗します

ブラウザで確認

データベースに登録されている内容が表示されれば成功です

さいごに

WordPressを動かそうとしていろいろ試行錯誤した結果を残して置きます。

また時間を作ってWordPressを動かすところまでやりたいと思います。