AWSのS3やLambdaを使って、サーバーレスでウェブサイトを公開してみました。付随して使ったサービスやランニングコストを紹介していきます。
- 機能要件とアーキテクチャ選択
- アクセス数とランニングコスト
- 実装概要
機能要件とアーキテクチャ選択
- ページ数は5ページぐらい
- お問い合わせフォーム
- SSL化
サーバーを構築して、ミドルウェアのアップデートやら脆弱性のキャッチアップ、稼働率や死活監視など、気にかけるのが面倒と思ってしまいサーバーレスで作ることを決めました。
ランディングページのように、あまり更新が発生しないので、CMSを導入する必要もないという点も決め手の1つとなりました。
スポンサードサーチアクセス数とランニングコスト
アクセス数は、1日あたり50pvぐらいがほとんどです。
月間コストに関しては、$0.79です。日本円だと100円しないですね。使っているサービスごとのコスト利用料を見ると、ほとんどネームサーバー(Route53)の料金です。
仮に月間pv30,000とかになったとしても、金額は変わらなそうでした。
あと、ランニングコストとして忘れてはいけないのが管理費です。OSやらミドルウェア、SSL証明書の更新、コードの管理とかはほとんどかかりません。基本ほったらかしにしてます。。
一度だけAWSからnode.js VersionのEOLのお知らせメールが着て、アップグレードしたぐらいです。証明書はAmazon Certificate Managerを使っています。無料で使えますし、自動で更新されます。
イニシャルコストは、お名前.comで買ったドメインだけです。
実装方法
使った機能とそれぞれの設定手順を紹介します。初めてaws使ったので、もし間違いとかがあれば、お問い合わせよりご指摘下さいませ。
s3でサイトを公開する(Route53/CloudFront/ACM)
ウェブサイト公開用のバケットと問い合わせデータ格納先のバケット2つ用意します。公開はバケットのプロパティから「Static website hosting」の設定で行うことが出来ます。
お名前.comで既にドメインを購入していたので、先ほど公開したウェブサイトとドメインの紐付けを行いました。「Route53->ホストゾーン->ホストゾーンの作成」で、ドメイン名を入力します。
次にレコードセットの作成です。名前の枠に「www」入れて作成しました。
後は、この赤字で伏せてあるNSレコードの値を、お名前.com側に入力するだけです。「お名前.comへログイン」→「ネームサーバーの設定」→「対象ドメイン選択」→「他のネームサーバーを利用」で下記画面が出てきますので、そこへ入力します。
s3をCloudFront経由で公開する
s3でホスティングしたウェブサイトを、CloudFront経由で公開し、Amazon Certificate Manager(SSL証明書)を適用させます。
先にAmazon Certificate ManagerでSSL証明書を取得します。「Amazon Certificate Manager」→「証明書のリクエスト」→「パブリック証明書のリクエスト」ドメインを入力して、検証方法を選択して申請します。ワイルドカード証明書も無料なので、ワイルドカードをつけて申請した方が良いです。(*.taneroco.com)
申請が通れば、CloudFrontの設定を進めていきます。CloudFrontから「Create Distributions」を選択します。
Origin Domain Nameの入力枠にフォーカス当てて、公開したいバケットを選択します。
SSL Certificateで「Request or Import a Certificate with ACM」を押下し、先ほど取得したACMの証明書を適用させます。後はTTLを短くしたり、リダイレクトのルールを記載したり、お好みに合わせて設定を進めます。
設定完了後、https通信でアクセスして証明書が適用されているのを確認したら、公開用s3バケットのCROS設定を行います。下記サンプルです。
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>https://www.taneroco.com/*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>Authorization</AllowedHeader>
</CORSRule>
</CORSConfiguration>
これで公開用ウェブサイトの完成です。
お問い合わせフォーム(Lambda/Cognito/SES)
ウェブサイトの登録ボタンクリックで、問い合わせデータ格納先のバケットにフォームデータをputさせます。その後putされたイベントを拾ってLambda Functionを動かし、SESでメール通知させます。
SESの設定
「Simple Email Service」→「Email Addresses」→「Verify a New Email Address」で下記画面が開くので、メールアドレスを入力してください。リンクがメールで届くので押すだけで承認されます。
フォームの内容をputさせる処理(Cognito/s3)
アクセスしたユーザに対して、一時的にフォームデータ格納バケットへputできる権限を付与します。
「Cognito」→「新しいID Poolを作成」を選択します。その後、任意のプール名を入力し、認証されてないIDに対してのアクセスを有効にします。ロールに関しては、初期入力値のままで大丈夫です。
「作成したID Poolを選択」→「サンプルコード」を押下し、javascriptを選択し、サンプルコードをそのまま使います。
ホスティングされているウェブサイトのコードです。
<head>
<script src="https://sdk.amazonaws.com/js/aws-sdk-2.3.8.min.js"></script>
<script>
var $id = function(id) { return document.getElementById(id); };
AWS.config.region = "リージョン";
AWS.config.credentials = new AWS.CognitoIdentityCredentials({IdentityPoolId: "リージョン:cognitoのサンプルコードからそのままペースト"});
// s3にputするfanction
function putForm() {
var now = new Date();
AWS.config.region = 'リージョン';
var formDataBucket = 'フォームデータ格納用バケットネーム';
var s3 = new AWS.S3({params: {Bucket: formDataBucket}});
var url = location.href;
// s3にputするデータを格納
var object = {
"name":$id("name").value,
"mail":$id("mail").value,
"contents":$id("contents").value,
"date": now.toLocaleString(),
"url": url
};
// 文字列からblobを生成
var blob = new Blob([JSON.stringify(object, null, 2)], {type:'text/plain'})
// 格納先にput
s3.putObject({Key: "formdatas/" +now.getTime()+".txt", ContentType: "text/plain", Body: blob, ACL: "authenticated-read"},
function(err, data){
if(data !== null){
alert("お問い合わせ完了しました。");
location.reload();
}else{
alert("お問い合わせできませんでした。¥n再度お試しください。");
}
}
);
}
</script>
</head>
<body>
<div class="section">
<h2 class="major">Contact</h2>
<form method="POST" id="postForm">
<div class="field half">
<label for="name">* Name</label>
<input type="text" name="name" id="name" />
</div>
<div class="field half">
<label for="email">* Email</label>
<input type="email" name="mail" id="mail" />
</div>
<div class="field">
<label for="message">* Message</label>
<textarea name="contents" id="contents" rows="4"</textarea>
</div>
<ul class="actions">
<li><input onClick="putForm()" type="button" value="Send Message" class="button special"/></li>
</ul>
</form>
</div>
登録ボタンクリックで、フォームの内容をフォームデータ格納用バケットにputします。
フォームデータ格納用バケットのCROS設定を行います。s3put攻撃を避けるためにリファラーの設定しましょう。AllowedOriginにputするサイトのURLを記載します。
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>https://taneroco.com</AllowedOrigin>
<AllowedMethod>PUT</AllowedMethod>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
Lambdaファンクションの作成とメール送信
「lambda」→「関数の作成」でファンクションを作成します。
好きな関数名を入力して、アクセス権限を設定します。AMIロールを作成し、ポリシーをアタッチします。今回はメール送るだけなので、「AmazonSESFullAccess」があれば動くはず。
関数の作成をしたらトリガーの設定をします。s3をトリガーのところにドラック&ドロップして、フォームデータ格納用バケットを選択し、putイベントを選択します。
あとは、Lambdaのコードを記載し保存します。下記はサンプルコードです。
var aws = require('aws-sdk');
var s3 = new aws.S3({apiVersion: '2006-03-01'});
var ses = new aws.SES({apiVersion: '2010-12-01', region: 'リージョン' });
exports.handler = function(event, context) {
var bucket = event.Records[0].s3.bucket.name;
var key = event.Records[0].s3.object.key;
s3.getObject({Bucket: bucket, Key: key},
function(err, data) {
if (err){
context.done('error', 'error getting file' + err);
} else {
var message = JSON.parse(data.Body);
var datas = {
Destination: {
ToAddresses: ["送信先メールアドレス"]
},
Message: {
Body: {
Text: {
Data: "メールアドレス:" + message.mail+ "\n" + "名前:"+ message.name + "\n" + "お問い合わせ内容:"+ message.contents
}
},
Subject: {
Data: "【HPからのお問い合わせです】"
}
},
Source: "送り主メールアドレス"
};
// SESでメール送信
ses.sendEmail(datas);
}
}
}
あとはウェブサイトから実際にフォーム入力してみて、メールが届くのを確認してください。
まとめ
連携するサービスや設定項目が多くて結構大変だけど、安いからやる価値あるかもしれません。クラウドサービスは使った分だけ課金されてしまうこともあり、怯えながら作成しました。時間あるときもう少し綺麗にまとめようかと思ってます。。