Upgrade to Pro — share decks privately, control downloads, hide ads and more …

大量リダイレクトも怖くない!CloudFront KeyValueStoreでサービスサ...

大量リダイレクトも怖くない!CloudFront KeyValueStoreでサービスサイトリニューアルを楽々乗り越えた話 / How we redirect large-scale web pages by CloudFront Functions and CloudFront KeyValue Store

2024/7/5 に開催されたDevelopersIO 2024 SAPPORO Business Labでの登壇資料です。

サービスサイトリニューアルに伴うリダイレクトの設定が大量かつ正規表現化が困難という課題を、CloudFront FunctionsとCloudFront KeyValueStoreを活用して解決した事例をご紹介します。
本事例を通して、CloudFront FunctionsとCloudFront KeyValueStoreを使用したリダイレクト実装時にハマったことや本番切替時のトラブルシューティングなど、実践的な経験に基づいたノウハウも紹介します。
サービスサイトリニューアルを控えている方、大量リダイレクトの課題にお悩みの方、ぜひこの事例紹介を参考にしてみてください。
#devio2024

CO-OP Sapporo

July 05, 2024
Tweet

More Decks by CO-OP Sapporo

Other Decks in Technology

Transcript

  1. AWS SAMURAI 2015 JAWS-UGアーキテクチャ専門支部 JAWS-UG情シス支部 生活協同組合コープさっぽろ デジタル推進本部 システム企画部 インフラチーム 山﨑

    奈緒美 ご挨拶と自己紹介 大阪出身。 就職で上京し、ソフトハウスでインフラエンジニア 地図情報システム開発会社でひとり情シス 旅行会社の情シス部門でクラウド担当 2020年9月に東京から札幌へ移住し10月よりコープさっぽろへJOIN。 AWSのことならなんでも担当。 @nao_spon I ♡ Route53 IAM Organizations 夏はロードバイク、冬はスノボしてます。仲間募集中!
  2. 生活協同組合コープさっぽろ デジタル推進本部 システム企画部 アプリ・サイトチーム 木原 卓也 ご挨拶と自己紹介 AWS Community Builder

    (Frontend Web and Mobile, since 2021) Amplify Japan User Group 運営メンバー 熊本出身の普通のソフトウェアエンジニア。 愛知県の大学進学後、なぜか北海道で就職。 システム開発の会社で、業務系からゲーム系まで各種開発業務に従事。 2021年4月からコープさっぽろへJOIN。 店舗系のシステム導入やAWS移行を担当し、 2024年3月よりアプリ・サイトチームへ参画。 Flutter 1年生。 I ♡ AWS Amplify 好きなフィギュアスケートの技 スプレッド・イーグル
  3. 生活協同組合コープさっぽろについて(※2024年3月現在) 設立年月日 1965年7月18日 組合員数 201万人(北海道総人口523万人の約38%) 出資金額 897億円 総事業高 3,186億円 職員数

    14,743名(契約職員・パートアルバイト含む) 店舗数 109店舗 移動販売車 94台(134市町村) 宅配物流センター 41センター 10デポ 車両1,300台 配食工場 6工場(札幌、函館、苫小牧、旭川、釧路、帯広) 生産工場 石狩食品工場、江別食品工場、はまなす食品、江別物流センター 生鮮センター(PC)、ドリームファクトリー(函館)
  4. 旧ページと新ページのURLが全く異なる 25 • ほぼ全てのページがクエリパラメーター ◦ /content/?id=xxxx の形式 ◦ idの番号はページを作った順に割り当てられており法則性がない ◦

    リニューアル後はディレクトリ構成を明確化することになった ◦ パラメーターはidだけではない ◦ 正規表現化できない
  5. 旧ページと新ページのURLが全く異なる 26 ページタイトル 旧URL 新URL コープさっぽろの事業概要 /corporate/content/?cat=1 /about/overview/ 理事長からのご挨拶 /corporate/content/?cat=2

    /about/greeting/ 広報誌「Cho-co-tto」(ちょこっと) TOP /content/?id=61 /about/pr-magazine/ お店でのポイントのため方・使い方 /content/?id=3225 /shopping/store/point/ 宅配「トドック」 /content/?id=11 /shopping/todok/ トヨヒコスイーツ /content/?id=3007 /item/toyohiko/ コープの共済 /content/?id=55 /service/cooperative/ 畑でレストラン /content/?id=1903 /event-activity/hatake-de-restrauant/ えほんがトドック /content/?id=1302 /children/book-gift/
  6. 旧ページと新ページのURLが全く異なる 27 • 一部ディレクトリが移行対象外のためサブドメイン化して分離 ◦ 子会社サイト ◦ 採用サイト ◦ カルチャースクールサイト

    • 店舗情報画面をSaaSサービスへ移行した ◦ サブドメインが変わる ◦ 各店舗URLに店舗コードを振るようにした ◦ スマホアプリと連動させる時に店舗コードの方が都合がよかった
  7. 旧ページと新ページのURLが全く異なる 28 ページタイトル 旧URL 新URL 北海道はまなす食品株式会社 /content/?id=46 https://h-hamanasu.jp/ 採用サイト /corporate/recruit/

    https://recruit.sapporo.coop/ 新卒採用情報 /newgraduate/ https://newgraduate.sapporo.coop 店舗情報 /shop/ https://map.sapporo.coop/store/ 新さっぽろ店 店舗詳細 /shop/detail.html?no=117 https://map.sapporo.coop/store/detail/0103 生活文化教室 /culture/ https://life-culture.sapporo.coop
  8. どのようにしてリダイレクトさせるか 34 • 選定のポイント CloudFront Functions Lambda@Edge 同時実行数 上限なし デフォルトでMAX1000

    必要に応じて上限緩和申請が必要 課金 100万回実行時 $0.1 1リクエストあたり$0.0000001 実行時間に対する課金なし 100万回実行時 $0.6 1リクエストあたり$0.0000006 実行時間に対する課金あり
  9. CloudFront Functions & KeyValue Store 40 CloudFront KeyValue Storeに登録するKeyとValue ページタイトル

    旧URI=Key 新URL=Value コープさっぽろの事業概要 /corporate/content/?cat=1 https://www.sapporo.coop/about/overview/ えほんがトドック /content/?id=1302 https://www.sapporo.coop/children/book-gift/ お店でのポイントのため方・使い方 /content/?id=3225 https://www.sapporo.coop/shopping/store/poin t/ 宅配「トドック」 /content/?id=11 https://www.sapporo.coop/shopping/todok/ トヨヒコスイーツ /content/?id=3007 https://www.sapporo.coop/item/toyohiko/ 採用サイト /corporate/recruit/ https://recruit.sapporo.coop/ 店舗情報 /shop/ https://map.sapporo.coop/store/ 生活文化教室 /culture/ https://life-culture.sapporo.coop
  10. CloudFront Functions & KeyValue Store 42 • 直面した課題 ◦ 使用しているクエリパラメーター名が複数ある

    ◦ GoogleAnalytics用のクエリパラメーターは無視する ◦ ディレクトリ宛等、クエリパラメーターがないパターンもある ◦ S3オリジンのためディレクトリインデックス処理が必要 ◦ for文とwhile文どっちがいいかな ◦ asyncってなんやねん ◦ constとletは何がちゃうねんん ◦ thisってどれのことやねんんんんんんんん
  11. アプリエンジニア観点での理解 47 CloudFront Functions, CloudFront KeyValue Store で 要件を満たす実装はできる? •

    基本的な新旧変換は KVS による変換で問題なさそう ◦ サンプルにある通り。 • 速度命 (<1ms) ◦ Node.js ではなく、独自の JavaScript ランタイムっぽい。 ◦ どのくらい機能が使えるか?
  12. CloudFront Functions でできるのか? 48 CloudFront Functions の JavaScript ランタイム (2.0)

    https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/functions-javascript-runtime-20.html CloudFront Functions の JavaScript ランタイム環境は ECMAScript (ES) バージョン 5.1 に準拠しており、ES バージョン 6~12 の一部の機能をサポートしています。また、 ES 仕様に含まれない非標準メソッドも提供しています。 ざっとみた感じ、現代的な書き方で十分に書けそう。 (できればfor文を書かずにいきたい。)
  13. ペアプロをどう進めるか 52 ペアプロ = ペア・プログラミング どういう目的で「二人一組で」行うかを考えておく必要がある。 一般的なペアプロの目的 • ペア対象者の教育 (技術を持つ人から、もう一人へ継承)

    • 完成までの期間短縮 (双方の技術を同時に注ぎ込み課題解決) 付随効果で、複数人でコードをみながら開発するので、 開発したコードへの理解が双方に得られる。 今回は...?
  14. ペアプロをやる前に目標設定 53 本番リリースまで時間がない →完成までの期間短縮(担当者の独力以上の速度で走り切る) • ある程度コードスニペットをこちらから提示し、いい感じに配置していく形式で進める。 • 多少の書き味の乱れには目を瞑る。 • 機能を満たす形で完成させることが第一。

    実施する中で自分が心がけたこと: 1. 教育ではないので、育成に重きを置きすぎない。 2. リリース直後に修正は発生するが一週間もすれば触らなくなるので、 メンテナンス性に重きを置きすぎない。 3. コード量も少なく内容も把握しているので、今後何かあっても木原が何とかできる。
  15. ペアプロを実施 55 書いてもらったコードの一部(クエリパラメータの処理) //リクエストのクエリストリング名を取得 const queryNameArray = Object.keys(event.request.querystring); // 使用するクエリパラメーター名

    =id, cat, area, no, ID // 使用するクエリパラメタ名があるかを判定してパラメタ名を取得 const targetQueryKeys = Object.keys(event.request.querystring) .filter((item) => ['id', 'area', 'cat', 'no'].includes(item.toLowerCase()) ); // URI部分とパラメタ名=Valueを文字列連結 // クエリパラメタがEmptyもしくは使用しないパラメタの場合は URI部分のみを返す const targetKey = event.request.uri + String(targetQueryKeys.length > 0 ? '?' + targetQueryKeys .map((item) => `${item.toLowerCase()}=${event.request.querystring[item].value}`) : '');
  16. ペアプロを実施 56 書いてもらったコードの一部(クエリパラメータの処理) //リクエストのクエリストリング名を取得 const queryNameArray = Object.keys(event.request.querystring); // 使用するクエリパラメーター名

    =id, cat, area, no, ID // 使用するクエリパラメタ名があるかを判定してパラメタ名を取得 const targetQueryKeys = Object.keys(event.request.querystring) .filter((item) => ['id', 'area', 'cat', 'no'].includes(item.toLowerCase()) ); // URI部分とパラメタ名=Valueを文字列連結 // クエリパラメタがEmptyもしくは使用しないパラメタの場合は URI部分のみを返す const targetKey = event.request.uri + String(targetQueryKeys.length > 0 ? '?' + targetQueryKeys .map((item) => `${item.toLowerCase()}=${event.request.querystring[item].value}`) : ''); このパラメータもKVSのキーのマッ チングに使うため、 絞り込んで保持しておく必要あり。 マッチング用のURLの作成。
  17. 本番切替時に発生した問題 60 • リダイレクト不要なアクセスが全部TOPにリダイレクト • wwwなしからwwwありのドメインへのリダイレクト漏れ • ディレクトリインデックス処理が店舗情報管理SaaS宛にも適用されていた • リダイレクト対象の誤りや漏れがあった

    • 別サブドメイン化したサイトへのリダイレクトがごっそり漏れていた ◦ 全てTOPにリダイレクトするようにしていたが認識齟齬が発生していた • セキュリティヘッダー処理の追加が必要なことに気がついた
  18. 本番切替時に発生した問題 61 • リダイレクト不要なアクセスが全部TOPにリダイレクト • wwwなしからwwwありのドメインへのリダイレクト漏れ • ディレクトリインデックス処理が店舗情報管理SaaS宛にも適用されていた • リダイレクト対象の誤りや漏れがあった

    • 別サブドメイン化したサイトへのリダイレクトがごっそり漏れていた ◦ 全てTOPにリダイレクトするようにしていたが認識齟齬が発生していた • セキュリティヘッダー処理の追加が必要なことに気がついた 当日中に全て対応(木原さんに泣きついた
  19. CloudFront KeyValue Storeで困ったこと 64 • cloudfront-keyvaluestore put-keyコマンド ◦ --if-matchオプションでKeyValue StoreのETagを指定する必要がある

    ◦ KeyValue Pairsの追加が行われるたびにETagが変わる =CONCATENATE( "aws cloudfront-keyvaluestore put-key --key ",A2," --value ",B2," --kvs-arn arn:aws:cloudfront::123456789012:key-value-store/kvsid --if-match $( aws cloudfront-keyvaluestore describe-key-value-store --kvs-arn arn:aws:cloudfront::123456789012:key-value-store/kvsid --query 'ETag' --output text )" )