LoginSignup
5

More than 1 year has passed since last update.

posted at

updated at

Movable Type Serverless - LambdaコンテナでMTを動かしてみた

Movable Type Advent Calendar 2020 12日目です。

つい先日、AWS LambdaがDockerコンテナをサポートしました。

これはアツい!ということでMTを(途中まで)動かしてみた、という話です。

Movable Type Serverless

最終的にはこんなイメージでいます。

mt-serverless-arch2.png

MTの静的出力という特徴を活かし、Webサーバーだけ常時起動させますが、MTは普段存在すらしておらず、編集者が使うときだけ起動します。

  • 夜中や休日など編集者が使わない時間はコンピューティングコスト節約!
  • 利用者が増えたらRDSやEFSの限界まで事実上無限に性能拡張!
  • もし再構築を並列で実行できたら時間を圧倒的に短縮!

なにこれすごい!

とりあえずログインできるところまでやってみた

今回はRDSと連携してログインできるところまでやってみました。

mt-serverless-trial2.png

  • EFSとの連携以降はやってません。
  • セキュリティ考慮していません。
  • 日本語だと文字化けしたのでとりあえず英語で動かします。

とまあ、PoCレベルなので誰か遺志を継いでください。

手順1 RDSインスタンスを起動

RDSにMT用のMySQLデータベースインスタンスとユーザーを作成します。

一時的な実験なのでセキュリティは考慮していません! 随時読み替えて適切に設定してください。

create user 'mt_serverless'@'%' identified by '(データベースのパスワード)';
grant all on mt.* to 'mt_serverless'@'%' with grant option;
flush privileges;

手順2 MTのプログラム一式を展開する

ここからはRDSに接続可能な環境で作業します。

開発者ライセンスでMTを入手します。

手順3 mt-config.cgi

MTを展開したディレクトリ直下に設定ファイルmt-config.cgiを作成します。

※ 日本語が文字化けしちゃったので英語UIに逃げます。

mt-config.cgi
CGIPath        /mt/
StaticWebPath  /mt/mt-static/
StaticFilePath /app/mt-static

ObjectDriver DBI::mysql
Database mt
DBUser mt_serverless
DBPassword (データベースのパスワード)
DBHost (RDSインスタンスのホスト名)

EmailAddressMain (管理者メールアドレス)

DefaultLanguage en

ImageDriver Imager

手順4 Dockerfile

ひとまずplack/PSGIで動かすため、MTを展開したディレクトリ直下にこんなDockerfileを作ります。

個人的に慣れてるUbuntuを使っていますが、ディストリビューションはお好きにどうぞ。

Dockerfile
FROM ubuntu:20.04

RUN apt-get update \
  && apt-get install -y perl cpanminus build-essential \
    libdbi-perl libdbd-mysql-perl \
    libimager-perl libxmlrpc-lite-perl \
    libplack-perl libcgi-psgi-perl \
  && cpanm XMLRPC::Transport::HTTP::Plack

RUN cpanm AWS::Lambda

