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

LambdaのCustom RutimeとしてStackery社から提供されている環境を拡張し、PHPのExtensionを組み込んでみました!! これで本番環境のLambdaもステップ実行可能に!!
2018.12.03

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

本番環境のLambdaをステップ実行してデバッグすることは可能ですが、実行時間に応じて課金が発生する点についてご留意下さい。

はじめに

サーバーレス開発部@大阪の岩田です。 re:Invent2018で発表されたLambdaのCustom Runtime機能でPHPを動かしてみたので、手順等をご紹介します。

Custom Runtime機能については既に色々とブログが公開されているので、下記のブログもご参照下さい。

【アップデート】 もう言語で悩まない!AWS LambdaでCustom Runtimeが利用できるようになりました! #reinvent

AWS Lambda の新機能でサーバーレス・シェルスクリプト! カスタムランタイムのチュートリアルを動かしてみた #reinvent

AWS LambdaのカスタムランタイムでC++を動かしてみた #reinvent

AWS LambdaのCustom RuntimeでRustを実行してみた #reinvent

やること

  • LambdaのCustom Runtime機能を使いPHPのコードを動かす
  • PHPのCustom Runtime環境を拡張し、Extension(Xdebug)を導入する
  • 導入したExtension(Xdebug)を使ってLambdaのコードをリモートデバッグする

やってみる

まずは簡単なPHPのコードを動かしてみます。

PHPのCustom RuntimeはStackery社から提供されています。 Stackery社のGitHubリポジトリのREADMEを参考に進めていきます。

コード

ディレクトリ構成は下記のような構造になります。

└── lambda
    ├── src
    │   └── php
    │       └── index.php
    └── template.yml

SAMテンプレートです

template.yml

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
      Layers:
        - !Sub arn:aws:lambda:${AWS::Region}:887080169480:layer:php71:4
      Events:
        api:
          Type: Api
          Properties:
            Path: /{proxy+}
            Method: ANY

Layersで指定しているarn:aws:lambda:${AWS::Region}:887080169480:layer:php71:4がStackery社から提供されているCustom Runtimeのレイヤーになります。

PHPのソースコードです

index.php

<?php
phpinfo();

デプロイ

まずはここまでで一旦デプロイしてみます。

sam package --template-file template.yml --output-template-file serverless-output.yaml --s3-bucket <S3バケット名>
sam deploy --template-file serverless-output.yaml --stack-name custom-runtime-php71  --capabilities CAPABILITY_IAM

テスト

作成されたAPI GW のエンドポイントにブラウザからアクセスしてみます。

動きました!!

Extensionの導入と有効化

次にExtensionの導入と有効化を行います。

READMEによると2018/12/2現在下記のExtensionについてはCustom Runtimeのレイヤー内に導入済みとのことです。

bz2.so
calendar.so
ctype.so
curl.so
dom.so
exif.so
fileinfo.so
ftp.so
gettext.so
iconv.so
json.so
phar.so
posix.so
shmop.so
simplexml.so
sockets.so
sysvmsg.so
sysvsem.so
sysvshm.so
tokenizer.so
wddx.so
xml.so
xmlreader.so
xmlwriter.so
xsl.so
zip.so

上記のExtensionについてはデプロイパッケージ内のphp.ini

extension=json.so

のような記述を追加することで有効化することが可能です。

このブログでは、デフォルトでは導入されないExtensionを導入してみます!!

Extension追加の手順

PHPのExtension追加手順はざっくり下記の通りになります。

  • Extensionをビルドする
  • ビルドしたExtensionを含むレイヤーを追加

今回はPHPのExtensionの1つXdebugを追加してみます。

Xdebugのビルド

Extensionのビルドにはlambci/lambda:build-nodejs8.10のDockerイメージを使うのが推奨されている様です。 PHPのビルドなのになんでNode?!と思いますが依存関係のあるライブラリが良い感じに揃っていたのではないかと推測されます。

下記のDockerfileを使ってxdebugをビルドします。

