cloud frontとlambda@edgeでUA判定をしてPCページとSPページを出し分けてみた
Qiitaに出してた記事ですが、Qiitaそのものが炎上してるようなのでこちらにも同じ記事を展開
cloud frontとlambda@edgeでUA判定をしてPCページとSPページを出し分けてみた
Webサービスを構築する仕事がきたのですが、ビジネスロジックが不要なサービスだったので できるだけカンタンに作ろうと思いサーバーレス構築に挑戦してみた際にやってみたレポートになります。
使ったAWSのサービス
- cloud front
- lambda( @Edgeはバージニア北部リージョンで関数作成する必要があります )
- S3
- Route53(ドメイン設定の話なので今回は特に登場させません)
- ACM(SSL証明書の話なので今回は特に登場させません)
- Cloud Watch(lambdaを利用したら自動でついてきた)
やったこと
- S3にHTMLを配置
- cloud frontとS3を紐付け
- ドメインにアクセスした際にどのHTMLを出すか設定(トップページの設定)
- lambda@edgeにUA判定処理を実装
- lambda@edgeとcloud frontの紐付け(トリガー設定)今回はViewerリクエストをトリガーに設定
ざっくり動くまででだいたい2〜3時間くらいでできました。
lambda側に設定する必要のあるポリシーの信頼関係※記載わすれてたので追記しました(2019/07/10)
アクセス権限はS3とCloudFrontによしなに設定してあげてください。 お手軽にやるならフル権限つけてしまってよいかと思います。
~~~ { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "Service": "edgelambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] } ~~~
1〜3はいろいろなところに記事が落ちているのでlambdaのソースと引っかかったポイントを紹介します。
lambdaに実装したソース
~~~javascript 'use strict';
const whitelist = [ 'android', 'iphone', 'ipad' ];
const isSpBrowser = (uas) => { if (uas && Array.isArray(uas) && uas.length > 0) { return uas.some(ua => whitelist.some(w => ua.value.toLowerCase().indexOf(w) !== -1)); } return false; };
// User-AgentからPC・SP判定を行ない描画ページを切り替える exports.handler = (event, context, callback) => { const request = event.Records[0].cf.request; const headers = request.headers;
// console.log('headers.user-agent. = ' + headers['user-agent']);
if (headers['user-agent']) {
// URI整理
// cloud frontにトップページとして設定したindex.htmlがURIに入ってくるので除去
var uri = request.uri.slice(-10) === ('index.html') ? request.uri.slice(0, -10) : request.uri;
uri = uri.slice(-1) === ('\/') ? uri : uri + '\/';
console.log('headers[user-agent] = ' + headers['user-agent'][0].value);
// URI判定
// ファイル読み込みで呼び出された場合中断
if(uri.match(/( \.png|\.css|\.js|\.jpeg|\.jpg|\.bmp|\.gif|\.pdf|\.svg|\.zip)/)) {
callback(null, request);
return;
}
// UA判定
if (isSpBrowser(headers['user-agent'])) {
uri = uri + 'SP用描画ファイル名'
} else {
uri = uri + 'PC用描画ファイル名'
}
console.log('uri = ' + uri);
request.uri = uri
callback(null, request);
return;
}
callback(null, request);
}; ~~~
PC/SPの判定においてはこういう書き方もできます。(というか今はこっちのほうが推奨されてます) ※下記ソースで行う場合トリガーをオリジンリクエストイベントに設定する必要があります。 (ビューワーリクエストイベントの後に CloudFront-Is-*-Viewer ヘッダーを追加されるため) ※Whitelist HeadersにHostsを混ぜるとS3へのリクエストはCloudFrontが署名を行う形になり想定外の挙動になる可能性があるので要注意
javascript
// UA判定 前述コードのUA判定部分と置き換える
if (headers['cloudfront-is-desktop-viewer']
&& headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
uri = uri + 'PC用描画ファイル名'
} else if (headers['cloudfront-is-mobile-viewer']
&& headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
uri = uri + 'SP用描画ファイル名'
} else if (headers['cloudfront-is-tablet-viewer']
&& headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
uri = uri + 'SP用描画ファイル名' // タブレットでSP画面で見せる場合
} else if (headers['cloudfront-is-smarttv-viewer']
&& headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
uri = uri + 'PC用描画ファイル名' // SmartTVVでPC画面で見せる
}
ポイント
- ログはCloud Watchに出力されるがアクセスされたcloud frontのリージョンに出力されたのでlambdaを配置しているリージョンではない
- ドキュメントは基本英語版が正
- トリガー設定はlambda側からやるほうがらく
- 困ったら迷わず有料サポートを使う(結果そのほうが早いのでトータルでの費用が下がる)
最後に
イベントページなどの後ろにビジネスロジックがいらないサービスであればこの構成は結構おすすめできると思います。 インフラ作業部分(エンジニアが必要な作業分)は大体5人日程度で収まったのでその分デザインやプロモーションにコストがまわせるのも強みかと ※初回なのでこれくらいですが慣れればもっと短くなる予感
最近よく作るAWSのインフラ構成図
ちょこちょこ実際の事業上の都合で必要になって作るAWSでの構成があるので公開しておきます。
オンプレとAWS環境を同じドメインで共存させたい
特定ディレクトリはAWS、別のディレクトリはオンプレ(現行のサーバー)などの構築が必要になることがあったのでその時につくった構成はこんな感じです。
オンプレ環境にはCloudFront経由でしかアクセスをとおさないようにすることでキャッシュとかをある程度使えるようにしつつも今後オンプレの部分もAWSや他のクラウドに載せ替えたいといった際にはCloudFrontのビヘイビアの差し替えをすることでメンテ時間を短くできるなどがありますので順次AWSに移設していくなどもできるかなと思ってます。
lightsailを使ってCloudfrontも使いたい
lightsailを使って定額でWordPress作りたいけどlightsailの前にCloudFrontを配置してキャッシュをうまく使いたいなんて場合の構成はこんな感じにしました。
カスタムオリジンを使うことでオンプレの時同様にCloudFront経由のアクセスにしています。
lightsail自体にもDNSゾーンの機能があるのですがそれを使うとCloudFrontをうまく設定できなかったので現状はこのような構成にしています。
ちょいちょい、依頼主の現状の都合で作るパターンが溜まってきているので今後もある程度溜まったら構成図にしていきたいと思います。
cloud frontとLambda@Edgeを使ってリダイレクト設定入れるやりかた
配下のWebサーバーではなくcloud frontとLambda@Edgeでリダイレクトしたい
意図としては配下のEC2なりにアクセスを通してWebサーバー(nginx、Apacheなど)でリダイレクトするのではなく 上位層でリダイレクトすることでWebサーバーへ不要なアクセスをさせない良いにしておきたい。
やりかた
①リダイレクトさせたい(アクセスさせたくない)URLをcloud frontのビヘイビアに設定する
[Behaviors]タブにてCreate Behaviorを押下
[Path Pattern]に"/hoge/*"みたいな感じでドメイン以下のURLを記載する
※ほか項目はよしなに入力して保存
②Lambda関数を作る
リージョンは バージニア北部 を選択(@Edgeとして利用するため) IAMロールはアクセス権限はよしなに設定し信頼関係は下記で作成
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "lambda.amazonaws.com" }, "Action": "sts:AssumeRole" }, { "Effect": "Allow", "Principal": { "Service": "edgelambda.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
コードの内容は下記を入力し保存する
'use strict'; exports.handler = (event, context, callback) => { const response = { // 用途に応じたhttpステータスを記載する status: '308', statusDescription: 'Permanent Redirect', headers: { location: [{ key: 'Location', value: '飛ばしたい先のURLを記載', }], }, }; // リダイレクト用レスポンスを返す callback(null, response); };
新規バージョンを作成し先程設定したcloud frontに紐づけし
イベントタイプは origin-request
パスパターンに先程作成したビヘイビアの[Path Pattern]に設定したものを選択
ボディを含めるはいいえで保存します。
基本これでリダイレクト設定完了です。
ひさびさにサーバーレス関連の記事を書いてみましたw
最近コード書いてなかったですがコード書くと楽しいですね!
DynamoDBにLambda(node.js)からデータを登録するサンプルコード
なんかいろいろサンプル見ながらやったんですが仕様が変わったのか通らなかったコードが結構あったのでサンプルコードとして残しときます。 2019/01/17時点で動作確認済み
var AWS = require('aws-sdk'); AWS.config.update({ region: "us-west-2" }); var dynamo = new AWS.DynamoDB.DocumentClient(); exports.handler = (event, context, callback) => { // 現在時刻の取得 var date = new Date(); // 日本の時間に修正 date.setTime(date.getTime() + 32400000); // 1000 * 60 * 60 * 9(hour) // 日付を数字として取り出す var year = date.getFullYear(); var month = date.getMonth()+1; var day = date.getDate(); var hour = date.getHours(); var min = date.getMinutes() // 値が1桁であれば '0'を追加 if (month < 10) { month = '0' + month; } if (day < 10) { day = '0' + day; } if (hour < 10) { hour = '0' + hour; } if (min < 10) { min = '0' + min; } // 出力 var date_now = year + '/' + month + '/' + day + ' ' + hour + ':'+ min; console.log('Received event:' + date_now); console.log("event:", event); dynamo.put({ "TableName": "Complaints", "Item": { "text": event.text, "created_at": date_now } }, function( err, data ) { console.log("dynamo_err:", err); context.done(null, data); }); };
AWS Digital Advertising Japan Seminar 2018 Machine Learning 事例まつり メモ書き
AWS Digital Advertising Japan Seminar 2018 Machine Learning 事例まつり
参加してきたのちょろっとだけメモ書き公開!
オープニングトーク
AWSのサービス開発の流れ
- プレスリリース
- FAQの作成
- お客様の体験の流れの整理
グローバル:デジタルアドバータイジング
ML(マシンラーニング)はデジタル広告ビジネス成功の中核をなす
キーワード
- AmazonSagemaker
- DeepLearningAMI
アプデートのご案内
Rekognitionが東京で使えるようになった
文字起こしサービス、翻訳サービスがサービスイン
Sagemakerが東京リージョンで利用可能になった
- ハイパーパラメータチューニングが可能になった
- Chainer、PyTorchに対応
- Tensorflow1.8対応
- 推論エンドポイントがオートスケーリング、Batch推論に対応
- ノートブックインスタンスで起動時に自動実行されるスクリプトを定義可能になった
- ビルトインアルゴリズム拡充
- Object Detection:物体検出アルゴリズム
- DeepLens
- Kinesis Video Streamsへのカメラ動画送信をサポート
ML Ops on AWSのアーキテクチャ紹介
Infrastructure as a Code
※環境依存を起こさない
micsoservices
※依存関係を大きくして個別にデプロイできないという状況をつくらない * AWS SageMaker * AWS ECS
Continuous Delivery
※新しいことをしたら悲惨な結果を返すことがある
※ちゃんと切り戻しができるようにしておくことが大切
いちばん大切な事例は参加者の特権ということで。。。
あとでまとめられたらアップ方向で検討!
CloudFront+S3+Lambda@Edgeを使ったときにVaryヘッダを返してみる
CloudFront+S3+Lambda@Edgeを使ったときにVaryヘッダを返してみる
最近ちらっと困ったのでメモ書きのように残しておきます!
下のソースをLambdaに書いてビューアーレスポンストリガに設定しておけばできましたので!
続きを読む