RUN apt-get -y remove build-essential \
  && apt-get -y clean \
  && apt-get -y autoremove \
  && rm -rf /var/lib/apt/lists/* /var/cache/apt/*

ADD . /app
WORKDIR /app

CMD [ "plackup", "mt.psgi" ]

ここでインストールしているナイスなモジュールAWS::Lambdaが、Lambda - Dockerコンテナ - PSGIアプリケーションとしてのMTを繋いでくれます。

手順5 PSGI版で動作確認兼DBセットアップ

docker build -t mt-serverless .
docker run -it --rm -p 5000:5000 mt-serverless

ブラウザから、http://localhost:5000/mt/mt.cgi (localhostは作業環境に合わせて)にアクセスすると、セットアップウィザードが開始されます。

Create_Your_Account___Movable_Type_Pro.png

文字化けの問題で、LanguageEnglishでひとまず進めてください。

Initializing_database______Movable_Type_Pro.png

データベースのセットアップができたらOKです。

手順6 コンテナ用エントリーポイント bootstrap

コンテナサポート ≒ カスタムランタイム

AWS Lambdaのコンテナサポートは、2年前にリリースされたカスタムランタイムのプロトコルを踏襲しています。

カスタムランタイムはHTTPによりLambdaサービスと連携しますが、コンテナも同様です。なのでコンテナの情報はまだ少ないのですが、カスタムランタイムについて調べると大抵の問題は解決できます。

この試みでもAWS Lambdaカスタムランタイム用のモジュールAWS::Lambdaを大いに活用します。

MTのディレクトリ直下にbootstrapを作成し、実行権限を付与します。

boostrap
#!/usr/bin/perl

use strict;
use AWS::Lambda::Bootstrap;
use File::Basename;

# Lambdaサービスから渡されそうだが…
$ENV{'LAMBDA_TASK_ROOT'} = dirname(__FILE__);

# Lambdaにハンドラの入力欄がないからこちらは来ないかも?
# AWS::Lambda向けに固定
$ENV{'_HANDLER'} = 'mt-lambda.handler';

my $bootstrap = AWS::Lambda::Bootstrap->new;
$bootstrap->handle_events;

カスタムランタイムと違い、名前はbootstrapでなくてもよいのですが慣習に沿います。

chmod +x bootstrap

手順7 Lambdaコンテナ用MTエントリーポイント mt-lambda.pl

次にbootstrapから呼び出されるMTのエントリーポイントmt-lambda.plを作成します。

mt-lambda.pl
use strict;
use lib $ENV{MT_HOME} ? "$ENV{MT_HOME}/lib"    : 'lib';
use lib $ENV{MT_HOME} ? "$ENV{MT_HOME}/extlib" : 'extlib';
use MT::PSGI;
use AWS::Lambda::PSGI;
my $app = MT::PSGI->new()->to_app();
my $func = AWS::Lambda::PSGI->wrap($app);

sub handler {
  my $payload = shift;
  return $func->($payload);
}

1;

このファイル名とsub handlerの名称は、下記のようにbootstrapの記述と関連しているので変更する場合は併せて行う必要があります。

$ENV{'_HANDLER'} = 'mt-lambda.handler';
# mt-lambda → mt-lambda.pl
# handler → sub handler

最初はbootstrapファイル内ですべて完結させる予定でしたが、AWS::Lambdaモジュールの実装によりそれが難しかったのでbootstrapmt-lambda.plに分かれています。

手順8 ECRにプッシュ

ECR上のリポジトリにイメージをプッシュします。細かい説明は省略します。

docker tag mt-serverless (固有のID).dkr.ecr.ap-northeast-1.amazonaws.com/mt-serverless:latest
docker push (固有のID).dkr.ecr.ap-northeast-1.amazonaws.com/mt-serverless:latest

手順9 Lambdaを作成

コンテナイメージを元にLambda関数を作成します。

Lambda.png

今のコンテナイメージはplackupでPSGI版のMTを起動するので、CMDにLabmda用のエントリポイント/app/bootstrapを指定します。

作成後にLambdaのメモリとタイムアウトを変更します。

適切な設定は要検討ですが、とりあえずタイムアウト3秒は短いので30秒にしました。

Lambda.png

手順10 Elastic Load Balancerの作成

やっと終わりが見えてきました…

Application Load Balancerを作成してLambda関数を割り当てます。

  • Application Load Balancer (HTTP/HTTPS) を作成します。
  • リスナーは必要に応じてHTTPSを追加してください。
  • サブネットはふたつ必要です。

ロードバランサーの作成___EC2_Management_Console.png

次に証明書とセキュリティグループを選択します。

セキュリティグループは当然、ポート80と443でのインバウンド接続を許可していください。

新しいターゲットグループとして、種類=Lambda関数を選択します。

ロードバランサーの作成___EC2_Management_Console.png

最後に作成したLambda関数を選択します。

ロードバランサーの作成___EC2_Management_Console.png

ついにMovable Type Serverlessにアクセス

DNSの反映に少し時間がかかるようですが、ブラウザで次のURLを開くと…

http://(ロードバランサーのDNS名)/mt/mt.cgi

き…きたー! MTのログイン画面!

Sign_in___Movable_Type_Pro.png

セットアップ時に登録したユーザーでログインすると…

Dashboard___Movable_Type_Pro.png

ダッシュボードだー!

しかも意外と動作が軽快。

PSGIをインターフェースとして使っていますが、ライフサイクルとしてはリクエストごとにプロセスが発生するので、挙動としてはCGIに近いはずです。もしかしたらLambdaサービスはPSGI的にプロセスがリクエスト間で使い回しているかもですが。

でもコンテナを読み込むオーバーヘッドは感じられず、実用性も期待できます。

続きを期待します

他の動作確認はしていません。そもそもEFSなどでファイルシステムを永続化しないとサイトも公開できません。

でも使った分だけ課金、並列爆速再構築など、期待が膨らみますね。

あなたがここまで読んだということは、私はすでに力尽きているだろう。ここから先のチャレンジは、あなたに道を譲ろうと思う。検討を祈る!

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
What you can do with signing up
5