ayumu_aoの日記

SIerから事業会社に転職したエンジニアが技術についてや組織論、本の話、今までの体験談などなどを個人的に垂れ流しています。

cloud frontとlambda@edgeでUA判定をしてPCページとSPページを出し分けてみた

Qiitaに出してた記事ですが、Qiitaそのものが炎上してるようなのでこちらにも同じ記事を展開

cloud frontとlambda@edgeでUA判定をしてPCページとSPページを出し分けてみた

Webサービスを構築する仕事がきたのですが、ビジネスロジックが不要なサービスだったので できるだけカンタンに作ろうと思いサーバーレス構築に挑戦してみた際にやってみたレポートになります。

使ったAWSのサービス

  • cloud front
  • lambda( @Edgeはバージニア北部リージョンで関数作成する必要があります
  • S3
  • Route53(ドメイン設定の話なので今回は特に登場させません)
  • ACMSSL証明書の話なので今回は特に登場させません)
  • Cloud Watch(lambdaを利用したら自動でついてきた)

やったこと

  1. S3にHTMLを配置
  2. cloud frontとS3を紐付け
  3. ドメインにアクセスした際にどのHTMLを出すか設定(トップページの設定)
  4. lambda@edgeにUA判定処理を実装
  5. 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画面で見せる }

参考: https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html#lambda-examples-redirecting-examples

ポイント

  1. ログはCloud Watchに出力されるがアクセスされたcloud frontのリージョンに出力されたのでlambdaを配置しているリージョンではない
  2. ドキュメントは基本英語版が正
  3. トリガー設定はlambda側からやるほうがらく
  4. 困ったら迷わず有料サポートを使う(結果そのほうが早いのでトータルでの費用が下がる)

最後に

イベントページなどの後ろにビジネスロジックがいらないサービスであればこの構成は結構おすすめできると思います。 インフラ作業部分(エンジニアが必要な作業分)は大体5人日程度で収まったのでその分デザインやプロモーションにコストがまわせるのも強みかと ※初回なのでこれくらいですが慣れればもっと短くなる予感

最近よく作るAWSのインフラ構成図

ちょこちょこ実際の事業上の都合で必要になって作るAWSでの構成があるので公開しておきます。

オンプレとAWS環境を同じドメインで共存させたい

特定ディレクトリはAWS、別のディレクトリはオンプレ(現行のサーバー)などの構築が必要になることがあったのでその時につくった構成はこんな感じです。

f:id:pergere:20190821173637p:plain
オンプレ+AWS構成図

オンプレ環境にはCloudFront経由でしかアクセスをとおさないようにすることでキャッシュとかをある程度使えるようにしつつも今後オンプレの部分もAWSや他のクラウドに載せ替えたいといった際にはCloudFrontのビヘイビアの差し替えをすることでメンテ時間を短くできるなどがありますので順次AWSに移設していくなどもできるかなと思ってます。

lightsailを使ってCloudfrontも使いたい

lightsailを使って定額でWordPress作りたいけどlightsailの前にCloudFrontを配置してキャッシュをうまく使いたいなんて場合の構成はこんな感じにしました。

f:id:pergere:20190822173813p:plain
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のサービス開発の流れ

  1. プレスリリース
  2. FAQの作成
  3. お客様の体験の流れの整理

グローバル:デジタルアドバータイジング

ML(マシンラーニング)はデジタル広告ビジネス成功の中核をなす

キーワード

  • AmazonSagemaker
  • DeepLearningAMI

アプデートのご案内

Rekognitionが東京で使えるようになった

文字起こしサービス、翻訳サービスがサービスイン

Sagemakerが東京リージョンで利用可能になった

  • ハイパーパラメータチューニングが可能になった
  • Chainer、PyTorchに対応
  • Tensorflow1.8対応
  • 推論エンドポイントがオートスケーリング、Batch推論に対応
  • ノートブックインスタンスで起動時に自動実行されるスクリプトを定義可能になった
  • ビルトインアルゴリズム拡充
  • DeepLens
    • Kinesis Video Streamsへのカメラ動画送信をサポート

ML Ops on AWSアーキテクチャ紹介

Infrastructure as a Code

※環境依存を起こさない

micsoservices

※依存関係を大きくして個別にデプロイできないという状況をつくらない    * AWS SageMaker * AWS ECS

Continuous Delivery

※新しいことをしたら悲惨な結果を返すことがある

※ちゃんと切り戻しができるようにしておくことが大切

  • AWS SageMaker
  • AWS Greengrass
  • AWS StepFunctions

いちばん大切な事例は参加者の特権ということで。。。

あとでまとめられたらアップ方向で検討!

CloudFront+S3+Lambda@Edgeを使ったときにBasic認証してみる

CloudFront+S3+Lambda@Edgeを使ったときにBasic認証してみる

最近要望が多かったのでサーバレス構成でBasic認証してみたのでその時のソースをメモメモ。。

下のソースをLambdaに書いてビューアーリクエストトリガに設定しておけばできましたので!

続きを読む

CloudFront+S3+Lambda@Edgeを使ったときにVaryヘッダを返してみる

CloudFront+S3+Lambda@Edgeを使ったときにVaryヘッダを返してみる

最近ちらっと困ったのでメモ書きのように残しておきます!

下のソースをLambdaに書いてビューアーレスポンストリガに設定しておけばできましたので!

続きを読む

VagrantでAmazonLinux環境を構築してみる

VagrantでAmazonLinux環境を構築してみる

VagrantインストールからRubyRailsのインストールまで

動作環境構築について【Vagrant

最新のAmazonLinuxのboxを持ってくる(Virtualbox Only)

続きを読む

AWSでワードプレスを立ち上げてみる

AWSワードプレスを立ち上げてみる

今回はできるだけカンタンにワードプレスの立ち上げをしてみようと思います。

どのサービスを使うか

できるだけ簡単にということでlightsailを利用します!

↓これ

続きを読む

AWS Summitに行ってきた!レポート※セッションメモ (2017/06/02)[Sansan]AWSが支えるEightのリコメンデーションエンジンの裏側

AWS Summitに行ってきた!レポート※セッションメモ (2017/06/02)[Sansan]AWSが支えるEightのリコメンデーションエンジンの裏側

AWS Summit 2017/06/02 [Sansan]AWSが支えるEightのリコメンデーションエンジンの裏側

自己紹介

  • 鈴木康寛

サーバーレス×ビッグデータ

アジェンダ

  • Eightにおけるリコメンデーション
  • リコメンドアーキテクチャ刷新
  • リコメンドデータ洗い替え
続きを読む