FROM lambci/lambda:build-nodejs8.10
RUN yum install -y php71-devel \
  && curl -fsSL 'https://xdebug.org/files/xdebug-2.6.1.tgz' -o xdebug-2.6.1.tar.gz \
	&& mkdir -p xdebug-2.6.1 \
	&& tar -xf xdebug-2.6.1.tar.gz -C xdebug-2.6.1 --strip-components=1 \
  && cd xdebug-2.6.1 \
  && phpize \
  && ./configure --enable-xdebug \
  && make \
  && make install
docker build -t cm-iwata/lambda_php_with_xdebug .

ビルドできたらコンテナを起動し、xdebug.soを抽出します。

docker cp <コンテナ名>:/usr/lib64/php/7.1/modules/xdebug.so .

Xdebug用のレイヤーを作成

ビルドしたxdebug.soをZIPに固めてXdebug用のレイヤーを作成します。

PHPのExtensionを読み込ませるためには、レイヤーが解凍された際にxdebug.soが/opt/lib/php/7.1/modules/xdebug.soに配置される必要があります。 空のディレクトリをいくつか作成し、下記の様なディレクトリ構成でZIPに固めます。

.
└── lib
    └── php
        └── 7.1
            └── modules
                └── xdebug.so

ZIPが作成できたらマネジメントコンソールの「レイヤーの作成」からレイヤーを追加します。

先ほど作成したZIPファイルをアップロードしてレイヤを作成します。

Lambdaにレイヤーを追加

レイヤーが作成できたら最初にデプロイしたLambdaのレイヤーにXDebugのレイヤーを追加します。

php.iniを追加

Xdebugを読み込ませるためにデプロイパッケージにphp.iniを追加します。 ディレクトリ構成はこのようになります。

└── lambda
│   ├── src
│   │   └── php
│   │       ├── index.php
│   │       └── php.ini
│   └── template.yml

php.iniの中身は下記のようになります。

php.ini

zend_extension='xdebug.so'

Xdebugなのでextension=...ではなくzend_extension=...という記述になります。

php.iniが追加できたら再度デプロイしておきます。

確認してみる

ここまで出来たら再度API GWにアクセスし、phpinfoの出力を確認します。

Xdebug関連の出力が追加されました!! 無事Extensionが読み込めていそうです。

リモートデバッグしてみる

仕上げとしてXdebugを使ってAWS上で稼働するLambdaをローカルのEclipseからリモートデバッグしてみます。 Lambdaからローカルマシンに対してXDebugのセッションを張ってもらう必要があるので、踏み台用のEC2を立ててSSHのトンネルを利用してリモートデバッグします。

踏み台構築

適当にEC2を構築し、セキュリティグループの設定でSSHと9000番ポート(Xdebug用)へのアクセスを許可します。 EC2が起動したら、SSHのトンネル用にsshdの設定を変更します。  

sshd_config

GatewayPorts yes

設定ファイルを変更したらリロードしておきます。

systemctl reload sshd

php.iniを修正

LambdaがEC2に対してXdebugのセッションを張るようにphp.iniの設定を変更します。 ついでにリモートデバッグ周りの設定も追加します。 SSHトンネルのオーバーヘッド等を考慮し、念のためタイムアウトを長めに取っています。

php.ini

zend_extension='xdebug.so'
xdebug.remote_host = <踏み台EC2のGIP>
xdebug.remote_autostart = true
xdebug.remote_enable = true
xdebug.remote_timeout = 5000

修正できたら再度デプロイしておきます。

SSHのトンネルを掘る

ローカルマシンからEC2にSSHのトンネルを掘ります

ssh -g -i <秘密鍵のパス>  -R *:9000:127.0.0.1:9000 ec2-user@<踏み台EC2のGIP>

これでLambda<->EC2<->ローカルマシンという流れでXdebugの通信が可能になります。

確認してみる

リモートデバッグできるように適宜IDEを設定した後にAPI GWのエンドポイントにアクセスすると・・・

止まりました!! 環境変数の中身がバッチリ確認できています。

まとめ

Lambdaカスタムランタイム環境(PHP用)にExtensionを追加導入する手順をご紹介しました。 Xdebugはネタ気味ですが、OPcacheやAPCuを導入したいと言うニーズは非常に多いと思います。 Xdebugの導入と手順は同じなので、ぜひ参考にしてみて下さい!!