LoginSignup
18
13

More than 5 years have passed since last update.

AWS lambdaでステートフルWEBアプリを使う方法

Last updated at Posted at 2018-07-24

はじめに

AWS lambdaでWEBアプリを動かしたいけど、API Gatewayの仕様が窮屈で諦めた。
ステートフルなWEBアプリをステートレスに改修するのは面倒。
そのような方に、API Gatewayを使わない構成をご紹介します

AWS lambdaでWEBアプリは無理?

AWS lambdaの主な特徴

  • サーバの面倒をみなくてよい
  • 使わないと料金が発生しない
  • 急なリクエスト増にも標準で対応できる

この最初の2つの特徴を知って、多くの人が驚き、喜び、そして、

サーバーの面倒みなくていいし、使わない時間の料金は請求されなくて安いし、
月に2, 3回数人が数分使うだけの社内の簡易WEBアプリを動かすのに最適じゃない?

と、ごくごく自然に考えます。

じゃ、さっそく試そうとすると、

2018-07-24 09_46_37-lambda-ngrok - draw.io.png

AWS lambdaをHTTPSで使うには、図のようにAPI Gatewayを前段に使う必要があることがわかり、
じゃ、API Gatewayを使おうとすると、

  • API Gatewayには最大30秒のタイムアウト制約があり、AWS lambdaを30秒しか動かせない
  • API Gatewayが、常に同じlambdaインスタンスを使ってくれる保証がない
  • API Gatewayがヘッダーを書き換えてしまうので、WEBアプリに実装されているBASIC認証が使えない。API Gateway専用のlambdaを作る必要がある
  • websocketが使えない

と、いろいろ面倒(仕様上の制約)な壁にぶつかります。

WEBアプリを改修すればAWS lambda + API Gatewayで動かせますが、
そもそも利用頻度も低く手間をかけたくないという動機なので、
わざわざWEBアプリをステートレスに改修する手間はかけたくありません。

できないじゃん、、、残念

と私も諦めていました。この方法を発見するまでは。

この方法でAWS lambdaのWEBアプリにHTTPSアクセスできる

Ngrok(エングロック)というサービスをご存知でしょうか。
URLのエンドポイントを提供してくれる、開発者に人気のリバースプロキシ―サービスです。
PC上で開発中のアプリを起動し、Ngrokで一時的なURLを取得してインターネットに公開。
インターネットからのアクセスを開発中アプリに流すことができます。

ある時思い立って、AWS lambdaからNgrokに接続しやると、
Ngrokで取得したURLで、ステートフルなWEBアプリが普通に使えるではありませんか!!

この図の構成が普通に動くのです。
2018-07-24 09_59_04-lambda-ngrok - draw.io.png

この構成であれば、

  • 30秒のタイムアウト制約はありません。300秒AWS lambdaの実行時間最大まで使えます
  • 同じインスタンスがリクエストを処理してくれるので、ステートフルWEBアプリがそのまま動きます
  • WEBアプリに実装されているBASIC認証がそのまま使えます
  • websocketも使えます

今まで、WEBアプリをlambdaで動かすには、次の記事のように

AWS Serverless Expressなどを使って改修して動かすことができましたが、
この構成では、Ngrokに接続するラッパーだけ書けばいいのでとても簡単です。

サンプル

この構成のサンプルアプリ(Node.js + express)をGithubに登録しましたので試してみてください。

'use strict';

const app = require('./app');
const PORT = 3000;
const ngrok = require('ngrok');
const EventEmitter = require('events').EventEmitter;
const ev = new EventEmitter();

const slack_url = process.env.SLACK_URL;
const https = require('https');
const url = require('url');

const postSlack = (msg) => {
  const opts = url.parse(slack_url);
  opts.method = 'POST';
  opts.headers = {'Content-Type': 'application/json'};
  const req = https.request(opts, (res) => {
    console.log("Send a slack message: " + res.statusCode);
  });
  req.on('error', (err) => {console.log(err)});
  req.write(JSON.stringify({text: msg}));
  req.end();
}

module.exports.server = (event, context, callback) => {
  let URL;
  // (1)
  const server = app.start(PORT, context, (req, res) => { // (4)
    ngrok.kill();
    console.log('Receive Bye request');
    postSlack('Closed ' + URL);
    ev.emit('kill');
  })
  // (2)
  ngrok.connect(PORT, (err, url) => {
    if (err) {
      callback(err, null);
      return;
    }
    URL = url;
    ev.once('kill', () => {
      const response = {
        statusCode: 200,
        body: JSON.stringify({ message: 'Good bye' }),
      };
      server.close();
      console.log('App closed.');
      callback(null, response);
    })
    postSlack('Go ' + URL);  //(3)
    console.log('Connect to %s', URL);
  })
};

簡単に解説すると

  1. アプリをPort 3000で起動する
  2. NgrokでPort 3000を公開して、一時URLを取得する
  3. 一時URLをSLACKで通知する
  4. 特定のリクエストがきたら終了する
  • 4の終了処理をきれいにやるためにコードが少し多くなっていますが、lambdaのタイムアウト終了でよしと割り切ればシンプルにできます

おわりに

AWS lambdaの中からリバースプロキシ(Ngrokなど)との間に接続を張り、リバースプロキシ経由でWEBアプリを利用する。これでAPI Gatewayの仕様上の制約から解放されて、AWS lambda上でステートフルなWEBアプリを使うことができました。

2018-07-24 10_08_49-lambda-ngrok - draw.io.png

AWS lambdaの最大実行時間300秒の制約の範囲内でですが、
社内のちょっとした処理など一回2,3分アプリが動けば十分なケースで使える構成かと思います。試してみてください。

補足

  • この記事はServerless Meetup Tokyo #8のLTで話した内容です
  • 参考 LT資料@SlideShare
  • 他の言語や他のWEBサーバーでも同様のアプローチでWEBアプリを使う事ができるようになります
  • Ngrokはセキュリティーが気になるという場合は、自前でlocaltunnelサーバーを建てるか、SSHポート転送を使う事でも問題を解決できます。
18
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
13