Check! AWS IoT ボタンで PHP on AWS Lambda を動かす小ネタ

こんばんは、[cloudpack][] の @dz_ こと[大平かづみ][]です。

Prologue - はじめに
====
友達から re:Invent のお土産でもらった [AWS IoT ボタン](https://aws.amazon.com/iot/button/ "AWS IoT ボタン")を使って、 [AWS Lambda](https://aws.amazon.com/jp/lambda/ "AWS Lambda (サーバーレスでコードを実行・自動管理) | AWS") 、しかも **PHP** を動かしてしまおうという小ネタです。

(PHP on AWS Lambda は、[神宮前Geek Night！！](http://connpass.com/event/23126/ "神宮前Geek Night！！ - connpass")のLTネタ探ししてるときに、「やってる人いるからできるよー」と、@yoshidashingo さんにそそのかされました(笑) でもいい経験になりました！ありがとうございます！)

ちなみに、 Lambda Advent Calendar に投稿しようと思ったのですがすでに満員だったので、 [AWS IoT Advent Calendar 2015](http://qiita.com/advent-calendar/2015/awsiot) に投稿してみました。小ネタですが済みません！

早速、準備
====
さっそく AWS IoT ボタン + PHP on AWS Lambda の環境を作っていきます。

1. AWS IoT ボタンの準備（アクティベーション、サンプル動作確認）
2. AWS Lambda で PHP を動かす ← **難関!!**
3. AWS IoT ボタンから PHP on AWS Lambda を起動！

AWS IoT ボタンの準備
====
今この時点で、すでに AWS IoT ボタンのアクティベーションとサンプルの動作は終わっています。手順にまとめようと思いましたが、あまりにも簡単ですし、他に情報がたくさんありますので、割愛します。

AWS IoT ボタンって？
----
念のため、AWS IoT ボタンについて軽く説明しておきます。

この AWS IoT ボタンは、デバイス固有のコードを書くことなく、AWS の IoT プラットフォームである [AWS IoT](https://aws.amazon.com/iot/ "AWS IoT") を利用できる、ベータ版プロトタイプ端末です。

AWS IoT ボタンは、WiFi経由でインターネットに接続でき、初回は同梱されているアクティベーションコードとDSN（※）を入力して自身のAWSアカウントと紐づけます。（※ボタン端末の裏側に記載されている Device Serial Number）

WiFi接続や、アクティベーションとサンプルを動かすチュートリアルについては、「[ご利用開始にあたって](https://aws.amazon.com/jp/iot/button/ "ご利用にあたって - AWS IoT ボタン")」を参照してみてください。

AWS Lambda で PHP を動かす
====
それでは、AWS Lambda で PHP を動かす準備に入ります。

ご存知の通り、AWS Lambda では、Node.js, Java, Python が動きます。だがしかし、 **PHP はサポートされていません！**

とはいえ、動いているのは Linux です。試しに、Lambda の動いている環境で `uname -a` を実行してみると、 `Linux ip-10-0-74-236 3.14.48-33.39.amzn1.x86_64 #1 SMP Tue Jul 14 23:43:07 UTC 2015 x86_64 x86_64 x86_64 GNU/Linux` と返却されます。

また、Lambda は、単一のソースコードファイルだけでなく、zipにまとめてアップロードすることで、複数ファイルやディレクトリを含んだ環境を動かすこともできます。

これらの仕組みを利用して、 @yoshidashingo 氏から教えてもらったこちらの資料を参考に、Linuxで動作するPHPの実行ランタイムをアップロードして、Lambda でも PHP を動かしてしまおうという魂胆なのです！

<iframe src="//www.slideshare.net/slideshow/embed_code/key/LwW6qUDMdmREXC" width="595" height="485" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC; border-width:1px; margin-bottom:5px; max-width: 100%;" allowfullscreen> </iframe> <div style="margin-bottom:5px"> <strong> <a href="//www.slideshare.net/oh-sky/php-55467177" title="PHPという概念が存在しない退屈な世界" target="_blank">PHPという概念が存在しない退屈な世界</a> </strong> from <strong><a href="//www.slideshare.net/oh-sky" target="_blank">Yoshihiro Ohsuka</a></strong> </div>

さて、ここで胆となるPHP実行ランタイムをどう入手するかというと、Lambda の環境で動くように、ビルドして作成しなければなりません。

1. Linux 環境を用意する
2. Lambda 環境で動く PHP をビルドする
3. Lambda用のzipアーカイブを作成する
4. Lambda にアップロードする

Linux環境を用意する
----
作業環境が Windows なので、別途 AWS EC2 で Linux 環境を作ります。手っ取り早く、Amazon Linux を利用しました。

Lambda環境で動く PHP をビルドする
----
Lambda のコードが配置されるディレクトリは、 `/var/task` です。なので、Linux 環境に `/var/task` を作成してそこで PHP が動くようにビルドします。

PHP のビルドについては、こちら「[PHP 5.5.1をインストールする（ソースからビルドする方法）](http://momoto.github.io/blog/2013/07/31/guide-to-compiling-and-installing-php-5-dot-5-1/ "PHP 5.5.1をインストールする（ソースからビルドする方法） - momoto.github.io")」を参考にしました。とても分かりやすかったです。

ちなみに、リリースされたばかりの PHP 7 で試してみました！

```bash
# Lambda の実行環境と同じディレクトリ構成を作成する
mkdir /var/task

# PHP配置用ディレクトリを作成する
mkdir /var/task/php

# ビルドに必要なパッケージをインストールする
yum install gcc
yum install make

# PHP のソースコードをダウンロードする
wget http://jp2.php.net/get/php-7.0.0.tar.gz/from/this/mirror -O php-7.0.0.tar.gz

# 展開して、展開先ディレクトリへ移動する
tar xfz php-7.0.0.tar.gz -C /usr/local/src/
cd /usr/local/src/php-7.0.0/

# ビルドファイルを作成する (インストール先は /var/task/php 、追加機能はすべて無効)
./configure --prefix=/var/task/php --disable-all

# ビルド
make

# インストール
make install
```

これで、 `/var/task/php` 配下に PHP 環境がインストールされました。

```bash
# インストールされた PHP ランタイム (※ tree コマンドで 3 階層まで表示)
 tree -L 3 /var/task
/var/task
└── php
    ├── bin
    │   ├── php
    │   ├── php-cgi
    │   ├── php-config
    │   ├── phpdbg
    │   └── phpize
    ├── include
    │   └── php
    ├── lib
    │   └── php
    ├── php
    │   └── man
    └── var
        ├── log
        └── run
```

Lambda 用のzipアーカイブを作成する
----
Lambda は、実行時の起点となるハンドラーを指定します。Node.js 版のハンドラーには通常 `index.handler` が指定されていて、これは `index.js` の `exports.handler` に登録された関数を実行するということです。今回の構成もこれに倣います。

`index.js` および PHP実行用のファイルを作成し、zipで固めます。

```bash
# Lambdaの疑似ディレクトリへ移動する
cd /var/task

# AWS Lambda 関数用のコードを作成する
vi index.js

# PHP 実行用のシェルスクリプトを作成する
vi run.sh

# 実行する PHP ファイルを作成する
vi info.php

# 各ファイル作成後の構成
tree -L 3 /var/task
/var/task
├── index.js
├── info.php
├── php
│   ├── bin
│   │   ├── php
│   │   ├── php-cgi
│   │   ├── php-config
│   │   ├── phpdbg
│   │   └── phpize
│   ├── include
│   │   └── php
│   ├── lib
│   │   └── php
│   ├── php
│   │   └── man
│   └── var
│       ├── log
│       └── run
└── run.sh
```

作成したファイルの内容はこちらです。

```js:index.js
var exec = require('child_process').exec;

exports.handler = function(event, context) {
    var child = exec("sh ./run.sh", function(error) {
        // Resolve with result of process
        context.done(error, 'Process complete!');
    });

    // Log process stdout and stderr
    child.stdout.on('data', console.log);
    child.stderr.on('data', console.error);
};
```
```bash:run.sh
#!/bin/sh
./php/bin/php info.php
```
```php:info.php
<?php
phpinfo();
```

zipにまとめます。zipを回答したときに、直下に `index.js` が配置されるように zip 化してください。

```bash
# Lambda の疑似ディレクトリで、zip アーカイブを作成する
cd /var/task
zip -r task.zip .
```

この `task.zip` を手元にダウンロードし、Lambda にアップロードします。今回は 10KB 以上になってしまったので、S3経由でアップロードしました。

動作確認して問題なければ、次に進みましょう！

AWS IoT ボタンから PHP on AWS Lambda を起動！
====
それでは、AWS IoT ボタンの押下で PHP on Lambda が起動するように設定しましょう。

AWS IoT ボタンのサンプルを設定したあとは、`Things`,`Roles`,`Certificates`,`Policies`が1個ずつ設定された状態になっています。

このうち、`ルール` を選択し、「Edit Actions」から「Add New Action」をクリックします。
![スクリーンショット_120715_122121_AM.jpg](https://qiita-image-store.s3.amazonaws.com/0/3812/3308c6b6-d8d2-a05e-1497-1c3134b38bb8.jpeg)

「Choose an action」で、「Insert this message into a code function and excute it (Lambda)」を指定します。
![全画面_120715_122527_AM.jpg](https://qiita-image-store.s3.amazonaws.com/0/3812/192e1fff-35bc-cf46-350b-e90071741a12.jpeg)

「Function Name」に、先ほど作ったLambda関数を指定します。
![スクリーンショット_120715_125149_AM.jpg](https://qiita-image-store.s3.amazonaws.com/0/3812/7373e882-f8d8-6265-3ac3-878c25226757.jpeg)

それでは！
AWS IoT ボタンを押してみます。

ポチっとな！
![無題クリップ_120715_020400_AM.jpg](https://qiita-image-store.s3.amazonaws.com/0/3812/346609e8-6728-53fe-bf83-7f412e0591c0.jpeg)


CloudWatch を確認してみると…
![スクリーンショット_120715_013316_AM.jpg](https://qiita-image-store.s3.amazonaws.com/0/3812/a797824c-c6e6-68bc-303d-acfab22231d5.jpeg)

キターーー！ヾ(o´∀`o)ﾉ

Epilogue - おわりに
====
AWS IoT から Lambda を利用する方法もわかり、いろいろ勉強になりました。今回、一番難関と思っていた PHP のビルドですが、ちょっと師匠 @kazumihirose にアドバイスをいただきつつ手順に倣うとすんなりできました！

しかし、今回はわざわざ AWS IoT ボタンから PHP を動かしましたが、これ、特に何も役に立たないので、次はもっと役立ちそうなものを作りたいですね(笑)

[cloudpack]:https://cloudpack.jp/ "cloudpack（クラウドパック） 400社を超える導入実績! EC2などAWSクラウドの導入設計、構築、運用保守サービス"
[cloudpack-blog]:http://blog.cloudpack.jp/ "cloudpack技術情報サイト"
[大平かづみ]:http://blog.cloudpack.jp/author/oohira/ "大平かづみの記事一覧"