(注意)投稿者があとで読み直したところ、記事の内容がふんわりし過ぎていると感じたので、後日書き直します。一時的に非公開にしたかったのですが、できなさそうなので注意書きで。。。
はじめに
AWSにおいて、下記のサービスを組み合わせてシステムを開発すると従量課金で動くシステムが作成できます。理論上は。
- AWS Lambda (リクエスト回数および実行時間に応じた課金)
- Amazon API Gateway (APIコール回数および転送データ量に応じた課金)
- Amazon DynamoDB (書き込み読み込みリクエスト回数および利用ストレージに応じた課金)
しかし、オープンソースでないということもあるのか、DynamoDBを利用したシステム例をあまり見かけません。そこで、上記3サービスを利用してブログシステム(CMSっぽいなにか)を構築してみたので、感想をまとめます。
ソースコード
いわゆるフロントエンドエンジニアと呼ばれるようなことをやってなかったので、HTML書いたりCSS書いたりするのが大変でした......
まだまだ直す部分はありますが、とりあえず全体の思考だけでも先にQiitaにまとめます。
構成
adminpage(管理用ページの構築)、webpage(閲覧用ページの構築)、database(データ格納先の構築)の3つのフォルダが存在し、管理用ページと閲覧用ページはServerless Frameworkを利用して構築、データ格納先はCloudFormationを利用して構築しています。
管理用ページと閲覧用ページを分けているのは、別ドメインにした方が若干セキュリティが上がるのではないかと考えたことと、サイト単位でbasic認証かけた方が楽ではないかと思ったからです。
データ格納先を分けているのは、開発の都合上スタックごとまるっと削除したいとき、データが消えるのが嫌だったからです。
データ格納先の構築
データの格納先は、DynamoDBとS3を併用しています。CloudFormationのテンプレートを作成し、DynamoDBとS3バケットをまとめて構築しています。
DynamoDBのインデックスは、すべてGSIで貼り付けました。LSIだとDB構築後にインデックスを変更できない、BillingModeをPAY_PER_REQUEST(オンデマンドキャパシティー)にしてあるので各GSIごとにキャパシティプランニングをする必要がない、というのが理由です。
また、のちに機能を追加した際に、不要なインデックスが出てきた場合でも、GSIであればデータベースを削除せずにインデックスの削除ができるのも強みです。
S3は画像の保存用として利用しています。静的webサイトホスティングを利用し、API GatewayからHTTP_PROXYで読み込ませるようにします。
SLSBlogDB:
Type: AWS::DynamoDB::Table
Properties:
TableName: !Sub ${ResourceName}-DB
BillingMode: PAY_PER_REQUEST
...(省略)...
GlobalSecondaryIndexes:
- IndexName: DateIndex
KeySchema:
- AttributeName: type
KeyType: HASH
- AttributeName: date
KeyType: RANGE
Projection:
ProjectionType: ALL
- IndexName: updateIndex
KeySchema:
- AttributeName: type
KeyType: HASH
- AttributeName: updateDate
KeyType: RANGE
Projection:
ProjectionType: ALL
- IndexName: createIndex
KeySchema:
- AttributeName: type
KeyType: HASH
- AttributeName: createDate
KeyType: RANGE
Projection:
ProjectionType: ALL
閲覧用ページの構築
閲覧用ページ(ユーザーがWebサイトを訪問した際に表示されるページ)は@vendia/serverless-express
を利用し、Expressで記述したものをlambdaに変換して表示させています。
以下のサイトが参考になりました。
https://zenn.dev/yuta_saito/articles/8b543a1957c375593ee5
閲覧用ページでこだわった点は以下の点です。
- Dynamo DB上にマークダウンで保存してある情報を変換して表示させる
Dynamo DB上にマークダウンで保存してある情報を変換して表示
これは、変換するだけであればmarked
を使用するだけです。ただ、見た目を調整するのがつらかったです。(CSSの知識がないだけ)
管理用ページの構築
開発中は、Cloud9のエディタでExpressをローカルで動かしながら開発し、デプロイ時に@vendia/serverless-express
のパッケージを利用してLambdaで利用できる形式に変換しました。
以下のサイトが参考になりました。
実は、@vendia/serverless-express
のパッケージを見つける前に、自力でいろいろなサイトを探しながらコードを書いたりしていました。例えば、BASIC認証をかけようとする際、最初は以下のサイトを参考にしていました。
しかし、Expressを利用すると、以下のコードを書いて、app.jsから呼び出してあげるだけです。(参考サイト)
const auth = require('basic-auth');
module.exports = function (request, response, next) {
const admins = {
[process.env.BASIC_AUTH_USERNAME]: { password: process.env.BASIC_AUTH_PASSWORD },
};
const user = auth(request);
if (!user || !admins[user.name] || admins[user.name].password !== user.pass) {
response.set('WWW-Authenticate', 'Basic realm="example"');
return response.status(401).send();
}
return next();
};
もちろん、basic-authというパッケージがいろいろやってくれているので、これだけ短いコードで済むのですが、サーバレスでも既存の資産を利用できるというメリットが大きいと感じました。
あと、Lambda側に機能を寄せられるので、APIGatewayではHTTP APIを利用して料金を節約できます。
サンプルサイト
投稿用ページ(ユーザー名:username パスワード:password)
投稿用ページは荒らされないように、データベースへの書き込み権限を剥奪しています。
一定期間経ったら消す予定です。
おわりに
同じような構成のシステムを作成しようと考えている方の参考になりましたら幸いです。