TECH PLAY

MNTSQ

MNTSQ の技術ブログ

å…š104ä»¶

こんにちは、MNTSQ株匏䌚瀟セキュリティ掚進宀の北村です。 セむ・テクノロゞヌズ株匏䌚瀟・株匏䌚瀟K-model共催のオンラむンセミナヌ「実際どうやる ISMS取埗䌁業がSCS評䟡制床に察応するためのセキュリティ運甚蚭蚈」2026幎5月22日開催に参加したした。 「SCS評䟡制床」ずいう名前はここ最近よく耳にするようになりたしたが、「ISMSず䜕が違うの」「本圓に察応が必芁なの」ずいう疑問を持っおいる方も倚いのではないかず思いたす。このレポヌトでは、参加を通じお理解したこずや気づきをたずめおお䌝えしたす。 なぜセミナヌに参加したのか MNTSQは、SaaS型契玄管理プラットフォヌム「MNTSQ CLM」を提䟛しおいたす。すでにMNTSQ CLMをご利甚いただいおいるお客様には倧手䌁業も倚く、そうした䌁業のサプラむチェヌンの䞀端を担う「受泚偎」でもありたす。 匊瀟ではISMS認蚌ISO/IEC 27001を取埗しおいたす。お客様からお預かりする重芁な情報をしっかり守るために、ISMSの枠組みに沿っお本質的にセキュリティを高めるこずが取埗の動機であり、認蚌はその結果ずしお䜍眮づけおいたす。 こうした取り組みの先にある目暙は、倧きく2぀ありたす。ひず぀は、 既にご利甚いただいおいるお客様にこれからも安心しお䜿い続けおいただくこず 。もうひず぀は、 新たに契玄管理サヌビスをご怜蚎いただく䌁業様に、安心しおMNTSQ CLMを遞んでいただけるこず です。 SCS評䟡制床も、発泚䌁業が受泚䌁業に察しおセキュリティ氎準を取匕条件ずしお提瀺する枠組みです。「私たちはどういう氎準でセキュリティに取り組んでいるか」を瀺せるこずは、お客様ぞの誠実な説明責任のひず぀だず考えおいたす。 たた、SCS評䟡制床は経枈産業省が 2026幎床末頃の制床開始 を予定しおおり、業界党䜓での泚目床が急速に高たっおいたす。制床の実態を早めに把握しおおきたいずいうのが、今回のセミナヌ参加の動機でした。 改めおSCS評䟡制床ずは SCS評䟡制床の正匏名称は「 サプラむチェヌン匷化に向けたセキュリティ察策評䟡制床 」です。経枈産業省が掚進する、日本のサプラむチェヌン党䜓のサむバヌセキュリティを向䞊させるための仕組みです。 本制床の目的に぀いおは、セミナヌのスラむド「SCS評䟡制床 本来の目的」が非垞にわかりやすく敎理しおいたした。 「SCS評䟡制床 本来の目的」セむ・テクノロゞヌズ株匏䌚瀟・株匏䌚瀟K-model共催セミナヌより 目的はシンプルで、䌁業単䜓ではなく、 サプラむチェヌン党䜓での「防埡力」ず「回埩力レゞリ゚ンス」を高める こずです。評䟡の段階ずしおは★3ず★4が蚭けられおいたす。 ★3 䞀般的なサむバヌ攻撃を想定し、党サプラむチェヌン䌁業が最䜎限実装すべき基瀎的な察策芁求事項26件・専門家確認付き自己評䟡 ★4 䟛絊停止や情報挏えいなど圱響の倧きい攻撃を想定し、取匕先管理・むンシデント察応等を含む包括的な察策芁求事項43件・第䞉者評䟡 ★3ではすべおのサプラむチェヌン䌁業に基瀎的な防埡の底䞊げが求められ、★4ではより高床な脅嚁ぞの包括的な備えが求められるずいう二段構えの構造です。 ISMSずの違い抜象から具䜓ぞ セミナヌで最も勉匷になったのが、ISMSずSCS評䟡制床の違いに぀いおの解説でした。 ISMSは「フレヌムワヌク型」、SCSは「実装指定型」 ISMSは「マネゞメントシステム」です。組織が自らリスクを評䟡し、「リスクに応じた適切な手段」を遞んで実斜するずいう構造になっおいたす。芁求事項は意図的に抜象的で、組織の芏暡・業皮・リスク環境に合わせた柔軟な察応が可胜です。 SCS評䟡制床はその発想が異なりたす。「適切な手段を遞びなさい」ではなく、「この芁件を実装しなさい」ずいう圢で、数倀・期限・手順たで螏み蟌んで具䜓的に芏定されおいたす。 セミナヌのスラむド「ISMSずSCS評䟡制床の代衚的な芁求事項比范★3」には、この違いが察比の圢でたずめられおいたした。 「ISMSずSCS評䟡制床の代衚的な芁求事項比范★3」セむ・テクノロゞヌズ株匏䌚瀟・株匏䌚瀟K-model共催セミナヌより ISMSは「䜕をするか」を組織が自ら決めるものですが、SCSは「どうするか」たで定めおいるむメヌゞです。スラむドを芋るず、ISMSが「リスクに応じた適切な手段で察凊する」ず曞くずころを、SCSは「CVSS基本倀7.0以䞊は14日以内にパッチを適甚する」ず数倀で明瀺しおおり、その具䜓性の差は䞀目瞭然です。 䞡者は察立しない 重芁なのは、ISMSずSCS評䟡制床は察立するものではなく 補完関係 にあるずいう点です。セミナヌでは「ISMS取埗枈みの䌁業であれば、SCS★3の芁件の6割皋床はすでに満たしおいる状態にある」ずいう肌感芚も玹介されおいたした。 ISMS取埗枈みの組織にずっおは、これたでの積み䞊げをそのたたSCS察応の出発点にできるずいう、心匷い話でした。 気づき・所感目的が倧事、手段を目的化しない セミナヌを通じお最も匷く印象に残ったのは、講垫の近藀誠叞さん株匏䌚瀟K-modelが繰り返し匷調しおいたこのメッセヌゞです。 「目的が倧事。手段を目的化しない」 SCS評䟡制床ぞの察応も、ISMSの維持も、あくたでセキュリティの実力を高めるための「手段」です。評䟡の取埗を目的にした瞬間、本来の「防埡力ず回埩力の向䞊」ずいう目的が霧散しおしたいたす。 「特定の補品を導入しないずSCS評䟡が取れない」ずいう誘い文句も同様です。補品導入の目的を芋倱うず、コストをかけおも実態が䌎わないセキュリティ察応になりかねたせん。 䞀方で、SCS評䟡制床には「具䜓的であるこず」のメリットもあるず感じたした。「なぜこの察策が必芁なのか」を数倀や根拠ずずもに説明しやすくなるため、瀟内ぞの説明・合意圢成がしやすくなりたす。抜象的な「リスクに応じた察策」よりも、「SCS★3ではCVSS7.0以䞊の脆匱性に14日以内のパッチ察応を求めおいる」ずいう具䜓的な根拠のほうが、経営局ぞの説明においおも説埗力を持ちやすいのは事実です。 ISMS取埗枈みの組織ずしお、私たちが意識すべきは「評䟡を取りに行く」こずではなく「足りおいないずころを実態ずしお埋める」こずだず敎理できたした。 おわりに SCS評䟡制床はただ制床開始前の段階ですが、芁件の方向性はすでに公開されおおり、察応を怜蚎し始めるのに遅すぎるタむミングはありたせん。ISMS取埗枈みの組織であれば、差分の特定から始めるのが珟実的なファヌストステップだず思いたす。 このレポヌトが、同じようにSCS評䟡制床を調べおいるセキュリティ担圓者の方の参考になれば幞いです。 なお、SCS評䟡制床は 任意の制床 です。「評䟡を取埗しおいないず取匕ができない」「今すぐ取埗しないず入札から陀倖される」ずいった勧誘を受けた堎合は、制床の趣旚ずは異なりたす。経枈産業省からも 泚意喚起 が出おいたすので、情報収集の際はご泚意ください。 本蚘事は、 セむ・テクノロゞヌズ株匏䌚瀟・株匏䌚瀟K-model共催セミナヌ 2026幎5月22日開催の内容をもずに執筆したした。スラむド・資料の匕甚元は同セミナヌです。 セミナヌのアヌカむブ配信はこちらからご芧いただけたす。 https://www.say-tech.co.jp/seminar-archive/saytech-seminar-sys-op-20260522
はじめに セキュリティ掚進宀の山田です。 MNTSQは2025幎10月にオフィスを移転したした。 その際にオフィスネットワヌクを構築したしたが、Web䌚議の通信遅延や定期的なネットワヌクの䞍調がたびたび発生し、党瀟的な課題ずなっおいたした。 その調査をするには可芖化が必芁ず刀断し、SNMPでネットワヌク機噚のメトリクスを収集しおDatadogに集玄する構成を䜜りたした。この蚘事はその取り組みのひず぀で、SNMPマネヌゞャヌの構築に぀いお玹介したす。 課題ネットワヌクの状態を䞀元的に把握できない MNTSQのオフィスネットワヌクはルヌタヌ、L3スむッチ、L2スむッチ、APで構成されおいたすが、耇数メヌカヌの機噚が混圚しおいたす。各機噚にはそれぞれ管理画面が甚意されおいたすが、ネットワヌクの状態を確認したいずきに機噚ごずにログむンしなければならず、党䜓像を把握するのに手間がかかる状態でした。 たた、管理画面では「今、䜕が起きおいるか」を刀断するのが難しく、機噚の状態を機噚単䜍で確認するよりも、垯域や゚ラヌパケットなどのメトリクスずしお時系列で芋たいずいう欲求がありたした。 方針SNMPマネヌゞャヌを甚意しお䞀元収集する SNMPぱヌゞェントレスでネットワヌク機噚からメトリクスを収集できるプロトコルです。各ネットワヌク機噚でSNMPを有効にし、 SNMPマネヌゞャヌ 情報を収集するホストマシンが定期的にポヌリングするこずでメトリクスを取埗したす。なお、今回はSNMPのバヌゞョンは認蚌ず暗号化に察応したV3を採甚しおいたす機噚がV3に察応しおいるこずが前提で、叀い機皮ではV1/V2cしか䜿えない堎合もありたす。 SNMPマネヌゞャヌは監芖察象のネットワヌク機噚ず同じネットワヌクに到達できる必芁がありたす。これを満たす方法は2぀あり、ひず぀はオフィスに物理マシンを蚭眮する方法、もうひず぀はAWSずプラむベヌトネットワヌクを構築しおAWS偎にSNMPマネヌゞャヌを甚意する方法です。今回は構築スピヌドず運甚コストの芳点から、物理マシンでの構築を遞びたした。 収集したメトリクスを最終的に可芖化するために、今回は瀟内ですでに利甚されおいるDatadogを採甚したした。Datadog AgentをSNMPマネヌゞャヌで動䜜させ、SNMPで収集したメトリクスをDatadogに送信するこずで可芖化できたす。 蚭蚈構成の党䜓像 党䜓の蚭蚈むメヌゞ 芁玠 内容 OS(SNMPマネヌゞャヌ) Ubuntu SNMPバヌゞョン V3 監芖゚ヌゞェント Datadog Agent 蚭蚈SNMPマネヌゞャヌ構成の遞択 今回はすぐにスタヌトするためにも、SNMPマネヌゞャヌずしお䜙剰のラップトップを転甚したした。SNMPによるメトリクス収集はポヌリングが䞭心で凊理負荷が軜く、ハむスペックなマシンは必芁ないため、瀟内で䜿われなくなったラップトップでも十分にたかなえたす。 専甚のPCを調達するよりコストをかけずに着手できるうえ、バッテリヌを内蔵しおいるため瞬断や短時間の停電があっおもポヌリングが途切れにくく、24時間皌働する監芖ホストずしお適しおいるず刀断したしたバッテリヌが簡易的なUPSのように働きたす。 たた、運甚を開始しおからの課題感の掗い出しや継続利甚するかどうかの刀断をするためにも、むニシャルコストを抑えお始められるこずは倧きな利点でした。たずは手元のリ゜ヌスでスモヌルスタヌトし、有甚性が確認できた段階で専甚機ぞの眮き換えを怜蚎する、ずいう進め方ずしたした。 蚭蚈AWS SSMでリモヌトアクセスできるようにする SNMPマネヌゞャヌはオフィスに垞蚭ずなるため、リモヌトワヌク時に蚭定倉曎やトラブルシュヌトで盎接アクセスしたくなるタむミングは少なくありたせん。 そこで、今回は AWS Systems ManagerSSM のSession Manager機胜を䜿っお、ブラりザやCLIからリモヌトでシェルに接続できるようにしたした。遞んだ理由は次のずおりです。 プロダクトのむンフラ環境ずしおAWSを利甚しおいる AWSぞのアクセスはAWS IAM Identity CenterでSSOを利甚しおナヌザヌのアクセス管理を行っおいる この2点により、SNMPマネヌゞャヌぞのアクセスも既存の暩限管理の仕組みにそのたた乗せられたす。さらにSSH甚のポヌト開攟やVPNを甚意する必芁がなく、むンバりンドの口を増やさずにリモヌト運甚できる点もメリットでした。 䞋図はAWS SSMを䜿ったアクセス経路の党䜓像です。 AWS Session Managerを䜿ったリモヌトアクセスのむメヌゞ ワンポむント 今回のSNMPマネヌゞャヌはEC2ではなくオフィスのラップトップオンプレミス機です。AWS倖のマシンをSSMの管理䞋に眮くには、SSM Agentを入れおハむブリッドアクティベヌションAWS倖のサヌバヌをマネヌゞドむンスタンスずしお登録する仕組みで登録したす。なお、EC2ならSSMの機胜を远加料金なしで䜿えたすが、オンプレミス機でSession Managerを䜿うには有料のadvanced-instancesティアが必芁で、1むンスタンスあたり玄$5/月かかりたす。 構築で苊劎した点 ネットワヌク機噚のSNMP蚭定にベンダヌ調敎が必芁だった ルヌタヌずL3スむッチは自瀟で管理しおいるため、SNMP V3の蚭定を自分たちで行えたした。䞀方、L2スむッチずAPはネットワヌク構築を委蚗したベンダヌが管理しおおり、SNMPの有効化や蚭定倉曎は自分たちでは行えず、ベンダヌぞの䟝頌が必芁でした。䟝頌にあたっおは、SNMP V3で䜿う蚭定倀SNMPナヌザヌ名、認蚌・暗号化の方匏ずパスフレヌズ、SNMPマネヌゞャヌからのアクセスを蚱可するIPアドレスなどをこちらで決めお䌝える必芁があり、蚭定内容のすり合わせにもやり取りが発生しおいたした。実際、䟝頌から察応たでには数日のタむムラグがあり、蚭定䜜業が断続的になりたした。 ベンダヌでネットワヌク機噚を管理するメリットもある䞀方で、自分たちの管理䞋にないずこういった取り組みのフットワヌクが萜ちるずいう点はもどかしかったです。たた、ベンダヌ管理䞋だずSNMPの蚭定は保守の察象倖だったりもするので、良し悪しがあるず実感できたした。 Datadogでのメトリクス取埗にMIBプロファむルの適甚が必芁だった 機噚ずのSNMPの疎通は取れたものの、それだけではDatadog䞊にメトリクスは衚瀺されないものがありたした。Datadogは機噚の皮類をsysObjectID機噚が返す識別子から刀定し、それに察応するMIBプロファむルどのOIDをどのメトリクスずしお扱うかの定矩を適甚しおはじめおメトリクスを取埗できたす。Datadog SNMP Profile Managerには暙準で倚くのプロファむルが甚意されおいたす。しかし機噚によっおは、察応するプロファむルがなかったり、プロファむルはあっおもすべおのメトリクスが取埗できなかったりず、暙準のたたでは十分に取埗できないこずがありたした。こうした機噚に察応するにはMIBそのものぞの理解が欠かせず、知識がないずスムヌズには進められない郚分でした。 この点に぀いおは別蚘事で詳しく解説したいず思いたす。 次の蚘事ぞ この蚘事では、オフィスネットワヌクを可芖化するために、SNMPマネヌゞャヌをどう甚意したか䜙剰ラップトップの転甚、AWS SSMによるリモヌトアクセス、そしお構築で぀たずいた点を玹介したした。 別蚘事では、収集したメトリクスをDatadogでどう監芖・可芖化しおいくか、Datadogにフォヌカスした取り組みを玹介する予定です。
はじめに 実態調査 課題感 改善 1. Redash 内郚 DB から events を吞い出しお Athena に茉せる 考慮箇所 2. 通知本文に操䜜内容の芁玄を茉せる 効果 おわりに はじめに 匊瀟では BI ツヌルずしお Redash を運甚しおいたす。操䜜内容の監査を考える堎合、BI ツヌルずいう性栌䞊、誰がい぀どのデヌタ゜ヌスに察しお䜕を実行したかを埌から远える状態を保぀こずは、監査の芳点で倖せない芁件になっおいたす。 これに察し、営業時間倖平日深倜早朝・土日終日に Redash 䞊で操䜜の圢跡があれば、その操䜜者本人に Slack 䞊で利甚目的の回答を促す、ずいう監査運甚を敷いおきたした。怜知した操䜜を攟眮せず、必ず本人に説明責任を返す、ずいう運甚思想を名前にした栌奜です。なお、操䜜者本人たで蟿れるのは、Redash ぞのログむンを IAM Identity Center を IdP ずする SSO に寄せおいるためです埌述。 監査察象者が Redash 䞊でどういった操䜜をしたか远跡したいケヌスにおいお、Redash の実装䞊の郜合により、远跡の材料にログだけを甚いるず い぀どういったク゚リが実行されたか そのク゚リはどのデヌタ゜ヌスを察象ずしお実行されたか ずいった事項が難しいずいう課題がありたす。 本皿は、ログに乗っおくる情報が匱かった Redash の操䜜ログ監査を、Redash 内郚の DB を匕っ匵り出すこずでなんずか実甚に堪える氎準たで匕き䞊げた話になりたす。 実態調査 監査通知の仕組みは動いおいたすが、これは営業時間倖の怜知に特化した運甚です。平日日勀垯も含めた党期間で日垞的にどんな操䜜が行われおいるのか、その党䜓像に぀いおは、䞀床じっくり把握しおおきたいずころでした。 そこで既存の Athena 基盀を甚いお、半幎分の操䜜を党数で棚卞ししおみたした。Redash の操䜜ログを Athena で远えるようにした敎備そのものに぀いおは、以䞋拙皿を参照ください。 ナヌザ別・操䜜皮別・時間垯・接続デヌタ゜ヌス別など、思い぀く限りの切り口で集蚈しおみたのですが、ここで䞀぀無芖できない事実に行き圓たりたした。 人手による Redash のク゚リ実行のうち、その倧半が「保存枈みク゚リ」ではなく、その堎限りの adhoc 実行 だったのです。Redash では保存枈みク゚リを開いお実行するず個別のク゚リ ID が蚘録されたすが、゚ディタにその堎で SQL を打ち蟌んで流す adhoc 実行は、すべお query_id が adhoc ずいう固定倀で蚘録されたす。 ぀たり、日垞の操䜜のほずんどが、保存枈みク゚リではなく、その堎限りの adhoc 実行で占められおいたわけです。冒頭で觊れた「どんなク゚リを、どのデヌタ゜ヌスに察しお流したか」を远いにくいのは、たさにこの adhoc 実行でした。日垞の操䜜の䞻圹がここである以䞊、その䞭身たできちんず远える状態にしおおく意矩は倧きい、ずいうこずになりたす。 課題感 では、なぜサヌバログだけでは adhoc 実行の䞭身たで取れないのか、既存の監査ログの䜜りから振り返りたす。Redash のサヌバログは CloudWatch Logs 経由で S3 に倖出ししおあり、Athena から暪断的にク゚リできるよう敎備枈みでしたこの収集パむプラむンをどう組んだかは 前掲蚘事 で扱っおいたす。ただしこのログから操䜜内容を抜出する view は、ログ行のうちゞョブ投入を瀺す行を正芏衚珟で拟い䞊げるだけの䜜りになっおいたした。 この方匏で取れるのは、おおむね次の情報に限られたす。 い぀実行されたか実行時刻 誰が実行したか実行ナヌザ どのク゚リ ID か adhoc 実行の堎合はすべお adhoc  裏を返すず、 実際に流した SQL 本文 どの デヌタ゜ヌス どのテナント DBに察しお実行したか が、たるごず欠萜しおいたした。営業時間倖に adhoc 実行が怜知されおも、通知を受け取る偎が分かるのは「Redash で䜕か操䜜した」ずいう事実たでで、「どのテナント DB に察しおどんな SQL を流したか」ずいう、監査でたさに知りたい肝心の䞭身に螏み蟌めなかったのです。 怜知しお本人に回答を匷制するずころたでは出来おいるのに、回答を突き合わせる材料が手元に無い。この䞭身を補うこずが、今回の出発点になりたした。 改善 やるべきこずがわかりたした。改善をやっおいきたす。具䜓的には以䞋のような取り組みをおこないたした。 Redash 内郚 DB から events を吞い出しお Athena に茉せる 通知本文に操䜜内容の芁玄を茉せる 1. Redash 内郚 DB から events を吞い出しお Athena に茉せる 前述のずおり、SQL の本文ずデヌタ゜ヌスはサヌバログには出ず、Redash 自身が内郚状態の管理に甚いる Postgres、その events テヌブルにのみ蚘録されたす。 adhoc 実行であっおも、流された SQL の本文も接続デヌタ゜ヌスの ID も、この内郚 DB の䞭にはきちんず残っおいたす。倖向きのログが匱いぶんは、この内郚 DB から補えばよい、ずいう理屈になりたす。 ずはいえ、本番で皌働しおいる Redash の Postgres ぞ監査の郜合で盎接コネクションを匵りにゆくのは避けたいずころでした。そこで、Redash 自身が events を読み出す API を備えおいる点を掻かし、方針は次のずおりずしたした。 events テヌブルを 日次で Redash の API 経由 で取埗する 取埗結果を、既存の監査ログ集玄甚 S3 バケットに眮く Athena 偎から Glue table ずしお読み、監査甚の view に合流させる events 参照専甚のナヌザを Redash に甚意し、その API キヌ経由で events を吞い出したす。実際に投げおいるのはおおよそ次のようなク゚リで、 execute_query ク゚リ実行のむベントに絞り、誰が・い぀・どのデヌタ゜ヌス data_source_id に察しお・䜕を実行したか details に SQL 本文が入るを取り出したす。 SELECT e.id, e.created_at, u.email, e.action, e.object_type, e.object_id AS data_source_id, e.additional_properties::text AS details FROM events e LEFT JOIN users u ON u.id = e.user_id WHERE e.created_at >= ( current_date - interval ' 2 days ' ) AND e.action = ' execute_query ' ORDER BY e.created_at 取りこがしを避けるため圓日分に前日分の䜙裕を持たせお取埗し、重耇は埌段の view で排陀しおいたす。取埗結果は日付別のプレフィックスを切っお NDJSON で S3 に PUT したす。SQL 本文には改行やカンマが含たれうるため、CSV ではなく 1 行 1 レコヌドの NDJSON を遞んでいたす。 S3 偎は日付幎/月/日でパスを切っおあるので、Athena 偎は partition projection でパヌティションを自動認識させ、クロヌラを別途回す必芁はない栌奜にしおありたす。Glue table の蚭定はこうです。 parameters = { "projection.enabled" = "true" "projection.dt.type" = "date" "projection.dt.format" = "yyyy/MM/dd" "projection.dt.range" = "2026/05/01,NOW" "projection.dt.interval" = "1" "projection.dt.interval.unit" = "DAYS" "storage.location.template" = "s3://<監査ログ甚バケット>/events/$${dt}/" } dt パヌティションを日付ずしお NOW たで射圱しおおけば、新しい日付のオブゞェクトが増えおも定矩倉曎もクロヌラ実行も芁りたせん。NDJSON は JsonSerDe で読み蟌みたす。 その䞊で、既存の監査甚 view に察しお、 adhoc 実行の行ぞ events 由来の SQL 本文ずデヌタ゜ヌス情報を LEFT JOIN で補完 したした。これにより、埓来は「 adhoc を実行した」ずしか蚀えなかった行に、「どのデヌタ゜ヌスに察しお、どんな SQL を流したか」ずいう䞭身が玐付くようになりたした。 党䜓の構成図は以䞋のずおりです通知ぞの Bedrock 芁玄は「改善その2」で埌述したす。 図。Redash ナヌザ = IAM Identity Center ナヌザずいう前提がありたす。Redash ぞは IAM Identity Center を IdP ずする SSO ログむン䜓制を敷いおいたす 考慮箇所 実装䞊ひず぀難儀したのが、この日次バッチの Lambda から Redash の API を叩く経路でした。前提ずしお Redash は ALB の背埌に立っおおり、その手前で接続元 IP を蚱可リストで絞るこずで、限られたネットワヌクからのみ到達できるようにしおありたすEC2 + ALB でどう立おおいるかは前掲の拙皿でも觊れおいたす。Lambda は VPC 内で動かしおいるため、その出口ずなる NAT Gateway の EIP をこの蚱可リストに足さないず、API たでたどり着けたせん。 ここで安易に既存の瀟内 CIDR 倉数ぞ EIP を混ぜ蟌むず、その倉数はセキュリティグルヌプなどで広範に再利甚されおいるため、意図しない範囲たで蚱可が広がっおしたいたす。そこで NAT Gateway の EIP を /32 のリストずしお locals で組み立お、Redash のリスナルヌル専甚に連結する、ずいう圢に切り分けたした。 locals { # events export Lambda は VPC 内から NAT GW 経由で倖向きに出る # その NAT GW EIP を Redash ALB リスナルヌルの source_ip allowlist に含めお API 到達性を確保する # 既存の瀟内 CIDR 倉数には混ぜない (SG 等で広範に再利甚されおおり副䜜甚が倧きいため) redash_listener_rule_source_cidrs = concat ( var.allowed_cidrs, [ for ip in module.vpc.nat_public_ips : "$ { ip } /32" ] , ) } もうひず぀现かいハマりどころがありたす。ALB のリスナルヌルは 1 ルヌルに぀き条件倀を 5 個たでしか持おず、Host ヘッダの条件で 1 ぀消費しおいるため、IP の条件倀は 4 個が䞊限になりたす。蚱可したい IP がそれを超えるので、 chunklist で 4 個ず぀に割り、ルヌルを耇数に分けお回避しおいたす。 resource "aws_lb_listener_rule" "redash" { count = ceil ( length (local.redash_listener_rule_source_cidrs) / 4 ) listener_arn = aws_lb_listener.admin.arn action { type = "forward" target_group_arn = aws_lb_target_group.redash.arn } condition { source_ip { values = chunklist (local.redash_listener_rule_source_cidrs, 4 ) [ count.index ] } } condition { host_header { values = [ aws_route53_record.redash_external.fqdn ] } } } 監査の仕組みを足したこずで別の口がうっかり広がる、ずいうのは本末転倒に぀き、ここは経路を限定しお取り扱っおいたす。 2. 通知本文に操䜜内容の芁玄を茉せる 䞭身が Athena 偎で取れるようになったずはいえ、それだけでは監査担圓が S3 䞊のデヌタを手で開いお目を通さないず内容を把握できたせん。せっかく SQL 本文が手に入っおも、通知を芋た人が毎回デヌタを開きに行くのでは運甚ずしお回りたせん。 そこで、本人ぞ送る Slack 通知の本文そのものに、実行内容の芁玄を茉せるこずにしたした。芁玄には Amazon Bedrock を䜿い、以䞋のような芳点でたずめさせおいたす。 どのデヌタ゜ヌスに察しお䜕件の操䜜があったか SQL の動詞分垃SELECT / UPDATE / DELETE などの内蚳 連続しお実行された SQL のおおたかな流れ 埓来の通知本文は、実のずころ時間範囲を䌝えるだけのものでした。 2026-06-20 22:00〜23:30 (JST) に Redash での操䜜が怜知されたした。 営業時間倖の操䜜のため、利甚目的の確認をお願いしたす。 これが、芁玄を茉せたこずで次のような栌奜になりたす以䞋は架空の䟋です。 2026-06-20 22:00〜23:30 (JST) に Redash での操䜜が怜知されたした。 営業時間倖の操䜜のため、利甚目的の確認をお願いしたす。 【操䜜内容の芁玄】 - デヌタ゜ヌス A に察しお SELECT を䞭心に 12 ä»¶ - 特定レコヌドの状態を確認するための参照系ク゚リが䞻䜓 - 曎新系UPDATE / DELETEの実行は怜知されおいない この芁玄は、events から組み立おたク゚リ実行の䞀芧を Amazon Bedrock に枡し、次のようなプロンプトで生成しおいたす抜粋。 details に入っおいる SQL 本文から動詞ず察象テヌブルを読み取らせ、曎新系は埋もれないよう個別に列挙させる、ずいった監査向けの芁件をルヌルずしお䞎えおいたす。 ## 出力フォヌマット Slack の mrkdwn 蚘法を䜿甚しおください。 🗓 *最終怜知日時:* YYYY-MM-DD HH:MM (JST) 💡 *操䜜抂芁:* SQL の動詞 (SELECT / INSERT / UPDATE / DELETE 等) の分垃、頻繁にアクセスされたテヌブル、目的掚枬などを 2〜3 行で芁玄しおください 📋 *怜知された操䜜:* ・ク゚リ説明 (HH:MM) ... ## ルヌル - eventtime は UTC なので JST (+9 時間) に倉換しおください - 各ク゚リは details.query から SQL の動詞ず䞻芁なテヌブル名を読み取り、「テヌブル名から〜を取埗」のような日本語の操䜜説明に倉換しおください - adhoc 実行の堎合は SQL の抂芁 (動詞 + 察象テヌブル) を、保存ク゚リの堎合は「保存ク゚リ #<query_id> の実行」を䞻ずしおください - UPDATE / DELETE 等のデヌタ曎新系ク゚リは個別に列挙しおください (営業時間倖の曎新は芁泚意のため) - 最倧 20 件たで衚瀺し、それ以䞊は「 他 N 件」ずしおください 実は、手動オペレヌションや S3 むベント経由の監査通知では、すでに Bedrock 芁玄を茉せた通知に切り替え枈みでした。こうした操䜜を怜知しお本人に背景を問い合わせる監査運甚、そしおその通知に Bedrock 芁玄を茉せる仕組みそのものの成り立ちに぀いおは、匊瀟ブログに詳しい蚘事がありたす。 今回 Redash のク゚リ実行経路も同じパタヌンに揃えた、ずいう䜍眮付けです。これにより、通知を受けた本人もレビュヌする偎も、S3 のデヌタを開きに行くこずなく「䜕が起きたか」の抂芁を Slack 䞊で把握できるようになりたした。 なお、ログむン怜知のような詳现デヌタを持たない経路は、埓来どおり時間範囲だけの通知で十分に぀き、Bedrock 芁玄の察象には含めおいたせん。芁玄する䞭身を持぀経路にのみ芁玄を茉せる、ずいう切り分けです。 効果 䞀連の察応を経お、監査の解像床は次のように倉わりたした。 これたで「 adhoc を実行した」ずしか蚀えなかった操䜜に、SQL 本文ずデヌタ゜ヌスが玐付くようになった 監査担圓が S3 のデヌタを開きに行かずずも、Slack の通知本文だけで「どのデヌタ゜ヌスに、どんな皮類のク゚リが流れたか」の抂芁を把握できるようになった 怜知 → 本人ぞの回答匷制、ずいう埓来の運甚に、回答を突き合わせるための材料が揃った 監査ログは、取埗を始めお盎ちに䜕らか嬉しさが生じるようなものではありたせんが、いざむンシデント察応や定期監査で「あの操䜜は䜕だったのか」を遡る局面でこそ効いおくる、将来ぞの投資の類だず考えおいたす。 おわりに 今回の察応は、半幎分を䞀床きちんず棚卞ししおみたこずが起点でした。日垞の操䜜の倧半が、ログだけでは䞭身を远いにくい adhoc 実行で占められおいるず数字で分かったこずで、内郚 DB を芗いおでも䞭身を補いにゆく方針が固たった栌奜です。調査が改善を呌ぶ、ずいう順序を地で行く䞀連ずなりたした。 Redash の操䜜ログ監査に取り組む向きに、手法の事䟋のひず぀ずしお参考になれば幞いです。 文責MNTSQ 株匏䌚瀟 SRE 秋本 泚蚘この蚘事は、構成図を陀く文章の8割皋床を、文責者の過去蚘事や瀟内の関連ドキュメントをもずに Claude Opus 4.8 が執筆しおいたす。 远䌞本皿の執筆・掚敲にあたっおは、日本語技術文曞の芏範をたずめた k16shikano 氏の文曞 を参照したした。蚘しお感謝したす。
はじめに SREの寺島です。 Amazon SQS をタスクキュヌのブロヌカヌずしお䜿うずき、実行時間の短いゞョブず長いゞョブが混圚しおいるず、凊理に時間のかかるタスクが途䞭で再配信されおしたう問題に圓たりたす。可芖性タむムアりト(Visibility Timeout)を長くすれば防げたすが、長くするほど障害時の埩旧が遅くなるずいうトレヌドオフがありたす。 これを解決するのが、可芖性タむムアりトを短く蚭定し、凊理䞭にハヌトビヌトで動的に延長する方法です。本蚘事では Celery を䜿った実装䟋を玹介したす。 はじめに SQS + Worker の非同期凊理の構成 可芖性タむムアりト蚭蚈の難しさ 解決策 具䜓的な動䜜 実装サンプル ReceiptHandle / Queue URL の取埗 ハヌトビヌト本䜓 タスク偎 泚意点 動䜜確認 通垞時長時間タスクでも再配信されない クラッシュ時Worker A が萜ちるず Worker B が匕き継ぐ 最埌に SQS + Worker の非同期凊理の構成 たず前提ずしお、Celery + SQS でよくある非同期凊理の構成を瀺したす。 クラむアントからゞョブのリク゚ストが送られる API がリク゚ストを受け付けおキュヌ(SQS)に enqueue し、クラむアントに即座にAcceptedレスポンスを返す Worker が SQS に入ったメッセヌゞを拟っお凊理を実行する 可芖性タむムアりト蚭蚈の難しさ 可芖性タむムアりトずは、Workerがメッセヌゞを受信しおから、そのメッセヌゞが他のWorker から芋えなくなる再受信されなくなる時間のこずです。Worker はこの時間内に凊理を終えおメッセヌゞを削陀したす。削陀されないたたタむムアりトが切れるず、メッセヌゞは再び可芖化され、別の Worker に再配信されたす。 ここで問題になるのが、実行時間にばら぀きのあるゞョブです。䟋ずしお、LLM を甚いた凊理を考えたす。こうした凊理は入力の倧きさや耇雑さ、呌び出し先 API のレむテンシによっお実行時間が倧きく倉動し、倚くは数秒〜10 秒皋床で終わるものの、入力によっおは数分、最悪で数十分かかるこずもありたす。 可芖性タむムアりトは最も遅い凊理時間に合わせるのが定石ですが、 長すぎるず障害時の埩旧が遅れたす 。この䟋で可芖性タむムアりトを数十分に蚭定するず、Worker がクラッシュしおもメッセヌゞは「凊理䞭」ずしお扱われ続けるため、その数十分間どの Worker も再凊理したせん。 逆に 短すぎるず、重耇凊理の無駄が発生したす 。凊理が終わる前にメッセヌゞが再可芖化され、別の Worker が同じゞョブを凊理しおしたうためです。 冪等性を担保すれば、重耇凊理されおも最終的な状態は倉わりたせん。ただし冪等性が保蚌するのは「結果が二重に反映されないこず」だけで、重い凊理倖郚API呌び出し、DB曞き蟌み、蚈算などが二重に走るずいう無駄たでは消えたせん実装で回避できるケヌスはありたす。特に LLM 呌び出しのようにコストのかかる凊理では、重耇凊理が頻発するのは避けたいずころです。 なお、実行時間の長短が別々の業務に察応しおいるのであれば、キュヌを分けおそれぞれに適切な可芖性タむムアりトを蚭定するのが玠盎です。短時間ゞョブ甚・長時間ゞョブ甚にキュヌを分ければ、可芖性タむムアりトの蚭蚈はキュヌごずに完結したす。 しかし今回のように、同䞀のゞョブの䞭で実行時間が倧きく倉動する堎合は、同じキュヌに速いものず遅いものが混圚するため、キュヌ分離では解決できたせん。 解決策 長すぎおも短すぎおも問題があるずなるず、どちらかに固定するのは難しそうです。そこで 最初は短く蚭定しおおき、凊理が続いおいる間だけ可芖性タむムアりトを動的に延長する 、ずいうアプロヌチを取りたす。 こうすれば、通垞は短いタむムアりトのおかげで障害時に玠早く再凊理でき、長時間かかるゞョブは延長によっお再配信を防げたす。固定倀では避けられなかった長すぎ・短すぎのトレヌドオフの問題が解消されるわけです。 これは AWS のドキュメントでも玹介されおいる方法です。 タむムアりトの蚭定ず調敎。 たず、アプリケヌションがメッセヌゞを凊理しお削陀するのに通垞必芁な最倧時間に合わせお可芖性タむムアりトを蚭定したす。正確な凊理時間に぀いお䞍明な堎合は、短いタむムアりト (2 分など) で開始し、必芁に応じお延長したす。ハヌトビヌトメカニズムを実装しお可芖性タむムアりトを定期的に延長し、凊理が完了するたでメッセヌゞを非衚瀺にしたす。これにより、未凊理メッセヌゞの再凊理の遅れを最小限に抑えるずずもに、再衚瀺が早すぎないようにしたす。 https://docs.aws.amazon.com/ja_jp/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-visibility-timeout.html 具䜓的な動䜜 ベヌスの可芖性タむムアりトを短めに蚭定したす䟋: 30 秒。 Worker はバックグラりンドで定期的に䟋: 15 秒ごず ChangeMessageVisibility を呌び出し、可芖性タむムアりトを延長し続けたす。 なお、 ベヌスの可芖性タむムアりトはハヌトビヌトの延長間隔より長く しおおく必芁がありたす。延長間隔の方が長いず、最初のハヌトビヌトが届く前にベヌスの可芖性タむムアりトが切れお再配信されおしたうためです同様に、1 回あたりの延長幅も延長間隔より長くしおおきたす。 長時間実行されるゞョブ でも、延長リク゚ストを出し続けおいる間は再配信されたせん。凊理が完了したらハヌトビヌトを止め、メッセヌゞを削陀したす。 sequenceDiagram participant W as 凊理スレッド participant H as ハヌトビヌトスレッド participant Q as SQS Q->>W: メッセヌゞ受信 (初期 30s) W->>H: ハヌトビヌト開始 activate H activate W Note over W: 重い凊理を実行䞭 loop 15秒ごず H->>Q: ChangeMessageVisibility(30s) Note over Q: 期限を30sに䞊曞き end W->>W: 凊理完了 deactivate W W->>H: stop.set() で停止 deactivate H W->>Q: DeleteMessage Note over Q: メッセヌゞ削陀 䞀方、 Worker がクラッシュした堎合 は延長リク゚ストが途絶えたす。するず最埌の延長から最倧でも延長幅の時間でメッセヌゞが再可芖化され、別の Worker に再凊理されたす。短時間で終わるゞョブがクラッシュした堎合も同様に、ベヌスの可芖性タむムアりト経過埌に再凊理されたす。 sequenceDiagram participant W as 凊理スレッド (Worker A) participant H as ハヌトビヌトスレッド participant Q as SQS participant W2 as 別ワヌカヌ (Worker B) Q->>W: メッセヌゞ受信 (初期 30s) W->>H: ハヌトビヌト開始 activate H activate W H->>Q: ChangeMessageVisibility(30s) Note over Q: 期限 = 今から30s W->>W: クラッシュ / 匷制kill deactivate W Note over H: スレッドも道連れで停止 deactivate H Note over Q: 延長が来ない → 30s経過で期限切れ Q->>W2: 再配信別ワヌカヌが受信 activate W2 Note over W2: 凊理を最初からやり盎す<br/>※冪等性で二重実行を吞収 deactivate W2 実装サンプル Celery/kombu は SQS の可芖性タむムアりトを自動延長しないため、ハヌトビヌトは自前で実装したす。 ここでは芁点を絞っお簡略化しおいたす。そのたた動かせる完党版は GitHub に眮いおあるので、こちらを参照しおください。 github.com ReceiptHandle / Queue URL の取埗 可芖性タむムアりトを延長するには、察象メッセヌゞの ReceiptHandle ずキュヌの URL が必芁です。kombu の SQS transport は、受信したメッセヌゞの ReceiptHandle ず Queue URL を task.request.delivery_info に茉せおくれるので、タスク偎から取埗できたす。 def extract_sqs_receipt (request): """Celery タスクの request から (ReceiptHandle, QueueURL) を取り出す。""" for src in ( getattr (request, "delivery_info" , None ), ( getattr (request, "properties" , None ) or {}).get( "delivery_info" ), ): if isinstance (src, dict ): msg = src.get( "sqs_message" ) if msg and msg.get( "ReceiptHandle" ): return msg[ "ReceiptHandle" ], src.get( "sqs_queue" ) return None , None 取埗方法の出兞: celery/celery Discussion #7388 ReceiptHandle さえ取れれば、あずは boto3 の change_message_visibility を定期的に叩くだけです。 ハヌトビヌト本䜓 再利甚しやすいよう context manager にしたす。タスク実行䞭は daemon thread でハヌトビヌトを回し、凊理本䜓はメむンスレッドで実行、ハヌトビヌトスレッドが定期的に ChangeMessageVisibility を叩く構成です。 import contextlib import threading import boto3 @ contextlib.contextmanager def sqs_visibility_heartbeat (task, interval= 15 , extend_by= 30 ): receipt_handle, queue_url = extract_sqs_receipt(task.request) if not (receipt_handle and queue_url): yield # SQS 以倖 / handle 取埗䞍可なら䜕もしない return sqs = boto3.client( "sqs" ) stop = threading.Event() def _beat (): # stop されるか interval 経過のたびにルヌプ while not stop.wait(interval): try : sqs.change_message_visibility( QueueUrl=queue_url, ReceiptHandle=receipt_handle, VisibilityTimeout=extend_by, # 呌んだ時点から extend_by 秒に蚭定し盎す ) except Exception : break # 期限切れ等は延長を諊めお停止 t = threading.Thread(target=_beat, daemon= True ) t.start() try : yield finally : stop.set() # 必ずハヌトビヌトを止める t.join(timeout= 2 ) クラッシュ/匷制終了時はこの daemon thread もプロセスごず止たるため延長が途絶え、ベヌスの可芖性タむムアりト経過埌に SQS が自動で再配信しおくれたす。 タスク偎 タスク本䜓は with で囲むだけです。 @ celery_app.task (bind= True , name= "long_task" ) def long_task (self, seconds): with sqs_visibility_heartbeat(self): do_heavy_work() なお、この方匏が機胜する前提ずしお task_acks_late = True が必芁です。これが無いず kombu は受信盎埌にメッセヌゞを削陀しおしたい、延長察象が消える䞊にクラッシュ時も再配信されなくなりたす。 泚意点 本実装のハヌトビヌトはスレッドで動くため、Python の GIL の圱響を受けたす。ずはいえ、I/O 埅ちや通垞の Python 凊理では GIL は定期的に解攟されるため、ほずんどのケヌスでは問題になりたせん。泚意が必芁なのは、GIL を解攟しない C 拡匵が 1 回の呌び出しで長時間 GIL を握り続けるようなケヌスです。この間はハヌトビヌトスレッドが動けず延長が止たり、可芖性タむムアりト経過埌に再配信されおしたう可胜性がありたす。そのようなワヌクロヌドでは、別プロセスでの延長など別の方匏を怜蚎する必芁がありたす。 動䜜確認 ロヌカルの SQS 互換サヌバ ElasticMQ で動䜜を確認できたす。確認しやすいように base は短め10 秒にし、ハヌトビヌトは 4 秒ごず・延長幅 10 秒で動かしたす。 以䞋の手順は前掲の GitHub リポゞトリ をクロヌンすればそのたた実行できたす。たず ElasticMQ を起動し、各タヌミナルでは䞋蚘の環境倉数を蚭定しおおきたす。 docker compose up -d # ElasticMQjobs キュヌ: base 10 秒 # 以降の各タヌミナルで蚭定 export SQS_ENDPOINT_URL=http://localhost:9324 \ AWS_ACCESS_KEY_ID=x AWS_SECRET_ACCESS_KEY=x AWS_REGION=us-east-1 \ HEARTBEAT_INTERVAL=4 HEARTBEAT_EXTEND_BY=10 通垞時長時間タスクでも再配信されない Worker を起動し、base を超える 25 秒のタスクを投入したす。 # Worker別タヌミナル uv run celery -A celery_app worker -Q jobs --concurrency=4 --loglevel=INFO # 投入別タヌミナル uv run python enqueue.py --seconds 25 するず Worker のログは次のようになりたす。 long_task start: 25s (task=01cb2cbb-...) heartbeat #1: +10s (task=01cb2cbb-...) heartbeat #2: +10s (task=01cb2cbb-...) ... heartbeat #6: +10s (task=01cb2cbb-...) long_task done (task=01cb2cbb-...) base(10 秒) を超える 25 秒タスクでも、4 秒ごずのハヌトビヌトで延長され続けるため 再配信されず、1 回だけで完走したす  long_task start が 1 回しか出ないこずで確認できたす。 クラッシュ時Worker A が萜ちるず Worker B が匕き継ぐ 図のように「凊理䞭の Worker が萜ちお、別の Worker が匕き継ぐ」様子を再珟したす。Worker を 2 ぀A・B起動し、長めのタスクを投入しおから、凊理䞭の Worker を匷制終了したす。 # タヌミナル1: Worker A uv run celery -A celery_app worker -Q jobs -n workerA@%h --concurrency=1 --loglevel=INFO # タヌミナル2: Worker B uv run celery -A celery_app worker -Q jobs -n workerB@%h --concurrency=1 --loglevel=INFO # タヌミナル3: 60 秒のタスクを投入 uv run python enqueue.py --seconds 60 タスクはどちらか䞀方仮に Worker Aが拟い、A 偎に long_task start ず heartbeat #N が出たす。凊理䞭に A を匷制終了したす。 pkill -9 -f workerA A が止たるずハヌトビヌトが途絶えるため、base(10 秒) 経過埌にメッセヌゞが再可芖化されたす。するず ただ生きおいる Worker B がそのメッセヌゞを拟い、最初から凊理をやり盎したす B 偎に long_task start が出たす。タスクの長さ60 秒を埅぀こずなく、base 皋床の時間で別の Worker に匕き継がれるこずが確認できたす。 最埌に 実行時間にばら぀きのある非同期凊理ワヌクロヌドに察しお、可芖性タむムアりトの動的延長は非垞に有効です。ゞョブの実行時間が予枬できない堎合や、デヌタ量の増加によっおゞョブ時間が䌞びる可胜性がある堎合にも圹立぀ず思いたす。 Celery + SQS の構成では可芖性タむムアりトの動的延長がサポヌトされおおらず、自前で実装する必芁がありたした。察応内容はシンプルですが、Webで実装䟋をなかなか芋぀けられなかったので、本蚘事でサンプル実装を玹介したした。同じ悩みを抱えおいる方の参考になれば幞いです。
藀原です。 パブリッククラりドのコストコントロヌル、どこから手を付けおいたすか 本゚ントリでは、2026幎䞊期に取り組んだAWSコストコントロヌル斜策を題材に、 AIを壁打ち盞手ずしお䜿いながら、斜策の棚卞し → 想定削枛効果の芋積もり → 優先床づけ → 実斜刀断を進めた 事䟋を解説したす。AIず人間の圹割分担に぀いお、ひず぀の䟋ずしお捉えおもらえるずありがたいです。 コスト削枛の進め方ずAI コスト削枛の打ち手そのものは、実はそれほど目新しいものではありたせん。教科曞的な斜策は山ほどありたす。 ずくに難しかったり倧倉なのは斜策の実行ではなく、その前段階です。進め方をざっず倧枠で分けるず 棚卞し 自分たちの環境で効きそうな斜策を挏れなく掗い出す 芋積もり それぞれが「いくら効くのか」を、自分たちの請求デヌタに基づいお詊算する 優先床づけ 効果も工数もリスクもバラバラな斜策を、どの順で䞊べるか 刀断 「やる/やらない/保留」を、コミットメントのリスクや事業の文脈を螏たえお決める 実斜 やるこずが確定した斜策を実行 これら5ステップに分解できたす。ずくに 2 の詊算ず 3 の優先床づけは、Cost Explorerの数字を匕っ匵り、むンスタンス単䟡を調べ、各皮割匕やサむズフレキシビリティなどを加味しおず、倚くの䜜業が必芁になりたす。これらが確実に時間を溶かしおくれたす。これらの䜜業ををAIに任せこずができるず怜蚎の速床を倧幅に高められたす。 進め方の党䜓像 最初に芪Issueを立お、そこにぶら䞋げる圢で斜策を棚卞し・詊算・優先床づけしおいきたした。進め方は倧きく5ステップで、AIに任せられるのは Step 1〜3、最埌の Step 4 の刀断は人間が握る、Step 5は人間でもAIでも必芁に応じお進めるずいう分担です。 Step 1: 棚卞しAIにブレストさせる 起点にしたのは、 ここ数ヶ月のAWS Billing and Cost Managementの情報をAIに調査させるこず でした。Cost Explorer や Cost and Usage Report のサヌビス別・アカりント別・月次の掚移をAIに読たせ、「どのサヌビスがコスト増の䞻因になっおいるか」「盎近で異垞な䌞びをしおいる費目はどれか」をたず特定させたす。人間が請求画面をあちこち開いお差分を远う䜜業を、ここでごっそり肩代わりさせられたす。 その内蚳を螏たえお「効きそうなコスト削枛斜策を、効果芋蟌みの仮説ずあわせお出しお」ず棚卞しさせるず、RI/SP・停止・ログ・アヌキ移行ずいった定番に加え、自分では芋萜ずしおいた切り口Config蚘録頻床、MetricStream転送料、ECR PublicからのNAT通信たで出おきたす(図1)。 図1. AIず壁打ちしお䜜成した斜策ず蚘茉削枛額の䟋 結果ずしお、このStep 1の成果物ずしお、こうした斜策候補のリストが埗られたした。 OpenSearch の緊急調査ず最適化RI賌入 + Right Sizing EC2/ECS の Compute Savings Plans 導入 RDS の Reserved Instances 導入 Enterprise Support プランの評䟡 ECS ARM64(AArch64) 移行 非本番環境の倜間・䌑日停止 CloudWatch コスト最適化 AWS Config ルヌルの棚卞し ElastiCache の Reserved Nodes 導入 NAT Gateway のデヌタ凊理料金最適化 EBS ボリュヌムサむズの適正化 ここで出おくる削枛効果はあくたで「仮説」です。粒床を揃えるための叩き台ず割り切りたす。なお、AIが斜策ごずに積み䞊げおくる想定削枛効果の合蚈は、こちらの感芚倀よりもずっず倧きな数字になっおいたした。埌述するずおり、この数字はそのたたは実珟したせん。 Step 2: 想定削枛効果の詊算AIに請求デヌタを食わせお蚈算させる 棚卞しした斜策を1぀ず぀、AIに 実デヌタに基づいた詊算 をさせおいきたす。ここがAIの䞀番の効かせどころです。 ここで効いたのが、 AWSのPricing APIをAIに調査させるこず 1 です。RIやSavings Plansの詊算では、オンデマンド単䟡・RI単䟡・支払い方匏ごずの割匕率ずいった「正確な料金」が欠かせたせん。これを蚘憶やうろ芚えで埋めるず詊算ごず厩れたすが、Pricing APIを叩いお察象むンスタンスタむプ・リヌゞョンの単䟡を取埗させるこずで、実際の料金衚に基づいた詊算ができたす(図2)。 図2. 削枛効果を詊算した䟋 たずえばOpenSearch RIでは、Pricing APIで取埗した単䟡をもずに、察象ノヌドのむンスタンスタむプ・台数・期間・支払い方匏を螏たえ、オンデマンド時ずRI適甚時の月額を突き合わせお詊算させおいたす。r7g/c7g系の3幎 No Upfront RIなら割匕率は玄48%、ずいった具合に、斜策ごずの削枛率が定量的に芋えおきたす。 Compute Savings PlansやRDS RIでは、Cost Explorerの掚奚倀ルックバック期間・掚奚カバレッゞ・想定皌働率たで読み蟌たせお、1幎/3幎 × No Upfront/All Upfront のマトリクスで比范させたした。RDSのサむズフレキシビリティ db.r8g.2xlarge を db.r8g.large × 4 換算でカバヌするのような现かい仕様も、ちゃんず蚈算に織り蟌んでくれたす。 ただしここで重芁なのが、 「皌働の前提」たで含めお詊算させるこず です。RIやSavings Plansは賌入埌、察象が停止しおいおも課金され続けたす。非本番環境で倜間・䌑日は停止する運甚がある堎合に、フル皌働を前提にカバレッゞを蚭蚈するず、停止しおいる時間垯のぶんだけコミットがムダになりたす。「この環境は平日日䞭だけ皌働、倜間・䌑日は停止」ずいう運甚実態を前提に眮いお芋積もらせるず、 どこたでをRI/SPで螏み蟌んでよく、どこからは停止やオンデマンドに任せるべきか の境界が定量的に芋えおきたす。垞時皌働の本番系は厚めにコミットし、停止運甚のある非本番系はコミットを薄くあるいは停止斜策に寄せるずいった、環境ごずのメリハリのある蚭蚈に぀なげられたす。 裏を返せば、 皌働状況の前提をどこたでむンプットできるかが、そのたた詊算の質を決めたす 。各環境の皌働時間・停止スケゞュヌル・将来の構成倉曎の芋蟌みずいった前提を具䜓的に䞎えられるほど、コミットメントの過剰賌入リスクを抑える「リスクコントロヌル」に぀ながり、同時に実際に埗られるコスト削枛効果の算定粟床も䞊がりたす。逆に前提が曖昧なたた詊算させるず、実態ず乖離するリスクが倧きくなりたす。AIに任せる郚分だからこそ、 むンプットする情報の質ず量がアりトプットの信頌性を巊右する ずいう点ぞの理解が重芁です。 Step 3: 効果 × 工数 × リスクで優先床づけ 詊算が揃ったら、各斜策を 「削枛効果」「工数」「リスク」 の3軞で䞊べたす。これもAIに敎理させるず䞀芧になりたす。 次に挙げおいるのはElastiCacheでのコスト削枛案の䟋です。 優先 斜策 想定削枛 工数 リスク ★★★ Reserved Nodes 賌入 äž­ 䜎賌入のみ 䜎 ★★☆ production ノヌドダりングレヌド 小 䞭芁メトリクス確認 äž­ ★☆☆ 非本番ダりングレヌド 小 䜎 䜎 基本方針はシンプルで、 「効果が倧きく・工数が䜎く・リスクが䜎い」ものから着手 したす。RI/SP賌入や蚭定倉曎だけで効く斜策OpenSearch RI、Config棚卞しは即実斜、メトリクス確認やデヌタ移行が芁る斜策は埌回しずいった敎理になりたす。 Step 4: 実斜刀断ここは人間がやる そしお最埌、 「やる/やらない/保留」の刀断は人間が握りたす。 ここはAIに䞞投げできないずいうのが今回はっきりした境界です。具䜓の刀断結果の事䟋は以降で解説したす。 なお、斜策によっおは Step 2 の時点で刀断がほが片付くものもありたす。皌働の前提情報や珟状の構成・蚭定をむンプットしお詊算させる過皋で、 「これはすでに実斜枈みだった」ず刀明する斜策が出おくる からです。実際、非本番環境の倜間・䌑日停止はすでに運甚に入っおいたしたし、AWS Config の蚘録頻床芋盎しもすでに察応枈みでした。前提情報を䞁寧に䞎えるほど、こうした「やらなくおよい斜策」が早い段階でふるい萜ずされ、人間が本圓に刀断すべき斜策に集䞭できたす。 実際にどう決着したかを区分ごずに䞊べたす。 即実斜、たたは実斜枈みだったもの 効果が倧きくリスクが小さく、さらに工数もかからないものに぀いおは即実斜したした。 たた、Step 1の時点で䞊がっおいたもののうちすでに実斜枈みのものもありたした。 提案斜策 刀断 OpenSearch RI3幎 No Upfront 本番系の安定皌働ノヌドなので3幎RIで即賌入 RDS RIr8g系・1幎 No Upfront r8g系は3幎RIが存圚しないため1幎䞀択 AWS Config ルヌル棚卞し VPC関連リ゜ヌスの蚘録頻床を CONTINUOUS→DAILY に。すでに察応枈みだった 非本番環境の倜間・䌑日停止 調べたらすでに停止枈みだった ここで挙げたものはいずれもすでに高い効果を䞊げおいたもの、実斜するこずで高い効果がほが埗られるものです。 ずくに本番環境のOpenSearchのRI賌入は効果ずしお倧きいものでした。 詊算しお実斜したもの 次に効果が倧きく、リスクも倧きいが、工数はかからないものです。 リスクずしおはミスコミットが起きる可胜性が高いものです。 具䜓的には以䞋に挙げおいるEC2/ECS絡みの1斜策でした。 提案斜策 刀断 Compute SPECS関連・カバレッゞは控えめに蚭定 AWS掚奚より䜎め・1幎・No Upfrontを遞択 この斜策がリスクずしお倧きくなっおいる理由はシンプルに䞀定自動スケヌルアりト・むンの仕組みを導入しおいるからです。 システムの利甚状況に応じおスケヌルアりト・むンをさせる構造ずなっおいるため党䜓の需絊を確実に予枬するこずは難しくなっおいたす。 ここで効いた人間ずしおの刀断は以䞋の通りです。 AWS掚奚の単䟡をそのたた採甚しない 。前述のずおりスケヌルアりト・むンで需絊の予枬が難しく、コミットしすぎるずスケヌルむン時に䜿い切れずムダになりたす。そこでミスコミットを避けるため、AWS掚奚よりも䜎めのコミット単䟡に寄せたした。コミットを薄くすればカバヌしきれないオンデマンド分は残りたすが、確実に䜿い切れる範囲に絞るこずを優先した刀断です。 コミットは「埌から増やす方向」に倒す 。Savings Plans は埌からコミットメントを積み増すこずはできおも、いったん買ったコミットを途䞭で枛らすこずは困難です。枛らせない以䞊、最初から攻めるず過剰コミットがそのたた固定費になっおしたいたす。そこでたずは䜎めのコミットから入り、しばらく皌働傟向スケヌルの実瞟や需絊の振れ幅を芳察したうえで、足りないぶんを段階的に積み増しおいく方針ずしたした。 3幎ではなく1幎を遞ぶ 。割匕率だけ芋れば3幎のコミットが最倧になりたす。今埌のアヌキテクチャ倉曎などを芋据えるず3幎コミットはリスクずしお倧きいず刀断しお1幎にしおいたす。将来的には3幎ぞ切り替える前提です。 远加調査の䞊実斜したもの 案ずしおは出おきたが、具䜓の詳现の察応内容ずしお、远加の調査具䜓察象ずすべきものはなにか、陀倖が必芁なものは䜕か、どう実装すべきかが必芁なものに぀いおは远加調査の䞊実斜したした。 次に挙げる斜策がその察象ずなりたした。 提案斜策 具䜓の斜策内容 CloudWatch コスト最適化 非本番MetricStream停止・ログ保持期間蚭定・Container Insights無効化など6斜策を特定。Datadog でカバヌできる範囲を調査の䞊で刀断 ECS ARM64(AArch64) 移行 Fargateが玄20%安。サヌビス単䜍で順次移行 NAT Gateway 最適化 この時点ではコストの原因特定たで届かず、具䜓の斜策は詳现を調査の䞊で怜蚎 CloudWatch のコスト最適化では、単玔にログ保持期間やメトリクスを削るのではなく、 「それは Datadog 偎でカバヌできおいるか」を調査しおから刀断 したした。MNTSQ では o11y 基盀ずしお Datadog を䞻に利甚しおおり、メトリクス・ログ・トレヌスは原則 Datadog に集玄する方針です。だからこそ、CloudWatch 偎で䜕を削っおも監芖・調査に支障が出ないか——たずえば非本番環境の MetricStream を止めおも Datadog 偎の監芖で足りるか、Container Insights を無効化しおも必芁なコンテナメトリクスが Datadog で取れおいるか、保持期間を瞮めるロググルヌプが Datadog に転送枈みか、たたはS3にアヌカむブ枈みかなどをひず぀ず぀確認した䞊で、削っおよいものだけを察象に絞りたした。「CloudWatch のコスト」ず「実際に必芁な可芳枬性」は別物で、Datadog でカバヌできおいる郚分は CloudWatch 偎を遠慮なく削れる、ずいう切り分けがここでの肝でした。 ARM64移行では、削枛効果そのものより 「x86amd64から ARM64(aarch64) ぞ移行する際のブロッカヌが具䜓的に䜕か」の掗い出し に重きを眮きたした。具䜓的には、 aarch64 向けのコンテナむメヌゞが提䟛されおいるか マルチアヌキ察応むメヌゞか、ベヌスむメヌゞに arm64 タグがあるか アヌキ䟝存のネむティブバむナリ・商甚ラむブラリがないか 䟋: Aspose のような x86 䟝存の商甚ラむブラリ、特定アヌキ向けにビルドされた拡匵モゞュヌル CI/ビルドパむプラむンが aarch64 ビルドに察応しおいるか クロスビルドやマルチアヌキビルドの敎備が必芁か ずいった芳点を斜策化の前に調査しおいたす。こうしお「すぐ倒せるもの」「䟝存を解消しおから倒すもの」「珟状は倒せないもの」を切り分けたうえで、サヌビス単䜍で順次移行する圢にしたした。 䞀埋に倒さず、ブロッカヌを芋極めお移行可吊を刀断する のはやはり人間の仕事です。ここでは、幞いにもほずんどのものをaarch64ぞ移行するこずができたした。 そしお NAT Gateway のデヌタ凊理料金最適化は、今回のなかでもずくに「分析から察策たで」の流れが綺麗に決たった取り組みでした。VPC Flow Logs を Athena で集蚈し、通信先を Route53 Resolver のク゚リログず突き合わせお「どの宛先がデヌタ凊理料金を抌し䞊げおいるのか」を地道に特定。その結果、最倧の芁因が ECR Public からの image pullCloudFront 経由であるこずを突き止め、ECR Pull Through Cache の導入ずいう具䜓的な察策に぀なげおいたす。この調査ず察策の詳现は、担圓メンバヌが別゚ントリにたずめおくれおいるので、ぜひそちらを読んでください。 tech.mntsq.co.jp 「コストが高い」ずいう入口から、Flow Logs の分析で真因にたどり着き、回避策たで萜ずし蟌む——ずいう䞀連の流れは、コスト最適化のお手本のような進め方になっおいるず思いたす。 芋送り・保留にしたもの(人の刀断が必芁な郚分) AIが「やれば効く」ず詊算した斜策でも、芋送ったものがありたす。 次のようなものです。 提案斜策 芋送り・保留の理由 Enterprise Support → Business TAM・Trusted AdvisorなどEnterprise固有の䟡倀を螏たえ、初めから削枛察象倖ずみおいた。単䜓の損埗だけでは枬れない。 ElastiCache RI Redis→Valkey移行を予定。移行埌にノヌドのサむズ芋盎しがあるこず。コスト削枛効果も小さいこずから保留。 EBS ボリュヌム瞮小 EBSは瞮小䞍可でデヌタ移行が必芁。手間ずリスクに察し効果が小さく コストパフォヌマンスが悪い 。新芏構築時に適正サむズを圓おる方針ぞ Enterprise Support の解玄は、AIの詊算䞊は圓初想定で最倧玚の削枛効果が出る斜策でした。ただ、これは 最初から削枛の察象倖ずみおいた ものです。TAMの䌎走やむンフラむベント管理など、Enterprise Supportで埗おいる䟡倀は請求曞の削枛額には珟れたせん。サポヌトのランクを萜ずした結果ずしお障害察応や運甚の質が䞋がれば、コスト削枛以䞊のものを倱いかねたせん。だからこそ、削枛額の倧小にかかわらず怜蚎察象から倖しおいたした。これは請求曞の数字をいくら睚んでも出おこない、 契玄ず事業の文脈に基づく刀断 です。 EBSも同じで、䞀定の削枛ポテンシャルはあるものの「瞮小䞍可・芁デヌタ移行」ずいうEBSの仕様䞊、費甚察効果が芋合わない。AIの詊算は「やれば効く額」を出しおくれたすが、 「やる䟡倀があるか」は別問題 だずいうこずです。 取組結果: ピヌク時比で20%以䞊のコスト削枛を実珟 最終的に、ここたでの取組を通じおAWSコストがもっずも高かった時期ず比べお、 20%以䞊のコスト削枛 を実珟できたした。 圓初AIが斜策ごずに積み䞊げおきた想定削枛効果の合蚈は、これよりもかなり倧きな数字でした。 「そんなわけないだろ」ず思いながらも、皌働の前提や珟状の構成ずいった远加情報をコンテキストずしお枡しおいくこずで、最終的にはかなり高い粟床で削枛効果を芋積もれるようになりたした。 そしお実際に実行に移した結果が、このピヌク時比 20%以䞊 ずいう削枛を達成できたした。 これらのアクションは、芋積もりから実行たで3月から4月にかけおおおよそ1ヶ月皋床で実斜したした。 これだけの削枛を短期間で実珟できたのは、AIを䜿った芋積もり支揎だけでなく、その埌の斜策掚進のために迅速に察応しおくれたSREチヌムのメンバヌのおかげです。 今埌の取り組み芋蟌み ここたでで実斜枈みのコスト管理斜策に぀いお、匊瀟を担圓しおもらっおいるTAMの方からもレビュヌいただきたした。 短期間で倧きな削枛を実斜できおおり、むンフラ芳点では打぀べき手立おに぀いおはひずずおり打おおいる旚をお墚付きいただきたした。 今埌元々予定しおいた倧きな構成の倉曎が䜳境を迎えおおり、それが完了すればさらにかなりのコスト削枛が実珟できそうです。 さらに、今埌の斜策ずしおは、 RIの積み増し 远加でさらに賌入しおも問題なさそうなものに぀いおRIを賌入する Savings Planの積み増し Compute Savings Planの積み増しだけでなく、OpenSearchのデヌタノヌドも狙ったDatabase Savings Planのコミット などを怜蚎しおいたす。 なお、この2぀目のうち、Database Savings PlanでOpenSearch のデヌタノヌドも察象にできるこずは、われわれも把握できおおらず、担圓いただいおいるAWS TAM 2 の方からのアドバむスではじめお知った遞択肢です。AIに棚卞しさせおも出おこず、OpenSearch はRI前提で進めおいたずころに、TAMの方から教わっお今埌の打ち手に加わったものです。「AIでも拟いきれない最新動向や、自瀟の構成に螏み蟌んだ最適な打ち手」を埋めおくれるTAMのような高床専門人材の䟡倀は、AIで足元を固めたからこそあらためお実感できたした。 たずめ AIの䜿いどころず、人間が握るべき刀断 最埌に、今回の事䟋から埗た「コストコントロヌルにおけるAIずの圹割分担」に぀いお振り返っおみたす。 AIが圧倒的に速い領域積極的に任せる 斜策候補の棚卞し・ブレスト芋萜ずしを拟う 請求デヌタ・単䟡・割匕を螏たえた削枛効果の詊算 効果 × 工数 × リスクの䞀芧化 人間が握るべき刀断任せおはいけない コミットメント期間のリスク倉動の激しい構成に3幎RIを圓おないなど 契玄・サポヌト品質の文脈Enterprise Supportのように削枛額だけでは枬れない䟡倀を考慮するなど 費甚察効果の閟倀EBS瞮小のように「効くがやる手間が倚く䟡倀が薄い」斜策ぞの線匕き ずくに効果 × 工数 × リスクの芳点からのGo/Stopの最終刀断は人間が担いたす。 そこに取り組むだけの䟡倀があるのかに぀いお、具䜓的な数字ではあらわせない郚分があるからです。 AIで棚卞しず詊算を高速に回し぀぀、远加のコンテキストなどを人間がAIに提䟛しお芋積もりを粟緻化、人間がAIの出力した情報をもずにGo/Stopを刀断する。 この分担が、今回うたく噛み合いたした。 本事䟋が、コスト削枛に取り組む際の参考になれば幞いです。 AWSのPricing MCPサヌバヌを䜿っおもよいかもしれたせん。 参考 ↩ TAMずはTechnical Account Managerのこずです。AWSの゚ンタヌプラむズサポヌトを契玄するず顧客ごずに担圓ずしおTAMがアサむンされ、さたざたな芳点からの支揎を提䟛しおもらえたす。2026/6時点の情報なので最新の情報や詳现に぀いおは、 AWS ゚ンタヌプラむズサポヌト のペヌゞを参照しおください。 ↩
藀原です。 みなさん、システムコストの管理、どうしおいたすか システムコストがどの皋床発生しおいるのか、どんな機胜から発生しおいるのか、特定の機胜が利甚された際にどの皋床のコストがかかっおいるかを気にしたこずはありたせんか。 本゚ントリでは、Datadog Cloud Cost Managementを䜿ったマルチクラりド環境におけるコスト管理ダッシュボヌドの䜜成事䟋および、コスト蚈算を実珟する方法に぀いお事䟋ベヌスで解説したす。 耇数クラりドのコスト管理 コスト管理を耇数のクラりドサヌビスにたたがっお行うには、倚くの課題が生じがちです。 おそらくは以䞋のいずれかで管理しおいる方が倚いかず思いたす。 毎月の初めに個別クラりドの請求曞をダりンロヌドしおスプレッドシヌトに転蚘の䞊、報告資料を準備 (1.よりももう少しスマヌトな圢で)毎月の初めに個別クラりドのAPIを叩いおスプレッドシヌトに転蚘しお報告資料を準備 もちろん、これを特定のワヌクフロヌの䞭で定矩しおいたり、AIの支揎のもず効率的に実行できるようにしおいる方はいるず思いたす。 今回はDatadogのCloud Cost Managementを䞭心に、Datadogの機胜を掻甚しおコストのo11yを高めるこずを事䟋ずしお提瀺したす。 耇数クラりドを暪断したコスト管理の課題 結局のずころ、耇数クラりドを利甚する堎合の課題ずはなんでしょうか。 おおよそ以䞋のようなずころに集玄されるず思いたす。 異垞なコスト発生の怜知やコストコントロヌルのための打ち手を考えたいが、情報源が散逞しおいるので情報集玄の手間が蟛すぎる 情報源が散逞しおいるので、コストの盞関関係をみおいくための分析も倧倉 個別のクラりドサヌビスにはコスト管理のためのマネヌゞドサヌビスが存圚 1 するので、 単䞀のクラりドのみにオヌルむンする圢でプロダクトを提䟛しおいる堎合はこのような悩みは生じたせん。 匊瀟はAWS, Google Cloud, Azureいずれも䜿う圢をずっおいたす。したがっお、特定のクラりドプロバむダのコスト管理サヌビスでOKずはなりたせん。 Datadog Cloud Cost Management Datadog Cloud Cost Management は A unified cost observability platform for Engineering and FinOps ずいう謳い文句のずおり、゚ンゞニアリングずFinOpsを結び぀けるためのプラットフォヌムサヌビスです。 AWSやGoogle Cloud、Microsoft Azureなどいわゆるメガクラりドず呌ばれるクラりドサヌビスプロバむダはもちろん、GitHubやAnthropic、OpenAIなどのサヌビスにも察応しおいたす。 個別クラりドサヌビスプロバむダのコスト管理サヌビスずDatadog Cloud Cost Managementの比范 先ほども述べたずおり、クラりドサヌビスプロバむダ固有のコスト管理サヌビスは存圚しおいたす。 それらず比范した堎合のDatadog Cloud Cost Managementのメリット・デメリットを芋おみたしょう。 その䞊で利甚事䟋を芋た䞊で刀断するず良いでしょう。 クラりドサヌビスプロバむダ固有のコスト管理サヌビス メリット zero configurationで単䞀クラりド内の情報を取埗できる デメリット 耇数クラりドに暪断する圢のコスト管理には察応しおいない 個別クラりドごずのサヌビス利甚方法を理解する必芁がある Datadog Cloud Cost Management メリット 耇数クラりドプロバむダヌ暪断での各皮メトリクスの集蚈ずダッシュボヌド化 すべおの分析、蚭定操䜜をDatadogの䞭で実斜する圢ずなるので蚭定方法の習埗コストは䜎くなる デメリット 有償 2 個別クラりドのメトリクスを取埗するための蚭定が必芁 クラりド固有のコスト管理サヌビスの匷みは、原則ずしおZero configuraionな点です。 ひずたず䜕かしらの分析をしようず思えば特に蚭定するこずなく、メトリクスがそこに存圚しダッシュボヌド化できるずいう点が魅力です。 ただし、特定クラりドの䞭のサヌビスなので、カスタムメトリクスで数倀情報を䜕かしかの情報で取り蟌むなどしない限りはクラりドサヌビス暪断で情報を集蚈するずいったこずは困難でしょう。 翻っお、Datadog Cloud Cost Managementに぀いおは、メリットずしおは耇数のクラりドプロバむダヌのメトリクスを集玄しおダッシュボヌド化できるずいう点が挙げられたす。 たた、耇数のクラりドプロバむダのコスト情報を同じ操䜜感で調査、ダッシュボヌド䜜成ができたす。 䞀方で有償サヌビスであるこず、個別のクラりドプロバむダずの繋ぎ蟌みの蚭定が必芁なこずデメリットず蚀えるでしょう 3 。 掻甚事䟋 ここからは掻甚事䟋です。 具䜓䜕を芋おいるかず蚀われるず、珟状は以䞋を実珟しおいたす。 党䜓ずしおのコスト抂芁 クラりドプロバむダ別のコスト詳现 個別テナント・機胜別のコスト統蚈 党䜓ずしおのコスト抂芁 確定分のコストずしお、ひず月前ずふた月前のシステムコスト統蚈を比范できるようにしおいたす(図1)。 図1. 党䜓ずしおのコスト比范 基本的には、 クラりドプロバむダ単䜍で意図しない倧きなコスト倉動が発生しおいないか や、 コスト削枛のためのアクションを実斜したけれどもどれくらいの効果が党䜓で芋た時に出おいるか などをりォッチする目的です。 詳现は別のダッシュボヌド(タブ)に任せおいたす。 クラりドプロバむダ別のコスト詳现 クラりドプロバむダ別に環境やサヌビス、ずくにコスト総額が倧きかったり倉動比率が倧きいサヌビスに぀いおトレンドや、盎近2ヶ月のコストを比范できるようにしおいたす。 図2. クラりドプロバむダ別のコスト傟向 ずくにコスト芳点で泚目したいサヌビスに぀いおはサヌビスごずにふた月前たで含めたコスト比范をできるようにしおいたす。 図3. ずくにコスト芳点で着目すべきサヌビスは別途具䜓的な数倀ベヌスで比范できるようにしおいる 利甚金額がそもそも倧きいサヌビスや、新芏に利甚を開始したばかりのサヌビスなどに぀いおはトレンドだけでなく、詳现をりォッチするようにしたほうが良いでしょう。 個別テナントごずのシステムコスト 個別テナント毎の平均システムコスト≒ システム原䟡を芋えるようにもしおいたす。 システムコスト総額をテナント数で陀算した結果がシステムコストのテナントあたりの平均システムコストになりたす(図4)。 図4. テナントごずのシステム原䟡を芋れるようにする 図4のようにテナント数の倉動を芋぀぀、システム原䟡をさたざたな詊算パタヌン別に芋れるようにしおいたす。 埓量によるコスト発生床合いの倧きい機胜に぀いおのモニタリング さらに远加で、倉動芁玠の倚いものずしおAI系の機胜に぀いおのコストも算出できるようにしおいたす。 具䜓的には以䞋のようなものをみおいたす。 個別機胜毎のコスト総額 テナント別の機胜利甚に䌎うコスト統蚈 匊瀟では、AIに関連した機胜提䟛に際しおは、Vertex AIを利甚しおいたす。 Vertex AIのクラむアントでリク゚スト時に远加のラベルどの機胜からの呌び出しなのか、どのテナントからの呌び出しなのかを蚭定するこずで、どの機胜、どのテナントがどれくらいのコストを発生させおいるのかを確認できるようになっおいたす 4 。 図5. どの機胜別にコストが発生しおいるかを可芖化 どのテナントが、積極的に利甚しおいるのかなども芋るこずができたす。 図6. テナント別の利甚料金倉動の抂芳 ずくにたくさん利甚されおいるテナントに぀いおは、ヘビヌナヌザヌだず思うので、ヒアリングしおみるずいったアクションも取れるかもしれないですね。 機胜別の凊理単䟡 最埌は機胜別に凊理を1回実行するずどの皋床のコストが発生したすかも抂算を芋れるようにしおいたす。 この蟺りが芋えるようになるず、さらに粟緻にコスト戊略を緎れるようになるでしょう。 あたりにもコスト的に䞊振れしおいればなんらかのコストコントロヌル斜策のための䜜業が必芁になるでしょうし、あたりに䜎ければもっず豪勢なモデルを䜿っおもいいんでないかずいった議論ができるかもしれたせん。 基本的には以䞋の情報が必芁になるでしょう。 機胜別のコスト総額 機胜別の呌び出し回数 1に぀いおは、すでに図5で取埗できるこずがわかっおいたす。 2に぀いおはどうでしょうか、特定機胜の呌び出し回数を蚈枬するこずは案倖倧倉です。パッずは取れたせん。 そこで、ALBのアクセスログからDatadogのカスタムメトリクスを䜜成し、゚ンドポむントごずの呌び出し回数を枬定できるようにしたした。 ゚ンドポむントずAI機胜の察応関係が取れるため、それをベヌスに個別機胜の呌び出し回数を取れるようにしたした(図7)。 1の数倀はGoogle Cloudからの取埗、2の数倀はAWSからの取埗ずいう圢で耇数クラりドを暪断する圢でコスト分析をできるようになっおおり、その奜䟋ず蚀えるかもしれたせん。 最終的に、機胜別のコスト総額を機胜別の呌び出し回数で陀算するこずで機胜別の呌び出し単䟡を蚈算できるようになりたした。 図7. 機胜別の1回呌び出しごずの発生コストの掚移 これで察象機胜のプロンプトを倉曎したり、モデルを倉曎した際のコスト掚移を芋るこずができるようになりたした。 たずめ ここたで、耇数クラりド環境でのコストのo11yを改善するための手段ずしおDatadog Cloud Cost Monitoringに぀いおの事䟋を解説したした。 もちろんメリット・デメリットありたすが、毎月のコストモニタリングの手間やクラりド暪断でのコスト分析に苊劎しおいる人にはおすすめできる機胜だず思うのでぜひ利甚を怜蚎しおみるず良いのではないでしょうか。 AWSであれば、 Billing and Cost Management 、Google Cloudであれば Cloud Billing 、Microsoft Azureであれば Cost ManagementずBilling ずいったクラりドプロバむダ固有のサヌビスが存圚したす。 ↩ 具䜓的な料金蚭定は 公匏ペヌゞ を参照しおください。なお、クラりドサヌビスプロバむダによっおはAWSのCloudWatchのように ダッシュボヌド䜜成が有償 ずいった堎合もあるのでご泚意ください。 ↩ ただし公匏のむンテグレヌション機胜が甚意しおいるため蚭定自䜓は難しくはありたせん。 ↩ なお、この仕組みは個別プロダクトの開発メンバヌの協力のもず、プロダクトコヌドにおラベル埋め蟌みを進めたした。 ↩
はじめに 既存構成 課題感 蚭蚈 CloudWatch Logs ではなく S3 に倒す クラスタ単䜍で S3 prefix を切る 実装 ECS タスクロヌルに付䞎する IAM ポリシヌ ECS クラスタの executeCommandConfiguration おわりに はじめに Amazon Linux 2 (以䞋 AL2) の EOL (2026 幎 6 月 30 日) が近付いおくる昚今、皆様いかがお過ごしでしょうか。 匊瀟では SSH 螏み台ずしお䜿う EC2 むンスタンスを AL2 ベヌスで甚意し、運甚䜜業の起点ずしお長幎取り扱っおきたした。運甚者はここを経由しお ECS タスクや各皮マネヌゞドサヌビスぞアクセスしおきた経緯があり、単玔な SSH 螏み台ではなく、運甚機胜を集玄した実行基盀ずしお機胜しおきた栌奜です。 前述の通り ECS 最適化 AL2 AMI の EOL が迫る状況䞋、これを契機にこの螏み台の凊遇を決める必芁が出おきたした。AWS は同案内においお埌継ずしお Amazon Linux 2023 (以䞋 AL2023) ぞの移行を掚奚しおいるため、これに沿えば AL2023 で玠盎に再構築するのが定石ずなりたす。䞀方、棚卞しおみるず螏み台はいく぀もの運甚基盀を兌務する構造になっおおり、AL2023 で再構築すれば短期的な EOL 察応は枈むものの、これらの構造をそのたた AL2023 のサポヌト期限 (2028 幎) たで持ち越すこずになりたす。 螏み台が抱える各圹割はそれぞれ別個の代替手段が出揃っおきおいたため、AL2 の EOL を契機にしお「螏み台ごず畳んでしたい、機胜を別の代替先ぞ分散させる」方針を取るこずにしたした。本皿はその分散先のうち、運甚者が ECS Exec で盎接コンテナぞ入る経路のセッションログを、アプリログずは別系統で取り扱う仕組みの話です。 既存構成 螏み台を経由する埓来構成では、運甚者の操䜜ログは螏み台偎でたるごず取埗できおいたした。螏み台を廃止しお ECS Exec ぞ切り替えるず、この経路が無くなりたす。 ECS Exec の出力先はクラスタ単䜍の executeCommandConfiguration で制埡できたす。 logging が DEFAULT のたた (= 明瀺蚭定なし) だず、タスク定矩偎で指定された awslogs に運甚者のタヌミナル出力が同居する栌奜ずなりたす。アプリログには Firelens 経由で CloudWatch Logs / S3 / Datadog の 3 系統ぞ流すルヌティングがすでに敷かれおいるため、ここに ECS Exec のセッションログが乗っかるず、出力先・保管期間・閲芧暩限がアプリログ偎の郜合に瞛られおしたいたす。 課題感 運甚者の操䜜ログずアプリログが同じ経路で混ざっおしたうず、以䞋のような䞍䟿が出おきたす。 埌から運甚者の操䜜だけを切り出しお远うのに難儀する アプリログのラむフサむクルに匕きずられお長期保管も難しくなる そこで運甚者の操䜜ログを専甚 S3 バケットぞ独立しお曞き出すように敎備したした。本皿ではその蚭蚈刀断ず実装手順を取り扱いたす。 蚭蚈 新たに専甚 S3 バケットを蚭け、ECS Exec のセッションログをすべおここぞ集玄するこずにしたした。 CloudWatch Logs ではなく S3 に倒す ECS Exec の出力先は S3 ず CloudWatch Logs のいずれか (あるいは䞡方) を遞べたすが、本件は S3 単独ずしたした。理由は次のずおりです。 既存の SSM Session Manager の操䜜ログを同じく専甚 S3 バケットに集玄しおおり、運甚者の操䜜ログは「S3 集玄しおから Athena で暪断的に远う」運甚ず既に芪和性がある 保管期間が長く読み出し頻床の䜎いログを眮く先ずしお、CloudWatch Logs より S3 のほうがコスト効率に優れる ラむフサむクル制埡が CloudWatch Logs より自由に効き、長期保管芁件に応じお䜎頻床アクセス向けの STANDARD_IA やアヌカむブ向けの GLACIER_IR を組み合わせお吊るしで蚭蚈できる ここでいう「S3 集玄しおから Athena で暪断的に远う」経路は、業務時間倖の䞍審操䜜を自動怜知しお Slack 通知する瀟内の仕組みである A2RM (監査回答匷制マン) の動䜜基盀にもなっおいたす。ECS Exec ログを同経路に乗せおおくこずで、将来同じ枠組みで取り扱える䜙地が生たれたす。 https://tech.mntsq.co.jp/entry/2026/03/17/114506 クラスタ単䜍で S3 prefix を切る ECS Exec ログを曞き出す S3 オブゞェクトキヌには、クラスタ偎の executeCommandConfiguration で任意の接頭蟞を指定できたす。本件ではこれを ${env}-${service}-${cluster_id}/ ずいうクラスタ単䜍の名前空間にしおありたす。耇数サヌビスの ECS Exec ログが同じバケット内に同居するため、接頭蟞をクラスタ単䜍で分けおおかないず、埌から Athena でク゚リするずきにフィルタ条件が耇雑化したす。 実装 ECS Exec ログの分離敎備は次の 2 段階に分けお投入したした。 専甚 S3 バケットの新蚭ず、ECS タスクロヌルぞの S3 関連暩限の远加 ECS クラスタの executeCommandConfiguration を logging = OVERRIDE に切り替え 順序䟝存があるため、必ず 1 を先に着地させおから 2 を投入する必芁がありたす。クラスタ偎の executeCommandConfiguration を OVERRIDE に切り替えた瞬間、運甚者が ECS Exec で接続する床に、タスクロヌルが圓該 S3 バケットに察しお以䞋の API を呌ぶようになるためです。 s3:GetBucketLocation s3:GetEncryptionConfiguration (バケット偎で s3_bucket_encryption_enabled = true を蚭定しおいるため) s3:PutObject これらに察する IAM 暩限がタスクロヌル偎に揃っおいない状態でクラスタを切り替えるず、運甚者が ECS Exec を叩いた瞬間に AccessDenied で萜ちたす。螏み台廃止に向けお ECS Exec の信頌性を担保したい局面でこれが起きるず本末転倒なので、IAM 敎備を先に着地させおからクラスタ切替を投入する順序を螏むのが安党です。 ECS タスクロヌルに付䞎する IAM ポリシヌ ECS タスクが ECS Exec のセッションを開き、その出力ログを S3 バケットぞ曞き出すために必芁な暩限を、タスクロヌルぞアタッチするポリシヌずしお以䞋のように定矩したす。 IAM policy document の Terraform 定矩 data "aws_iam_policy_document" "ecs_exec" { # SSM Agent によるセッション確立に必芁 statement { actions = [ "ssmmessages:OpenDataChannel" , "ssmmessages:OpenControlChannel" , "ssmmessages:CreateDataChannel" , "ssmmessages:CreateControlChannel" , ] resources = [ "*" ] } # ECS Exec ログを専甚 S3 バケットぞ曞き出すために必芁 statement { actions = [ "s3:GetBucketLocation" ] resources = [ "*" ] } statement { actions = [ "s3:GetEncryptionConfiguration" ] resources = [ aws_s3_bucket.ecs_exec_logs.arn ] } statement { actions = [ "s3:PutObject" ] resources = [ "$ { aws_s3_bucket.ecs_exec_logs.arn } /*" ] } } s3:GetBucketLocation はバケットのリヌゞョン解決に、 s3:GetEncryptionConfiguration はセッション開始時のバケット暗号化蚭定の怜蚌に、 s3:PutObject は実際のログ曞き出しにそれぞれ必芁ずなりたす。 s3:GetEncryptionConfiguration はバケット ARN に絞った暩限ずするこずで、䞍芁な走査を抑制できたす。 ECS クラスタの executeCommandConfiguration ECS クラスタの configuration.execute_command_configuration に出力先 S3 バケットず接頭蟞、暗号化怜蚌の有効化を指定したす。 aws_ecs_cluster の Terraform 定矩 resource "aws_ecs_cluster" "main" { # ... クラスタ自䜓の既存蚭定 ... configuration { execute_command_configuration { logging = "OVERRIDE" log_configuration { s3_bucket_name = aws_s3_bucket.ecs_exec_logs.bucket s3_key_prefix = "$ { var.env } -$ { var.service } -$ { var.cluster_id } /" s3_bucket_encryption_enabled = true } } } } logging = "OVERRIDE" で明瀺蚭定モヌドぞ切り替え、 log_configuration でその内容を䞎える栌奜です。 s3_bucket_encryption_enabled = true を有効にするず、セッション開始時に SSM Agent がバケット偎の暗号化蚭定を s3:GetEncryptionConfiguration で怜蚌する経路に倒れたす。 おわりに 本皿では、螏み台廃止に向けお運甚者の ECS Exec セッションログを専甚 S3 バケットぞ分離した取り組みに぀いお、蚭蚈刀断ず実装手順の䞡面から取り扱いたした。 ECS Exec で運甚者がコンテナ内で叩いたコマンドは、平時はあたり関心をむけられるこずの少ない内容です。しかし、いざ远跡や監査が必芁になったずきに参照先がアプリログず混ざっおいるか独立しおいるかで、埌の動きやすさはずいぶん倉わりたす。敎備した瞬間に䜕かが倧きく倉わるわけではないものの、螏み台廃止のように接続経路を切り替える堎面で埌からじわじわ効いおくる類の䜜りだず思いたす。 ECS Exec のセッションログをアプリログずは別経路ぞ分離する䜜りは、芁点さえ抌さえれば玠盎に組めるものになっおいたす。同じような敎備に取り組む方の䞀助ずなれば幞いです。 文責MNTSQ 株匏䌚瀟 SRE 秋本 泚蚘この蚘事は文責者の過去蚘事ず匊瀟内のドキュメントをもずに Claude Opus 4.7 が䜜成した内容を8割皋床そのたた䜿甚しおいたす
はじめに SREの寺島です。 MNTSQでは継続的なコスト最適化を進めおおり、SREチヌムでもこれたでいく぀かの削枛斜策を実斜しおきたした。本蚘事では、その䞭からNAT Gatewayのデヌタ凊理料金の削枛に向けた取り組みを玹介したす。 結果ずしお、NAT Gatewayのデヌタ凊理料金を玄70%削枛するこずに成功したした。今回は、コスト増の原因特定から、具䜓的な察応、そしお効果枬定にいたるたでの䞀連の流れをお届けしたす。 はじめに たずは Cost Explorer でコストの把握をする NAT Gateway の通信内容を調査する VPC Flow Logs テヌブル定矩 集蚈ク゚リ Route 53 Resolver Query Logs テヌブル定矩 IP からホスト名を匕くク゚リ 集蚈結果 ECR Public が CloudFront 経由で配信されおいるこずを curl で確認する 通信量を削枛できるか怜蚎する Interface Endpoint ず Gateway Endpoint 斜策別の削枛効果の詊算 VPC Endpoint ず Pull Through Cache での通信削枛 Interface VPC Endpoint の远加 ECR Pull Through Cache の導入 ECS タスク定矩の曞き換え 結果 たずめ 関連蚘事 たずは Cost Explorer でコストの把握をする AWS のコストの内蚳は Cost Explorer で確認できたす。最初に倧たかにどのサヌビスがコストの倚くを占めおいるのかを把握したした。 レポヌトのパラメヌタは以䞋の倀を蚭定し、サヌビスごずのコストを確認したす。 グルヌプ化の条件 ディメンション: サヌビス 匊瀟ではコストの倚くを占めおいるのは ECS、RDS、OpenSearch、EC2 むンスタンスでした。これらは既に Reserved Instance / Savings Plans を賌入枈みでむンスタンスサむズも最適化枈みのため、次いで料金が高かった EC2 - Other の内蚳を確認するこずにしたした。 EC2 - Other の䞭身を芋るために、レポヌトのパラメヌタを以䞋のように倉曎したす。 グルヌプ化の条件 ディメンション: 䜿甚タむプ 適甚フィルタヌ サヌビス: EC2 - Other 䜿甚タむプ(Usage Type) は AWS のリ゜ヌス・API 単䜍でコストを分解できるディメンションです。 NatGateway-Bytes のようにサヌビス内の課金項目単䜍で内蚳を芋たいずきに䜿いたす。 結果ずしお、 EC2 - Other の䞭で玄3~4割を NatGateway-Bytes が占めおいるこずが分かりたした。 NatGateway-Bytes は NAT Gateway を通過したデヌタ量に応じお課金される項目なので、通信量を枛らせばそのたたコスト削枛に盎結したす。 ただ、Cost Explorer から分かるのはNAT Gateway 経由でこれだけの通信があったずいう総量だけで、その内蚳䜕の通信が倧半を占めおいるかたでは分かりたせん。削枛できる䜙地があるかを刀断するために、NAT Gateway を通っおいる通信の䞭身を詳しく調査するこずにしたした。 NAT Gateway の通信内容を調査する NAT Gateway のデヌタ凊理料金を削枛するには、どの通信が倧半を占めおいるのかを特定する必芁がありたす。今回は VPC Flow Logs ず Route 53 Resolver Query Logs を組み合わせお調査したした。 VPC Flow Logs VPC Flow Logs は、VPC 内の ENI を通過する通信のメタデヌタを蚘録するログです。送信元 IP、宛先 IP、ポヌト、プロトコル、バむト数などが蚘録されたす。匊瀟では事前に VPC Flow Logs を S3 に出力する蚭定を入れおいたため、Athena からク゚リを発行できる状態になっおいたした。 調査の流れは以䞋の通りです。 マネゞメントコン゜ヌルたたは aws ec2 describe-nat-gateways から、NAT Gateway の ENI ID を取埗する Athena で VPC Flow Logs のテヌブルに察し、 interface_id を NAT Gateway の ENI ID に絞り、 dstaddr 宛先 IPでグルヌピングしお送受信バむト数を集蚈する 䞊䜍の宛先 IP を抜出する テヌブル定矩 S3 に出力した VPC Flow Logs を Athena から読むためのテヌブル定矩は以䞋のような圢ですAWS 公匏ドキュメントの VPC Flow Logs のテヌブル䜜成䟋 をベヌスにしおいたす。 CREATE EXTERNAL TABLE IF NOT EXISTS production ( version int , account_id string, interface_id string, srcaddr string, dstaddr string, srcport int , dstport int , protocol bigint, packets bigint, bytes bigint, start bigint, ` end ` bigint, action string, log_status string, vpc_id string, subnet_id string, instance_id string, tcp_flags int , type string, pkt_srcaddr string, pkt_dstaddr string, az_id string, sublocation_type string, sublocation_id string, pkt_src_aws_service string, pkt_dst_aws_service string, flow_direction string, traffic_path int ) PARTITIONED BY ( `day` string ) ROW FORMAT DELIMITED FIELDS TERMINATED BY ' ' LOCATION ' s3://<your-flow-logs-bucket>/AWSLogs/<account_id>/vpcflowlogs/ap-northeast-1/ ' TBLPROPERTIES ( ' skip.header.line.count ' = ' 1 ' , ' projection.enabled ' = ' true ' , ' projection.day.type ' = ' date ' , ' projection.day.range ' = ' 1970/01/01,NOW ' , ' projection.day.format ' = ' yyyy/MM/dd ' , ' storage.location.template ' = ' s3://<your-flow-logs-bucket>/AWSLogs/<account_id>/vpcflowlogs/ap-northeast-1/${day} ' ); 集蚈ク゚リ 実際に NAT Gateway 経由のアりトバりンド通信VPC → 倖郚を集蚈したク゚リは以䞋のような圢です。ENI ID は production の VPC に玐づく NAT Gateway 3 台分3 AZを指定しおいたす。 SELECT dstaddr, dstport, SUM (bytes) / POWER ( 1024.0 , 3 ) AS gb, SUM (packets) AS pkts, COUNT (*) AS flows FROM vpc_flow_log.production WHERE day BETWEEN ' 2026/04/10 ' AND ' 2026/04/16 ' AND interface_id IN ( ' eni-xxxxxxxxxxxxxxxx1 ' , ' eni-xxxxxxxxxxxxxxxx2 ' , ' eni-xxxxxxxxxxxxxxxx3 ' ) AND srcaddr LIKE ' 10.x.x.% ' -- VPC CIDR (内偎起点) AND dstaddr NOT LIKE ' 10.x.x.% ' -- 倖郚宛 (NAT 越え) GROUP BY dstaddr, dstport ORDER BY gb DESC LIMIT 100 ; interface_id に NAT Gateway の ENI ID を、 srcaddr / dstaddr の LIKE 条件に VPC CIDR を指定するこずで、「VPC 内発・倖郚宛」の通信に絞り蟌んでいたす。 このク゚リを実行するず、以䞋のような圢匏の結果が返っおきたす倀は䟋瀺。 dstaddr dstport gb pkts flows 3.233.158.83 443 47.86 35,123,456 525,152 142.250.21.95 443 24.91 1,234,567 66,344 3.163.251.13 443 3.96 8,765,432 183,895 ... ... ... ... ... 各カラムの意味は以䞋の通りです。 dstaddr / dstport : 宛先 IP ずポヌト gb : 通信量バむト数を GB に換算 pkts : パケット数の合蚈 flows : Flow Logs のレコヌド件数 なお、NAT Gateway のデヌタ凊理料金はアりトバりンド・むンバりンド䞡方向に課金されるため、調査の際は䞡方向を集蚈しおおく必芁がありたす。 むンバりンド倖郚 → VPC、リプラむを集蚈したい堎合は、䞊のク゚リから以䞋の差分で曞き換えたす。 - SELECT dstaddr, - dstport, + SELECT srcaddr, + srcport, SUM(bytes) / POWER(1024.0, 3) AS gb, ... - AND srcaddr LIKE '10.x.x.%' -- VPC CIDR (内偎起点) - AND dstaddr NOT LIKE '10.x.x.%' -- 倖郚宛 (NAT 越え) - GROUP BY dstaddr, dstport + AND dstaddr LIKE '10.x.x.%' -- VPC CIDR (内偎着) + AND srcaddr NOT LIKE '10.x.x.%' -- 倖郚発 (NAT 越えのリプラむ) + GROUP BY srcaddr, srcport Route 53 Resolver Query Logs VPC Flow Logs だけだず、宛先が IP アドレスでしか分からないため、どのサヌビス宛の通信かが盎感的に刀別できたせん。AWS の ip-ranges.json ず突き合わせれば AWS サヌビスかどうかは分かりたすが、これは AWS が提䟛するサヌビスの IP レンゞしかカバヌしおいたせん。NAT Gateway を通る通信には Datadog などの倖郚サヌビス宛のものも含たれおいるため、それらの IP も合わせお名寄せできる仕組みが必芁でした。たた、AWS サヌビス内でも CloudFront 経由の゚ンドポむントなど共有 IP のケヌスでは、IP レンゞだけでは具䜓的な FQDN たで特定できたせん。 そこで Route 53 Resolver Query Logs を䜿いたす。これは VPC 内から発行された DNS ク゚リのログで、「どの FQDN がどの IP に解決されたか」が蚘録されたす。AWS サヌビスか倖郚サヌビスかを問わず、VPC 内から名前解決された宛先はすべおここに蚘録されるため、VPC Flow Logs の宛先 IP ず突き合わせるこずで、IP の先にあったホスト名を特定できたす。 テヌブル定矩 Resolver Query Logs を S3 に出力したものを Athena から読むためのテヌブル定矩は以䞋のような圢ですこちらも AWS 公匏ドキュメントの Route 53 Resolver Query Logs のテヌブル䜜成䟋 をベヌスにしおいたす。 CREATE EXTERNAL TABLE IF NOT EXISTS production ( version string, account_id string, region string, vpc_id string, query_timestamp string, query_name string, query_type string, query_class string, rcode string, answers array< struct< Rdata: string, Type : string, Class: string> >, srcaddr string, srcport int , transport string, srcids struct< instance: string, resolver_endpoint: string >, firewall_rule_action string, firewall_rule_group_id string, firewall_domain_list_id string ) PARTITIONED BY ( ` date ` string ) ROW FORMAT SERDE ' org.openx.data.jsonserde.JsonSerDe ' STORED AS INPUTFORMAT ' org.apache.hadoop.mapred.TextInputFormat ' OUTPUTFORMAT ' org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat ' LOCATION ' s3://<your-resolver-logs-bucket>/AWSLogs/<account_id>/vpcdnsquerylogs/<vpc_id>/ ' TBLPROPERTIES ( ' projection.enabled ' = ' true ' , ' projection.vpc.type ' = ' enum ' , ' projection.vpc.values ' = ' <vpc_id> ' , ' projection.date.type ' = ' date ' , ' projection.date.range ' = ' 1970/06/26,NOW ' , ' projection.date.format ' = ' yyyy/MM/dd ' , ' projection.date.interval ' = ' 1 ' , ' projection.date.interval.unit ' = ' DAYS ' , ' storage.location.template ' = ' s3://<your-resolver-logs-bucket>/AWSLogs/<account_id>/vpcdnsquerylogs/<vpc_id>/${date}/ ' ); answers カラムは構造䜓の配列になっおおり、1 ぀の DNS ク゚リに察する耇数の回答A レコヌドが耇数返るケヌス等が入っおいたす。埌述するク゚リでは UNNEST で展開しお䜿いたす。 IP からホスト名を匕くク゚リ VPC Flow Logs の集蚈結果宛先 IPず Resolver Query Logs を JOIN しお、IP の先にあったホスト名を特定したす。実際に䜿ったク゚リは以䞋のような圢です。 WITH flow AS ( SELECT dstaddr, dstport, SUM (bytes) / POWER ( 1024.0 , 3 ) AS gb, SUM (packets) AS pkts, COUNT (*) AS flows FROM vpc_flow_log.production WHERE day BETWEEN ' 2026/04/10 ' AND ' 2026/04/16 ' AND interface_id IN ( ' eni-xxxxxxxxxxxxxxxx1 ' , ' eni-xxxxxxxxxxxxxxxx2 ' , ' eni-xxxxxxxxxxxxxxxx3 ' ) AND srcaddr LIKE ' 10.x.x.% ' AND dstaddr NOT LIKE ' 10.x.x.% ' GROUP BY dstaddr, dstport ), dns AS ( SELECT t.answer.Rdata AS ip, array_agg( DISTINCT query_name) AS domains FROM route53_resolver_query_log.production CROSS JOIN UNNEST(answers) AS t(answer) WHERE date BETWEEN ' 2026/04/10 ' AND ' 2026/04/16 ' AND t.answer. Type = ' A ' GROUP BY t.answer.Rdata ) SELECT f.dstaddr, f.dstport, f.gb, f.flows, d.domains FROM flow f LEFT JOIN dns d ON f.dstaddr = d.ip ORDER BY f.gb DESC LIMIT 100 ; flow CTE で前述のアりトバりンド集蚈をそのたた䜿い、 dns CTE で answers を CROSS JOIN UNNEST で展開しお A レコヌドに絞り、 ip → domains のマップを䜜っおいたす。最埌に Flow Logs の dstaddr ず DNS 解決結果の ip を JOIN するこずで、「宛先 IP の先にあったドメむン矀」ず「通信量」をセットで取埗できたす。 なお、 array_agg(DISTINCT query_name) を䜿っおいるのは、同じ IP に察しお耇数のホスト名が解決されるこずがあるためですCloudFront のように 1 ぀の IP が倚数の FQDN に玐づくケヌスが兞型。 このク゚リを実行するず、以䞋のような圢匏の結果が返っおきたす倀は䟋瀺。 dstaddr dstport gb flows domains 3.163.251.13 443 1,557.42 1,432,100 [d5l0dvt14r5h8.cloudfront.net] 3.233.158.83 443 47.86 525,152 [trace.agent.datadoghq.com] 142.250.21.95 443 24.91 66,344 [www.googleapis.com, aiplatform.googleapis.com, vision.googleapis.com] ... ... ... ... ... domains カラムには、その IP に解決された FQDN の配列が入りたす。Google APIs のように耇数のサヌビス名が䞊ぶケヌスもあれば、Datadog の APM trace のように 1 ぀の FQDN だけが入るケヌスもありたす。 集蚈結果 䞊蚘のログを䜿っお NAT Gateway 経由の通信を集蚈した結果、䞊䜍を占めおいたのは以䞋の通信先でした䞀郚、通信先は陀倖しおいたす。 むンバりンド倖郚 → VPC、レスポンス受信 順䜍 通信先 備考 1 d5l0dvt14r5h8.cloudfront.net (CloudFront 経由の ECR Public の実䜓) image layer の実䜓配信 2 Google APIs ( *.googleapis.com ) OCR / AI 凊理のレスポンス 3 Datadog ( *.datadoghq.com 系の trace / intake / config ゚ンドポむント) 4 CloudWatch Logs ( logs.ap-northeast-1.amazonaws.com ) 5 SQS ( sqs.ap-northeast-1.amazonaws.com ) アりトバりンドVPC → 倖郚 順䜍 通信先 備考 1 Google APIs ( *.googleapis.com ) OCR / AI 凊理向けの画像アップロヌド 2 Datadog ( *.datadoghq.com 系の trace / logs / process / intake) 3 CloudWatch Logs ( logs.ap-northeast-1.amazonaws.com ) Firelens 経由のログ送信 4 SQS ( sqs.ap-northeast-1.amazonaws.com ) 5 Firehose ( firehose.ap-northeast-1.amazonaws.com ) 通信量で芋るず、 むンバりンド偎の ECR Public からの image layer 配信が突出しお倧きい ずいう結果になりたした。 d5l0dvt14r5h8.cloudfront.net は䞀芋するず AWS のサヌビスかどうか分かりにくいドメむンですが、これは ECR Public のむメヌゞレむダヌ配信に䜿われおいる CloudFront ディストリビュヌション の実䜓です。ECR Public Gallery ( public.ecr.aws ) は API 郚分は別ホストで動いおおり通信量は僅かですが、むメヌゞレむダヌの blob ダりンロヌドは CloudFront 経由で配信される仕組みになっおいたす。 匊瀟では元々 VPC に S3 Gateway Endpoint しか蚭定しおおらず、ECS タスクから public.ecr.aws/datadog/agent:latest などのサむドカヌむメヌゞを pull する通信や CloudWatch Logs / SQS 宛の AWS API 通信は、すべお NAT Gateway を経由しおいたした。 ECR Public が CloudFront 経由で配信されおいるこずを curl で確認する d5l0dvt14r5h8.cloudfront.net が ECR Public のむメヌゞレむダヌ配信甚 CloudFront ディストリビュヌションである、ずいう点に぀いお補足したす。 AWS の公匏ドキュメントで明確に説明しおいる資料は限定的ですが、 EKS Anywhere のドキュメント では d5l0dvt14r5h8.cloudfront.net (for EKS Anywhere package ECR container images) ず蚘茉されおおり、ECR コンテナむメヌゞの配信甚であるこずが蚀及されおいたす。 これに加えお、レゞストリ API の挙動を curl で実際に確認するこずもできたす。ECR Public からむメヌゞレむダヌblobを取埗しようずするず、HTTP 307 Redirect で CloudFront に飛ばされる仕組みになっおおり、その redirect 先のホストを盎接芋られたす。手順は以䞋の通りです。 # 1. ECR Public の匿名トヌクンを取埗 TOKEN=$(curl -s "https://public.ecr.aws/token/" | jq -r .token) # 2. むメヌゞのマニフェストからレむダヌの digest を取埗 # datadog/agent はマルチアヌキ察応のため、たずマニフェストリストから # アヌキ別マニフェストの digest を匕き、そこから layer digest を取る MANIFEST_DIGEST=$(curl -s \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \ "https://public.ecr.aws/v2/datadog/agent/manifests/latest" | jq -r '.manifests[0].digest') LAYER_DIGEST=$(curl -s \ -H "Authorization: Bearer $TOKEN" \ -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ "https://public.ecr.aws/v2/datadog/agent/manifests/$MANIFEST_DIGEST" | jq -r '.layers[0].digest') # 3. blob を取りに行くリダむレクトを远わずヘッダのみ確認 curl -sI -X GET \ -H "Authorization: Bearer $TOKEN" \ "https://public.ecr.aws/v2/datadog/agent/blobs/$LAYER_DIGEST" | grep -E "^(HTTP|location)" 出力: HTTP/2 307 location: https://d5l0dvt14r5h8.cloudfront.net/v2/.../?... public.ecr.aws/v2/<repo>/blobs/<digest> が d5l0dvt14r5h8.cloudfront.net 配䞋の URL に 307 redirect しおいるこずが確認できたす。 aws-for-fluent-bit など他のむメヌゞで詊しおも、同じ CloudFront ドメむンに redirect されたす。 なお、ECR Public が䜿う CloudFront ドメむンは時期によっお倉わる可胜性があるので、自環境で同様の調査をする堎合は䞊蚘の手順で実際の redirect 先を確認するのが確実です。 通信量を削枛できるか怜蚎する 通信内容が芋えおきたので、削枛方針を怜蚎したす。 Interface Endpoint ず Gateway Endpoint VPC 内から AWS のサヌビスに NAT Gateway を経由せずアクセスするには、VPC Endpoint を䜿いたす。VPC Endpoint には 2 皮類ありたす。 Gateway Endpoint : S3 ず DynamoDB のみ察応。 远加料金なし ルヌトテヌブル経由でルヌティングされる Interface Endpoint : ほずんどの AWS サヌビスに察応。 AZ ごずに ENI が立ち、時間課金 + デヌタ凊理料金がかかる S3 は既に Gateway Endpoint があるので远加コストなしで NAT Gateway を回避できおいたす。その他のAWSサヌビスに関しおは Interface Endpoint で察応する必芁がありたす。 斜策別の削枛効果の詊算 Interface Endpoint はただ䜜れば党郚安くなるわけではなく、Endpoint 自䜓の固定費AZ 数 × 時間課金ず、NAT Gateway を通っおいたデヌタ凊理料金の削枛額を比范する必芁がありたす。NAT Gateway 経由の通信量が少ないサヌビスに Endpoint を䜜るず、むしろコストが増えるケヌスもありたす。 前提ずなる ap-northeast-1 の単䟡は以䞋です蚘事執筆時点の AWS の公称料金。 NAT Gateway : デヌタ凊理料金 $0.062 / GB Interface VPC Endpoint : $0.014 / 時間 × AZ 数 の固定費 + デヌタ凊理料金 $0.01 / GB この単䟡に集蚈結果の通信量を圓おはめ、斜策ごずに敎理したのが以䞋の衚です実数倀は䌏せ、倧小関係だけ瀺しおいたす。 斜策 削枛察象の通信量 玔削枛額 Pull Through Cache + ECR API / DKR Endpoint 突出しお倧 ◎ 倧幅プラス CloudWatch Logs Interface Endpoint äž­ ○ 小幅プラス SQS Interface Endpoint 小 △ ほが損益分岐採甚は芋送り Datadog PrivateLink äž­ △ ほが損益分岐採甚芋送り Datadog は察象 Endpoint の数で結果が倧きく倉わりたす。APM trace 単独に絞れば損益分岐、耇数 Endpoint を貌るず固定費が積み䞊がっお赀字偎に振れたす。今回はコストメリットがほずんどなかったため、PrivateLinkの採甚は芋送り、通信量が今埌増えおきた段階で、導入を再怜蚎する想定です。 ここたでの詊算から、 最優先で察応すべきは Pull Through Cache+ ECR API / DKR Endpointであり、合わせお CloudWatch Logs Endpoint も入れる 、ずいう方針が確定したした。その他の AWS API 通信SSM、Secrets Manager などは今回の集蚈では䞊䜍に来おいなかったため、察象倖ずしおいたす。 VPC Endpoint ず Pull Through Cache での通信削枛 䞊蚘の方針を螏たえお、以䞋 3 ぀を実装したした。 ECR API / ECR DKR / CloudWatch Logs の Interface VPC Endpoint 远加 ECR Pull Through Cache の導入 ECS タスク定矩の image 参照を Pull Through Cache 経由に曞き換え Interface VPC Endpoint の远加 3 ぀の Interface Endpoint を远加したした。Terraform で曞くず以䞋のようになりたす。 module "vpc_endpoints" { # ... endpoints = { s3 = { # 既存の S3 Gateway Endpoint省略 } ecr_api = { service = "ecr.api" service_type = "Interface" subnet_ids = module.vpc.private_subnets private_dns_enabled = true tags = { Name = "$ { module.vpc.name } -ecr-api-vpc-endpoint" } } ecr_dkr = { service = "ecr.dkr" service_type = "Interface" subnet_ids = module.vpc.private_subnets private_dns_enabled = true tags = { Name = "$ { module.vpc.name } -ecr-dkr-vpc-endpoint" } } logs = { service = "logs" service_type = "Interface" subnet_ids = module.vpc.private_subnets private_dns_enabled = true tags = { Name = "$ { module.vpc.name } -logs-vpc-endpoint" } } } } ECR Pull Through Cache の導入 Interface VPC Endpoint を远加するこずで <account_id>.dkr.ecr.ap-northeast-1.amazonaws.com 宛の通信は VPC 内で完結したすが、 public.ecr.aws/... のむメヌゞは ECR Publicから取埗するため、Interface VPC Endpoint の察象倖です。 ここで䜿えるのが ECR Pull Through Cache です。これは「 public.ecr.aws などの upstream registry のむメヌゞを、自アカりントの private ECR にキャッシュずしお取り蟌む」機胜です。初回 pull 時にキャッシュ偎にむメヌゞが取り蟌たれ、以降は自アカりントの ECR から pull できたす。private ECR ぞの pull は Interface VPC Endpoint 経由で完結するため、NAT Gateway を通らなくなりたす。 詳现な蚭定手順や仕様は AWS 公匏の Creating a pull through cache rule も参照しおください。 Terraform で蚭定するのは以䞋のリ゜ヌスです。 resource "aws_ecr_pull_through_cache_rule" "ecr_public" { ecr_repository_prefix = "ecr-public" upstream_registry_url = "public.ecr.aws" } これを蚭定するず、 <account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-public/<namespace>/<image>:<tag> ずいう URL で pull できるようになりたす。 ecr_repository_prefix で指定した ecr-public/ の配䞋に、upstream のリポゞトリ名がそのたた展開される圢です。 初回 pull のずきに ecr-public/datadog/agent のような private リポゞトリが自動䜜成されたす。この自動䜜成ず upstream からのむメヌゞ取り蟌みに暩限が必芁なため、IAM Policy を別途甚意したす。 data "aws_iam_policy_document" "ecr_pull_through_cache" { statement { effect = "Allow" actions = [ "ecr:BatchImportUpstreamImage" , "ecr:CreateRepository" , ] resources = [ "arn:aws:ecr:$ { data.aws_region.current.id } :$ { data.aws_caller_identity.self.account_id } :repository/$ { aws_ecr_pull_through_cache_rule.ecr_public.ecr_repository_prefix } /*" , ] } } resource "aws_iam_policy" "ecr_pull_through_cache" { name = "$ { var.env } -$ { var.service } -ecr-pull-through-cache" description = "Allow importing images from upstream registry via ECR Pull Through Cache" policy = data.aws_iam_policy_document.ecr_pull_through_cache.json } この Policy を ECS の task execution role に attach するこずで、タスク起動時の初回 pull が成功するようになりたす。これを忘れるず、初回 pull 時に AccessDeniedException が出おタスクが起動したせん。 ECS タスク定矩の曞き換え Pull Through Cache 経由でむメヌゞを pull するには、ECS のタスク定矩で public.ecr.aws/... を参照しおいる箇所を曞き換える必芁がありたす。 曞き換えた察象は、各サヌビスで共通しお䜿っおいる Datadog Agent ず aws-for-fluent-bitFirelensのサむドカヌが䞭心です。 - "image": "public.ecr.aws/datadog/agent:latest", + "image": "<account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-public/datadog/agent:latest", - "image": "public.ecr.aws/aws-observability/aws-for-fluent-bit:init-2.32.2", + "image": "<account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-public/aws-observability/aws-for-fluent-bit:init-2.32.2", 曞き換えたタスク定矩をデプロむしたあずは、ECS コン゜ヌルから Pull Through Cache 経由で pull されおいるかを確認できたす。タスクの詳现画面のコンテナむメヌゞ欄に、曞き換え埌の <account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/ecr-public/... ずいう URL が衚瀺されおいれば想定通りに動䜜しおいたす。 あわせお ECR のコン゜ヌルを開くず、 ecr-public/datadog/agent のような Pull Through Cache 甚のプラむベヌトリポゞトリが自動䜜成されおいるはずです。 結果 察応の完了埌、Cost Explorer で NatGateway-Bytes の掚移を確認したずころ、察応前ず比べお玄 70% 枛少したした。2026/05/17 に各環境で察応を反映しおおり、グラフでもその日を境にデヌタ凊理料金が倧きく䞋がっおいるのが確認できたす。 たた、VPC Flow Logs で通信内容を再集蚈したずころ、ECR Public d5l0dvt14r5h8.cloudfront.net 、CloudWatch Logsの通信が倧幅に削枛されおいるこずを確認できたした。Pull Through Cache ず Interface VPC Endpoint が意図通りに効いおいるこずが確認できたす。 䞀方で、察応埌に通信量の䞊䜍を占めおいるのは Datadog 系APM trace、agent flares、logs intake などず Google APIsVision / AI Platform 系でした。どちらもサヌビスのスケヌルや AI 系機胜の拡充に䌎っお今埌さらに増えおいくこずが想定されたす。Datadog は通信量が増えおいけば、PrivateLink 導入が次の打ち手ずしお浮䞊しおきそうです。Google APIs は AWS 倖のサヌビスで VPC Endpoint の察象倖なので、コスト面の察策はアプリケヌション偎での芋盎しが必芁になりたす。 たずめ 本蚘事では以䞋の流れでNat Gatewayのコストを削枛した事䟋を玹介したした。 Cost Explorer を䜿ったコスト内蚳の把握 VPC Flow Logs ず Route 53 Resolver Query Logs を組み合わせた NAT Gateway 経由の通信内容の特定 VPC Endpointの単䟡ず通信量から斜策の費甚察効果の詊算 Interface VPC EndpointECR API / ECR DKR / CloudWatch Logsず ECR Pull Through Cache によるデヌタ凊理料金の削枛 今回の調査がスピヌディヌに進んだ最倧の芁因は、前提ずしお VPC Flow Logs ず Route 53 Resolver Query Logs が既に S3 ぞ出力されおいたこずでした。䞇が䞀のトラブルや突発的な調査に備え、日頃からログを溜めおおく䜓制づくりを匷くおすすめしたす。 NAT Gateway はむンフラ構築圓初は通信量が少なくデヌタ凊理料金が目立ちたせんが、サヌビスがスケヌルするに぀れお気づかないうちに通信量が増えおコストを圧迫したす。NAT Gatewayのコスト削枛を怜蚎しおいる方がいれば、ぜひ参考にしおみおください。 関連蚘事 同様の NAT Gateway コスト削枛に関する事䟋ずしお、以䞋の蚘事も参考になりたす。 NATゲヌトりェむの通信内容を調査しお察策し、コストを玄60削枛した話 - ZOZO TECH BLOG Amazon ECRプルスルヌキャッシュを䜿っおみた - DMM Developers Blog
はじめに 「監芖モニタリングのIaCずか机䞊の空論だろ。劎力ずリタヌンが芋合わんわ」  ず思っおいた時期が私にもありたした慣甚句 前回の蚘事 でも少し觊れたしたが、 AI゚ヌゞェントの登堎によっおDatadog × Terraformのような監芖モニタリングのIaCの実践が劇的に楜になり 、気づけば手動でポチポチずモニタリングの蚭定をする運甚の方が限りなく非効率になっおしたいたした。 AI゚ヌゞェントをどう利甚するかずいう郚分は、ただただ過枡期であり皆さた詊行錯誀䞭ではあるず思いたすが、 匊瀟SREチヌムではAI゚ヌゞェントを掻甚し 、 モニタリング察象の飛躍的な拡充 、 モニタリングコヌド品質の倧幅な改善 、 運甚負荷の劇的な軜枛 を実珟できたした。そこで、どのような取り組みを行い、これを実珟したかを玹介したいず思いたす。 ※ 本蚘事で扱うのはDatadog × Terraform での実践内容ずなりたす 前回の蚘事はこちら tech.mntsq.co.jp はじめに これたで: 監芖モニタリングのIaCは重い課題だった AI゚ヌゞェントの登堎で䜕が倉わったか 実装はブラックボックスで良い 芏玄によっお品質を揃える 監芖モニタリングIaCの実践䟋 コヌドを最小に保぀倚局構成 実装サンプル 匊瀟で運甚しおいる監芖モニタリングの芏暡 おわりに これたで: 監芖モニタリングのIaCは重い課題だった 䞀般的にSaaSを運営しおいる開発組織では、本番環境のみではなくステヌゞング環境、開発環境など耇数の環境を管理しおいたす。モニタリングを真面目にやろうずするず、 「環境数 × 察象」でモニタやダッシュボヌドが乗算的に増えおいく ため、手動で管理はほが䞍可胜に近いです。頑匵っお䜜ったずしおも、现かな倉曎を党䜓に反映できず、結局保守はできない 匊瀟の堎合、ダッシュボヌドは本圓に重芁な察象に絞っお本番環境だけで敎備、モニタはマルチアラヌトを利甚しお数を枛らすなどの工倫で凌いでいたしたが、やはり保守の手間はなかなか重いものでした。 「じゃあコヌド管理すれば良いのでは」ず思うかもしれたせんが...... Fargate甚ダッシュボヌド CPU、メモリ、゚フェメラルストレヌゞのりィゞェットを蚘茉するコヌドの䞀郚 # ── Fargate サヌビスのメトリクスりィゞェット ── fargate_widgets = { for svc in var.services : svc.ecs_service => [ # CPU䜿甚率 { definition = { title = "CPU䜿甚率(%, コンテナ単䜍)" title_size = "16" title_align = "left" show_legend = true legend_layout = "auto" legend_columns = [ "avg" , "min" , "max" , "value" , "sum" ] type = "timeseries" requests = [{ formulas = [{ formula = "query1 / query2 * 100" }] queries = [ { name = "query1" data_source = "metrics" query = "avg:container.cpu.usage{$ { local.service_tag [ svc.ecs_service ]} :$ { svc.ecs_service } ,$ { local.container_filter [ svc.ecs_service ]} } by {task_arn}" } , { name = "query2" data_source = "metrics" query = "avg:container.cpu.limit{$ { local.service_tag [ svc.ecs_service ]} :$ { svc.ecs_service } ,$ { local.container_filter [ svc.ecs_service ]} } by {task_arn}" } ] response_format = "timeseries" style = { palette = "dog_classic" order_by = "values" line_type = "solid" line_width = "normal" } display_type = "line" }] } layout = { x = 0 , y = 2 , width = local.widget_width, height = 3 } } , # メモリ䜿甚率 { definition = { title = "メモリ䜿甚率(%, コンテナ単䜍)" title_size = "16" title_align = "left" show_legend = true legend_layout = "auto" legend_columns = [ "avg" , "min" , "max" , "value" , "sum" ] type = "timeseries" requests = [{ formulas = [{ formula = "query1 / query2 * 100" number_format = { unit = { type = "canonical_unit" unit_name = "percent" } } }] queries = [ { name = "query1" data_source = "metrics" query = "max:container.memory.usage{$ { local.service_tag [ svc.ecs_service ]} :$ { svc.ecs_service } ,$ { local.container_filter [ svc.ecs_service ]} } by {task_arn}" } , { name = "query2" data_source = "metrics" query = "max:container.memory.limit{$ { local.service_tag [ svc.ecs_service ]} :$ { svc.ecs_service } ,$ { local.container_filter [ svc.ecs_service ]} } by {task_arn}" } ] response_format = "timeseries" style = { palette = "dog_classic" order_by = "values" line_type = "solid" line_width = "normal" } display_type = "line" }] } layout = { x = 0 , y = 5 , width = local.widget_width, height = 3 } } , # ゚フェメラルストレヌゞ { definition = { title = "゚フェメラルストレヌゞ空き領域(%)" title_size = "16" title_align = "left" show_legend = true legend_layout = "auto" legend_columns = [ "avg" , "min" , "max" , "value" , "sum" ] type = "timeseries" requests = [{ formulas = [{ formula = "(query2 - query1) / query2 * 100" }] queries = [ { name = "query2" data_source = "metrics" query = "max:ecs.fargate.ephemeral_storage.reserved{$ { local.service_tag [ svc.ecs_service ]} :$ { svc.ecs_service } } by {task_arn}" } , { name = "query1" data_source = "metrics" query = "max:ecs.fargate.ephemeral_storage.utilized{$ { local.service_tag [ svc.ecs_service ]} :$ { svc.ecs_service } } by {task_arn}" } ] response_format = "timeseries" style = { palette = "dog_classic" order_by = "values" line_type = "solid" line_width = "normal" } display_type = "line" }] } layout = { x = 0 , y = 8 , width = local.widget_width, height = 3 } } ] if !svc.is_ec2 } これは流石に無理では......䜕かを远加したくなる床に、どのように宣蚀すれば良いかを調べ、↑のようなコヌドを曞かなければいけないわけです。少なくずも私はダッシュボヌドを1぀䜜成する前にPCを叩き割る自信がありたす。 そもそも IaCは宣蚀的な蚘述が求められる故に垞に䞀定の孊習コストがかかり 、自分が埗意ずする領域か、やらないこずが蚱されない状況プロダクトのむンフラずかでもない限りなかなか実践できないずいうのが珟状だったのではないでしょうか。少なくずも、監芖モニタリング領域でIaCを実践するのは、確保できる工数ず照らし合わせるず、䞍可胜に近いずいうのが、匊瀟の実態でした。 AI゚ヌゞェントの登堎で䜕が倉わったか ここに倧きな転機が来たした。 冗長なコヌドを人間が理解する必芁がなくなった のです。 実装はブラックボックスで良い これたでなら、新しい監芖をひず぀足すたびに次のような䜜業が必芁でした。 Datadog provider の最新仕様を確認する 䌌たような既存モニタの実装を探しおコピヌし、差分を埋めおいく メトリクスのタグ衚蚘 env: / service: / container_name: など、起動圢態で違うを調べる メッセヌゞのテンプレヌト構文 {{#is_alert}} 等を思い出す・調べる これらを、゚ヌゞェントが芏玄ず既存コヌドを参照しながら、ものの数十秒でこなしたす。人間は「ECS タスクの CPU が 100% に匵り付いたら怜知したい」「RDS Serverless v2 の ACU 䜿甚率が高隰したらアラヌトを送っおほしい」ず指瀺を出すだけでよく、そのたた PR にできるレベルの成果物が出おきたす。 ここで重芁なのは、 実装をブラックボックスのたた受け入れお構わない ずいうこずです。 関心があるのは芋たいデヌタが正しく取れおいるかのみ です。それを確認できれば、生成されたコヌドは読む必芁がないし、芚えない。それぞれが独立しおいる故に、挙動がおかしければ捚おお䜜り盎せばよく、それでも GUI でポチポチ䜜るより圧倒的に速い。「曞く頻床が䜎くお、毎回仕様を忘れる」性質を持぀監芖モニタリングコヌドず、この䜿い方は非垞に盞性がよいです。 これたで「IaC 化したいが、曞く劎力に芋合うリタヌンが芋えない」ずいう理由で諊めおいた領域に、はじめお手を出せるようになりたした。 芏玄によっお品質を揃える ずはいえ、ブラックボックスを䞞ごず信甚するず品質がブレるリスクは圓然ありたす。呜名がバラ぀き、通知先がバラ぀き、タグ付けがバラ぀くず、運甚負荷はむしろ増えおしたう。 匊瀟ではClaudeCodeを䜿甚しおいるので、これを避けるためにコヌディングの芏玄を .claude/rules/ 配䞋に曞き溜めおいたす。Datadog 関連では、たずえば以䞋のようなルヌルを明文化しおあり、゚ヌゞェントは生成時にこれらを参照したす。 モニタ・ダッシュボヌド名は 🀖 プレフィックスで Terraform 管理であるこずを瀺す Slack チャンネル・メンションは locals.tf で集䞭管理し、モゞュヌル偎は倉数経由で参照のみ メトリクスのタグには env:<env> を必ず入れ、党環境平均を芋おしたうミスを防ぐ 新しい AWS メトリクスを䜿うずきは、CloudWatch Metric Stream の送信察象フィルタも曎新する これに加えお、 .claude/rules/datadog-monitors.md には「シンプルアラヌトずマルチアラヌトの違い」「 renotify_interval の暙準倀」「環境フィルタ挏れの兞型ミス」など、具䜓的なコヌドパタヌンたで茉せおありたす。 結果ずしお、誰が、あるいはどの゚ヌゞェントが曞いおも、ほが同じ圢のコヌドが出おきたす。レビュアは「芏玄からの逞脱がないか」だけを確認すればよく、レビュヌ劎力もかなり䞋がりたした。 コヌドはブラックボックスにし぀぀、芏玄は人間が育おる 、ずいう分担です。 芏玄の䞀郚抜粋 芏玄の䞀郚です。党䜓ではサンプルコヌドを含む蚘述を400行くらい曞いおたす # .claude/rules/datadog-monitors.md --- paths: - "terraform/aws/services/datadog/monitors_*.tf" - "terraform/aws/services/datadog/modules/monitors/**" - "terraform/aws/services/datadog/*.tf" --- # Datadog Monitor 䜜成ガむド ## アヌキテクチャ抂芁 モニタヌは以䞋の 3 局構造で実装する: 1 . **呌び出し局**: `terraform/aws/services/datadog/monitors_<監芖察象>.tf` - 監芖察象リ゜ヌスの䞀芧を `locals` で定矩 - `for_each` で環境 × リ゜ヌスの組み合わせごずにモゞュヌルを呌び出す 2 . **モゞュヌル局**: `terraform/aws/services/datadog/modules/monitors/<monitor_name>/` - `main.tf` にモニタヌの実䜓`datadog_monitor` リ゜ヌスを定矩 - `variables.tf` にモゞュヌルの入力倉数を定矩 3 . **共通蚭定**: `terraform/aws/services/datadog/locals.tf` - `slack_channel`, `error_channel`, `mention` 等 ## ファむル配眮 ``` terraform/aws/services/datadog/ ├── monitors_<監芖察象>.tf # 呌び出し局 ├── modules/monitors/<monitor_name>/ │ ├── main.tf # モニタヌ実䜓 │ └── variables.tf # 入力倉数 ├── locals.tf # 共通蚭定slack_channel, mention等 ├── variables.tf # サヌビスモゞュヌルの入力倉数 └── provider.tf # プロバむダ蚭定 ``` ## モニタヌ名の芏玄 - 必ず `🀖` プレフィックスを付けるTerraform管理であるこずを瀺す - 環境名は **Terraform倉数 `var.env`** で埋め蟌む: `🀖 【$ { var.env } 】...` - 旧: `【 {{ env.name }} 】`Datadogテンプレヌト倉数→ 廃止 - 新: `【$ { var.env } 】`Terraform倉数、for_eachで環境ごずに生成するため - **Datadogテンプレヌト倉数` {{ xxx.name }} `をモニタヌ名・メッセヌゞに䜿わない** - 環境名、リ゜ヌス名等はすべおTerraform倉数`var.env`, `var.display_name` 等で埋め蟌む - Datadogテンプレヌト倉数はモニタヌ䞀芧画面では未展開のたた衚瀺されるため芖認性が悪い - 䟋倖: ` {{ value }} `アラヌト発火時の倀や ` {{ #is_alert}}` 等の条件分岐は匕き続き䜿甚する ## Slackチャンネル / メンションの䜿い方 `locals.tf` で定矩された倉数をモゞュヌルに枡しお䜿う: ```hcl # Slackチャンネル重芁床別 local.slack_channel.info # 情報レベル local.slack_channel.warning # 譊告レベル local.slack_channel.alert # 緊急レベル # ゚ラヌ通知チャンネル環境別 local.error_channel [ env ] # 環境ごずの゚ラヌ通知先 # メンションチヌム別 local.mention.sre # SREチヌム local.mention.swe # SWEチヌム local.mention.eng # ゚ンゞニア党䜓 local.mention.cre # CREチヌム local.mention.algo # アルゎチヌム local.mention.ai_agent # AI Agentチヌム ``` --- <以䞋省略> --- 監芖モニタリングIaCの実践䟋 具䜓的に匊瀟がどのようなコヌド構成をずっおいるかも軜く玹介しおおきたす。 コヌドを最小に保぀倚局構成 Datadog 関連リ゜ヌスは、以䞋の 3 局で管理しおいたす。 envs/<env>/datadog/datadog.tf ← ① 環境呌び出し局 │ â–Œ services/datadog/ ← ② サヌビスラッパヌ局module ├─ monitors_<察象>.tf locals + for_each で展開 └─ dashboard_<察象>.tf │ â–Œ services/datadog/modules/ ← ③ モゞュヌル局sub module ├─ monitors/<name> datadog_monitor の実䜓 └─ dashboards/<name> datadog_dashboard_json の実䜓 å±€ 圹割 ① 環境呌び出し局 環境リストや、プロダクトコヌドのoutputなどの䟝存関係を枡しおサヌビスを呌ぶだけ ② サヌビスラッパヌ局 監芖察象や閟倀などの蚭定差分を列挙しモゞュヌル局に枡す ③ モゞュヌル局 datadog_monitor / datadog_dashboard の実䜓。「SQSの滞留モニタ」「ECSサヌビスのダッシュボヌド」ずいった、環境やプロダクト毎によらない抜象的なコヌドを配眮する 匊瀟の堎合、Datadogのアカりントは本番甚ず開発甚の2アカりントで運甚しおいるため、①の環境呌び出し局でproduction, stagingなどの耇数の環境をたずめおサヌビスラッパヌ局に枡しおいたす。 ポむントは ② のラッパヌ局で、 setproduct関数 を䜿っお「環境 × 監芖察象リ゜ヌス」や「監芖察象 × 閟倀」などの組み合わせを for_each で展開しおいる点です。こうするこずで、䟋えば新しい環境を足すずきは環境リストに 1 行、新しい監芖察象を足すずきも locals に 1 行ずいった具合に、 远加コストが "リスト 1 行" にたで圧瞮されおいる のがこの構成の効きどころです。AI゚ヌゞェントによる生成ずも非垞に噛み合いたす。 実装サンプル ③モゞュヌル局、②サヌビスラッパヌ局の実装サンプルはこんな感じです。 ③SQSのDLQにメッセヌゞが萜ちおきたこずを知らせる抜象モニタ # ~/modules/monitors/sqs_dlq/main.tf locals { doc_link_line = var.doc_link != "" ? "\n[こちらの手順]($ { var.doc_link } )を参考に察凊しおください。" : "" } resource "datadog_monitor" "main" { name = "🀖 【$ { var.env } 】$ { var.service_display_name } SQS DLQ $ { var.display_name } にメッセヌゞが芋぀かりたした" type = "query alert" query = "min(last_5m):min:aws.sqs.approximate_number_of_messages_visible{queuename:$ { var.env } -$ { var.queue_name } ,env:$ { var.env } } > 0" message = <<EOT $ { var.slack_channel.alert } $ { var.mention.sre } $ { var.mention_team } {{ #is_alert}} $ { var.env } 環境の $ { var.service_display_name } SQS DLQ $ { var.display_name } にメッセヌゞが入りたした。 ワヌカヌの凊理に倱敗したメッセヌゞが存圚しおいる可胜性がありたす。$ { local.doc_link_line } メッセヌゞ数: {{ value }} メッセヌゞ {{ /is_alert }} {{ #is_recovery}} $ { var.env } 環境の $ { var.service_display_name } SQS DLQ $ { var.display_name } からメッセヌゞが削陀されたした。 DLQぞの倱敗メッセヌゞぞの察応が完了したした。 {{ /is_recovery }} EOT monitor_thresholds { critical = 0 } on_missing_data = "show_no_data" require_full_window = false renotify_interval = 120 renotify_statuses = [ "alert" ] tags = [ "service:$ { var.service_tag } " , "env:$ { var.env } " , "managed_by:terraform" , ] } ②DLQモニタに「CLM」ずいうプロダクトのSQSを蚭定を枡すラッパヌ局 # ~/services/datadog/monitor_clm_sqs_dlq.tf # CLM SQS DLQモニタヌ # DLQにメッセヌゞが萜ちおきた時にアラヌトを発する # 環境 × キュヌごずにモニタヌを生成する # キュヌ定矩は locals_clm_sqs.tf の local.clm_sqs_queues から導出 module "monitor_clm_sqs_dlq" { for_each = { for pair in setproduct (var.dashboard_envs, local.clm_sqs_queues) : "$ { pair [ 0 ]} -$ { pair [ 1 ] .display_name } " => { env = pair [ 0 ] queue_name = "$ { pair [ 1 ] .queue_name } -dlq" display_name = pair [ 1 ] .display_name } } source = "./modules/monitors/sqs_dlq" env = each.value.env queue_name = each.value.queue_name display_name = each.value.display_name service_display_name = "CLM" service_tag = "clm" mention_team = local.mention.clm doc_link = "<察応手順曞のURL>" slack_channel = local.slack_channel mention = local.mention } # ~/services/datadog/locals_clm_sqs.tf locals { # CLM SQSキュヌ定矩環境プレフィックスなし # listを䜿甚しお定矩順序を保持 clm_sqs_queues = [ { queue_name = "clm-default-app-job-worker-sqs-critical" display_name = "critical" } , # 取り扱うキュヌを列挙する。ここでは省略 ] } ②のコヌドはあくたで「CLM」ずいう特定のプロダクトのDLQモニタを定矩するものです。別のプロダクトの監芖を行いたいずきは、②のコヌドを別途䜜成したす。③のコヌドは再利甚可胜です。 このように抜象化を行うこずによっお、コヌドを最小にしお倚数のモニタを管理するこずが可胜になりたす。そしお実装郚分はAI゚ヌゞェントに䞞投げしおしたえば、 少ない運甚工数 で、 倚数のモニタリング察象 を、 高品質なコヌド で管理できる わけです。 匊瀟で運甚しおいる監芖モニタリングの芏暡 2026/05/20珟圚の芏暡感は以䞋のずおりです。 項目 数 察象環境 8 環境 モニタヌ皮別 / 生成されるモニタヌ数 30 皮類 / 箄 800 個 ダッシュボヌド皮別 / 生成されるダッシュボヌド数 7 皮類 / 箄 80 個 ラッパヌモゞュヌル局のコヌド行数 箄 1 䞇行 おおざっぱに 1 䞇行で 800 のモニタず 80 のダッシュボヌドを支えおいる 蚈算です。 先に述べたように、実装芏玄を敎備したおかげで誰でも気軜に察象の远加ができるようになり、珟圚でも日々監芖モニタリングは充実しおいっおいたす。 おわりに 本蚘事執筆のきっかけは、AI゚ヌゞェントの登堎による監芖モニタリングIaCの倉化は、 単に"運甚が楜になった" だけの話ではない ず感じたためです。 これたでは「重芁なものだけ厳遞しお監芖するのが限界」ず蚀わざるを埗たせんでした。リ゜ヌスを増やすほど管理コストが増えるため、芳枬察象は垞に「これは本圓に必芁か」ずいうフィルタを通っお絞り蟌たれおいたわけです。 それが、远加コストがほが無芖できるほど軜くなった瞬間、 「芳枬したいものは党郚芳枬する」ずいうスタンスに振り切れる ようになりたした。新しいワヌカヌを足したら CPU・メモリ・レむテンシのアラヌトを同時に足し、新しい SQS キュヌを切ったら滞留ず DLQ のモニタも足し、新しい RDS クラスタを建おたらスロヌク゚リやコネクション数のダッシュボヌドも足す ── これらが開発フロヌの䞭で容易に実珟できるようになりたす。 開発組織党䜓ぞの良い圱響もありたす。 新機胜リリヌス時に「ずりあえずダッシュボヌドはある」状態が暙準 になり、初動の異垞怜知が早くなりたす。モニタを足すコストも軜いため、開発者が「この指暙を芋たい」ず SRE に盞談する敷居も䞋がる、あるいは開発者自身がダッシュボヌドやモニタを远加するこずも今埌可胜ずなっおいくはずです。 モニタリングは「保険」のように扱われがちで、最沢な工数を割きづらい領域です。だからこそ、 コストを劇的に䞋げおくれる手段が出おきたなら、芳枬の "深さ" そのものを倉えにいくべきでしょう。 MNTSQ株匏䌚瀟 SRE 西宀
はじめに セキュリティ掚進宀の山田です。 MNTSQぱンタヌプラむズ䌁業を䞻な顧客ずしおいたす。 契玄ずいう、顧客䌁業の事業戊略に盎結するような情報を取り扱う性質䞊、さたざたな芳点からセキュリティをしっかりず担保する必芁があり、DMARCぞの察応もそうした取り組みのひず぀です。 DMARCはなりすたしメヌル察策の仕組みであり、実質的な効果を持たせるにはポリシヌをp=quarantineたたはp=rejectに蚭定する必芁がありたす。 しかしMNTSQでは、DMARCレコヌド自䜓は存圚しおいたものの、ポリシヌはp=noneの状態が続いおいたした。本蚘事ではDMARCポリシヌをp=rejectたで厳栌化した取り組みに぀いお玹介したす。 DMARCずは DMARCはSPF・DKIMの認蚌結果を照合し、ポリシヌに埓っおメヌルを凊理する仕組みです。認蚌に倱敗した堎合、蚭定されたポリシヌの倀に応じお凊理されたす。 DMARCポリシヌの蚭定倀は3぀ありたす。 Step0: 珟状の敎理ず取り組みの方針の決定 珟状を敎理した結果、次のようなステップで取り組む必芁が出おきたした。 自瀟ドメむンからのメヌル送信元の確認 → Step1: DMARCレポヌトの分析 SPF・DKIMなどDNSレコヌド蚭定の適切性の確認 → Step2: DNSレコヌドの棚卞し メヌル送信元の管理方匏の策定 → Step3: 未察応サヌビスの掗い出しず察応 DMARCレコヌドの管理者の策定 → Step4: DMARCポリシヌの厳栌化 Step1: DMARCレポヌトの分析 DMARCレポヌトずは、メヌルを受信したサヌバヌが送信ドメむンの管理者に送る集蚈レポヌトです。どのIPアドレスから自瀟ドメむンを名乗ったメヌルが送られおいるかを把握できたす。 MNTSQではすでにValimailがレポヌトの送信先ずしお蚭定されおいたため、たずはValimailを䜿っお送信元の掗い出しを詊みたした。Google Workspaceなど日頃から利甚しおいるSaaSがレポヌトずしお䞊がっおいた䞀方で、Valimailだけでは䞍十分だずわかりたした。数日ほど集蚈レポヌトを眺めおいるず、利甚しおいるにもかかわらずレポヌトに珟れないサヌビスがあるこずに気づきたした。レポヌトがサマラむズされおおり党容が把握しきれおいなかったこずが原因でした。たたSendGridのようにCNAMEで委譲された独自サブドメむン䟋sg-123.example.comからの送信がValimailで拟えおいなかったこずも、この時点では把握しきれおいたせんでした。 そこでDMARCレポヌトを分析するツヌルを自䜜したした。GASのコヌドはClaudeを掻甚しお䜜成しおおり、ツヌル自䜓は1日ほどで動くものができたした。その埌、衚瀺内容や確認したい情報が適切に出力されおいるかを1〜2日かけお調敎し、実甚的な状態に仕䞊げたした。生成AIを掻甚するこずで実装より蚭蚈に集䞭できるため、ツヌルを自䜜するずいう意思決定のハヌドルが䞋がった実䟋でもありたす。 GASは機胜ごずに分割し、機胜の橋枡しずなるようデヌタの構造を蚭蚈しおいる 䜜成したツヌルの仕組みは以䞋の通りです。 各受信サヌバヌからメヌル添付で届くDMARCレポヌトXMLをGASで収集し、Google Driveに栌玍 XMLをクレンゞングし、衚瀺に必芁な情報だけをスプレッドシヌトに䞭間デヌタずしお展開 DMARCレポヌトは1日あたり数個から十数個になる BIツヌル偎でXMLを盎接読み蟌むず凊理速床に圱響するので䞭間デヌタを生成する 䞭間デヌタをもずにGASでBIツヌルを構築 䞭間デヌタを挟むこずで䜎レむテンシヌでの衚瀺を実珟しおいる これによりValimailでは把握できおいなかった送信元を含めたDMARCレポヌトの党容が把握できるようになり、次のステップであるDNSレコヌドの棚卞しに必芁なサヌビス䞀芧が䜜成できたした。 スクショはサマリヌだけですが、トグルを開くず詳现レポヌトもみれたす Step2: DNSレコヌドの棚卞し Step1のDMARCレポヌト分析で埗たサヌビス䞀芧をもずに、SPFおよびDKIMのレコヌドが正しく蚭定されおいるかを確認しおいきたした。ヒアリングから入るず曖昧な情報に匕っ匵られるリスクがあるため、たずDMARCレポヌトずいうファクトをベヌスに実態を敎理しおから埓業員に確認する、ずいう順序を意識したした。 MNTSQではDNSレコヌドはTerraformで管理されおおり、倉曎はPRを䜜成しおSREチヌムにレビュヌ・デプロむを䟝頌する運甚になっおいたす。Terraformで管理されおいるずDNSレコヌドをコヌドずしお確認しながら棚卞しを進められる点は、䜜業を進める䞊で郜合が良かったです。Terraformの構成ファむルを読み進めおいくずStep1の時点で把握できおいなかったCNAMEサブドメむンの実態がようやく明らかになったのもこのずきでした。 Terraformのファむルを読み進めながら、各サヌビスのDKIMレコヌドが正しく定矩されおいるかを䞀件ず぀確認しおいきたした。以䞋はDKIMレコヌドの䞀䟋です。DKIMの公開鍵は長く、Route 53のTXTレコヌドは1件あたり255文字の制限があるため、format()を䜿っお文字列を分割しお定矩しおいたす。この分割が正しく行われおいないずレコヌドが有効にならず、DKIM認蚌が通らない状態になりたす。このコヌドは修正埌のものですが、それたでは正しく分割されおおらず、レコヌドが無効な状態になっおいたした。 resource "aws_route53_record" "txt__gws_dkim" { zone_id = local.zone_id name = "google._domainkey" type = "TXT" ttl = local.ttl records = [ format ( "%s\" \"%s" , "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmBKdjgohxRFnbL8pb0BTNajVMYNAFRHwUT7WgiKmxxsvr/H5dSIcq+1xTaKTYRA5f29yUG6K2D5UJ7MHaUr2q1F3YOX6XCbX6J1MBB0JTyfDINBXxwspf8xxx6W5I/J0nAaf4SS3LEHSSTymFRiPN27sTTI13vplwgjQ07n0JRrCg62KQTxNmV" , "ekhPuLwTZM4fqKYzZVcoP1ezQOqIlRdd80pnRvSsX1bmGmofJXBxaNlRnHA9x4z8yPN09v18jT8yJRFgXw44uyMcE9+4a9l4AOHik2Z3z/yHpxjEZY47G+tXXefRlKWb0V6dDfywB/bMtkMY4O0ICH2+a0TvBbTQIDAQAB" , ) ] } こうしお机䞊で把握できる範囲の実態を敎理しきった段階で次のステップずしお党瀟ぞのヒアリングに移りたした。 Step3: 未察応サヌビスの掗い出しず察応 DMARCレポヌトずDNSレコヌドの棚卞しによっおメヌル送信サヌビスの実態はある皋床敎理できたしたが、机䞊の調査には限界がありたす。䟋えばDMARCレポヌトの収集期間倖にメヌルを送信しおいたサヌビスは、この時点では拟えおいないケヌスずしお残り埗たす。そこで党瀟員を察象にヒアリングを実斜したした。 ヒアリングの結果、リストに茉っおいないサヌビスがいく぀か出おきたした。内容を確認するず、メヌル送信サヌビス経由で送信しおいるため実態ずしおは問題ないケヌス、FromにはSaaS偎のメヌルアドレスが䜿われReply-Toに䌚瀟ドメむンが蚭定されおいるだけで自瀟ドメむンからの送信ではないケヌスなど、察応䞍芁ず刀断できるものが倚くありたした。䞀方で、瀟員自身では刀断が぀かないずしお連絡をもらったものもあり、そういった情報も含めお敎理を進めるこずができたした。 党瀟ぞのヒアリングは手間がかかるように思えたしたが、机䞊の調査だけでは拟いきれない情報を補完できた点で有効でした。 Step4: ポリシヌの厳栌化 DMARCレポヌトの分析、DNSレコヌドの棚卞し、党瀟ぞのヒアリングず倚角的に察応を進めた結果、自瀟ドメむンからメヌルを送信しおいるサヌビスの党䜓像が把握できたした。送信元のサヌビスごずにSPFおよびDKIMの蚭定が適切に行われおいるこずを確認し、DMARCポリシヌを匕き䞊げる準備が敎いたした。 ポリシヌの蚭定にあたっおは、p=quarantineを経由せずp=rejectに盎接移行するこずにしたした。ここたでの調査ず察応を通じおメヌルの送信状態はひず通り敎理できおおり、段階を螏むよりも䞀気に厳栌化した方が運甚䞊もシンプルだずいう刀断からです。 ポリシヌをp=rejectに移行した埌は、継続的な運甚䜓制の敎備に着手したした。今回の取り組みを通じお察応状況や蚭定内容はNotionにドキュメントずしお敎備したした。DMARCはメヌルを送信するサヌビスが远加・倉曎されるたびに蚭定の芋盎しが必芁になりたすが、運甚の暙準化ずいう芳点では担圓者が耇数぀けられる芏暡になっおいないず難しい面もあり、珟時点では筆者が責任を持っお管理する䜓制ずしおいたす。 たずめ 今回の取り組みを通じお埗られた知芋を敎理したす。 DMARCポリシヌの厳栌化は、蚭定よりも実態の把握が本質的な難しさ DMARCレコヌド自䜓は存圚しおいおも、送信元サヌビスの党䜓像が把握できおいなければポリシヌの匕き䞊げはできたせん。レコヌドを曞き換えるこず自䜓は簡単ですが、そこに至るたでの調査ず敎備に倧半の時間がかかりたした。 自䜜の分析ツヌルが調査の粟床を䞊げた Valimailでは拟えおいなかったCNAMEサブドメむン経由の送信元を含め、DMARCレポヌトの党容を把握できるようになりたした。ツヌルを自䜜しお可芖化したこずで、調査の抜け挏れを防ぐこずができたした。 党瀟ヒアリングで調査の粟床を䞊げた 机䞊の調査だけでは拟いきれないケヌスを補完するために党瀟ヒアリングを実斜したした。瀟員が自発的に刀断の぀かない情報を連絡しおくれたこずで、調査の粟床が䞊がりたした。゚ンゞニアだけで抱え蟌たず、早めに党瀟ぞ展開するこずが有効だず感じたした。 送信状態が敎理できたらp=rejectたで䞀気に匕き䞊げる p=quarantineは段階的な移行のための䞭間蚭定ずしお有効ですが、今回はStep1Step3を通じお送信元の党䜓像を把握した䞊でポリシヌを匕き䞊げたため、p=quarantineを経由せずp=rejectに盎接移行したした。送信状態の敎理が完了しおいれば、段階を螏たずに䞀気に厳栌化するこずで運甚䞊シンプルにするこずができたした。 おわりに DMARCポリシヌの厳栌化は䞀床察応すれば終わりではなく、新しいサヌビスの導入や蚭定倉曎のたびに送信元の管理が必芁になりたす。今回の取り組みで敎備した分析基盀を掻甚しながら、継続的に運甚しおいく予定です。 たた、TerraformのDNSレコヌド倉曎にあたり、PRのレビュヌずデプロむを䜕床もSREチヌムに䟝頌したしたが、快く察応いただきたした。この堎を借りお感謝を䌝えたいず思いたす。同様の課題を抱える組織の参考になれば幞いです。
3行で芁玄するず CUJCritical User Journeyベヌスのダッシュボヌドを䜜る前提ずしお、各 CUJ に玐づく Critical API を客芳的に特定する必芁がありたした Playwright の route API による fault injection を䜿い、E2E テストから Critical API を自動抜出する仕組みを䜜りたした ある皋床汎甚的に䜿えそうなので npm にも眮いおいたす critical-api-finder はじめに SREの寺島です。 特定の API の゚ラヌやレむテンシヌの悪化が、どのナヌザ䜓隓に圱響しおいるのか、容易に刀断できるようになりたいず思ったこずはありたせんか MNTSQ は「すべおの合意をフェアにする」をミッションに、契玄業務を支揎するプロダクトを提䟛しおいたす。 SRE チヌムでは、顧客向けに提䟛しおいるプロダクトにおいお重芁な操䜜のナヌザ䜓隓の劣化を早期に怜知し、継続的に远うために、CUJCritical User Journeyベヌスのダッシュボヌドを䜜りたした。 その構築の過皋で、各 CUJ に玐づく Critical API を Playwright のE2E テストから自動抜出するためのツヌルを䜜りたした。本蚘事では、このツヌルを䞭心に、ダッシュボヌド構築の流れず合わせお玹介したす。 3行で芁玄するず はじめに CUJ ずは ダッシュボヌド構築の流れ 人手で仕分ける難しさ Playwright を䜿ったアプロヌチ 動䜜むメヌゞ 仕組み 1. import の曞き換え 2. baseline 実行 — API リストの収集 3. パスの正芏化 4. ブロックルヌプ ダッシュボヌドぞの組み蟌み 最埌に CUJ ずは CUJ は、ナヌザがプロダクトを通じお達成したい䞭栞的な操䜜の流れを指したす。 MNTSQでは「契玄曞をアップロヌドする」「契玄レビュヌを䟝頌する」ずいった操䜜が代衚的な CUJ にあたりたす。 各 CUJ に぀いお、関連する API のメトリクス゚ラヌレヌト、P95 レむテンシ等を䞀枚の画面で芋られるようにしおいたす。 ダッシュボヌド構築の流れ このダッシュボヌドの構築は、以䞋のような流れで進めたした。 PDM に重芁な画面操䜜ナヌザが毎日必ず行う操䜜や、壊れたら業務が止たるレベルの操䜜をヒアリング 各 CUJ に玐づく API の特定・敎理 ダッシュボヌドの構築 本蚘事の䞻題は、この 2 番目の「API の特定・敎理」をどう進めたかずいう話です。この特定・敎理を進めるなかで、たず問題になるのが「どの API をメトリクスの察象にするか」ずいう仕分けです。 ずいうのも、MNTSQ のアプリでは、1 ぀の画面操䜜の裏偎で、䞻力の凊理から補助的なものたで数倚くの API が動いおいたす。 契玄曞本䜓の保存やメタデヌタの登録のように、倱敗がそのたたゞャヌニヌの䞭断に盎結する API ナヌザアむコンの取埗や通知バッゞ件数のポヌリングのように、倱敗しおもナヌザ操䜜自䜓は継続できる API これらを区別せずにすべおダッシュボヌドに䞊べおしたうず、重芁な倉化がノむズに埋もれおしたいたす。運甚しやすく、か぀意味のあるダッシュボヌドにするためには、「それが止たるずゞャヌニヌが完遂できない APICritical API」を正確に特定し、絞り蟌む必芁がありたした。 人手で仕分ける難しさ いざ Critical な API の仕分けをやろうずするず、意倖ず根拠を持たせるのが難しいこずに気づきたした。 ヒアリングの限界 : 開発者に確認しおも、フロント゚ンドの゚ラヌハンドリングの詳现この API がコケおも画面は止たらない、等たで正確に網矅するのは負担が倧きく、属人化も避けられたせん。 LLM に掚定させる難しさ : Claude Code にコヌドベヌスを読たせお Critical な API を掚定させる方法も詊したした。実行時の振る舞いではなくコヌド䞊の文脈から掚定する以䞊、画面遷移埌に裏で発火するプリフェッチ系のような「実際に動かさないず芋えない」䟝存関係は取りこがしやすく、刀定根拠の再珟性も担保しにくい結果でした。 メンテナンス性 : プロダクトの改修に合わせお API の䟝存関係は倉わるため、その郜床手動で調査し盎すのは珟実的ではありたせん。 そこで、「人間が刀断するのではなく、実際に API を 1 ぀ず぀止めおみお、挙動の倉化を機械的に芳枬すればいいのではないか」ず考えたした。 Playwright を䜿ったアプロヌチ もっずも単玔な方法は、Chrome DevTools の "Block request URL" 機胜を䜿っお 1 ぀ず぀ API をブロックし、画面操䜜を手動で確かめおいくやり方です。ただし、CUJ ひず぀あたり数十個の API があるず、これを毎回手䜜業で繰り返すのは珟実的ではありたせん。手䜜業の負担はもちろん、人の刀定が入るこずで属人化や再珟性の問題も再発しおしたいたす。 そこで着目したのが Playwright の Network API です。 page.route / context.route を䜿うず、ブラりザのネットワヌク通信をスクリプト偎から傍受したり、改倉したりできたす。たずえば「特定の URL パタヌンに合臎するリク゚ストだけ 500 を返す」ずいった操䜜が数行で曞けたす。 await context.route( "**/api/contracts" , async ( route ) => { await route.fulfill( { status : 500 , body : JSON . stringify ( { error : "blocked" } ) } ); } ); これを䜿えば、E2E テストを 1 床曞いおおくだけで、 テストを 1 床走らせお、ゞャヌニヌ䞭に呌ばれる API をすべお蚘録する 蚘録した API を 1 ぀ず぀ 500 で短絡しながら、テストを再実行する テストが萜ちた API を Critical、通った API を非 Critical ず刀定する ずいう流れを完党に自動化できたす。刀定の根拠は「テストが通る通らない」ずいう二倀の客芳的なシグナルで、人間の解釈を挟みたせん。プロダクトに改修が入っお䟝存関係が倉わっおも、テストを曎新しお回し盎せば最新の Critical API リストが手に入りたす。 たた、Playwrightを採甚した背景ずしおは、ちょうど MNTSQ では Autify から Playwright ぞの E2Eテストの移行プロゞェクトが進んでおり、QA が曞くテストをそのたたむンプットずしお䜿える芋蟌みがある、ずいう事情もありたした。 動䜜むメヌゞ このアプロヌチをツヌルずしおたずめたものが critical-api-finder です。Playwright のテストファむルを甚意しおコマンドを叩くだけで動きたす。 npm install -D @playwright/test critical-api-finder npx critical-api-find tests/contract-upload.spec.ts 実行するず、ツヌルが内郚でテストをN+1回繰り返し実行したす最初の 1 回で API を蚘録 → 各 API を 1 ぀ず぀ブロックしながら再実行。終わるず critical-api-results/verify-contract-upload.json に結果が出力されたす { " journeyId ": " contract-upload ", " testPath ": " tests/contract-upload.spec.ts ", " entries ": [ { " method ": " POST ", " pattern ": " /api/contracts ", " critical ": true } , { " method ": " GET ", " pattern ": " /api/contracts/:id ", " critical ": true } , { " method ": " GET ", " pattern ": " /api/v2/user/me ", " critical ": false } , { " method ": " GET ", " pattern ": " /api/v2/notifications/count ", " critical ": false } ] } 仕組み ツヌル内郚は倧きく 4 ぀のコンポヌネントから成りたす。 ※ コヌドは簡略化しお茉せおいたす。 1. import の曞き換え テストファむルを盎接曞き換えたくないので、CLI は sibling file tests/contract-upload.spec.ts → tests/contract-upload.critical.spec.ts ずしお耇補したうえで、 @playwright/test の import だけを critical-api-finder 自身に差し替えたす。 // テストファむル䞭のこの import を  import { test , expect } from "@playwright/test" ; // 自動的にこちらに曞き換える import { test , expect } from "critical-api-finder" ; 曞き換えは正芏衚珟ベヌスの単玔眮換です。 const IMPORT_RE = /^([\t ]*import\b[^;]*?\bfrom\s+['"])@playwright\/test(['"])/gm ; const REQUIRE_RE = /^([\t ]*(?:const|let|var)\b[^;]*?\brequire\s*\(\s*['"])@playwright\/test(['"]\s*\))/gm ; export function rewriteImports ( source : string ): string { return source . replace (IMPORT_RE, `$1critical-api-finder$2` ) . replace (REQUIRE_RE, `$1critical-api-finder$2` ); } critical-api-finder は @playwright/test の公開 API ã‚’å…š re-export しおいるので、ナヌザのテストはコヌド倉曎れロで、こちらの拡匵 fixtureroute handler 入りを匕き継いで動きたす。 2. baseline 実行 — API リストの収集 最初の 1 回はブロックなしでテストを走らせ、 context.route で党 API を傍受しおリスト化したす。 await context.route( "**/api/**" , async ( route ) => { const method = route.request(). method (); const pathname = new URL (route.request(). url ()). pathname ; const normalized = normalizePathname(pathname); appendFileSync(collectFile, ` ${ method } ${ normalized } \n ` ); await route.continue(); } ); ここでは route.continue() で玠通しさせるだけなので、テストの挙動には圱響を䞎えたせん。芳枬したリク゚ストはあずで重耇排陀しお、ブロックルヌプの察象リストずしお䜿いたす。 3. パスの正芏化 API の path には、リ゜ヌス ID のように実行のたびに倀が倉わる動的セグメントが含たれるこずがありたす。たずえば、芳枬時に /api/contracts/12345 だった path が、次の実行では /api/contracts/67890 のように別の倀になっおいお、そのたたブロック察象ずしお蚘録しおおいおも圓たらない、ずいうこずが起こりたす。 そこで、芳枬した path を以䞋のルヌルで正芏化したす。 セグメント プレヌスホルダ 数倀 id ( /12345 ) :id UUID :uuid ISO date ( /2026-04-22 ) :date 長い hex hash (20+ 桁) :hash 実装は順序付きの眮換ルヌルを䞊べただけのシンプルなものです。 const RULES = [ // UUID数倀 id より先に刀定 { regex : /\/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(?=\/|$)/gi , replacement : "/:uuid" } , // ISO date { regex : /\/\d{4}-\d{2}-\d{2}(?=\/|$)/g , replacement : "/:date" } , // 長い hex hash { regex : /\/[0-9a-f]{20,}(?=\/|$)/gi , replacement : "/:hash" } , // 数倀 id { regex : /\/\d+(?=\/|$)/g , replacement : "/:id" } , ] ; function normalizePathname ( pathname : string ): string { let result = pathname; for ( const { regex , replacement } of RULES) { result = result. replace (regex, replacement); } return result; } これにより、 /api/contracts/12345 も /api/contracts/67890 も同じ /api/contracts/:id ずいう論理的な゚ンドポむント単䜍に集玄されたす。ブロック時は逆にこのプレヌスホルダを正芏衚珟に展開し、圓該パタヌンに合臎するリク゚ストだけを 500 にする、ずいう流れです。 4. ブロックルヌプ 正芏化したパタヌンを 1 ぀ず぀取り出しお、Playwright を再実行したす。route handler は同じ堎所ですが、今床は察象パタヌンに合臎したリク゚ストだけを 500 で short-circuit したす。 await context.route( "**/api/**" , async ( route ) => { const pathname = new URL (route.request(). url ()). pathname ; // ブロック察象パタヌンに合臎するリク゚ストだけ 500 にする if (blockedRegex. test (pathname)) { await route.fulfill( { status : 500 , contentType : "application/json" , body : JSON . stringify ( { error : "Blocked by critical-api-finder" } ), } ); return ; } await route.continue(); } ); これでテストが萜ちれば Critical、通れば非 Critical ず刀定したす。なお、毎むテレヌションで --retries=0 --max-failures=1 を匷制するこずで、Playwright project 偎の retry 蚭定によらず「ブロックした瞬間に exit」させ、無駄な再詊行を防いでいたす。 ダッシュボヌドぞの組み蟌み 実際にE2Eテストにこのツヌルを圓おお Critical API のリストを取り出し、そのたたダッシュボヌドのメトリクス察象ずしお組み蟌みたした。ダッシュボヌドは Datadog 䞊に Terraform で管理しおおり、CUJ の定矩は次のような圢で曞いおいたす。 locals { cuj_dashboards = { sample_journey = { title = "ナヌザ䜓隓: サンプルゞャヌニヌ" service = "sample-service" trace_name = "rack" steps = [ { name = "ステップ 1" endpoints = [{ display_name = "POST /api/sample/foo" resource_name = "resources::v2::fooapi_post_/foo" }] } , { name = "ステップ 2" endpoints = [ { display_name = "GET /api/sample/foo/:id" resource_name = "resources::v2::fooapi_get_/foo/:id" } , { display_name = "GET /api/sample/bar" resource_name = "resources::v2::barapi_get_/bar" } , ] } , ] } # 他の CUJ も同じ圢で䞊べる } } module "cuj_dashboard" { for_each = local.cuj_dashboards # CUJダッシュボヌド詳现を管理するためのモゞュヌル。本筋ではないため本皿では陀倖 source = "./modules/cuj_dashboard" dashboard_title = each.value.title service = each.value.service trace_name = each.value.trace_name steps = each.value.steps } これによっお CUJ ごずにダッシュボヌドが生成され、ステップ単䜍で゚ラヌレヌト・レむテンシ・リク゚スト数が䞊ぶ圢になりたす。 最埌に ダッシュボヌドに茉せる Critical API のリストを、人の刀断ではなく「テストの通る通らない」ずいう客芳的なシグナルから匕けるようになり、属人化ずメンテナンスの問題は倧きく緩和できたした。 副次的な発芋ずしお、Playwrightが「回垰を防ぐためのE2Eテストツヌル」ずいう甚途以倖にも䜿えそうだずいう気づきがありたした。fault injection ずの組み合わせには、䟝存関係の抜出以倖にもいろいろな応甚が効きそうで、䟋えば個人プロダクト甚の安䞊がりな脆匱性蚺断ツヌルやカオス゚ンゞニアリングツヌルなどを䌌たような仕組みで自䜜できそうだず思いたした。このあたりは今埌も探っおいきたいず思っおいたす。 同じような課題に取り組んでいる方は、ぜひ芗いおみおください。(フィヌドバック・PRも歓迎しおいたす) リポゞトリ github.com/kterashi02/critical-api-finder
はじめに システムが成長し、扱うデヌタ量やトラフィックが増倧しおくるず、非同期凊理の安定性ずスケヌラビリティがサヌビス党䜓の課題ずなりたす。 匊瀟のサヌビスの根幹郚分はRuby on Railsを採甚しおいるため、長らく暙準の非同期凊理のキュヌずしおResque (Redis) を䜿甚しおいたした。しかし、サヌビス芏暡の拡倧に䌎い、 Redisベヌスの運甚では「ワヌカヌのオヌトスケヌル最適化」が困難である ずいう課題が浮き圫りになっおきたした。 本蚘事では、この非同期凊理のバック゚ンドを Amazon SQS に移行した背景ず、移行に䌎っお行ったキュヌ蚭蚈・オヌトスケヌル最適化の取り組みに぀いお玹介したす。 はじめに なぜSQSなのか 非同期凊理でのオヌトスケヌル実珟の課題 RedisからSQSぞ ─ 移行のメリットず留意点 ─ 2千䞇件のログからサヌビス特性を分析する キュヌの再蚭蚈 前提: アプリケヌション偎での事前敎備 優先床ベヌスのキュヌ: 短時間ゞョブを守るための4段構成 機胜ベヌスのキュヌ: 倧芏暡オペレヌションの隔離 キュヌごずのオヌトスケヌル戊略 モニタリングず改善のルヌプ Datadogによるモニタリング モニタリング → 仮説 → 修正のルヌプ 結果 次の䞀手: SQS Fair Queue おわりに なぜSQSなのか 非同期凊理でのオヌトスケヌル実珟の課題 倉化の激しいワヌクロヌドに察しお、理想的なオヌトスケヌルを実珟するためには、以䞋の芁玠が必芁䞍可欠です。 グレヌスフルなシャットダりン : オヌトスケヌルむンを行うずいうこずは、凊理途䞭であっおも䞭断が発生しうるずいうこずです。このようなむベントに察しお、適切なハンドリング・リトラむを行える必芁がありたす。 现かなメトリクスを取埗できる : オヌトスケヌルを運甚に乗せるためには、実際のワヌカヌ台数の倉化を、メッセヌゞの滞留数や滞留時間、同時実行数などず照らし合わせ、適切な蚭定になっおいるかを評䟡する必芁がありたす。たた、オヌトスケヌルの条件もこれらのメトリクスを参照するこずになりたす。 RedisからSQSぞ ─ 移行のメリットず留意点 ─ Redisをバック゚ンドに䜿甚しおいるず、キュヌの滞留数やゞョブの状態をリアルタむムで詳现に把握するために、独自のメトリクス収集の仕組みを構築・維持しなければなりたせん。たた、゚ラヌ時のリトラむ機構に぀いおも、アプリケヌション偎で慎重に蚭蚈・実装する必芁がありたした。 これらの課題を解決するため、バック゚ンドを Amazon SQS ぞ移行するこずを決断したした。 SQSの 「 可芖性タむムアりト 」 を利甚すれば、ゞョブ実行䞭の゚ラヌやオヌトスケヌルに䌎う䞭断が発生しおも、メッセヌゞを安党にキュヌぞ戻し、自動で再詊行できたす。これにより、耇雑な゚ラヌハンドリングを行うこずなく、グレヌスフルシャットダりンを容易に実珟できたす。たた、 暙準で提䟛される滞留数や滞留時間ずいった匷力なメトリクス をそのたたオヌトスケヌルのトリガヌに利甚できるため、独自のモニタリング基盀を維持するコストも䞍芁になりたす。柔軟にスケヌルし、か぀壊れにくい基盀を䜜るには、SQSのマネヌゞドな特性をフル掻甚するこずが最適解だず刀断したした。 泚意点ずしお、SQSには"優先床"の抂念がありたせん。Redisベヌスのゞョブキュヌ(Resque/Sidekiqなど)では1぀のキュヌ内でゞョブの優先床を衚珟できたすが、SQSではキュヌそのものを分割しお、優先床の高いゞョブが滞留しないような蚭蚈を取る必芁がありたす。たた、at-least-once配信を前提ずしたゞョブの冪等化や、可芖性タむムアりトを超える長時間ゞョブの二重実行察策ずいった、アプリケヌション偎で事前に手圓おすべきポむントもありたす(これらの具䜓的な察応に぀いおは埌述したす)。 ぀たりSQS移行においおは、サヌビスのゞョブ特性を正しく理解した䞊で、最初に適切なキュヌ構成を蚭蚈できるかが成吊を分けたす。 2千䞇件のログからサヌビス特性を分析する 最適な蚭蚈を行うため、ゞョブ党䜓の傟向や特城を把握する必芁がありたす。 「ワヌカヌのオヌトスケヌルの最適化」ずは、即ち「顧客䜓隓」ず「運甚コスト」の最適化 です。なんずなくワヌカヌが増えたり枛ったりしおいるずいう状況はゎヌルではありたせん。匊瀟のサヌビスではアップロヌドした契玄曞の条項の分解や、怜玢甚のむンデックス䜜成など、顧客䜓隓に関わる凊理も非同期で行われたす。このような凊理が、特定のテナントや初期導入に䌎う倧量解析や、実行時間が比范的長時間にわたるゞョブの圱響所謂 ノむゞヌネむバヌ問題 を受けないような蚭蚈にしたいずころです。たた、無駄なスケヌルアりトはコスト芳点から奜たしくないです。 ずいうこずで、以䞋が匊瀟サヌビスでのゞョブの特城です。芋やすいように察象を絞っお衚瀺しおいたす 凊理時間別 ゞョブの実行数のグラフ(察数軞) このグラフは、デヌタベヌスのゞョブの実行を管理するテヌブルのここ半幎分のデヌタ玄千䞇件を集蚈したものです。ゞョブの実行ログをデヌタベヌスに蓄積しおいれば、リヌドレプリカでSQLを叩いおExcelで集蚈するだけなので、特別な分析基盀がなくおも気軜に行えたす瞊軞がゞョブの実行数、暪軞がゞョブの実行時間を衚したす。暪軞も察数チックな軞になっおいたすたた、同じゞョブでも実行時間にバラツキがあるため、同䞀のゞョブは同じ色で衚珟しおいたす。 このグラフから、以䞋のようなワヌクロヌドの特性が芋えおきたした。 78%のゞョブは1秒未満、 99%のゞョブは10秒以内に完了する。 1秒未満のゞョブは営業時間䞭に分間200件以䞊積たれる䞀方、10秒超えのゞョブは分間1件未満しか積たれない 実行時間に 数秒から数時間のバラツキがあるゞョブが存圚する たた、匊瀟のサヌビスでは以䞋のようなオペレヌションが発生する点も考慮する必芁がありたす。 初期導入: 顧客の運甚開始の準備ずしお倧量のドキュメントをアップロヌドし、ファむル倉換や解析を行う䜜業がある 再むンデクシング: 怜玢機胜の拡匵などで、倧量の怜玢甚むンデックスを曎新・再䜜成する䜜業がある よっお、䞊蚘を考慮し぀぀、 10秒未満のゞョブをいかに滞留させずに捌けるかが蚭蚈における重芁な課題 でした。 キュヌの再蚭蚈 蚭蚈の出発点はシンプルです。 99%を占める短時間ゞョブを、長時間ゞョブやバヌストワヌクロヌドに巻き蟌たれず安定しお捌くこず 。これを実珟するため、キュヌを「 優先床ベヌス 」ず「 機胜ベヌス 」の2軞で分割したした。 前提: アプリケヌション偎での事前敎備 SQSのクラむアントずしおは shoryuken を採甚したした。 キュヌ蚭蚈の話に入る前に、SQSをバック゚ンドにする䞊でアプリケヌション偎で先に手圓おした2点に觊れおおきたす。これらは蚭蚈段階で必芁になるこずが予想できたため、本栌的なチュヌニングに入る前に枈たせたした。結果ずしお、埌段のチュヌニングフェヌズではこの2点が問題になるこずはありたせんでした。 ひず぀めは、 ゞョブの冪等化 です。SQSはat-least-once配信のため、同䞀メッセヌゞが耇数回配信される前提で実装する必芁がありたす。移行に䌎っおゞョブの抜象クラスを芋盎し、すべおのゞョブが冪等に動䜜するよう統䞀したした。 ふた぀めは、 長時間ゞョブにおける可芖性タむムアりトの動的延長 です。SQSの可芖性タむムアりトは、凊理䞭のメッセヌゞが他のワヌカヌに再配信されないようにするための仕組みですが、ゞョブの実行時間が可芖性タむムアりトを超えるず、凊理䞭にもかかわらず別ワヌカヌで二重実行されおしたいたす。これを防ぐため、長時間ゞョブに察しおはアプリケヌション偎でハヌトビヌト的に可芖性タむムアりトを延長する実装を入れたした。これによりむンフラ偎では 可芖性タむムアりトのチュヌニングにシビアになる必芁がなくなった のは蚭蚈䞊のポむントです。 優先床ベヌスのキュヌ: 短時間ゞョブを守るための4段構成 通垞業務のゞョブは、 実行時間ず投入パタヌン の2軞で4぀のキュヌに振り分けたす。 キュヌ名 甹途 想定される投入パタヌン critical 顧客䜓隓に盎結し時間にシビアなJobUIからのファむルアップロヌド・解析、メヌル発信など 単発・䜎頻床 high 顧客䜓隓に盎結するが倧量投入される可胜性があるJobZip解凍やそれに䌎う解析など バヌスト default 顧客業務に圱響するが即時性䞍芁なJobメヌル連携・定時タスク起点、倖郚連携起点 䞭頻床 low 実行時間が10秒を超える可胜性のあるJob台垳のexport/importなど 䞍定 蚭蚈の肝は2぀ありたす。 ひず぀めは、 実行時間 10秒 を境界に 短時間ゞョブキュヌcritical / high / defaultず 長時間ゞョブキュヌlowを分離する こず以䞋、10秒ルヌル。ゞョブ党䜓の99%は10秒以内に完了する䞀方、残り1%には数十秒〜数時間に及ぶゞョブが混ざっおいたす。これを同じキュヌに流すず、1本の長時間ゞョブがワヌカヌを占有しおしたいたす。これでは滞留時間をオヌトスケヌル条件にしたずき、無駄にスケヌルアりトをしおしたいたす。しかし、10秒ルヌルを導入するこずにより、短時間ゞョブキュヌ 偎ではSQSの滞留時間メトリクスを盎接オヌトスケヌルのトリガヌに䜿えるようになりたす。 ふた぀めは、短時間ゞョブをさらに 「UI起点で単発投入されるものcritical」ず「UI起点だが倧量投入されうるものhigh」で分けた こず。たずえば「Zipアップロヌド埌の䞀括解析」は、1回の操䜜で数癟件のゞョブが䞀気に積たれる可胜性がありたす。これを critical に流すず、1人のナヌザヌが倧きなZipをアップロヌドしただけで、他のナヌザヌの単発操䜜が裏で詰たる、ずいう兞型的な ノむゞヌネむバヌ問題 が発生したす。バヌスト性のあるワヌクロヌドを high に隔離するこずで、 critical は垞に䜎い滞留数を保ち、最も厳しいSLOを圓おられるようにしおいたす。 機胜ベヌスのキュヌ: 倧芏暡オペレヌションの隔離 優先床ベヌスの分割だけでは扱いきれないのが、冒頭でも觊れた初期導入ず再むンデクシングです。あるテナントの倧量凊理が他テナントの通垞業務を圧迫しないよう、このような 特䟋のオペレヌションには、通垞業務ずは完党に分離した専甚キュヌ を甚意したした。 キュヌ名 甹途 introduction 初期導入など、通垞業務ず分離したいJobファむルアップロヌド・解析・むンデクシング reindexing å…šä»¶indexing / 暩限倉曎時の倧量indexing甹 これらの機胜ベヌスキュヌは 普段はワヌカヌ0台で埅機し、必芁なタむミングでのみ起動 したす。そのため、キュヌ区分が増えるこずによる定垞的なコスト増は発生したせん。隔離したい単䜍でキュヌを切る刀断を、コストを気にせず行えるのがSQS + オヌトスケヌル構成の利点です。 キュヌごずのオヌトスケヌル戊略 各キュヌには、特性に応じたオヌトスケヌル蚭定を割り圓おおいたす。短時間ゞョブキュヌcritical / high / defaultは滞留時間をトリガヌにスケヌルアりトし、こためにスケヌルむンする運甚にしおいたす。「滞留時間数秒以内」ずいう明確なSLOがあるため、滞留時間ベヌスで反応させるのが最もシンプルです。䞀方、長時間ゞョブキュヌlowはメッセヌゞ数がワヌカヌ最小数を超えたらスケヌルアりト、キュヌが空になったらスケヌルむンずしおおり、SLOを蚭けない代わりにMAX台数を絞るこずでコストを抑制しおいたす。 モニタリングず改善のルヌプ キュヌを再蚭蚈しお移行が完了しおも、それで終わりではありたせん。 蚭蚈が想定通りに機胜しおいるかを継続的に芳枬し、ズレを芋぀けお现かく修正しおいくフェヌズ こそが、オヌトスケヌル運甚の本番です。蚭蚈時点で党おを正解にするこずは䞍可胜なので、 動かしながら最適化する前提 でモニタリング基盀を敎えたした。 Datadogによるモニタリング たず、Datadog䞊にキュヌ運甚のためのダッシュボヌドを構築したした。ダッシュボヌドでは䞻に以䞋の芳点を䞀芧できるようにしおいたす。 ワヌカヌ台数の掚移 キュヌごずの滞留数 キュヌごずの滞留時間 凊理䞭メッセヌゞの数 これらを䞊べお眺めるこずで、「 high キュヌだけ滞留時間が䌞びおいるがワヌカヌ台数が頭打ちになっおいる → スケヌルアりト䞊限が䜎すぎる」「 default キュヌはスケヌルアりトしおいるのに、凊理䞭のメッセヌゞ数が垞に少なく滞留時間が慢性的に長い → 10秒以䞊かかるこずがある長時間Jobが玛れ蟌んでいる」ずいった 具䜓的な問題を即座に切り分けられる ようになりたした。 加えお、前述の2千䞇件分析にも䜿ったゞョブ実行履歎テヌブルに察し、enqueue時のキュヌ名の蚘録や怜玢甚むンデックスの远加ずいった改修を行い、ダッシュボヌドで気になった事象を SQLで即座に深掘りできる フロヌも敎えおいたす。 ダッシュボヌドが「胜動的に芋にいく」仕組みである䞀方、 異垞をプッシュで怜知する仕組みずしお、Datadogモニタ も「キュヌの皮類 × 指暙」の組み合わせで網矅的に仕蟌みたした。滞留時間、滞留数、ワヌカヌのCPU/メモリ、DLQのメッセヌゞ数などをキュヌごずに監芖するこずで、ダッシュボヌドを芋おいない時間垯でもSLO違反や異垞な振る舞いに即座に気付ける䜓制を䜜っおいたす。キュヌ数 × 指暙数で監芖項目はそれなりの芏暡になりたすが、AI゚ヌゞェントの登堎によっおこれらを実珟するこずが可胜になりたした。 モニタリング → 仮説 → 修正のルヌプ 道具が揃ったあずは、ひたすら地道な改善ルヌプを回したした。 時間のかかっおいるゞョブを発芋 : 実装を芋盎し、必芁に応じおリファクタリング。 low ぞの移動で枈むケヌスもあれば、ロゞック自䜓に改善の䜙地があるケヌスもある。Datadog APMのトレヌスを仕蟌んでひたすら問題の凊理を特定するなど、時にはアプリケヌションの深い郚分に螏み蟌んで改善を行った オヌトスケヌルがうたくいっおいない : メトリクスから原因を考察し、閟倀や䞊限台数などオヌトスケヌル関連のさたざたな蚭定を䜕床も芋盎した キュヌ配眮のミスマッチ : 想定ず異なる挙動をするゞョブを適切なキュヌに移動した ひず぀の修正で党おが解決するこずは皀で、「盎すず別の歪みが芋える」を繰り返すのが実態でした。しかし、モニタリングの敎備をしっかり行ったこずによっお、䜕が課題かが垞に明確であり、継続的に改善掻動を行えおいたす。 結果 こうしたルヌプを繰り返した結果、ただただ課題はありたすが、珟圚ではかなり安定しおオヌトスケヌルが機胜しおいたす。 ブログ執筆時点でのオヌトスケヌルの様子 次の䞀手: SQS Fair Queue 本蚘事の蚭蚈を進めおいる最䞭、AWSから SQS Fair Queue ずいう機胜がリリヌスされたした 。これは、MessageGroupId をテナント識別子ずしお蚭定するこずで、SQSがノむゞヌテナントを自動怜出し、他テナントぞのメッセヌゞ配信を優先する機胜です。本蚘事で扱っおきた「ノむゞヌネむバヌ問題」の䞀郚を、マネヌゞドな仕組みで解決しおくれたす。 ただしFair Queuesは「ゞョブ特性ごずのキュヌ分離」(本蚘事の introduction / reindexing / low など) を代替するものではなく、短時間ゞョブのキュヌ内で発生するテナント間の䞍公平を緩和する䜍眮づけです。本蚘事で構築したキュヌ蚭蚈ず組み合わせるこずで、よりきめ现やかなノむゞヌネむバヌ察策が可胜になるず期埅しおいたす。 匊瀟ではすでに本機胜を導入枈みで、本番ワヌクロヌドでの効果を芳察しおいるフェヌズです。結果に぀いおはい぀か別蚘事でレポヌトできればず思いたす。 docs.aws.amazon.com おわりに 長くなりたしたが、改めお今回の取り組みを通しお埗られた孊びを敎理したす。 蚭蚈の前にデヌタを芋る : 2千䞇件のログから「99%が10秒未満」ずいうワヌクロヌド特性を掎めたこずが、10秒ルヌルやキュヌ分割ずいう具䜓的な蚭蚈刀断に盎結したした。「なんずなくスケヌルしおいる」状態から脱华するには、定量的にサヌビス特性を把握するこずが出発点になりたす マネヌゞドサヌビスの特性に乗る : 可芖性タむムアりトや暙準メトリクスずいったSQSの匷みをそのたた蚭蚈の前提に組み蟌むこずで、独自の監芖・リトラむ基盀を維持するコストから解攟されたした。「自前で頑匵る」を枛らし、マネヌゞドな仕組みに乗っかれる箇所は培底的に乗っかる方が、結局シンプルで壊れにくい構成になりたす 蚭蚈は仮説、運甚しながら最適化する : 蚭蚈時点で党おを正解にするこずは䞍可胜で、動かしながら现かく修正しおいくフェヌズの方がむしろ重芁でした。そしお、そのルヌプを高速に回すには モニタリングを疎かにしないこず が倧切です 最埌の点に぀いお補足するず、今回これだけ现かい粒床でダッシュボヌドやモニタを敎備できたのは、 AI゚ヌゞェントClaude Codeの存圚が倧きい です。キュヌ × 指暙の組み合わせで倧量のモニタを䜜成・保守する䜜業は、人の手では䞍可胜に近いくらい倧倉です。珟実にはdev環境、staging環境など環境数分必芁になりたすし しかし、 「Datadogリ゜ヌスを定矩するコヌド」を完党にブラックボックスにしおも、AI゚ヌゞェントの力を借りれば問題なく保守し続けられる 、ずいう確信が持おたこずで、「芳枬したいものは党郚芳枬する」ずいう方針を恐れずに取れるようになりたした。これは単なる開発効率の話ではなく、むンフラ蚭蚈の意思決定そのものに圱響を䞎える倉化だず感じおいたす。 次回は、このDatadogダッシュボヌド・モニタをIaCで運甚するための工倫に぀いおも蚘事にしおみたいず思いたす。ここたで読んでくださり、ありがずうございたした。 MNTSQ株匏䌚瀟 SRE 西宀
はじめに モチベヌション 実装 むンフラリポゞトリTerraform 倉曎 アプリケヌションリポゞトリECS タスク定矩倉曎 暪展開Reusable / Caller 構成ぞの移行 運甚颚景 コスト圱響がない倉曎の堎合 コスト圱響がある倉曎の堎合 おわりに はじめに 匊瀟では AWS 䞊にマルチテナント構成のむンフラを耇数の環境にわたっお運甚しおおり、その構成管理を Terraform でおこなっおいたす。むンフラ偎のリ゜ヌス構成はもちろんのこず、アプリケヌション偎で管理されおいる ECS タスク定矩の CPU / メモリ割り圓おに察しおも、折に觊れお倉曎が入りたす。 こうしたリ゜ヌス倉曎は無論コストに跳ねたす。新芏コンポヌネントの远加やむンスタンスサむズの倉曎が Pull Request以䞋 PR、サヌビスによっおは Merge Request 等の呌称もありたすずしお䞊がっおくるたびに「で、これは月いくら増えるのか」ずいう問いが生じるわけですが、これたでは手動で料金衚を匕いお詊算するか、詊算そのものをおこなわずにマヌゞしおしたうかの二択ずいう栌奜でした。 本皿では、この問題を Claude Code による自動コスト詊算で解決した取り組みず、それを GitHub Actions の Reusable Workflow 構成で党瀟の䞻芁リポゞトリに暪展開した方法に぀いお玹介したす。 モチベヌション きっかけは瀟内 Slack で「新芏コンポヌネント远加やリ゜ヌス割り圓お倉曎の際には、コスト芋積もりもセットでおこなう運甚にしたい」ずいう声が䞊がったこずです。 ただし、PR のたびに人の手でコスト詊算をおこなうずいうやり方には、以䞋のような難点が付きたずいたす。 属人的詊算する向きによっお粟床にバラ぀きが出る 忘れがち「あずでやろう」が「やらなかった」に垰結しがち 骚が折れるAWS の料金衚を匕いお、リ゜ヌス倉曎の前埌差分を蚈算しお、PR にコメントしお  ずいう䜜業を手でやるのは地味に骚が折れる 䞀方で匊瀟では Claude Code による PR レビュヌの自動化を既に導入しおおり、レビュヌワヌクフロヌの暪に「コスト詊算」ワヌクフロヌを䞊べるのは自然な延長線䞊にありたした。PR の倉曎内容からリ゜ヌスの远加・倉曎・削陀を読み取り料金に照らすような䜜業は、たさに LLM が埗意ずする領域です。 実装 むンフラリポゞトリTerraform 倉曎 最初の実装はむンフラリポゞトリに察しおのものです。既存のコヌドレビュヌワヌクフロヌ claude-code-review.yml ずは別ワヌクフロヌずしお claude-code-cost-estimate.yml を新蚭しおいたす。 name : Claude Code Cost Estimate on : pull_request : types : [ opened, synchronize, ready_for_review ] branches : [ main ] paths : - 'terraform/**' concurrency : group : claude-cost-estimate-${{ github.event.pull_request.number }} cancel-in-progress : true なお paths: に terraform/** を指定しおいるのは、このリポゞトリでは Terraform コヌドを terraform/ 配䞋にたずめる構成を採っおいるためです。Terraform に觊れない PR ではワヌクフロヌ自䜓が発火したせん。 レビュヌずコスト詊算を分離したこずの嬉しさは以䞋の通りです。 レビュヌコメントずコスト詊算コメントが混圚しない Terraform に觊れない PR ではスキップされる concurrency group が独立しおいるため互いにブロックしない ワヌクフロヌ内では Claude Code CLI を盎接むンストヌルし、プロンプトをファむル経由で枡す方匏を採っおいたす。圓初は anthropics/claude-code-action を䜿甚しおいたのですが、2026 幎 4 月䞭旬頃から本ワヌクフロヌの実行が継続的に倱敗する事象に遭遇し 1 、切り分けを重ねた末に回避策ずしお CLI 盎接実行方匏ぞ切り替えたした。 プロンプトの骚子は以䞋です。 gh pr diff で PR の差分を取埗する 远加・倉曎・削陀される AWS リ゜ヌスを特定する 必芁に応じお倉曎ファむルを Read で読み、リ゜ヌスの蚭定倀を確認する リ゜ヌスのリヌゞョン原則 ap-northeast-1、CloudFront・WAF 等は us-east-1に応じた料金に基づき月額コストを詊算する 詊算結果を PR コメントずしお投皿する 詊算察象のリ゜ヌスに぀いおは、プロンプト内で以䞋のように列挙しおいたす。 ## 詊算察象リ゜ヌス 以䞋のリ゜ヌスはコストむンパクトが倧きいため、必ず詊算に含めるこず: - **コンピュヌティング**: ECS (Fargate vCPU/メモリ), Lambda (リク゚スト数/実行時間), EC2 - **デヌタベヌス**: RDS (むンスタンスクラス/ストレヌゞ/Multi-AZ), ElastiCache (ノヌドタむプ/ノヌド数), DynamoDB - **ストレヌゞ**: S3, EBS, EFS - **ネットワヌク**: NAT Gateway ($0.062/h + デヌタ凊理料), ALB/NLB ($0.0243/h + LCU), VPC Endpoint - **怜玢**: OpenSearch (むンスタンスタむプ/ノヌド数/ストレヌゞ) - **監芖**: CloudWatch Logs (取り蟌み/保存), CloudWatch Metrics/Alarms - **CDN/グロヌバル**: CloudFront (リク゚スト/転送量, us-east-1 料金), WAF (WebACL/ルヌル/リク゚スト) - **その他**: KMS (キヌ/リク゚スト), Route53 (ホストゟヌン/ク゚リ) 詊算の際の现則に぀いおもプロンプトで指瀺しおおり、コスト圱響のない倉曎をどう扱うかもここに含めおいたす。 ## 詊算ルヌル - 料金は USD で算出するJPY 換算は䞍芁 - リヌゞョンは原則 ap-northeast-1東京だが、CloudFront・WAF・ACMus-east-1 発行等のグロヌバルサヌビスは us-east-1 の料金を䜿甚するこず - リ゜ヌスの削陀はコスト削枛ずしお負の倀で衚蚘する - コスト圱響がれロたたは無芖できる倉曎タグ倉曎、IAM ポリシヌ倉曎、セキュリティグルヌプルヌル倉曎等の堎合は「コスト圱響なし」ず簡朔に報告する - 正確な料金が䞍明な堎合は保守的高めに芋積もり、前提条件を明蚘する - 環境ごずのコスト差が明確な堎合むンスタンスサむズ違い等は環境別に蚘茉する アプリケヌションリポゞトリECS タスク定矩倉曎 匊瀟ではアプリケヌションリポゞトリを耇数運甚しおおり、いずれも ECS の起動タむプずしお Fargate を採甚しおいたす。これらのリポゞトリでは Terraform を盎接取り扱うこずはなく、ECS タスク定矩テンプレヌトJSONの CPU / メモリ割り圓お倉曎が䞻なコスト倉動芁因です。こちらは Terraform 版ずは異なる蚭蚈が必芁でした。 ずりわけ重芁だったのは Fargate 料金の動的取埗です。圓初は料金をプロンプト内にハヌドコヌドしおいたしたが、x86_64 ず ARM64Gravitonでは Fargate の料金が異なるにもかかわらず、プロンプトに蚘茉しおいたのは x86_64 の料金のみでした。そのため ARM64 タスクに察する PR であっおも x86_64 䟡栌で詊算されおしたう状態になっおおり、ARM64 移行が進み぀぀あった圓時の実態ず乖離した芋積もりが出おしたっおいたのです。 最終的には AWS 公開の Pricing Bulk API から最新の Fargate On-Demand 料金を動的に取埗し、タスク定矩テンプレヌトの runtimePlatform.cpuArchitecture からアヌキテクチャを刀定しお適切な料金を適甚しおいたす。プロンプト内では以䞋のように料金取埗手順を明瀺しおいたす。 ## Fargate 料金の取埗 料金をハヌドコヌドせず、以䞋の手順で動的に取埗するこず: 1. タスク定矩テンプレヌトを ` Read ` で読み、各タスクの CPU アヌキテクチャARM64 / X86 _ 64を特定する - ` runtimePlatform.cpuArchitecture ` の倀を確認する - テンプレヌト倉数や条件分岐でアヌキテクチャが切り替わる堎合は、倉数定矩偎も参照しお実際の倀を確定する - ` runtimePlatform ` が存圚しない堎合は X86 _ 64 ずみなす 2. 以䞋のコマンドで ap-northeast-1 の最新 Fargate On-Demand 料金を取埗する: ```shell-session $ curl -s "https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonECS/current/ap-northeast-1/index.json" | jq ' .terms.OnDemand as $terms | [.products | to_entries[] | select(.value.attributes.usagetype | test("Fargate")) | select(.value.attributes.usagetype | test("Windows|Ephemeral") | not) | .key as $sku | {usagetype: .value.attributes.usagetype, price_usd: ($terms[$sku] | to_entries[0].value.priceDimensions | to_entries[0].value.pricePerUnit.USD)}]' ``` 3. usagetype ずアヌキテクチャの察応: - ` Fargate-vCPU-Hours:perCPU ` / ` Fargate-GB-Hours ` → X86 _ 64 - ` Fargate-ARM-vCPU-Hours:perCPU ` / ` Fargate-ARM-GB-Hours ` → ARM64 4. タスクのアヌキテクチャに察応する料金ず 730h/月 で詊算する タスク定矩の ` cpu ` / ` task_cpu ` は vCPU ナニット1024 = 1 vCPU、 ` memory ` / ` task_memory ` は MiB 単䜍です。 暪展開Reusable / Caller 構成ぞの移行 圓初、耇数のアプリケヌションリポゞトリぞの暪展開は、ワヌクフロヌ YAML をそのたたコピペする栌奜でおこないたした。 しかしこの方匏はすぐに砎綻したした。暪展開を完了した盎埌から、料金算出ロゞックを実情に適うものにするためのプロンプトなどの修正や、利甚䞭アクションの SHA pin 曎新ずいった䜜業が連続で必芁になったのです。すべおのリポゞトリに同じ修正 PR を展開しお回るずいうのは、地味に手間のかかる䜜業です。ワヌクフロヌ定矩におけるリポゞトリ固有の内容は䞀郚に限られ、ゆえに倧半が共通化可胜なものでした。そこに修正が入るたびに、リポゞトリ間での蟻耄合わせの為に党リポゞトリぞ同じ倉曎を行う必芁が生じおいたした。 そこで GitHub Actions の Reusable Workflows を掻甚し、以䞋の二局構成に敎理し盎す栌奜ずしたした。 Reusable Workflowテンプレヌトリポゞトリ 共通プロンプトFargate 料金取埗ロゞック、詊算ルヌル、コメントフォヌマット、セキュリティ指瀺 workflow_call トリガにお倖郚から呌び出し可胜 inputs.repo_context リポゞトリ固有のタスク定矩構成説明ず inputs.additional_rules 远加ルヌルを受け取る Caller Workflow各アプリケヌションリポゞトリ トリガ条件 paths フィルタの定矩 repo_context にリポゞトリ固有の構成説明を蚘述 Reusable Workflow を呌び出すのみ Caller 偎のコヌドは以䞋のように簡玠です。 name : Claude Code Cost Estimate on : pull_request : types : [ opened, synchronize, ready_for_review ] paths : - 'app/task-definition/**' concurrency : group : claude-cost-estimate-${{ github.event.pull_request.number }} cancel-in-progress : true jobs : cost-estimate : uses : <自組織>/<テンプレヌトリポゞトリ>/.github/workflows/reusable-claude-code-cost-estimate.yml@main with : repo_context : | ## リポゞトリ構成 このリポゞトリでは ECS タスク定矩を以䞋のように管理しおいたす : - `app/task-definition/template-*.json`: タスク定矩テンプレヌト - `app/task-definition/variables-*/`: 環境ごずの倉数オヌバヌラむド ... secrets : CLAUDE_CODE_OAUTH_TOKEN : ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} uses: の蚘法は <owner>/<repo>/<path>@<ref> の圢匏で、別リポゞトリにある Reusable Workflow を参照したす GitHub 公匏ドキュメント 。 @<ref> にはブランチ・タグ・コミット SHA のいずれも指定できたす。 repo_context は Reusable Workflow 偎のプロンプトにそのたた埋め蟌たれる文字列入力です。リポゞトリ固有のタスク定矩の眮き堎所や YAML 構造、その他の泚意事項を自然蚀語で蚘述しおおけば、Claude は PR 差分をその文脈で解釈しおくれたす。埓来であればリポゞトリごずに構造解析ロゞックを曞き分ける必芁があったずころを、自然蚀語で補足を曞くだけで枈たせられるのは LLM を挟む倧きな利点です。 この構成の嬉しさは明癜です。Fargate 料金取埗ロゞックの修正や利甚䞭アクションの SHA pin 曎新が必芁になった際、テンプレヌトリポゞトリの 1 箇所を修正するだけで党リポゞトリに反映されたす。各リポゞトリの Caller は自身の構成説明 repo_context にのみ責任を持おばよく、共通郚分の保守から解攟されたした。 なお Reusable Workflow を別リポゞトリから参照するにあたっおは、以䞋 2 ぀の蚭定が必芁です。詳现は GitHub 公匏ドキュメント を参照しおください。 呌び出し元リポゞトリ : Settings > Actions > General にお "Allow <自組織>, and select non-<自組織>, actions and reusable workflows" を有効化する テンプレヌトリポゞトリ : Settings > Actions > General の "Access" セクションにお、他リポゞトリからの参照を蚱可する 運甚颚景 実際に投皿されおいるコメントの䟋を以䞋に玹介したす機密情報は適宜マスクしおいたす。 コスト圱響がない倉曎の堎合 Fluent Bit のログフィルタ蚭定远加のような、AWS リ゜ヌスの远加・倉曎・削陀を䌎わない PR に察しおは、以䞋のように簡朔に報告されたす。 コスト圱響なしの報告䟋: Fluent Bit のログフィルタ蚭定远加 PR に察するコメント コスト圱響がある倉曎の堎合 具䜓的な金額差を䌎う詊算結果の䟋ずしお 2 ぀挙げたす。 ECS タスクのアヌキテクチャを x86_64 から ARM64 に倉曎する PR では、Fargate 料金の差分がタスクごずに算出されたす。 コスト圱響ありの報告䟋: ECS タスク定矩の ARM64 移行 PR に察するコメント Terraform 偎の䟋ずしおは、OpenSearch マスタヌノヌドのむンスタンスタむプ倉曎 PR に察しお、むンスタンス単䟡の根拠ずずもに月額差分が算出されたす。 コスト圱響ありの報告䟋: OpenSearch マスタヌノヌドのむンスタンスタむプ倉曎 PR に察するコメント 前者のようにコヌド倉曎量は小さいがコスト圱響が芋えづらいケヌスや、埌者のように䞀芋スペックアップに芋えおコスト削枛ずなる盎感に反するケヌスにおいお、数倀根拠ず䜵せお即座に金額圱響が可芖化されるのはレビュアヌにずっおの刀断材料ずしお有甚です。 おわりに PR 䞊の Terraform 倉曎や ECS タスク定矩倉曎に察しお Claude Code にコスト圱響を自動詊算させる仕組みず、Reusable Workflow による党リポゞトリぞの暪展開に぀いお玹介したした。 今回の取り組みで埗られた利点は以䞋の通りです。 詊算の自動化コスト圱響の有無が PR 䞊で自動的に可芖化され、手動での料金衚匕きが䞍芁になった レビュヌ芳点の底䞊げ「このリ゜ヌス倉曎はいくらかかるのか」が PR コメントずしお残るため、レビュアヌがコスト芳点でも刀断できるようになった 保守性Reusable Workflow ぞの集玄により、共通ロゞックの修正が 1 箇所で枈むようになった 䞀方で、暪展開の過皋ではプロンプトの修正や利甚䞭アクションの SHA pin 曎新など、党リポゞトリに修正 PR を展開する矜目になる堎面が耇数ありたした。最初から Reusable Workflow 構成にしおおけばよかったず思わないでもないですが、たずは動くものを 1 リポゞトリで䜜り、その埌暪展開し぀぀構成を掗緎させおゆくアプロヌチは結果的には劥圓だったず考えおいたす。実際に暪展開をおこなっお初めお芋えおくる問題アヌキテクチャごずの料金差異などもあり、最初から完璧な蚭蚈を目指すよりも実地で鍛えおゆくほうが確実なものが出来あがる向きもありたす。 たた、暪展開ずは別の文脈ですが、実装初期に遭遇した小さな事件ずしお、コスト詊算コメントを gh pr comment --body オプションにむンラむンで枡しおいたずころ、本文䞭の $0 が bash のシェル倉数ずしお展開され /bin/bash に化けるずいうものもありたした。 --body-file 経由に倉曎しお解決したしたが、LLM を GHA 䞊で取り扱う際にはシェルずの境界に泚意が必芁であるずいう孊びを埗おいたす。 Terraform のコスト詊算ツヌルずしおは Infracost のような専甚ツヌルもありたす。今回それらを採甚しなかったのは、PR 䞊でザックリ差額感を掎めれば充分で、専甚ツヌルを導入・運甚するほどに粟緻な分析を求めおいたわけではない、ずいう向きが倧きいです。たた匊瀟では既に Claude Code を PR レビュヌの自動化で䜿っおおり、その延長線䞊で賄えるずいう点も埌抌しずなりたした。その点、Claude Code を䜿うアプロヌチは「diff の文脈を理解した䞊で、コスト圱響のない倉曎を適切にスキップできる」「プロンプトの修正のみで詊算ルヌルを柔軟に倉曎できる」ずいう固有の嬉しさがあり、芁求氎準に充分適うものずなりたした。 PR レビュヌプロセスにコスト芳点を自然に組み蟌みたい向きに、本皿で玹介した事䟋が䞀助ずなれば幞いです。 文責MNTSQ 株匏䌚瀟 SRE 秋本 泚蚘この蚘事は文責者の過去蚘事ず匊瀟内のドキュメントをもずに Claude Opus 4.7 が䜜成した内容をほがそのたた䜿甚しおいたす 圓時の調査では anthropics/claude-code-action の #1205 や #1126 ずいったものを参照しおいたした。原因の完党な特定には至らなかったのですが、CLI 盎接実行ぞの切り替えにより事象は解消しおいたす。 ↩
はじめに 匊瀟では耇数の Amazon OpenSearch Service ドメむンを運甚しおいたす。これらのドメむンはいずれも VPC 内に閉じた構成をずっおおり、セキュリティ匷化を目的に きめ现やかなアクセス制埡 Fine-grained Access Control; FGACを有効にしおいたす。 FGAC は「誰が OpenSearch 䞊でどの操䜜を蚱可されるか」をロヌル単䜍で制埡する仕組みです。蚭定には OpenSearch の Security API を VPC 内から呌び出す必芁がありたす。以前は手順曞にもずづいお手䜜業で蚭定しおいたしたが、OpenSearch を利甚するサヌビスやドメむンが増えるに぀れおスケヌルしなくなっおきたした。 本皿では、この課題ぞの察策ずしお以䞋アプロヌチを採るこずずしたした。 FGAC 蚭定ロヌルおよびマッピングの各定矩埌述を Terraform コヌドずしお定矩 OpenSearch ドメむンぞの FGAC 蚭定投入を VPC 内で CodeBuild を実行するこずで実斜するようリ゜ヌスを定矩 以䞋でこのアプロヌチの詳现に぀いお扱いたす。 FGAC で蚭定するもの FGAC の蚭定察象は倧きく2぀です。 ロヌル定矩 OpenSearch 䞊のロヌルが持぀暩限どのむンデックスにどの操䜜を蚱可するか等を蚘述する ロヌルマッピング 䞊蚘ロヌルに「誰を」玐づけるかを蚘述する。AWS 環境では IAM ロヌルや IAM ナヌザを OpenSearch 䞊のロヌルに玐付けるこずに察応する これを螏たえ、匊瀟では甚途に応じお以䞋の3皮のロヌルを定矩し運甚しおいたす。 ロヌル名 甹途 マッピング察象 all_access / security_manager 管理者暩限 OpenSearch の master user ずしお機胜する IAM ロヌル mntsq アプリケヌション甚 各サヌビスの ECS タスクロヌル等、OpenSearch にアクセスする IAM ロヌル mntsq_operators 運甚者甚 OpenSearch 運甚を担圓するメンバが䜿う IAM ロヌル 課題ず方針 OpenSearch を利甚するサヌビスが远加されるず、そのサヌビスの IAM ロヌルを mntsq ロヌルの backend_roles に远加する䜜業が発生したす。たた、新しい OpenSearch ドメむンが䜜成された堎合には3぀党おのロヌル蚭定を䞀から行う必芁がありたす。この䜜業を続けるこずで以䞋のような課題が浮かび䞊がっおきたした。 手順曞はあるが手䜜業であり、蚭定内容の差分管理やレビュヌができない OpenSearch ドメむンの远加や既存蚭定の修正が発生するたびに手䜜業が必芁ずなり、運甚負荷が高い こうした課題を解消すべく、FGAC 蚭定をコヌドずしお管理し、OpenSearch ぞの適甚も自動化するこずを目指したした。 terraform-provider-opensearch ではダメなのか OpenSearch のリ゜ヌスを Terraform で管理する手段ずしおは terraform-provider-opensearch が存圚したす。「それで十分ではないか」ずいう指摘はもっずもですが、匊瀟の構成ではこのアプロヌチは成立したせん。 理由は VPC にありたす。匊瀟の OpenSearch ドメむンは VPC 内に閉じおおり、パブリック゚ンドポむントを持ちたせん。terraform-provider-opensearch は Terraform の実行環境から盎接 OpenSearch にリク゚ストを送る必芁がありたすが、匊瀟では Terraform の実行䞻䜓は GitHub Actions であり、VPC の倖にいたす。぀たり Provider が OpenSearch に到達できたせん。 「であれば Terraform の実行環境自䜓を VPC 内に配眮すればよいのではないか」ずいう考えもありたす。たずえば CodeBuild を GitHub Actions の self-hosted runner ずしお VPC 内で動かす方法 がありたす。しかしこの構成では FGAC 蚭定に限らず党おの terraform plan / terraform apply が VPC 内を経由するこずになりたす。FGAC の蚭定のためだけに Terraform 党䜓の実行環境を切り替えるのは割に合いたせん。 採甚したアヌキテクチャ terrarform-provider-opensearch を䜿わず、か぀必芁な蚭定内容を IaC し、その反映も自動化したい。こうした些か欲匵りな芁件を満たすため、「定矩ず適甚を分離する」アプロヌチを採甚したした。考え方はシンプルです。 Terraform が担うこずFGAC のロヌル定矩・ロヌルマッピングをコヌドずしお宣蚀し、CodeBuild プロゞェクトの環境倉数に JSON ずしお泚入する CodeBuild が担うこずVPC 内で起動し、環境倉数から受け取った JSON を OpenSearch Security API に PUT する 抂念図 Terraform はあくたで「䜕を蚭定するか」を管理し、「蚭定を OpenSearch に届ける」郚分は VPC 内で動ける CodeBuild に委ねる圢です。 実装 FGAC ロヌル定矩 3皮のロヌル定矩ずロヌルマッピングは Terraform の locals ブロックで宣蚀しおいたす。 locals { fgac = { # 管理者甚 admin = { assign_json_content = { backend_roles = [ aws_iam_role.opensearch_master_role.arn, # 必芁に応じお管理操䜜を行う IAM ロヌルを远加 ] } } # アプリケヌション甚 app = { assign_json_map = { for key, config in var.opensearch : key => { backend_roles = flatten ( [ for role_name in config.user_iam_roles : data.aws_iam_roles.targets [ role_name ] .arns ] ) } } role_json_content = { description = "Role for MNTSQ services" cluster_permissions = [ "*" ] index_permissions = [{ index_patterns = [ "*" ] fls = [] masked_fields = [] allowed_actions = [ "*" ] }] tenant_permissions = [{ tenant_patterns = [ "*" ] allowed_actions = [ "kibana_all_write" ] }] } } # 運甚者甚 ops = { assign_json_map = { backend_roles = [ for group in data.aws_identitystore_groups.main.groups : group.group_id if contains ( [ "group-a" , "group-b" , "group-c" ] , group.display_name) # OpenSearch 運甚を担圓するメンバが所属する IAM Identity Center グルヌプ名を列挙 ] } role_json_content = { description = "Role for MNTSQ operators" cluster_permissions = [ "manage_snapshots" , "cluster_monitor" , "cluster:admin/opendistro/ism/policy/search" , "cluster:admin/opendistro/ism/policy/get" , # ... ISM / 通知関連の暩限が続く ] index_permissions = [{ index_patterns = [ "*" ] allowed_actions = [ "get" , "search" , "read" , "indices_monitor" , "manage" ] }] tenant_permissions = [] } } } } アプリケヌション甚 app に぀いお補足したす。 var.opensearch は OpenSearch ドメむンをキヌずする map で、環境局の main.tf にお各ドメむンごずに user_iam_roles そのドメむンにアクセスする必芁のある IAM ロヌル名のリストを定矩しおいたす。 assign_json_map はこの user_iam_roles をもずに IAM ロヌル ARN を匕き圓お、ドメむンごずの backend_roles を動的に構築したす。OpenSearch ドメむンによっおアクセス元のサヌビスが異なるため、マッピングもドメむン単䜍で分かれる必芁があるずいうこずです。 運甚者甚 ops は少し毛色が異なりたす。アプリケヌション甚では IAM ロヌルを backend_roles に蚭定しおいたしたが、運甚者甚では IAM Identity Center のグルヌプ ID を backend_roles ずしお䜿いたす。 data.aws_identitystore_groups で運甚担圓のグルヌプを匕き、そのグルヌプに所属するメンバが OpenSearch Dashboards にアクセスした際に適切な暩限が付䞎されるようにしおいたす。 管理者甚 admin にはロヌル定矩がありたせん。 all_access ず security_manager は OpenSearch に組み蟌みで存圚するロヌルであり、暩限の内容を改めお定矩する必芁がないためです。ここで管理するのは「誰をそのロヌルに玐づけるか」ずいうマッピングだけです。 CodeBuild プロゞェクト FGAC の蚭定を OpenSearch に届けるには VPC 内から Security API を呌び出す必芁がある、ずいう点はここたでで述べたずおりです。CodeBuild には vpc_config を指定するこずでビルド環境を VPC 内のサブネットで起動する機胜がありたす。これを利甚すれば、Terraform 自䜓は VPC 倖で動かし぀぀、蚭定の適甚だけを VPC 内で実行できたす。 䞊述 locals を jsonencode() で JSON 文字列に倉換し、CodeBuild プロゞェクトの環境倉数に枡したす。HCL のオブゞェクトはそのたたでは環境倉数文字列ずしお泚入できないため、この倉換が必芁です。 resource "aws_codebuild_project" "fgac_configuration_manager" { for_each = var.opensearch name = "$ { var.env } -$ { var.service } -$ { each.key } -fgac-configuraton-manager" build_timeout = 10 service_role = aws_iam_role.opensearch_master_role.arn environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0" type = "LINUX_CONTAINER" environment_variable { name = "APP_ROLE_DEFINITION_JSON" value = jsonencode (local.fgac.app.role_json_content) } environment_variable { name = "APP_MAPPING_DEFINITION_JSON" value = jsonencode (local.fgac.app.assign_json_map [ each.key ] ) } environment_variable { name = "ADMIN_MAPPING_DEFINITION_JSON" value = jsonencode (local.fgac.admin.assign_json_content) } environment_variable { name = "OPS_ROLE_DEFINITION_JSON" value = jsonencode (local.fgac.ops.role_json_content) } environment_variable { name = "OPS_MAPPING_DEFINITION_JSON" value = jsonencode (local.fgac.ops.assign_json_map) } environment_variable { name = "OPENSEARCH_ENDPOINT" value = "https://$ { module.opensearch [ each.key ] .domain.custom_endpoint } " } } # VPC 内で実行するための蚭定 vpc_config { vpc_id = local.remote_state_core.vpc.vpc_id subnets = local.remote_state_core.vpc.private_subnets security_group_ids = [ aws_security_group.codebuild.id ] } source { type = "NO_SOURCE" buildspec = templatefile ( "$ { path.module } /buildspecs/fgac_configuration_manager.yaml.tmpl" , {} ) } } for_each = var.opensearch ずしおいるので、OpenSearch ドメむンの数だけ CodeBuild プロゞェクトが䜜成されたす。各プロゞェクトの環境倉数にはそのドメむン固有のマッピング情報が入りたす。 vpc_config ブロックで VPC ID、プラむベヌトサブネット、セキュリティグルヌプを指定するこずで、CodeBuild のビルド環境が VPC 内で起動するようになりたす。これが本構成の栞心です。 IAM ロヌル CodeBuild が OpenSearch の master user ずしお振る舞うためのロヌルを定矩しおいたす。 resource "aws_iam_role" "opensearch_master_role" { name = "$ { var.env } -$ { var.service } -opensearch-master" assume_role_policy = data.aws_iam_policy_document.opensearch_master_assume_role.json } data "aws_iam_policy_document" "opensearch_master_assume_role" { statement { effect = "Allow" actions = [ "sts:AssumeRole" ] principals { type = "Service" identifiers = [ "codebuild.amazonaws.com" , "opensearch.amazonaws.com" , ] } } } このロヌルは2぀のサヌビスから Assume されたす。 codebuild.amazonaws.com は CodeBuild の実行ロヌルずしお、 opensearch.amazonaws.com は OpenSearch ドメむンの master user ずしお、それぞれこのロヌルを䜿いたす。ひず぀のロヌルに䞡方の信頌関係を持たせるこずで、"CodeBuild がこのロヌルで実行する = OpenSearch の master user ずしお振る舞える" ずいう構図を成立させおいたす。 Buildspec FGAC のロヌル定矩やロヌルマッピングは、OpenSearch の Security REST API に察しお PUT リク゚ストを送るこずで蚭定したす。利甚する゚ンドポむントは以䞋の2぀です。 /_plugins/_security/api/roles/{ロヌル名} ロヌル定矩の䜜成及び曎新 /_plugins/_security/api/rolesmapping/{ロヌル名} ロヌルマッピングの䜜成及び曎新 CodeBuild ではこの API 呌び出しを以䞋の buildspec で実行しおいたす。 version : 0.2 phases : install : commands : - pip3 install awscurl build : commands : # 管理者暩限の蚭定 - 'echo "$ADMIN_MAPPING_DEFINITION_JSON" > assign_admin.json' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/security_manager" -d @assign_admin.json -X PUT' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/all_access" -d @assign_admin.json -X PUT' # アプリケヌション甚ロヌルの蚭定 - 'echo "$APP_ROLE_DEFINITION_JSON" > role.json' - 'echo "$APP_MAPPING_DEFINITION_JSON" > assign.json' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/roles/mntsq" -d @role.json -X PUT' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/mntsq" -d @assign.json -X PUT' # 運甚者甚ロヌルの蚭定 - 'echo "$OPS_ROLE_DEFINITION_JSON" > role.json' - 'echo "$OPS_MAPPING_DEFINITION_JSON" > assign.json' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/roles/mntsq_operators" -d @role.json -X PUT' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/mntsq_operators" -d @assign.json -X PUT' やっおいるこずは玠盎です。環境倉数から JSON を取り出しおファむルに曞き、 awscurl AWS SigV4 眲名付きの curlで OpenSearch Security API に PUT したす。各ロヌルに぀いお ロヌル定矩の PUT → ロヌルマッピングの PUT の順で実行したす。管理者甚は組み蟌みロヌルぞのマッピングのみなのでロヌル定矩の PUT はありたせん。 awscurl を䜿うこずで IAM ロヌルベヌスの認蚌が自動的に行われるため、API キヌやパスワヌドの管理は䞍芁です。 以䞊の実装をたずめたコヌド党䜓を以䞋に瀺したす。 Terraform コヌド党䜓クリックで展開 # ================================================== # デヌタ゜ヌス # ================================================== data "aws_iam_roles" "targets" { for_each = toset ( flatten ( values (var.opensearch) [ * ] .user_iam_roles)) name_regex = ".*$ { each.value } $" } data "aws_ssoadmin_instances" "main" {} data "aws_identitystore_groups" "main" { identity_store_id = tolist (data.aws_ssoadmin_instances.main.identity_store_ids) [ 0 ] } # ================================================== # FGAC ロヌル定矩locals # ================================================== locals { fgac = { admin = { assign_json_content = { backend_roles = [ aws_iam_role.opensearch_master_role.arn, # 必芁に応じお管理操䜜を行う IAM ロヌルを远加 ] } } app = { assign_json_map = { for key, config in var.opensearch : key => { backend_roles = flatten ( [ for role_name in config.user_iam_roles : data.aws_iam_roles.targets [ role_name ] .arns ] ) } } role_json_content = { description = "Role for MNTSQ services" cluster_permissions = [ "*" ] index_permissions = [{ index_patterns = [ "*" ] fls = [] masked_fields = [] allowed_actions = [ "*" ] }] tenant_permissions = [{ tenant_patterns = [ "*" ] allowed_actions = [ "kibana_all_write" ] }] } } ops = { assign_json_map = { backend_roles = [ for group in data.aws_identitystore_groups.main.groups : group.group_id if contains ( [ "group-a" , "group-b" , "group-c" ] , group.display_name) # OpenSearch 運甚を担圓するメンバが所属する IAM Identity Center グルヌプ名を列挙 ] } role_json_content = { description = "Role for MNTSQ operators" cluster_permissions = [ "manage_snapshots" , "cluster_monitor" , "cluster:admin/opendistro/ism/policy/search" , "cluster:admin/opendistro/ism/policy/get" , "cluster:admin/opendistro/ism/policy/write" , "cluster:admin/opendistro/ism/policy/delete" , "cluster:admin/opensearch/notifications/channels/get" , "cluster:admin/opensearch/notifications/configs/get" , "cluster:admin/opensearch/notifications/configs/create" , "cluster:admin/opensearch/notifications/configs/update" , "cluster:admin/opensearch/notifications/configs/delete" , "cluster:admin/opensearch/notifications/features" , "cluster:admin/opensearch/notifications/feature/send" , ] index_permissions = [{ index_patterns = [ "*" ] dls = "" fls = [] masked_fields = [] allowed_actions = [ "get" , "search" , "read" , "indices_monitor" , "manage" ] }] tenant_permissions = [] } } } } # ================================================== # IAM ロヌル・ポリシヌ # ================================================== data "aws_iam_policy_document" "opensearch_master_assume_role" { statement { effect = "Allow" actions = [ "sts:AssumeRole" ] principals { type = "Service" identifiers = [ "codebuild.amazonaws.com" , "opensearch.amazonaws.com" , ] } } } resource "aws_iam_role" "opensearch_master_role" { name = "$ { var.env } -$ { var.service } -opensearch-master" assume_role_policy = data.aws_iam_policy_document.opensearch_master_assume_role.json } data "aws_iam_policy_document" "codebuild_permissions" { statement { effect = "Allow" actions = [ "logs:CreateLogGroup" , "logs:CreateLogStream" , "logs:PutLogEvents" , "ec2:CreateNetworkInterface" , "ec2:DescribeDhcpOptions" , "ec2:DescribeNetworkInterfaces" , "ec2:DeleteNetworkInterface" , "ec2:DescribeSubnets" , "ec2:DescribeSecurityGroups" , "ec2:DescribeVpcs" , "ec2:CreateNetworkInterfacePermission" , ] resources = [ "*" ] } } data "aws_iam_policy_document" "opensearch_permissions" { statement { effect = "Allow" actions = [ "es:*" ] resources = [ "*" ] } } resource "aws_iam_policy" "codebuild" { name = "$ { var.env } -$ { var.service } -codebuild-permissions" policy = data.aws_iam_policy_document.codebuild_permissions.json } resource "aws_iam_role_policy" "opensearch_master" { role = aws_iam_role.opensearch_master_role.name policy = data.aws_iam_policy_document.opensearch_permissions.json } resource "aws_iam_role_policy_attachment" "opensearch_master" { role = aws_iam_role.opensearch_master_role.name policy_arn = aws_iam_policy.codebuild.arn } # ================================================== # セキュリティグルヌプ # ================================================== resource "aws_security_group" "codebuild" { name = "$ { var.env } -$ { var.service } -codebuild" vpc_id = local.remote_state_core.vpc.vpc_id egress { from_port = 0 to_port = 0 protocol = "-1" cidr_blocks = [ "0.0.0.0/0" ] } } # ================================================== # CodeBuild プロゞェクト # ================================================== resource "aws_codebuild_project" "fgac_configuration_manager" { for_each = var.opensearch name = "$ { var.env } -$ { var.service } -$ { each.key } -fgac-configuraton-manager" description = "Configure OpenSearch FGAC roles for $ { each.key } in $ { var.env } " build_timeout = 10 service_role = aws_iam_role.opensearch_master_role.arn artifacts { type = "NO_ARTIFACTS" } environment { compute_type = "BUILD_GENERAL1_SMALL" image = "aws/codebuild/amazonlinux2-x86_64-standard:5.0" type = "LINUX_CONTAINER" image_pull_credentials_type = "CODEBUILD" environment_variable { name = "APP_ROLE_DEFINITION_JSON" value = jsonencode (local.fgac.app.role_json_content) } environment_variable { name = "APP_MAPPING_DEFINITION_JSON" value = jsonencode (local.fgac.app.assign_json_map [ each.key ] ) } environment_variable { name = "ADMIN_MAPPING_DEFINITION_JSON" value = jsonencode (local.fgac.admin.assign_json_content) } environment_variable { name = "OPS_ROLE_DEFINITION_JSON" value = jsonencode (local.fgac.ops.role_json_content) } environment_variable { name = "OPS_MAPPING_DEFINITION_JSON" value = jsonencode (local.fgac.ops.assign_json_map) } environment_variable { name = "OPENSEARCH_ENDPOINT" value = "https://$ { module.opensearch [ each.key ] .domain.custom_endpoint } " } environment_variable { name = "AWS_REGION" value = data.aws_region.current.region } } logs_config { cloudwatch_logs { status = "ENABLED" } } vpc_config { vpc_id = local.remote_state_core.vpc.vpc_id subnets = local.remote_state_core.vpc.private_subnets security_group_ids = [ aws_security_group.codebuild.id ] } source { type = "NO_SOURCE" buildspec = templatefile ( "$ { path.module } /buildspecs/fgac_configuration_manager.yaml.tmpl" , {} ) } } Buildspec 党䜓クリックで展開 version : 0.2 phases : install : commands : - pip3 install awscurl build : commands : # 管理者暩限の蚭定 - 'echo "$ADMIN_MAPPING_DEFINITION_JSON" > assign_admin.json' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/security_manager" -d @assign_admin.json -X PUT' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/all_access" -d @assign_admin.json -X PUT' # アプリケヌション甚ロヌルの蚭定 - 'echo "$APP_ROLE_DEFINITION_JSON" > role.json' - 'echo "$APP_MAPPING_DEFINITION_JSON" > assign.json' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/roles/mntsq" -d @role.json -X PUT' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/mntsq" -d @assign.json -X PUT' # 運甚者甚ロヌルの蚭定 - 'echo "$OPS_ROLE_DEFINITION_JSON" > role.json' - 'echo "$OPS_MAPPING_DEFINITION_JSON" > assign.json' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/roles/mntsq_operators" -d @role.json -X PUT' - 'awscurl --service es --region ${AWS_REGION} -H "Content-Type: application/json" "${OPENSEARCH_ENDPOINT}/_plugins/_security/api/rolesmapping/mntsq_operators" -d @assign.json -X PUT' 運甚フロヌ 初期セットアップ 新しい OpenSearch ドメむンを䜜成した堎合の流れは以䞋のようになりたす。 terraform apply で OpenSearch ドメむンず CodeBuild プロゞェクトが䜜成される AWS コン゜ヌルから察象の CodeBuild プロゞェクト ${ENV}-search-${DOMAIN}-fgac-configuraton-manager を手動で Start build する FGAC のロヌルずマッピングが OpenSearch に蚭定される terraform apply は CodeBuild プロゞェクトの定矩環境倉数やビルド環境の蚭定を䜜成するのみで、ビルド自䜓の実行は行いたせん。そのため初回は手動で Start build する必芁がありたす。 蚭定が正しく反映されたかどうかは、OpenSearch の /_plugins/_security/authinfo API で確認できたす。察象の IAM ロヌルで OpenSearch にリク゚ストを送り、レスポンスの roles フィヌルドに期埅するロヌル名が含たれおいれば蚭定は成功です。 蚭定倉曎 新しいサヌビスが OpenSearch にアクセスする必芁が生じた堎合の流れは以䞋のようになりたす。 環境局の main.tf で該圓ドメむンの user_iam_roles にロヌル名を远加する PR を䜜成しおレビュヌを受けるここで蚭定差分がコヌドずしお芋える terraform apply で CodeBuild プロゞェクトの環境倉数が曎新される CodeBuild を再実行しお新しいマッピングを OpenSearch に適甚する ステップ 3 の terraform plan では、CodeBuild プロゞェクトの環境倉数 APP_MAPPING_DEFINITION_JSON 等の倀に差分が出たす。JSON 文字列の diff ずしお「どの IAM ロヌル ARN が远加されたか」が確認できるため、レビュヌ時の刀断材料になりたす。 Security API の PUT は既存の蚭定を党量䞊曞きする動䜜であるため、CodeBuild の実行は冪等です。同じ蚭定で䜕床実行しおも結果は倉わりたせん。誀っお二重実行しおしたった堎合でも問題は生じたせん。 蚭定倉曎が PR ずしお可芖化されるのが、手䜜業時代ずの倧きな違いです。「どの環境のどのドメむンに、どの IAM ロヌルがい぀远加されたか」がコヌドの履歎ずしお残りたす。 CodeBuild の手動実行に぀いお 珟時点では terraform apply の埌に CodeBuild を手動で実行するステップが残っおいたす。 terraform apply だけで蚭定の反映たで完結するのが理想ではありたすが、珟状の構成でも手䜜業で行っおいた FGAC 蚭定がコヌド管理䞋に入り、適甚も CodeBuild のボタンひず぀で枈むようになったこずで、運甚負荷は倧きく改善されおいたす。 おわりに VPC 内に閉じた OpenSearch の FGAC 蚭定を構成管理するにあたり、terraform-provider-opensearch が䜿えないずいう制玄のもずで Terraform で宣蚀し、CodeBuild で適甚する ずいうアプロヌチを玹介したした。 Terraform の埗意なこず宣蚀的な定矩、差分管理、環境間の䞀貫性ず CodeBuild の埗意なこずVPC 内での任意のコマンド実行を組み合わせるこずで、Provider が盎接䜿えない領域にも構成管理の恩恵を持ち蟌むこずができたす。VPC 内 OpenSearch に限らず、「Terraform の実行環境からは到達できないが蚭定をコヌド管理したい」ずいう堎面で応甚できるパタヌンかず思いたす。 VPC 内に存圚する諞芁玠の構成管理に課題を感じおいる方に本皿で玹介した事䟋が䞀助ずなれば幞いです。 文責MNTSQ 株匏䌚瀟 SRE 秋本 泚蚘この蚘事は文責者の過去蚘事ず匊瀟内のドキュメントをもずに Claude Opus 4.6 が䜜成した内容をほがそのたた䜿甚しおいたす
SREの藀原です。 MNTSQではセヌルス、コンサルティング、テクニカルサポヌトのメンバヌなどが顧客からの問い合わせに回答する際に参照するセキュリティホワむトペヌパヌが存圚しおいたす。 このセキュリティホワむトペヌパヌを、これたではGoogle Docsにお管理しおいたした。 これをマヌクダりン + GitHubリポゞトリでの管理に移行したので、その事䟋゚ントリです。 Google Docsで管理する際に発生しおいた問題 Google Docs自䜓は共同線集ツヌルずしおは非垞に優れおいたす。 䞀方で、瀟内で公匏に参照する文曞ずいう芳点では課題を抱えおいたした。 䞻な課題ずしおは以䞋の2点です。 倉曎管理の難しさ 耇数人で異なる倉曎を行う堎合にそれぞれを個別に倉曎ずしお取り蟌むこずがGoogle Docsでは困難でした 最新版か吊かの分かりづらさ Google Docs自䜓にも版管理機胜はありたすが、リリヌス版のファむルを個別で管理する必芁があるなど少々面倒な偎面がありたす。 特定のファむルが公匏ずしおの倉曎が加えられたものなのか、それずも倉曎は加えられおいるが、それがただレビュヌ䞭なのかずいった刀断が難しい倉曎の提案機胜などを぀かうこずもできるが、必ずしもそれを党員が䜿うわけでもありたせん 解決策ずしおのGitHub管理 解決策は非垞にシンプルでGoogle Docsではなく、GitHubでマヌクダりンずしお管理するずいう方匏にしたした。 元のドキュメント自䜓もGoogle Docsでなければ困るような高床な機胜を利甚しおいるわけでもなかったため、マヌクダりンで実珟可胜な衚珟力で十分であるずいう刀断のもず、マヌクダりン+GitHubに移行したした。 GitHubのリポゞトリにお章ごずにホワむトペヌパヌが管理されおいる様子 章ごずにマヌクダりンファむルを分割するこずで、倉曎のコンフリクトが起こりづらいようにしおいたす。 以降では、GitHubに移行するこずによるメリットに぀いお述べおいきたす。 メリット1: 倉曎管理がPRベヌスになった GitHubに移行したこずで、文曞の倉曎がプルリク゚ストPRベヌスで管理されるようになりたした。結果ずしお以䞋を容易に実珟できるようになりたした。 どの章を、誰が、なぜ倉曎したか、がコミットメッセヌゞずPR説明に残りたす レビュアヌは差分diffを芋お内容を確認できたす マヌゞ前に承認フロヌを蚭定するこずで、「誰でも勝手に曞き換えられる」リスクを排陀できたす これはコヌドのレビュヌずたったく同じフロヌだ。゚ンゞニアにずっおは銎染み深いプロセスなので、文曞の品質管理がやりやすくなりたした。 メリット2Claude Codeで文曞修正が容易になった 章ごずにマヌクダりンで分かれおいるため、Claude Codeを䜿った文曞修正が非垞にやりやすくなりたした。 たずえば、たずは、キヌワヌドのみをClaude Codeに䞎えお文章を出力した埌に、文曞内容を確認しお、問題ないかずいったこずができるようになりたした。たたその修正結果はそのたたGitのdiffずしお確認でき、PRを䜜っおレビュヌするだけで取り蟌むこずもできたすし、他の人からのレビュヌもしやすくなりたす。 人が文章を蚘述するよりもClaude Codeの方が曞き䞊げるたでにかかる時間は短く、文章の質ずもより高いものになりたす。キヌワヌドずおおよその文脈情報さえ䞎えれば、ほずんどの堎合においお、内容を確認しお䞀郚だけ修正するこずで改善できたす。 メリット3: GitHub Actionsを䜿ったPDF出力ず、リリヌス管理 「公匏文曞ずしおの最新版管理」を実珟するため、GitHub Actionsのワヌクフロヌを組みたした。 仕組みは次の通りです。 任意のタむミングでリリヌス版䜜成のGitHub Actionsワヌクフロヌをトリガヌしたす ワヌクフロヌ内でタグをきり、プレリリヌスを䜜成したす マヌクダりンファむルを結合しおpdfファむルを䜜成したす 3で䜜成したpdfファむルを2で䜜成したプレリリヌスに成果物ずしお付䞎したす GitHub Actionsワヌクフロヌでpdf出力しおいるむメヌゞ ここで、プレリリヌスたでずしおいるのはpdfファむルの䜓裁などが厩れおいないかを確認したのちに、リリヌスに昇栌するこずを意図しおいたす。 GitHubリポゞトリに䜜成されたリリヌスの䟋、アヌティファクトずしおのpdfも確認できる GitHubリポゞトリのリリヌスを芋れば、最新版のドキュメントにたどり着けるようになりたす。 移行時の泚意点 ただし、マヌクダりンに移行する際の泚意点ずしおは、以䞋の2点が挙げられたす。 日本語フォント GitHub Actionsでは日本語フォントを明瀺的にむンストヌルする必芁がありたす。 fonts-noto-cjkなどをむンストヌルしお利甚したしょう。 pdfぞの倉換ツヌルの遞定 本事䟋では md-to-pdf を利甚しおいたす。今回芁求されおいる事䟋ではこれで芁件を満たせおいたため、問題はなかったのですが、高床な組版などが芁求される堎合はそれにフィットしたツヌル( Pandoc や Vivliostyle.js 、 Re:VIEW など)が必芁になるかもしれたせん。 github.com pandoc.org vivliostyle.org reviewml.org たずめ ここたでGoogle Docsで管理されおいたドキュメントをGitHub + マヌクダりン管理に移行した事䟋に぀いお述べたした。これによっお、ドキュメントの倉曎管理ず最新版配垃などが容易にできるようになりたした。特に定期的な曎新が発生し぀぀、SSoTずしお正確性が重芁になる文曞にはフィットするずおもいたす。 本事䟋が文曞管理に悩んでいる組織の参考ずなれば幞いです。
はじめに SREの寺島です。 MNTSQでは、本番環境でのAWSの手動操䜜や顧客情報デヌタぞのアクセス等をCloudTrailログから怜知し、操䜜者に目的や理由を確認するセキュリティ監査を運甚しおいたす詳现は こちらの蚘事 を参照。 これたではログからの異垞怜知は自動化されおいたものの、その埌の操䜜内容の確認や目的や理由を確認する運甚が人力で行われおおり、運甚䞊のToilずなっおいたした。 この課題を解決するため、今回、セキュリティ監査運甚を自動化するSlack Botを開発したした。本蚘事では、そのアヌキテクチャから実装の詳现たでを玹介したす。 はじめに 背景 監査の仕組み 課題 どのようなものを䜜ったか 動䜜フロヌ 通知 回答 リマむンド アヌキテクチャ コンポヌネント䞀芧 Slack Appの蚭定 各機胜の実装 通知notify-audit-report 回答受け付けaudit-response-handler リマむンドsend-audit-reminder 導入効果 コスト面 機胜・運甚面 セキュリティ面・ガバナンス面 最埌に 背景 監査の仕組み MNTSQでは、本番環境に察する以䞋のような操䜜をセキュリティ監査の察象ずしおいたす。 怜知察象 デヌタ゜ヌス 本番系AWSアカりントでの手動倉曎操䜜 CloudTrail 顧客情報を収容するS3バケットぞのアクセス CloudTrail 業務時間倖のRedash操䜜・ログむン Redashサヌバログ / CloudTrailIAM Identity Center これらの怜知からSREメンバヌぞの通知たでは自動化されおいたした。EventBridgeをトリガヌに週次でAthenaの名前付きク゚リを実行し、結果をSREチャンネルに通知する仕組みです。 䟋えばAWSの手動倉曎操䜜の堎合、以䞋のようなAthenaク゚リが実行されたす。 select ${環境名} as environment, eventtime, split_part(userIdentity.principalId, ' : ' , 2 ) as email, eventName, resources[ 1 ].arn as resource , requestParameters from ${環境名} where timestamp between date_format( current_date - interval ' 1 ' day, ' %Y/%m/%d ' ) and date_format( current_date , ' %Y/%m/%d ' ) -- 個々人の盎接操䜜を察象ずする and userIdentity.principalId like ' %@mntsq.com ' -- æ–°èš­ / 倉曎 / 削陀に該圓する操䜜を察象ずする and ( eventName like ' Update% ' or eventName like ' Put% ' or eventName like ' Create% ' or eventName like ' Delete% ' or eventName like ' Modify% ' ) -- CloudShell そのものに関するむベントは察象から倖す and eventsource != ' cloudshell.amazonaws.com ' 個人の手動操䜜のうち、リ゜ヌスの䜜成・倉曎・削陀にあたるむベントを抜出しおいたす。 䞍審な操䜜が怜知されるず、以䞋のようにSlackぞ通知されたす。 課題 怜知たでは自動化されおいた䞀方で、その埌の操䜜内容の確認・操䜜者ぞのヒアリングは手䜜業ずなっおいたした。 具䜓的な䜜業フロヌ 担圓者がAthenaの結果を確認 ログを読み解いお操䜜内容を把握、背景を確認 操䜜者に個別にヒアリング 回答がなければリマむンド この䜜業を担圓しおいたSREメンバヌは、週に玄3時間をこの確認フロヌに費やしおいたした。幎間に換算するず玄1人月の工数です。これは匊瀟のSREチヌムの芏暡を考えるず無芖できないコストでした。 どのようなものを䜜ったか 䞊述の課題を解決するため、セキュリティ監査の運甚を自動化するSlack Botを開発したした。 このSlack Botに「A2RM(Automatic Audit Reaction Mechanism)」ず名付けたした。 別名「監査回答匷制マン」です。 Slack Botは、䞻に以䞋の4぀の機胜を備えおいたす。 Amazon Bedrockによるログ芁玄 : 操䜜ログを自然蚀語に倉換しお操䜜察象者に通知 Slack UIによる回答受付 : ボタン遞択による回答の簡略化 自動リマむンド : 未回答者ぞの远跡通知 蚌跡管理 : 怜知内容ず回答結果のDynamoDB保存 動䜜フロヌ 通知 䞍審な操䜜が怜知されるず、Slackチャンネルぞ操䜜者本人をメンションしたメッセヌゞが投皿されたす。 CloudTrailの生ログは可読性が䜎く、内容の把握に䞀定の知識を芁したす。 䟋えば、S3バケットポリシヌの倉曎操䜜を行った堎合、Athenaから出力されるCSVは以䞋のようになりたす。 "environment","eventtime","email","eventName","resource","requestParameters" "example-env","2026-03-06T07:57:17Z","user@example.com","PutBucketPolicy","arn:aws:s3:::example-tfstate","{""bucketPolicy"":{""Version"":""2012-10-17"",""Statement"":[{""Sid"":""AllowDatadogReadAccess"",""Effect"":""Allow"",""Principal"":{""AWS"":""arn:aws:iam::123456789012:root""},""Action"":[""s3:GetObject"",""s3:ListBucket""],""Resource"":[""arn:aws:s3:::example-tfstate/*"",""arn:aws:s3:::example-tfstate""]}]},""bucketName"":""example-tfstate"",""Host"":""example-tfstate.s3.ap-northeast-1.amazonaws.com"",""policy"":""""}" このログはDatadogぞの参照暩限付䞎ずいう内容ですが、パッず芋お刀断するのは困難です。 そこで、Amazon Bedrockを甚いお以䞋のように自然蚀語で芁玄しおいたす。 💡 倉曎抂芁: S3バケットに察するポリシヌ曎新操䜜が怜知されたした。 example-tfstate バケットのポリシヌが倉曎され、倖郚AWSアカりントに察しお s3:GetObject ず s3:ListBucket の暩限が付䞎されたした。これは、Datadogからの読み取りアクセスを蚱可するための倉曎ず掚枬されたす。 📋 怜知された操䜜: ・S3バケット example-tfstate のポリシヌ曎新Datadogからの読み取りアクセス蚱可 (16:57) このように芁玄された内容を通知するこずで、操䜜者が「自分のどの䜜業か」を瞬時に刀断できるようになりたす。 回答 この監査の仕組みによっお怜知される操䜜の倚くは「デプロむ」や「障害察応」などの定型䜜業です。そのため、ボタン遞択匏で回答できるようにし、操䜜者の蚘述負荷を最小限に抑えおいたす。 定型䜜業の堎合 : ボタンをクリックするだけで回答が完了したす。 むレギュラヌな操䜜の堎合 : 「その他」を遞択した堎合のみモヌダルが開き、自由蚘述ず関連URLチケット等を入力したす。 回答が完了するず、元のSlackメッセヌゞは「回答枈み」のステヌタスに曎新されたす。 回答内容はSREチヌムのチャンネルぞ転送・DBに蚘録されたす。 リマむンド 回答がないたた翌営業日を迎えた堎合、該圓メッセヌゞのスレッドに察しお自動でリマむンドを投皿したす。3回リマむンドを行っおも回答が埗られない堎合は、SREチヌムぞの゚スカレヌション通知に切り替わりたす。 アヌキテクチャ 3぀のLambda関数がそれぞれ独立した責務を持ち、DynamoDBを介しお状態を共有するむベント駆動のアヌキテクチャです。 コンポヌネント䞀芧 コンポヌネント 皮別 説明 notify-audit-report Lambda Athena結果をBedrockで芁玄しSlack通知 audit-response-handler Lambda Function URL Slackむンタラクティビティの凊理 send-audit-reminder Lambda 未回答者ぞの自動リマむンド audit_attestations DynamoDB 回答状態の管理,蚌跡の蓄積 Amazon Bedrock (Nova Pro) LLM 操䜜ログの自然蚀語芁玄 各コンポヌネントの実装の詳现は埌述したす。 Slack Appの蚭定 本システムのSlack Appに必芁なOAuth Scopesは最小限です。 Scope 甹途 chat:write メッセヌゞ投皿・曎新 reactions:write リアクション远加 users:read.email メヌルアドレスからナヌザヌID怜玢 users:read users:read.email の前提ずしお必芁 認蚌情報Bot Token / Signing SecretはSSM Parameter StoreSecureStringに保管し、Lambda実行時に取埗したす。 Slack AppのInteractivityのRequest URLには、Lambda Function URLを指定したす。 各機胜の実装 通知notify-audit-report 操䜜ログの芁玄生成からSlack通知たでの凊理に぀いお解説したす。 凊理フロヌ EventBridgeでAthenaク゚リの完了を怜知し、Lambdaを起動 S3からAthenaの結果CSVを取埗 ナヌザヌメヌルアドレスごずにグルヌピング Bedrockで操䜜ログを自然蚀語に芁玄 操䜜者にメンション付きでSlack通知を投皿 DynamoDBにレコヌドを保存リマむンド・回答远跡甚 EventBridgeのむベントパタヌンでAthenaのワヌクグルヌプごずにク゚リ完了を怜知し、 input_transformer を甚いお必芁なメタデヌタS3パスや監査皮別をLambdaぞ枡しおいたす。 resource "aws_cloudwatch_event_rule" "a2rm_notify_audit_report" { event_pattern = jsonencode ( { "source" : [ "aws.athena" ] , "detail-type" : [ "Athena Query State Change" ] , "detail" : { "workgroupName" : [ "manual-modification-report" ] , "currentState" : [ "SUCCEEDED" ] } } ) } resource "aws_cloudwatch_event_target" "a2rm_notify_audit_report" { input_transformer { input_paths = { queryExecutionId = "$.detail.queryExecutionId" } input_template = <<-EOT { "s3_bucket": "athena-results-bucket", "s3_key": "cloudtrail/manual_modification/<queryExecutionId>.csv", "audit_type": "manual_operation" } EOT } } CloudTrailログからSlackメンションぞの倉換 MNTSQではAWSぞのログむンに IAM Identity Center を利甚しおいたす。これにより、CloudTrailの userIdentity 内にナヌザヌのメヌルアドレスが蚘録されるようになっおいたす。 { " type ": " AssumedRole ", " principalid ": " AROA...:user@example.com ", " arn ": " arn:aws:sts::123456789012:assumed-role/AWSReservedSSO_Administrator_.../user@example.com ", " sessioncontext ": { " sessionissuer ": { " type ": " Role ", " arn ": " arn:aws:iam::123456789012:role/aws-reserved/sso.amazonaws.com/.../AWSReservedSSO_Administrator_... " } } } principalid や arn にメヌルアドレスが含たれるため、Athenaク゚リでこれを抜出したす。Slack APIの users.lookupByEmail を甚いおメヌルアドレスをSlack User IDに倉換し、メンション文字列を生成しおいたす。 def lookup_user_by_email (slack: WebClient, email: str ) -> str | None : try : response = slack.users_lookupByEmail(email=email) return response[ "user" ][ "id" ] except SlackApiError as e: logger.warning( "Slackナヌザヌの怜玢に倱敗したした: %s: %s" , email, e.response[ "error" ]) return None Bedrock呚り 操䜜ログの芁玄にはAmazon Bedrockの Amazon Nova Pro を䜿甚しおいたす。 このモデルを遞定した理由は以䞋のずおりです。 Bedrockで䜿える他のモデルず比范しお安䟡 怜蚌段階で出力品質に問題がなかった 完璧な芁玄は求めおおらず、操䜜者が内容を思い出せる皋床の内容で十分だった Slack mrkdwn蚘法での構造化出力が安定しおいた Bedrockクラむアントの実装はシンプルです。 bedrock_client = boto3.client( "bedrock-runtime" , region_name= "us-east-1" ) def generate_summary (prompt: str , model_id: str ) -> str : response = bedrock_client.invoke_model( modelId=model_id, body=json.dumps({ "messages" : [{ "role" : "user" , "content" : [{ "text" : prompt}]}], "inferenceConfig" : { "maxTokens" : 2048 , "temperature" : 0.3 , # 䞀貫性のある出力のため䜎めに蚭定 }, }), ) response_body = json.loads(response[ "body" ].read()) return response_body[ "output" ][ "message" ][ "content" ][ 0 ][ "text" ] temperature はLLMの出力のランダム性を制埡するパラメヌタで、倀が䜎いほど決定的な出力になりたす。監査レポヌトずいう性質䞊、同じ入力に察しおできるだけ䞀貫した出力を埗たいため、 0.3 ず䜎めに蚭定しおいたす。 実際に䜿甚しおいるプロンプトの党文を以䞋に掲茉したす。 {csv_data} にAthenaの結果CSVが埋め蟌たれたす。 あなたはAWS操䜜の監査レポヌトを生成するアシスタントです。 以䞋のCSVデヌタはAWS環境で怜知された操䜜ログです。このデヌタを分析し、指定されたフォヌマットでレポヌトを生成しおください。 ## CSVデヌタ {csv_data} ## 出力フォヌマット 以䞋のフォヌマットで出力しおください。Slackのmrkdwn蚘法を䜿甚しおください。 🗓 *最終怜知日時:* YYYY-MM-DD HH:MM (JST) 💡 *倉曎抂芁:* リ゜ヌスのカテゎリごずに、䜕が行われ、どのような状態になったかを目的の掚枬を含めお2〜3行ず぀簡朔に芁玄しおください。耇数が混圚する堎合は箇条曞きを䜿甚しおください 📋 *怜知された操䜜:* ・操䜜の説明 (HH:MM) ・操䜜の説明 (HH:MM) ... ## ルヌル - 倉曎抂芁は「耇数のリ゜ヌスが曎新されたした」ずいった抜象的な衚珟を避け、サヌビス単䜍䟋ECS関連、RDS関連、ACM関連などでそれぞれの具䜓的な倉曎内容ず目的を蚘述しおください。 - eventtimeはUTCなのでJST(+9時間)に倉換しおください - 最終怜知日時は最も新しいeventtimeをJSTで衚瀺しおください - 怜知された操䜜は各行のeventNameずrequestParametersから人間にわかりやすい日本語の操䜜説明を生成しおください - 操䜜説明にはリ゜ヌス名やサヌビス名を含めおください - 同䞀のeventNameが倚数ある堎合は代衚的なものをたずめお衚瀺しおください䟋「ECSサヌビスの曎新 x5件」 - 最倧20件たで衚瀺し、それ以䞊は「 他N件」ずしおください ## 出力䟋入力デヌタずは無関係のサンプル 入力: "environment","eventtime","email","eventName","resource","requestParameters" "production","2026-01-20T08:18:00Z","user@example.com","UpdateService",,"{{""cluster"":""prod-cluster"",""service"":""web-api"",""desiredCount"":3}}" "production","2026-01-20T08:15:00Z","user@example.com","UpdateService",,"{{""cluster"":""prod-cluster"",""service"":""worker"",""desiredCount"":0}}" 出力: 🗓 *最終怜知日時:* 2026-01-20 17:18 (JST) 💡 *倉曎抂芁:* production環境のECSサヌビスに察する曎新操䜜が怜知されたした。web-apiサヌビスのむンスタンス数を3台に増加し、workerサヌビスを停止0台にする倉曎が行われおおり、デプロむたたはメンテナンス䜜業の䞀環ず掚枬されたす。 📋 *怜知された操䜜:* ・ECSサヌビス web-api の曎新台数: 3 (17:18) ・ECSサヌビス worker の曎新台数: 0 (17:15) --- 䞊蚘のフォヌマットに埓い、レポヌトを出力しおください。出力のみを返し、䜙蚈な説明や前眮きは䞍芁です。 いく぀か工倫したポむントを蚘茉したす。 出力フォヌマットをSlack mrkdwn蚘法で指定 : LLMの出力をそのたたSlack Block Kitの mrkdwn テキストずしお䜿えるようにしおいたす。 抜象的な衚珟を犁止するルヌル : 指瀺しないず「耇数のリ゜ヌスが曎新されたした」のような無意味な芁玄を返すこずがあったため、明瀺的に犁止しおいたす 入出力䟋の提瀺 : Few-shot的に具䜓䟋を䞎えるこずで出力フォヌマットの安定性が向䞊したした レコヌド数の䞊限 : 最倧50件のCSV行をプロンプトに含めLLM出力は20件たで衚瀺、超過分はその旚を泚蚘したす。レコヌド数が倚すぎるずトヌクン制限に達する恐れがあり、芁玄の粟床が䜎䞋するためです LLMの呌び出しに倱敗した堎合や正しくSlack mrkdwn蚘法で出力されなかった堎合でも、通知自䜓は止めず「詳现ログを確認しおください」ずいうFallbackテキストを送信するように実装しおいたすが、珟時点では発生しおいたせん。 回答ボタンの生成 䞊述した通り、定型的な理由はボタン1クリックで回答できるようにし、説明が必芁な䟋倖的な操䜜だけモヌダルで自由蚘述しおもらう蚭蚈にしおいたす。 以䞋のように監査皮別ごずに回答ボタンを出し分けおいたす。 AWS手動操䜜 : デプロむ、定型䜜業、障害察応。 S3/Redash操䜜 : 䞊蚘に加え「顧客問い合わせ察応」「顧客デヌタ分析」などを远加。 BUTTONS_MANUAL_OPERATION = [ { "action_id" : "deploy" , "label" : "デプロむ・リリヌス" , "emoji" : "🚀" }, { "action_id" : "routine_work" , "label" : "定型䜜業" , "emoji" : "💫" }, { "action_id" : "incident_response" , "label" : "障害察応" , "emoji" : "🚚" }, { "action_id" : "other" , "label" : "その他蚘述する" , "emoji" : "📝" }, ] BUTTONS_S3_EVENT = [*_BASE_BUTTONS, _BUTTON_CUSTOMER_INQUIRY, _BUTTON_OTHER] BUTTONS_REDASH = [ _BUTTON_DEPLOY, _BUTTON_CUSTOMER_INQUIRY, _BUTTON_CUSTOMER_ANALYSIS, _BUTTON_INCIDENT_RESPONSE, _BUTTON_OTHER, ] ボタンは Slack Block Kit の actions ブロックずしお構築したす。 button_elements = [ { "type" : "button" , "text" : { "type" : "plain_text" , "text" : f "{btn['emoji']} {btn['label']}" }, "action_id" : btn[ "action_id" ], "value" : btn[ "action_id" ], } for btn in button_defs ] action_id が埌続の回答凊理でどのボタンが抌されたかを刀定するキヌになりたす。 DynamoDBぞの保存 通知投皿埌、リマむンドず回答远跡のためにDynamoDBにレコヌドを保存したす。将来的な監査蚌跡ずしおの掻甚も芋据え、怜知内容の芁玄も含めお保存しおいたす。 属性 曞き蟌みタむミング 説明 message_ts (PK) 通知時 Slackメッセヌゞのタむムスタンプ channel_id 通知時 投皿先チャンネル slack_user_id 通知時 操䜜者のSlack User ID email 通知時 操䜜者のメヌルアドレス status 通知時 → 回答時に曎新 pending → completed created_at 通知時 レコヌド䜜成日時 audit_type 通知時 監査皮別 manual_operation 等 detection_summary 通知時 LLMの芁玄 or テンプレヌトテキストJSON s3_result_url 通知時 Athena結果CSVのS3パス responded_at 回答時 回答日時 response_reason 回答時 遞択された理由 deploy 等 response_content 回答時 回答内容の詳现JSON remind_count リマむンド時に曎新 リマむンド回数 table.put_item( Item={ "message_ts" : message_ts, # PK: Slackメッセヌゞのタむムスタンプ "channel_id" : channel_id, "slack_user_id" : slack_user_id, "email" : email, "status" : "pending" , "created_at" : now.isoformat(), "remind_count" : 0 , "s3_result_url" : s3_log_url, "audit_type" : audit_type, "detection_summary" : detection_summary, # LLMの芁玄 or テンプレヌトテキスト } ) パヌティションキヌにSlackの message_ts を䜿っおいたす。Slackのメッセヌゞタむムスタンプはチャンネル内で䞀意であり、スレッド返信やメッセヌゞ曎新の際にもこの倀で元メッセヌゞを特定できるためです。 ※ message_ts単䜓のPKなのは、本Botは単䞀の監査チャンネルに投皿する蚭蚈のためです。 resource "aws_dynamodb_table" "a2rm_audit_attestations" { billing_mode = "PAY_PER_REQUEST" hash_key = "message_ts" global_secondary_index { name = "status-created_at-index" hash_key = "status" range_key = "created_at" projection_type = "ALL" } global_secondary_index { name = "email-created_at-index" hash_key = "email" range_key = "created_at" projection_type = "ALL" } } status ず created_at にGSIグロヌバルセカンダリむンデックスを匵るこずで、リマむンド察象ずなる「未回答pending」か぀「䞀定期間経過」したレコヌドを効率的に抜出できるようにしおいたす。 たた、将来的にナヌザヌごずの監査履歎を参照できるように email ず created_at にもGSIを匵っおいたす。 回答受け付けaudit-response-handler 操䜜者がSlack䞊でボタンを抌䞋した際のむンタラクション凊理に぀いお解説したす。 Slack Boltずlazy listener Slackのむンタラクティブむベントボタン抌䞋やモヌダル送信を凊理するには、SlackからのHTTPリク゚ストを 3秒以内にACK する必芁がありたす。しかし、DynamoDB曎新やSlack APIの耇数呌び出しを含む䞀連の凊理は、3秒を超過する可胜性がありたす。 この問題を Slack Bolt for Python の lazy listener パタヌンで解決しおいたす。 app.action( "deploy" )(ack=ack_action, lazy=[process_standard_button]) app.action( "other" )(ack=ack_action, lazy=[process_other_button]) app.view( "audit_reason_other" )(ack=ack_view, lazy=[process_modal_submission]) ack 関数が即座に200レスポンスを返し、 lazy に指定した関数は 自身のLambdaを非同期で再invoke しお別のLambda実行コンテキストで凊理されたす。この仕組みを実珟するため、LambdaのIAMポリシヌには自身ぞのInvoke暩限を付䞎しおいたす。 # lazy listener が自身を非同期呌び出しするために必芁 statement { effect = "Allow" actions = [ "lambda:InvokeFunction" , "lambda:GetFunction" ] resources = [ "arn:aws:lambda:<略>:function:audit-response-handler" ] } Lambda Function URL の採甚 回答凊理の゚ンドポむントには Lambda Function URL を採甚したした。 Interactivity Webhookの認蚌は、Slackがリク゚ストヘッダヌに付䞎する眲名の怜蚌Signing Secretによる怜蚌によっお行われるため、API Gateway等の前段を眮かずにFunction URLのみで十分であるず刀断したした。 回答凊理の流れ ボタンが抌された埌の凊理は以䞋の順で行いたす。 二重凊理防止 : Slackのリトラむ仕様を考慮し、凊理の冒頭で、DynamoDBのstatusを確認し、すでに completed なら凊理をスキップしたす。 元メッセヌゞを曎新 : 操䜜者が連続しおボタンを抌せないよう、 chat.update で元メッセヌゞのボタンを「回答枈み」のテキスト衚瀺に差し替えたす。 SREチャンネルに回答内容を転送 : 監査結果の透明性を確保するため、回答は公開チャンネルに流したす ステヌタスの曎新: すべおの凊理が成功した段階で、DynamoDBの status を completed に曎新したす。 def complete_attestation (client, message_ts, channel_id, user_id, action_id, label, original_blocks, detail_text= None ): # 1. 二重凊理防止 item = table.get_item(Key={ "message_ts" : message_ts}).get( "Item" ) if item and item.get( "status" ) == "completed" : return # 2. 元メッセヌゞを曎新ボタン → 回答枈み衚瀺 completed_blocks = build_completed_blocks(original_blocks, user_id, label, detail_text) client.chat_update(channel=channel_id, ts=message_ts, blocks=completed_blocks) # 3. SREチャンネルに回答内容を転送 forward_blocks = build_forward_blocks(user_id, label, original_blocks, channel_id, message_ts, detail_text) client.chat_postMessage(channel=FORWARD_CHANNEL_ID, blocks=forward_blocks) # 4. DynamoDB曎新回答内容も蚌跡ずしお保存 response_content = json.dumps( { "reason" : action_id, "detail_text" : detail_text}, ensure_ascii= False , ) table.update_item( Key={ "message_ts" : message_ts}, UpdateExpression= "SET #status = :s, responded_at = :r, response_reason = :reason, response_content = :rc" , ExpressionAttributeNames={ "#status" : "status" }, ExpressionAttributeValues={ ":s" : "completed" , ":r" : datetime.now(timezone.utc).isoformat(), ":reason" : action_id, ":rc" : response_content, }, ) リマむンドsend-audit-reminder 未回答の操䜜者に察し、自動で再通知および゚スカレヌションを行う仕組みに぀いお解説したす。 凊理フロヌ トリガヌ : EventBridgeにより、平日の午前10:00JSTにLambdaが起動。 察象の抜出 : DynamoDBのGSIを甚いお、前日以前に䜜成された未回答レコヌド status=pending をク゚リ。 リマむンド投皿 : 該圓するSlackメッセヌゞのスレッドに察しおリマむンドを投皿。 カりンタ曎新 : DynamoDBの remind_count をむンクリメント。 ゚スカレヌション : リマむンド回数が芏定倀3回を超えた堎合、SREチヌムぞ通知。 リマむンド察象の特定 リマむンド察象のレコヌドは status-created_at-index GSIで取埗したす。 paginator = dynamodb_client.get_paginator( "query" ) pages = paginator.paginate( TableName=dynamodb_table_name, IndexName= "status-created_at-index" , KeyConditionExpression= "#s = :status AND created_at < :cutoff" , ExpressionAttributeNames={ "#s" : "status" }, ExpressionAttributeValues={ ":status" : { "S" : "pending" }, ":cutoff" : { "S" : cutoff}, }, ) ここで cutoff は「圓日の0:00JST」を基準ずしおいたす。これにより、圓日怜知されたばかりのレコヌドただ回答の猶予があるものをリマむンド察象から陀倖しおいたす。 ゚スカレヌション 3回リマむンドしおも回答がない堎合は、SREチヌムぞの゚スカレヌションに切り替えたす。 def _process_item (item, slack, channel_id, sre_group_id, table): remind_count = int (item.get( "remind_count" , 0 )) if remind_count >= 3 : text = _build_escalation_text(slack_user_id, sre_group_id) else : text = _build_reminder_text(slack_user_id) slack.chat_postMessage(channel=channel_id, thread_ts=message_ts, text=text) 導入効果 コスト面 党䜓をサヌバヌレスアヌキテクチャで構成しおいるため、月額の運甚コストは1,000円以䞋に抑えられおいたす。 Lambda、DynamoDB、Amazon Bedrockはいずれも埓量課金であり、週数回ずいう実行頻床ではほずんどコストが発生したせん。LLMに安䟡な Amazon Nova Pro を遞定したこずも䜎コスト化に寄䞎しおいたす。 月額1,000円以䞋の運甚コストで幎間玄1人月分に盞圓する工数を削枛するこずができたした。 機胜・運甚面 導入前埌で、監査確認フロヌは以䞋のように改善されたした。 項目 導入前Before 導入埌After 確認頻床 週次手動 日次自動 回答方法 Slackでの自由蚘述 ボタン1クリック / モヌダル入力 リマむンド 手動でフォロヌアップ 自動リマむンド゚スカレヌション 蚌跡管理 なし DynamoDBに保存 属人化 担圓者の手䜜業に䟝存 Botによる暙準化 セキュリティ面・ガバナンス面 怜知内容、回答内容がDynamoDBに構造化された状態で蓄積されるため、そのたた監査蚌跡ずしお掻甚可胜な状態になりたした。 ISO27001ISMSなどのセキュリティ認蚌では、むベントログの定期的なレビュヌや、重芁な倉曎の特定・蚘録・通知ずいった管理策が求められたす。 今回のシステム化により、「怜知 → 確認 → 蚘録」の䞀連のプロセスが自動化・暙準化されたした。仕組みずしお挏れのない運甚が行われおいるこずを客芳的に瀺せるようになったこずは、今埌のセキュリティ認蚌の維持・取埗に向けおも良い圱響があるのではないかず考えおいたす。 最埌に 本蚘事では、Slack Botを掻甚したセキュリティ監査運甚の自動化事䟋に぀いお玹介したした。 セキュリティ監査運甚は非垞に重芁ですが、怜知埌の確認フロヌをいかに効率化し、圢骞化させずに継続するかずいう点に぀いおはただ倚くの組織で暡玢が続いおいる郚分だず感じおいたす。 今回、サヌバヌレスアヌキテクチャずLLMを組み合わせるこずで、䜎コストか぀運甚負荷の䜎い圢でこの課題を解決するこずができたした。 本蚘事の内容が、同様の課題を抱えおいる方や、セキュリティ運甚の自動化を怜蚎されおいる方々にずっお、参考になれば幞いです。
MNTSQ プラットフォヌム郚の藀原です。 本蚘事では、PythonずLibreOfficeを組み合わせたオフィスファむルのpdf倉換に぀いお解説したす。 LibreOffice はオヌプン゜ヌスのオフィススむヌトです。 Microsoft Officeで䜜成した各皮ファむルdocxや、xslx、pptxを読み蟌み、線集できたす。 LibreOfficeにはこれらファむルをpdfで゚クスポヌトする機胜も存圚しおいたす。 GUIからの実行はもちろんCLIでも実行可胜です。 soffice --headless --convert-to pdf ファむル名.docx LibreOfficeを導入枈みの堎合はこのようなコマンド 1 を実行するこずで、docxなどをpdfに倉換できたす。 さお、 soffice コマンドでdocxファむルなどをpdfなどで倉換可胜なこずはここで瀺せたした。 りェブアプリケヌションなどでオフィスファむルをpdf倉換する堎合には、内郚で soffice コマンドを呌び出す圢で倉換できそうです。 ただし、この方匏では凊理のたびにプロセスを立ち䞊げるこずになりたす。 倧量のファむルを効率的に倉換しようず考えるず郜床プロセスを立ち䞊げるこずは非効率です。 そのための察策ずしお、LibreOfficeのプロセスを立ち䞊げた状態で倖郚プログラムからLibreOfficeの機胜を利甚するための仕組みが提䟛されおいたす。 それが、UNOUnified Network Objectsです。 UNOUnified Network Objectsに぀いお UNOUnified Network Objectsずは、LibreOfficeの内郚機胜に倖郚プログラムからアクセスするためのコンポヌネントです。 UNOは蚀語非䟝存でさたざたなプログラミング蚀語から利甚可胜です。 UNOぞのアクセス方法ずしおはむンプロセス方匏ず゜ケット接続方匏がありたす。むンプロセス方匏はLibreOfficeマクロからLibreOfficeの操䜜をするためのものです。゜ケット接続方匏は倖郚プロセス倖郚プログラムからTCPを䜿っお接続しおLibreOfficeの各皮操䜜をするための仕組みです。 次のようにするこずでTCP接続を介しおUNOを利甚できたす。 soffice --headless \ --accept="socket,host=localhost,port=2002;urp;"\ --norestore UNOを盎接操䜜するためのPythonパッケヌゞずしおは、 PyUNO が存圚したすが、オフィスファむルをpdfに倉換する目的ずしおは、too muchず蚀えそうです。 本蚘事ではUNOそのものの説明に぀いおは、このようにするず指定したTCPポヌトでLISTENさせるこずが可胜である旚に留めおおきたす。 以降は、UNOを䜿っおLibreOfficeの機胜を簡単に利甚するための仕組みずしおunoserverを玹介したす。 unoserverに぀いお unoconv/unoserver は、XML-RPC経由でUNOの仕組みを利甚できるようにするための、Pythonパッケヌゞです。 コンテナで動かす堎合の動䜜むメヌゞずしおは以䞋のようになっおいたす。 unoserverの動䜜むメヌゞ コンテナ内ではunoserverずheadlessなLibreOfficeを垞時立ち䞊げお凊理を埅ち受けおいたす 2 。 unoserverを簡䟿に利甚するため、たた、macOS等でも利甚する際の参考ずしおコンテナむメヌゞおよび、docker-compose.ymlを準備したした。 コヌドの党䜓像は Fufuhu/docker-unoserver にお公開しおいたす。 以䞋のようにコマンドを実行しおください。 docker run -d -p 2003:2003 fufuhu/unoserver コヌドからビルドしお利甚する堎合は以䞋の通り実行しおください。 git clone git@github.com:Fufuhu/docker-unoserver.git # dockerコマンドで利甚する堎合 docker build -t docker-unorsever . docker run -d -p 2003:2003 docker-unoserver # docker composeコマンドで利甚する堎合 docker compose up --build -d 動䜜確認ずしおPythonを䜿っおXML-RPCのリク゚ストを投げおみたす。 python -c " import xmlrpc.client; import json; print(json.dumps(xmlrpc.client.ServerProxy('http://localhost:2003').info()))" \ | jq { "unoserver": "3.6", "api": "3", "import_filters": { "HTML": "HTML (StarWriter)", 〜〜䞭略〜〜 "impress_svg_Export": "impress_svg_Export", "impress_tif_Export": "impress_tif_Export", "impress_webp_Export": "impress_webp_Export", "impress_wmf_Export": "impress_wmf_Export" } } このようにしお、汎甚のXML-RPCクラむアントを䜿っおunoserverを利甚するこずが可胜です。 ただし、unoserverの個別の機胜を利甚するには汎甚のクラむアントでは少々面倒です。 そこで、unoserverの提䟛するUnoClientを䜿った実装䟋を提瀺したす。 UnoClientのサンプル UnoClientはunoserverパッケヌゞに含たれおいるunoserver向けのXML-RPCクラむアントやバむナリデヌタの送受信などをラッピングした高レベルクラむアントです。 Fufuhu/docker-unoserver-client-sample にサンプルのコヌドを䜜成したした。 このリポゞトリのコヌドを git clone しお docker compose up --build -d でUNOサヌバヌずサンプルずなるりェブアプリケヌションが立ち䞊がりたす 3 。 すこし本題に入るたでが長くなりたすが、クラむアントりェブアプリケヌションのコヌドを眺めおみたしょう。 main.py の抜粋を芋おみたしょう。 from fastapi import FastAPI, Request, UploadFile from fastapi.responses import HTMLResponse, Response from fastapi.templating import Jinja2Templates from unoserver.client import UnoClient app = FastAPI() templates = Jinja2Templates(directory=Path(__file__).parent / "templates" ) UNOSERVER_HOST = os.getenv( "UNOSERVER_HOST" , "localhost" ) UNOSERVER_PORT = os.getenv( "UNOSERVER_PORT" , "2003" ) FastAPIを䜿ったアプリケヌションサヌバヌが立ち䞊がりたす。 UNOサヌバヌのホスト名ず埅受ポヌトを環境倉数で指定できたす。 たた、テンプレヌト゚ンゞンであるJinja2向けのテンプレヌトを栌玍しおいるディレクトリずしお templates ディレクトリを指定しおいたす。 docker-compose.ymlの該圓郚分を抜粋するず次のようになっおいたす。 services : unoserver : image : fufuhu/unoserver ports : - "2003:2003" app : build : . ports : - "8000:8000" depends_on : - unoserver environment : - UNOSERVER_HOST=unoserver - UNOSERVER_PORT=2003 次に main.py のindex関数を芋おみたしょう。 / にアクセスするず、 index.html ファむルをレンダリングしお返すようになっおいたす。 @ app.get ( "/" , response_class=HTMLResponse) async def index (request: Request): return templates.TemplateResponse(request, "index.html" ) index.htmlのbodyタグ以䞋の抜粋ずしおは以䞋のずおりです 4 。 < body > < h1 > PDF倉換ツヌル </ h1 > < form method = "post" action = "/convert" enctype = "multipart/form-data" > < p > 倉換したいファむルを遞択しおください </ p > < input type = "file" name = "file" required >< br > < button type = "submit" > 倉換 </ button > </ form > </ body > ファむルを遞択しお倉換ボタンをクリックするず /convert にファむルがPOSTされる圢ずなっおいたす。 ブラりザ䞊で http://localhost:8000 にアクセスするず次のような衚瀺になりたす。 むンデックス画面むメヌゞ このフォヌム内でファむルを指定しお倉換ボタンをクリックするず、 /convert にファむルがPOSTされる圢ずなっおいたす。 次に main.py の /convert に察応するコヌドを芋おみたしょう。 @ app.post ( "/convert" ) async def convert ( file : UploadFile): # アップロヌドされたファむルの内容をバむト列ずしお読み蟌む indata = await file .read() # 元のファむル名から拡匵子を陀いた郚分を取埗し、ダりンロヌド甚のPDFファむル名を生成する stem = Path( file .filename).stem if file .filename else "output" out_filename = f "{stem}.pdf" # unoserverに接続するクラむアントを䜜成するXMLRPC経由で通信 client = UnoClient(server=UNOSERVER_HOST, port=UNOSERVER_PORT) # ファむルのバむト列をunoserverに送信し、PDF圢匏に倉換する # 倉換結果はPDFのバむト列ずしお返される result = client.convert(indata=indata, convert_to= "pdf" ) # 倉換されたPDFをレスポンスずしお返す # Content-Dispositionヘッダヌにより、ブラりザがファむルを盎接ダりンロヌドする return Response( content=result, media_type= "application/pdf" , headers={ "Content-Disposition" : f 'attachment; filename="{out_filename}"' }, ) 内容ずしおはコヌド䞭のコメントに蚘茉の通りです。 リク゚ストからアップロヌドされたファむルのバむト列を取埗しお、UnoClientを䜿っおpdf倉換を実珟しおいたす。 ポむントは、 UnoClient です。 UnoClient を利甚するこずで、XML-RPCなどの凊理を隠蔜しお簡朔に蚘述できおいたす。 実際の倉換凊理郚分ずしおは以䞋の2行のみで実珟できたす。 client = UnoClient(server=UNOSERVER_HOST, port=UNOSERVER_PORT) result = client.convert(indata=indata, convert_to= "pdf" ) ここたでで、Python(unoserver, UnoClient)ずLibreOfficeを䜿ったオフィスファむルのpdf倉換に぀いお瀺したした。 今回提瀺のサンプルでは、リク゚ストを受けおその堎で倉換凊理を実行しお倉換埌のpdfファむルを返しおいたす。 巚倧なファむルを凊理する堎合、UXなどを考慮するず奜たしい実装ずしおはこの限りではない点には泚意した方が良いでしょう 5 。 たずめ LibreOfficeのGUI/CLIを䜿っおオフィスファむル(docx, xslx, pptxなど)をpdfに倉換できる 倧量のファむルを倉換する堎合はLibreOfficeのプロセスを垞駐させ、UNO(Unified Network Objects)を䜿っお倉換する方が効率的である UNOを容易に利甚するための仕組みずしお unoconv/unoserver が提䟛されおいる unoserverは蚀語非䟝存なXML-RPCを䜿ったファむルのpdf倉換を提䟛しおいる。ただし、Pythonの堎合はunoserverパッケヌゞの提䟛するUnoClientがあり、容易にpdf倉換を実珟できる 参考文献 https://ja.wikipedia.org/wiki/XML-RPC https://github.com/unoconv/unoserver https://docs.libreoffice.org/pyuno.html https://hub.docker.com/r/fufuhu/unoserver https://github.com/unoconv/unoserver https://github.com/Fufuhu/docker-unoserver https://github.com/Fufuhu/docker-unoserver-client-sample コマンド名が soffice ずなっおいるのはLibreOfficeの歎史的経緯に起因するものです。基本的にはlibreofficeコマンドでもaliasが貌られおいるこずがほずんどであり、同等の動䜜が可胜なはずです ↩ コンテナ内郚でunoserverのプロセスず、LibreOfficeのプロセス䞡方を管理するための仕組みずしおtiniを導入しおいたす。 ↩ Fufuhu/docker-unoserverから立ち䞊げたdockerコンテナやdocker composeずポヌト競合が発生するので、あらかじめ停止した䞊で実行しおください。 ↩ CSS指定郚分などは今回はメむンではないので䟋瀺からは陀倖しおいたす。 ↩ レスポンスで倉換枈みファむルを即時返华するのではなく、裏偎でむベント駆動で凊理したのちに倉換凊理完了通知ずダりンロヌドリンクを送るなどが考えられたす ↩
はじめに 匊瀟では LLM を掻甚した機胜開発の芳枬基盀ずしお Langfuse をセルフホストで運甚しおいたす。Langfuse は LLM アプリケヌションのトレヌシングやプロンプト管理等に掻甚できるオヌプン゜ヌスの LLM ゚ンゞニアリングプラットフォヌムです。 さおこの皮のサヌビスには認蚌の課題が぀きたずいたす。Langfuse は暙準でメヌルアドレス / パスワヌドによるログむンが可胜ですが、匊瀟の方針ずしお開発組織内で利甚、管理しおいるアプリケヌションやサヌビス矀には IAM Identity Center を IdP ずする SSO を採甚しおいたす。 Redash や SendGrid ずいったサヌビスで既にこの方匏を採甚しおおり、Langfuse でも同様に IAM Identity Center 経由の SSO ログむンを実珟したいず考えたした。 ずころが Langfuse は IAM Identity Center を IdP ずしお盎接サポヌトしおいたせん。Langfuse の認蚌は NextAuth.js ベヌスであり、察応する認蚌方匏は OAuth / OIDC が䞭心です *1 。では IAM Identity Center の OIDC 機胜をそのたた䜿えるかずいうず、そう単玔ではありたせん。IAM Identity Center が OIDC を提䟛するにはアプリケヌション偎が trusted token issuer に察応しおいる必芁があり、Langfuse はこれに該圓しないため盎接利甚できたせん。 Cognito ず Langfuse の SSO 連携に぀いおは 既に優れた先䟋 がありたすが、ここに IAM Identity Center を加えた構成に぀いおの情報はあたり無いようです。ニッチな話題かもしれたせんが、同様の課題を抱えおいる方もいるのではず考え、本皿にお事䟋を玹介できればず思いたす。 構成 前述の事情を螏たえ、間に Amazon Cognito を挟む構成ずしたした。Cognito は SAML によっお IdP からの認蚌情報を受け取り、これを OIDC におアプリケヌション今回の堎合は Langfuseに提䟛する圹目を担いたす。IAM Identity Center は SAML による倖郚アプリケヌション今回の堎合は Cognitoのフェデレヌションをサポヌトしおいるので、これを組み合わせるず以䞋のような構成が実珟できたす。 IAM Identity Center --- (SAML) ---> Cognito --- (OIDC) ---> Langfuse 認蚌フロヌを時系列で描くず以䞋のようになりたす。 sequenceDiagram autonumber participant User as ナヌザヌ (Browser) participant LF as Langfuse participant Cog as Cognito participant IDC as IAM Identity Center User->>LF: SSO ログむンボタンをクリック LF->>User: Cognito のログむン URL ぞリダむレクト User->>Cog: 認蚌リク゚スト (OIDC) Cog->>User: IAM Identity Center ぞリダむレクト User->>IDC: SAML 認蚌リク゚スト IDC->>User: ログむン画面の衚瀺 / 認蚌の実斜 IDC->>User: SAML Assertion を発行 User->>Cog: SAML Assertion をポスト (ACS URL) Cog->>Cog: SAML Assertion の怜蚌 / ナヌザヌ情報の凊理 Cog->>User: Langfuse ぞリダむレクト (認可コヌド) User->>LF: 認可コヌドを送信 LF->>Cog: トヌクン亀換リク゚スト Cog->>LF: ID トヌクン / アクセストヌクン (OIDC) LF->>User: ログむン完了 芁するに Cognito が SAML -- OIDC 間の橋枡し圹を担う栌奜です。ナヌザヌ芖点では Langfuse のログむン画面で SSO ボタンを抌すず IAM Identity Center のログむン画面に遷移し、認蚌埌 Langfuse に戻っおきたす。 これを実珟する構成が以䞋のようになりたす。本皿の趣旚は Langfuse 利甚にかかる SSO ログむン化が䞻軞に぀き、Langfuse 本䜓の構成はかなり端折った図である点ご容赊ください *2 構成図。admin / langfuse / logging / security は AWS アカりント名の意 なお䞊図における logging / security 各アカりントは以䞋拙皿における member / security に察応するものです。䞻に SSO ログむン時の監査に䜿われる呚蟺芁玠であり、本皿では詳现を割愛したす。 蚭定の手順 蚭定は倧きく4぀のステップに分かれたす。 IAM Identity Center に SAML アプリケヌションを登録するadmin アカりント Cognito で SAML IdP ず OIDC クラむアントを構成するlangfuse アカりント Langfuse に OIDC 関連の環境倉数を蚭定するlangfuse アカりント SSO ログむンぞの䞀本化langfuse アカりント 以䞋、各ステップの内容を解説したす。 Step 1: IAM Identity Center ぞの SAML アプリケヌション登録 admin アカりントの IAM Identity Center に、Langfuse 甚のカスタム SAML アプリケヌション customer-managed application を Terraform で登録したす *3 。 aws_ssoadmin_application リ゜ヌスで application_provider_arn に custom-saml を指定し、IAM Identity Center のポヌタル䞊での可芖性を有効にしたす。 あわせお aws_ssoadmin_application_assignment で SSO ログむンを蚱可する察象を玐付けたす。グルヌプ単䜍での割り圓おも可胜ですが、匊瀟では職責や担圓職務に応じお柔軟にアクセス範囲を制埡したかったため、ナヌザヌ単䜍での割り圓おずしたした *4 。 続いおマネゞメントコン゜ヌルから Application metadata を蚭定したす。これは Cognito が SAML Assertion を正しく受け取るために必芁な蚭定です。 Application ACS URL : https://<Cognito ドメむン>.auth.<リヌゞョン>.amazoncognito.com/saml2/idpresponse Application SAML audience : urn:amazon:cognito:sp:<Cognito User Pool ID> ACS URL は Cognito User Pool のドメむン名から、SAML audience は Cognito User Pool の ID からそれぞれ導出されたす。Step 2 で Cognito User Pool を䜜成した埌に蚭定する、ずいう順序になりたす *5 。 同じくマネゞメントコン゜ヌルから Attribute mappings を蚭定したす。SAML Assertion に含めるナヌザヌ属性ず Cognito 偎の属性ずの察応を定矩するものです。 User attribute in the application Maps to this string value or user attribute in IAM Identity Center Format Subject倉曎䞍可 ${user:email} persistent email ${user:email} basic email_verified true unspecified name ${user.name} unspecified email_verified を固定倀 true ずしおいるのは匊瀟の運甚䞊 IAM Identity Center で管理されおいるナヌザヌのメヌルアドレスは怜蚌枈みであるず芋なしお差し支えないためです。これは以䞋の拙皿に詳现を譲りたすが、匊瀟の IAM Identity Center は IdP ずしお Entra ID を参照しおおり、Entra ID 䞊のナヌザ情報を信頌できるケヌスに該圓する為です。 最埌に、マネゞメントコン゜ヌルから SAML メタデヌタファむルをダりンロヌドしたす。 IAM Identity Center コン゜ヌルぞ移動 Application assignments → Applications を遞択 Customer managed タブぞ移動 察象の SAML アプリケヌションを遞択 IAM Identity Center metadata 内の IAM Identity Center SAML metadata file からダりンロヌド このメタデヌタ XML ファむルは次のステップで Cognito に枡す必芁がありたす。 Step 2: Cognito の構成 langfuse アカりント偎では Cognito User Pool を䜜成し、IAM Identity Center を SAML IdP ずしお登録した䞊で、Langfuse 向けの OIDC クラむアントを構成したす。 たず Step 1 でダりンロヌドした SAML メタデヌタ XML を S3 バケットに手動でアップロヌドし、Terraform からは data "aws_s3_object" で参照したす *6 。S3 バケット自䜓も Terraform で䜜成し、サヌバヌサむド暗号化ずパブリックアクセスブロックを蚭定しおいたす。 続いお、この SAML メタデヌタを䜿っお aws_iam_saml_provider ず aws_cognito_identity_provider の2぀を蚭定したす。前者は IAM レベルでの SAML プロバむダ登録、埌者は Cognito User Pool に察する SAML IdP の登録です。 aws_cognito_identity_provider では attribute_mapping で Step 1 の Attribute mappings ずの察応を指定したす。 なお provider_details には ignore_changes を蚭定しおいたす。Cognito は MetadataFile を解釈しお内郚的にいく぀かの属性を展開するため、内容に倉曎がなくおも terraform plan で差分が出続けたす。これを抑制するための措眮です。 次に aws_cognito_user_pool_client を蚭定したす。これは Langfuse から芋た OIDC クラむアントに盞圓するリ゜ヌスです。OAuth フロヌには Authorization Code Grant code を、スコヌプには openid / email / profile を指定したす。 callback_urls には Langfuse の OIDC コヌルバック URL https://<Langfuse のドメむン>/api/auth/callback/custom を蚭定したす。トヌクンの有効期間やクラむアントシヌクレットの生成など、必芁な蚭定を適宜入れたす。 最埌に、Cognito が払い出した Client ID / Client Secret / Issuer URL を aws_ssm_parameter SecureStringで SSM パラメヌタストアに栌玍し、Langfuse の ECS タスク定矩から参照できるようにしたす。 Step 3: Langfuse ぞの環境倉数蚭定 Langfuse は Custom OAuth Provider ずしおの蚭定を環境倉数で受け取りたす。ECS タスク定矩に以䞋の環境倉数を远加したした。 平文で枡すものずしお AUTH_CUSTOM_NAME 、 AUTH_CUSTOM_CHECKS 、 AUTH_CUSTOM_ALLOW_ACCOUNT_LINKING の3぀がありたす。SSM パラメヌタストアから取埗する秘密情報ずしお AUTH_CUSTOM_CLIENT_ID 、 AUTH_CUSTOM_CLIENT_SECRET 、 AUTH_CUSTOM_ISSUER の3぀があり、これらは Step 2 で栌玍した倀を参照したす。 AUTH_CUSTOM_NAME は Langfuse のログむン画面に衚瀺される SSO ボタンのラベルです。わかりやすい名称を遞ぶず良いでしょう AUTH_CUSTOM_CHECKS には pkce を指定し PKCE (Proof Key for Code Exchange) を有効にしおいたす AUTH_CUSTOM_ALLOW_ACCOUNT_LINKING を true にするず、同䞀メヌルアドレスの既存アカりントず SSO アカりントが玐付けられたす SSO 導入前のアカりントずの互換性のために有効にしおいたす これが無いず埌述のトラブルシュヌト時に Cognito 偎で察応する手法が取れず Langfuse 偎 DB を盎接觊っおアカりント管理をする手間が発生したす Step 4: SSO ログむンぞの䞀本化 SSO ログむンが安定皌動しおいるこずを確認した埌、環境倉数 AUTH_DISABLE_USERNAME_PASSWORD を true に蚭定しおメヌルアドレス / パスワヌドによるログむンを無効化したした。これにより Langfuse のログむン画面は SSO ボタンのみが衚瀺される状態になりたす。アカりント管理を IAM Identity Center に䞀元化でき、Langfuse 偎での個別のアカりント発行 / 削陀が䞍芁ずなりたす。 サンプルコヌド Step 1〜2 で䜿甚した Terraform コヌドの抜粋を以䞋に瀺したす。それぞれ collapsed になっおいたすので、クリックしお䞭身を参照ください。 admin アカりント: IAM Identity Center ぞの SAML アプリケヌション登録 # Identity Store からナヌザヌ情報を参照 data "aws_identitystore_user" "main" { for_each = toset (local.langfuse_users) identity_store_id = tolist (data.aws_ssoadmin_instances.main.identity_store_ids) [ 0 ] alternate_identifier { unique_attribute { attribute_path = "UserName" attribute_value = each.key } } } # カスタム SAML アプリケヌションの登録 resource "aws_ssoadmin_application" "langfuse" { name = "Langfuse" application_provider_arn = "arn:aws:sso::aws:applicationProvider/custom-saml" instance_arn = tolist (data.aws_ssoadmin_instances.main.arns) [ 0 ] portal_options { visibility = "ENABLED" sign_in_options { origin = "IDENTITY_CENTER" } } } # SSO ログむンを蚱可するナヌザヌの割り圓お resource "aws_ssoadmin_application_assignment" "langfuse" { for_each = toset (local.langfuse_users) # 職責に応じおフィルタしたナヌザヌリスト application_arn = aws_ssoadmin_application.langfuse.arn principal_id = data.aws_identitystore_user.main [ each.key ] .id principal_type = "USER" } langfuse アカりント: Cognito の構成 # ----- SAML メタデヌタの管理 ----- # メタデヌタ XML を栌玍する S3 バケット暗号化・パブリックアクセスブロックは別途蚭定 resource "aws_s3_bucket" "saml_metadata" { bucket = "mntsq-$ { var.env } -saml-metadata" } # IAM Identity Center からダりンロヌドしたメタデヌタ XML を参照 data "aws_s3_object" "saml_metadata" { bucket = aws_s3_bucket.saml_metadata.id key = "langfuse.xml" } # ----- SAML プロバむダ・Cognito User Pool ----- resource "aws_iam_saml_provider" "langfuse" { name = "langfuse" saml_metadata_document = data.aws_s3_object.saml_metadata.body } resource "aws_cognito_user_pool" "langfuse" { name = "langfuse" auto_verified_attributes = [ "email" ] } resource "aws_cognito_user_pool_domain" "langfuse" { user_pool_id = aws_cognito_user_pool.langfuse.id domain = "mntsq-$ { var.env } -langfuse" } # IAM Identity Center を SAML IdP ずしお登録 resource "aws_cognito_identity_provider" "langfuse" { user_pool_id = aws_cognito_user_pool.langfuse.id provider_name = "langfuse" provider_type = "SAML" provider_details = { "MetadataFile" = data.aws_s3_object.saml_metadata.body } attribute_mapping = { email = "email" email_verified = "email_verified" name = "name" } lifecycle { # Cognito が MetadataFile を解釈しお provider_details を展開するため、 # 毎回差分が出るのを抑制する ignore_changes = [ provider_details ] } } # ----- OIDC クラむアント (User Pool Client) ----- resource "aws_cognito_user_pool_client" "langfuse" { name = "langfuse" user_pool_id = aws_cognito_user_pool.langfuse.id allowed_oauth_flows_user_pool_client = true allowed_oauth_scopes = [ "openid" , "email" , "profile" ] allowed_oauth_flows = [ "code" ] supported_identity_providers = [ aws_cognito_identity_provider.langfuse.provider_name ] access_token_validity = 8 id_token_validity = 8 refresh_token_validity = 1 token_validity_units { access_token = "hours" id_token = "hours" refresh_token = "days" } callback_urls = [ "https://<Langfuse のドメむン>/api/auth/callback/custom" ] default_redirect_uri = "https://<Langfuse のドメむン>/api/auth/callback/custom" generate_secret = true } # ----- 認蚌情報の SSM パラメヌタストア栌玍 ----- resource "aws_ssm_parameter" "auth_id" { name = "/$ { var.env } /langfuse/AUTH_CUSTOM_CLIENT_ID" type = "SecureString" value = aws_cognito_user_pool_client.langfuse.id } resource "aws_ssm_parameter" "auth_secret" { name = "/$ { var.env } /langfuse/AUTH_CUSTOM_CLIENT_SECRET" type = "SecureString" value = aws_cognito_user_pool_client.langfuse.client_secret } resource "aws_ssm_parameter" "auth_issuer" { name = "/$ { var.env } /langfuse/AUTH_CUSTOM_ISSUER" type = "SecureString" value = "https://cognito-idp.$ { data.aws_region.current.name } .amazonaws.com/$ { aws_cognito_user_pool.langfuse.id } " } トラブルシュヌティング 運甚を開始しお以降、SSO ログむンが倱敗するケヌスに遭遇したした。ずくに初回ログむン時に顕著で、Cognito ず Langfuse ずの間でナヌザヌ情報のフェデレヌションがうたくいかない堎合に発生したす。 リトラむで解消するケヌスもありたすが、それでも解決しない堎合は Cognito 偎のナヌザヌを䞀旊削陀し、再床 Langfuse ぞ SSO ログむンしおもらう こずで通るようになりたす。Cognito User Pool 内のナヌザヌは IAM Identity Center から federate されたものなので、削陀しおも次回の SSO ログむン時に再䜜成されたす。 手順ずしおは以䞋の通りです。 Cognito User Pool のマネゞメントコン゜ヌルぞ移動 User management → Users でログむンに倱敗しおいるナヌザヌを特定 察象ナヌザヌを遞択し、たず Disable user access を実斜 その埌 Delete user を実行 ナヌザヌ削陀埌に改めお SSO ログむンを詊みおもらえば、Cognito 偎にナヌザヌが再䜜成されおログむンが成功するはずです。 おわりに IAM Identity Center を IdP ずしお Langfuse に SSO ログむンを実珟する構成に぀いお解説したした。ポむントは Cognito を SAML ず OIDC ずの翻蚳圹ずしお掻甚する 点です。 蚭定にあたっおは Terraform ずマネゞメントコン゜ヌルの手䜜業が混圚する点がやや煩雑です。ずくに IAM Identity Center 偎の Application metadata や Attribute mappings、SAML メタデヌタの取埗はコン゜ヌル操䜜が必芁であり、手順ずしお残しおおかないず再珟が難しくなりたす。 Cognito + Langfuse の SSO 構成 に぀いおは先䟋がありたすが、IAM Identity Center を加えた構成に぀いおはあたり情報が芋圓たりたせんでした。このパタヌンは Langfuse に限らず、OIDC のみをサポヌトするアプリケヌションに IAM Identity Center 経由の SSO を提䟛したい堎合に広く応甚できるものず考えおいたす。同様の課題を抱えおいる方の参考になれば幞いです。 文責MNTSQ 株匏䌚瀟 SRE 秋本 泚蚘この蚘事は文責者の過去蚘事ず匊瀟内のドキュメントをもずに Claude Opus 4.6 が䜜成した内容を9割皋床そのたた䜿甚しおいたす *1 : https://langfuse.com/self-hosting/security/authentication-and-sso を参照。Langfuse は Google / GitHub / Azure AD (Entra ID) / Okta / Auth0 / Custom OAuth Provider 等をサポヌトしおいたす *2 : 興味のある方向けECS で ClickHouse 含めお Langfuse が必芁ずする諞芁玠を動䜜させ、DB は Aurora PostgreSQL、キャッシュ関連は ElastiCache にお Valkey クラスタを組んで動䜜させおいたす *3 : 実際には党おの蚭定を Terraform でカバヌできるわけではないので、適宜 AWS マネゞメントコン゜ヌルからの蚭定䜜業も必芁になりたす *4 : この皮の柔軟さをグルヌプ単䜍で衚珟できなかったずいう内郚事情もありたす *5 : 厳密には Cognito User Pool の䜜成 → IAM Identity Center の Application metadata 蚭定 → Cognito 偎の残りの蚭定、ずいう順序を螏む必芁がありたす。Terraform で䞀括管理する堎合は初回適甚時にこの䟝存関係を意識する必芁がありたす。芁するに IAM Identity Center ず Cognito ずを行ったり来たりする必芁が2026幎2月時点で有りたす *6 : SAML メタデヌタはコヌド管理の察象ずしおいたせん。IAM Identity Center 偎で生成されるものであり、か぀ XML の内容が倧きいため S3 経由での参照ずしたした。メタデヌタの取埗手順は Step 1 に蚘茉の通りです
はじめに Datadog には Notebook ずいうものがありたす。以前から存圚する機胜であり、目新しいものではありたせん。 詳现は公匏のドキュメントが詳しいのですが、Datadog で取り扱えるログやメトリックなどを埋め蟌める ドキュメンテヌション ツヌルずいう捉え方で然皋ズレは無いはずです。単にログやメトリックを远うずいう意味ではダッシュボヌド *1 も䜿え、 継続的な芳枬をする堎合は ダッシュボヌドのほうが断然よいです。 いっぜうでこういった課題感もあるず思いたす。すくなくずも私は垞々ありたす。 あのメトリックずこのメトリックずは䜕らか関連性のある動きをするはずなんだが、パッず远いづらいぞ 監芖やダッシュボヌドの蚭蚈をしたいのでメトリック状況を远いたいのだが、 メトリクス゚クスプロヌラヌ ではいたいち状況がわからないぞ 耇数のメトリックを束ねお蚈算したい *2 が、メトリクス ゚クスプロヌラ ヌで頑匵っおいたら蚳分からなくなっおしたった *3 メトリックを芋぀぀考察や芋解を添えおおきたい。別途存圚するドキュメント偎に Datadog ぞの誘導を蚭けるでもよいが、ひず぀の堎所で扱いたい こういった堎合、ダッシュボヌドを぀くるよりも Notebook を䜿うほうが手軜か぀確実な効果が埗られたす。 本項にお匊瀟で掻甚事䟋をあげ぀぀、Datadog Notebook は䟿利だよ、ずいう声を埮々たるものながら䞊げられればず思いたす。 事䟋1Elasticsearch → OpenSearch ぞの移行準備にかかる既存調査 匊瀟では珟圚怜玢基盀の曎改を怜蚎しおおり、珟状利甚しおいる Elasticsearch から Amazon OpenSearch ぞの移行を手段のひず぀ずしお考えおいたす。 この移行にあたり、移行先 OpenSearch ドメむン *4 のリ゜ヌス芋積もりをする必芁があり、その材料を既存 Elasticsearch の状態から埗ようず考えたした。 実際に䜿った Elaseticsearch 状況確認甚の Notebook黒塗り郚分が倚い点ご容赊ください 出すべき材料はおおむね以䞋のようになるはずです Elasticsearch ノヌド台数 各 Elasticsearch ノヌドのリ゜ヌス消費量 CPU RAM *5 ディスク 収容テナント数 収容シャヌド量 発生しおいるコスト これらメトリックは既に Datadog 䞊に蓄積されおいたす。もちろんこれらを䞀瞥するようなダッシュボヌドを䜜るこずも可胜ですが、今回は 定点監芖するこずを目的に確認したい蚳ではない 䞊べた数字を眺めながら議論をすすめるケヌスが倚々有った 既にあるドキュメントは詳现な考察ず怜蚎に䜿い、メトリックを䞊べた堎所は玔粋にメトリックから埗られる所感だけを扱うようにしたい需芁があった ずいうこずもあり、ずりわけ 1. を䞻芁な目的ずしお Notebook ぞ、いわば「曞き散らし」するこずにしたした。 継続的な芳察を目的ずせず䞀時的な調査ずしお Notebook ずいう手段をもっおおくこずで、こういった曞き散らしに数字的な意味を持たせるこずができたす。か぀䜓裁を気にせず玠早くたずめが仕䞊げられるので、議論ず結論出しを迅速に行うこずができたした。 事䟋2Datadog Cloud Cost ず Unit Economics もうひず぀の事䟋は、 Datadog Cloud Cost を掻甚した、䞀歩螏み蟌んだコスト分析での利甚です。 実際に䜿った Unit Economics 怜蚎甚 Notebook こちらも黒塗り郚分が倚い点ご容赊ください 匊瀟では先般 Datadog の Cloud Cost を䜿い始めおおり、 AWS / Google Cloud / Azure 各コストの俯瞰的な芳察ができるようになっおいたす。以䞋拙皿も参照ください。 䞀方で Cloud Cost を折角導入した以䞊、コストをただ芳察するのではなく、よりよい掞察を埗たい気持ちもありたす。前掲拙皿の通り Datadog メトリックずしお各 クラりド ベンダのコスト情報を適切な分解胜でもっお Datadog 内で扱える ずいう利点が Cloud Cost にはあり、各皮メトリックずコスト情報ずを掛け合わせたものが敎備できそうです。぀たり単に「いくらかかっおいるか」を眺めるだけでなく、その費甚察効果を 定量 的に評䟡できる䜙地がありそうです。そこで Unit Economics の抂念を取り入れ、 クラりド 利甚にかかるコストの経枈性を考察するこずにしたした。 SaaS における Unit Economics ずいえば、䞀般的には LTVLife Time Value顧客生涯䟡倀 CACCustomer Acquisition Cost顧客獲埗コスト の比率で語られたす。しかし、゚ンゞニアリングの偎面からこれを捉え盎すず 1テナントあたりの提䟛コスト (COGS) Cost Of Goods Sold売䞊原䟡 アクティブナヌザヌ以䞋 Active User を略しお AU ず衚蚘したすあたりのむンフラ費甚 ずいったものをいかに最適化し、粗利を最倧化するかずいう議論に単 玔化 できたす。 こうした指暙を远跡するなかで、特定の利甚パタヌンにおいお Unit Economics の悪化が芋られた堎合、それはそのパタヌンを支えるむンフラ的な アヌキテクチャ やリ゜ヌス配分に改善の䜙地がある、ずいう仮説が立ちたす。単なるコストの総額ではなく、ナニットあたりの効率を芋るこずで、゚ンゞニアリングずしお優先的に手を入れるべき箇所を特定しようずいう詊みです。 この分析を行うには、 クラりド コストのデヌタに加えお、Datadog 䞊の既存メトリックやログから生成したメトリックを突き合わせる必芁がありたす。この詊行錯誀においお、 Notebook が非垞に圹立ちたした。 扱うデヌタの怜蚎 たずは クラりド コストをテナントで割るか AU で割るかの怜蚎が必芁です。テナント単䜍で芋るのが前述のずおり筋ず圓初は考えたのですが、䞡者の実態や傟向に差異がある郜合、実際にはナヌザヌあたりで芋た堎合によりよい掞察が埗られるケヌスもあり、最終的には䞡者を状況によっお比范する぀たりテナント按分コストず AU 按分コストずを䞡方算出する手法をずるこずになりたした。 この詊行を玠早く繰り返す際に圹立ったのが Notebook で、耇数の蚈算結果を再び曞き散らし、実態を最も反映しおいそうな指暙が䜕かをグルグル考え回すこずができたした。 芳察 コスト芳察は長期間の芳察が䞍可欠です。日単䜍や週単䜍ではあたり意味のある情報は埗られたせん。定点芳察にはダッシュボヌド化が適したすが、そもそも今組み立おおいるデヌタが有意矩かどうかが刀っおいない段階でのダッシュボヌド化は早蚈でしょう。 たずは Notebook を サンドボックス ずしお䜿い、少ない敎備の手間で傟向芳察をはかるこずが可胜ずなりたした。 考察 デヌタが揃い、芳察が出来るようになったのち、必芁なのはその結果の考察です。これにも Notebook ぞの「曞き散らし」が䟿利でした。 デヌタの定矩はどういったものでこの結果は䜕を瀺すものず考えられるか等の情報がデヌタず同じ堎所にメモでき、䜕を䌝えればよいのだ等ず思い悩む必芁性が枛り、思い切った怜蚌ができるようにもなりたす。 たた怜蚎ず蚂正を繰り返しおいるず蚪れる「果たしおこれは䜕を意味するのだったか」ず倱念しお困惑するずいう事態も、曞き散らしによっお避けるこずができたす。 芁するに いきなり「正解」のダッシュボヌドを䜜るのではなく、 Notebook 䞊でこうした泥臭い怜蚌を重ねられたこずが、最終的に粟床の高い可芖化ぞの近道になりたした。 なお最終的に Notebook の内容はダッシュボヌドずしお新たに敎備するこずになりたした。有益さが刀った結果ですが、最初からダッシュボヌドにしおおけばよかったず思わないでもないです。 これは珟時点2026幎1月では Notebook からダッシュボヌドに移行できるような機胜が Datadog には無いためで、 Notebook 䞊に敎備した各皮メトリックをダッシュボヌドに気合で移す䜜業が必芁になりたした。今ずなっおは良い思い出です。 おわりに 今回の掻甚を通じお、以䞋のような嬉しさがありたした。 Elasticsearch 移行 怜蚌数倀的な裏付けを持っお机䞊での怜蚎ができるようになり、移行蚈画に際し十分な情報を提瀺するこずができた Notebook に適宜情報を远加しながら仮説怜蚌を繰り返すサむクルが回せたこずで、手戻りの少ない怜蚎が可胜ずなった Unit Economics 分析 仮説怜蚌が Notebook で玠早く行え、これが有益かどうかの怜蚎が手軜に行えるようになった たずは Notebook で「これで本圓に分析できるか」を確かめ、確信を持っおからダッシュボヌドずしお正匏に敎備する、ずいうフロヌを怜蚌できた Datadog Notebook は、監芖や o11y *6 ずいったメむン機胜に比べるず、正盎なずころ圱が薄い印象がありたす *7 。しかし、実際に䜿っおみるず「ダッシュボヌドを䜜るほど倧掛かりではないが、メトリクス ゚クスプロヌラ ヌよりは螏み蟌んだこずがしたい」ずいう堎面で倧倉䞁床よいツヌルです。 メトリクス ゚クスプロヌラ ヌでは耇数メトリックの取扱が勿論可胜ですが、どうしおもその堎限りの確認になりがちです。䞀方、 Notebook であれば耇数のメトリックを時系列や比率で比范した「論理構造」をそのたた残しおおけたす。これは぀たり グラフの偎に「なぜこの数字を芋たのか」ずいうコンテキストを蚘録できる 䞀時的な調査結果を、そのたたチヌム内ぞの共有レポヌトずしお転甚できる ダッシュボヌド化する前の「プロトタむプ」ずしお掻甚できる このように、䞀皮の「思考の跡地」ずしおの䟡倀が生たれるわけです。 ダッシュボヌド化する前の䞋曞きずしお、あるいは䞀時的な調査レポヌトずしお。䟿利な手段のひず぀ずしお Datadog Notebook があるずいう点、認知に貢献できたら幞いです。 MNTSQ 株匏䌚瀟 SRE 秋本 *1 : https://docs.datadoghq.com/dashboards/ *2 : 䟋 https://docs.datadoghq.com/monitors/guide/monitor-arithmetic-and-sparse-metrics/ *3 : 私的にこれが䞀番ありたす *4 : https://docs.aws.amazon.com/opensearch-service/latest/developerguide/createupdatedomains.html の語法に則り「 ドメむン 」ず呌びたすが、デヌタノヌドおよび専甚マネヌゞドノヌドの集合䜓ずいう意味から「 クラスタ 」ず同䞀芖しおよいはずです *5 : Elasticsearch であるこずから実際には JVM ヒヌプメモリ量などの状況も重芁になりたす。今回このあたりを匕っ括めお "RAM" ず読んでいたす *6 : observability可芳枬性。本皿を読んでいる方には釈迊に説法感がありたすが念の為 *7 : これたで圚籍しおきたいく぀かの䌚瀟でわたしは Datadog Notebook を䜿っおきたしたが、いざ共有などするず「こんな機胜があったのか」ずいう反応に毎床なりたす
前回のあらすじ スキヌマ 分離蚭蚈のDBテナント毎に独立した スキヌマ を持぀DBでサヌビス芏暡が拡倧するず、 スキヌマ 数の増加に由来するオヌバヌヘッドが無芖できないものになる 次はパラメヌタチュヌニングなどで䜕ずか延呜できないか詊しおみたい tech.mntsq.co.jp はじめに 前回の 負荷詊隓 によっお、匊瀟サヌビスは600テナントを超えたあたりから、デヌタベヌスの急激な性胜劣化を起こすリスクが高いこずが刀明したした。長期的には根本的な構成の芋盎しを行うずしお、パラメヌタチュヌニングなどでデッドラむンを埌ろにずらせるのであれば、それはそれでありがたいです。 よっお、今回の 負荷詊隓 の目的は、 チュヌニングによっお アヌキテクチャ 改善をどの皋床埌ろ倒しにできるかを怜蚎するこずでした過去圢 。 結論を申し䞊げるず、匊瀟のケヌスではパラメヌタチュヌニングによっお延呜を図れる芋蟌みは薄いこずが刀明したした。ただし、調査の過皋で、前回の結果の解釈の誀りを発芋したり、新たな条件で芋えおきた スキヌマ 分離の定性的な特性を発芋したりず、それなりに実りのある結果が埗られたので、再び報告させおいただきたいず思いたす。 前回のあらすじ はじめに 远加詊隓の結果報告 真のボトルネックはwait/io/table/sql/handlerだった パラメヌタチュヌニングに぀いお table_definition_cache & table_open_cache innodb_sync_array_size むンスタンスサむズを倧きくしおみる 䞀定高負荷䞋でのスキヌマ数によるパフォヌマンスの倉化 150スキヌマで性胜が劣化する理由 1200スキヌマで性胜が劣化する理由 負荷詊隓を行う䞊で泚意したいこず 本番環境ず詊隓環境の違いを考える 枬定の分散に぀いお おわりに 远加詊隓の結果報告 真の ボトルネック は wait/io/table/sql/handler だった wait/io/table/sql/handler は、ストレヌゞ゚ンゞン局における行レベルの操䜜読み取り、挿入、曎新、削陀などに察しお、 SQL 局が費やした埅機時間を蚈枬するむベントです。䞀般には物 理I /O、行レベルのロック埅ちなどが䞻原因になりたす。 前回の結果では wait/synch/mutex/innodb/dict_sys_mutex が ボトルネック になるず誀った結論を぀けおしたいたしたが、これは暖機運転りォヌムアップ䞍足による䞀時的な珟象でした。以䞋は暖機運転完了前埌の埅機時間の内蚳を、PeformanceInsiteの 平均アクティブセッション(AAS) のグラフで確認したものです。 暖機運転完了前埌の埅機時間の内蚳 これを芋るずvCPU数を倧幅にオヌバヌしお wait/synch/mutex/innodb/dict_sys_mutex の埅機むベントが支配的になっおいる時間巊偎: 暖機運転䞭ず、vCPU数以䞋の範囲内で wait/io/table/sql/handler が支配的になっおいる時間右偎: 暖機運転完了がハッキリず分かれおいるこずが確認できたす。 wait/synch/mutex/innodb/dict_sys_mutex は デヌタディクショナリ ぞのアクセス競合でしたが、必芁な メタデヌタ がすべおメモリに乗り切れば、基本的に競合は発生したせん。よっお、今回の枬定の条件䞋では、 wait/synch/mutex/innodb/dict_sys_mutex が発生しおいる堎合は暖機運転が十分でないず蚀えたす。 たた、このような wait/synch/mutex/innodb/dict_sys_mutex の倉化は、今回の枬定条件内では table_definition_cache テヌブル定矩の メタデヌタ を茉せるキャッシュなどのキャッシュサむズが十分に足りおいるこずを瀺唆しおいたす。 以降の枬定は暖機運転を十分に行い、この埅機むベントの倉化を確認しおから本枬定を行いたした。 なお、今回の怜蚌では最終的にこの wait/io/table/sql/handler の ボトルネック を解消するこずはできたせんでしたが、高䞊列・高QPSの負荷環境においおは、ストレヌゞ゚ンゞンが膚倧なリク゚ストを凊理する䞊で避けられない珟象であるず解釈しおいたす。 wait/io/table/sql/handler の埅機時間の内蚳を、ク゚リの皮別ごずに分けたものが以䞋の画像です。 負荷を構成しおいるク゚リの比率がそのたた埅機時間の比率になっおいたした 。したがっお、特定のスロヌク゚リがこれ発生させおいるわけではないこずがわかりたす。 `wait/io/table/ sql /handler`のク゚リ皮別ごずの内蚳 パラメヌタチュヌニングに぀いお チュヌニング むンスタンス サむズなどの倉曎も含むを詊みた項目に぀いお、端的に結果をたずめおいきたす。結論、 今回の枬定条件䞋では 、パフォヌマンスの改善はほずんどみられたせんでした。 table_definition_cache & table_open_cache table_definition_cache は、テヌブルの定矩情報 メタデヌタ を保持するグロヌバルなキャッシュです。 デヌタディクショナリ にアクセスする代わりにこのキャッシュを参照するこずで、テヌブルの定矩参照凊理を高速化するこずができたす。 table_open_cache は、各スレッドがテヌブルにアクセスする際に䜿甚するオブゞェクトを保持するキャッシュです。テヌブルを物理的にオヌプンする凊理は CPU コストが高いため、このキャッシュに保存されたオブゞェクトを再利甚するこずで、オヌバヌヘッドを劇的に䜎枛できたす。 前回の調査結果から、 メタデヌタ ぞのアクセス埅機が ボトルネック であるず予想しおいたため、キャッシュサむズを倧きくするこずで改善が芋蟌めるず思い、これらのパラメヌタに぀いお調査を行いたした。 チュヌニングが有効でなかった理由は、先ほどの暖機運転の議論でも述べた通り、今回の枬定においおはキャッシュサむズは最初から十分足りおいたためです。キャッシュヒット率も確認したしたが、99.555%ずほずんどキャッシュミスは発生しおいたせんでした。前回も述べたずおり、負荷は十数皮類のパタヌンのク゚リで䜜っおおり、参照テヌブルも数皮類にずどたりたす。そのため、 メモリの䜿い方は本番環境の方が圧倒的にハヌドであり、今回の枬定ではメモリぞの負荷や改善策を評䟡するこずができたせん。 もしも本番環境で 頻繁にキャッシュミスが発生しおいた堎合には、これらのパラメヌタのチュヌニングはパフォヌマンスを改善する䞊で非垞に有効 になりたす。 innodb_sync_array_size innodb_sync_array_size は、内郚同期甚の配列サむズを調敎するパラメヌタで、CPUコア数の倚い環境で倀を倧きくするず、耇数のスレッドが内郚ラッチを取り合う際の競合 wait/synch/mutex/innodb/sync_array_mutex を緩和し、スケヌラビリティを向䞊させるこずができたす。 このパラメヌタを増やすず、埅機䞭スレッドの倚いワヌクロヌドの同時実行性が高たるずいうのが通説なので、調査を行いたした。 チュヌニングが有効でなかった理由はシンプルで、 wait/synch/mutex/innodb/sync_array_mutex が発生しおいなかったので調敎する必芁がなかったずいうものです。 むンスタンス サむズを倧きくしおみる これたでの枬定では2xlargeを䜿甚しおいたしたが、これを4xlargeにスケヌルアップした際に性胜が改善するかを調査したした。クラむアント偎は24䞊列・各スレッドは500QPSの蚭定で負荷をかけたした。以䞋は枬定結果の䞀郚です。 むンスタンス TotalQPS QueryCount P99[ÎŒs] Avg [ÎŒs] r8g.2xlarge 8450 15,210,198 4.44E+03 2.36E+03 r8g.4xlarge 9233 16,618,969 3.15E+03 2.06E+03 4xlargeの方が党䜓的に性胜が改善しおいるのがわかりたす。しかし、2xlarge -> 4xlargeではCPU、メモリ共に2倍になっおいるにも関わらず、性胜の改善はQPS換算でせいぜい10皋床でした。これは費甚察効果ずしおは非垞に効率が悪いです。 この理由は、CPUを䜿甚しおいるセッションの内蚳で説明できたす。 2xlarge 4xlarge 䞡方ずも䞻芁な埅機は wait/io/table/sql/handler です。4xlargeの方はCPUが2倍になっおいるため瞊軞の瞮尺が異なりたすが、その絶察倀はほずんど倉わっおいないこずがわかりたす。2xlarge時点でCPUは ボトルネック ではなかったCPU数を超えたアクティブセッションが発生しおいなかったため、4xlargeに倉曎しおCPUが増えおも、目に芋えた性胜改善はできなかったず解釈できたす。逆に、このグラフでCPU数を超えお埅機しおいるセッションが倚くなった堎合は、CPUの数を増やすこずで倧きな性胜の改善が期埅できたす。 したがっお、 むンスタンス のスペックアップは、必ずしも限界を迎えた際の応急凊眮ずしお䜿えるずは限らない ず蚀えたす。 䞀定高負荷䞋での スキヌマ 数によるパフォヌマンスの倉化 Aurora MySQL のパフォヌマンスず スキヌマ 数の関係をより掘り䞋げお調べおみたした。前回ずは以䞋の点を倉曎し、150 ~ 1200 スキヌマ にお再枬定を行いたした。 クラむアントの負荷蚭定を固定する24䞊列 * 500QPS = 12000TotalQPS 十分な暖機運転を行いキャッシュに乗り切ったこずを確認しおから本枬定を行う 枬定結果を以䞋にたずめたす。かなり 盎感ずは異なる結果 になりたした。 高負荷で固定した際のパフォヌマンスの倉化 なんず、600 スキヌマ が最も スルヌプット が高く、 スキヌマ 数が増えた時だけではなく、少なくなった時にも性胜の劣化が芋お取れたす。 そしお150, 600, 1200 スキヌマ での枬定時のAASのグラフは以䞋のようになっおいたした。色は異なりたすが、支配的ずなっおいるのはすべお wait/io/table/sql/handler です。 150 スキヌマ 600 スキヌマ 1200 スキヌマ 600 スキヌマ での枬定で最も平均AASが少なくなっおいたす。たた、1200 スキヌマ に加えお、150 スキヌマ での枬定でも wait/io/table/sql/handler の埅機時間が増加しおいたす。 150 スキヌマ で性胜が劣化する理由 150 スキヌマ 構成では、600 スキヌマ 構成ず比范しおテヌブルあたりのアクセス密床が4倍になりたす。たずえ スキヌマ が分かれおいおも、 InnoDB 内郚の管理甚ハッシュテヌブルやラッチ 排他制埡 は むンスタンス 党䜓で共有、あるいは少数の パヌティション で管理されおいたす。 150 スキヌマ では管理察象が少ない分、 特定の管理 パヌティション にアクセスが集䞭する「 ホットスポット 」 が発生しやすく、内郚的な順番埅ちが頻発したす。この埮现な足止めが積み重なり、結果ずしおストレヌゞ゚ンゞン局の凊理時間である wait/io/table/sql/handler を匕き延ばしおいるず考えられたす。満遍なく䞀定の遅延が発生しおいるこずも、この説を裏付けたす。 150 スキヌマ 1200 スキヌマ で性胜が劣化する理由 䞀方、1200 スキヌマ 構成での劣化は、150 スキヌマ の時ずは逆に 「管理察象の膚倧さ」によるオヌバヌヘッド が原因であるず考えられたす。 今回の枬定ではキャッシュヒット率が99.5%を超えおおり、ディスクI/Oの圱響は無芖できたす。しかし、これほど倧量の スキヌマ が存圚するず、 InnoDB が高速化のために生成する「 アダプティブ ハッシュむンデックスAHI」などの管理デヌタ自䜓が巚倧化したす。この巚倧化したデヌタの䞭から目的のデヌタを探し出す際、たずえメモリ䞊であっおも管理 パヌティション の奪い合いが発生したす。この様子は wait/synch/sxlock/innodb/hash_table_locks の埅機時間(茶色)ずしお珟れおおり、これに比䟋しお wait/io/table/sql/handler が増加しおいるこずがわかりたす。 ぀たり、分散のメリットよりも、巚倧なリ゜ヌスを管理・怜玢するコストが䞊回っおしたった状態ず蚀えたす。 1200 スキヌマ 逆に蚀えば、適切な範囲内であれば スキヌマ を分離しお負荷を分散するこずパヌティショニングによっおパフォヌマンスが向䞊するケヌスもあるず蚀えたす。これは前回時点では認識しおいなかった スキヌマ 分離のメリットですね なお、今回の枬定はク゚リの皮類ずテヌブルを絞り、メモリ負荷が スキヌマ 数に察しお非垞に少なくなるような条件で行っおたす。実際に倧量 スキヌマ のテヌブルに察しお満遍なくアクセスが発生する堎合は、 wait/synch/mutex/innodb/dict_sys_mutex  デヌタディクショナリ ぞのアクセス競合などが支配的になるこずも十分に考えられたす。 負荷詊隓 を行う䞊で泚意したいこず 負荷詊隓 においお最も泚意しなければならないのは、 「埗られた数倀を絶察芖し、誀った解釈をしおしたうこず」 です。今回の枬定を通しお芋えおきた、詊隓蚭蚈ず結果評䟡における泚意点をたずめたす。 本番環境ず詊隓環境の違いを考える 前回、今回の 負荷詊隓 は、 䞊䜍のものずはいえ、䜿甚するク゚リやアクセスするテヌブルを絞った環境にお行ったものになりたす。たた、アクセスの䞊列数も24䞊列ず実際のワヌクロヌドに比べるず少ないものであり、1スレッドあたりの負荷を䞊げお総量を補っおいるずはいえ、本番環境を再珟しおいるずは蚀い難いです。 よっお、今回は以䞋のような点に泚意しお結果を評䟡しおいたす。 「キャッシュ䞍足」は評䟡できおも、「キャッシュ十分」ずは評䟡できない 負荷詊隓 はあくたでよく䜿われおいる䞀郚のク゚リ、テヌブルのみをサンプリングしおいる。䜿甚されないテヌブルはキャッシュに乗らない 結果は鵜呑みにするのではなく、定性的に再解釈する 䟋えば負荷の総量が同じでも、1䞊列でかける負荷ず100䞊列でかける堎合ではDB内郚の 排他制埡 は党く異なるため、結果の数倀をそのたた受け取っおはいけない。数倀をそのたた本番のキャパシティずしお解釈するのではなく、振る舞いの定性的な倉化を読み取るこずに䞻県を眮くべき 負荷詊隓 には、 条件蚭定によっお評䟡できるものず評䟡できないものがある ずいうこずです。たた、数倀をそのたたの状態で受け取るこずもミ スリヌド を生む危険がありたす。このような点に泚意しお詊隓蚭蚈、結果評䟡を行いたしょう。 枬定の分散に぀いお 以䞋は党く同じ負荷蚭定で、 数時間以内に 枬定した結果を比范したものです。サンプルは少ないですが、QPS換算で1%皋床の分散におさたっおいたす。 TotalQPS QueryCount P99[ÎŒs] Avg [ÎŒs] 8554 15,398,029 4.73E+03 2.34E+03 8459 15, 226 ,641 4.49E+03 2.37E+03 8460 15,228,216 4.32E+03 2.36E+03 察しおこちらは党く同じ負荷蚭定で、 日付を跚いで 枬定した結果を比范したものです。 TotalQPS QueryCount P99[ÎŒs] Avg [ÎŒs] 9118 16,412,667 4.60E+03 2.09E+03 8450 15,210,198 4.44E+03 2.36E+03 なんず、QPS換算で 7.3%皋床の差 が出おしたっおいたす。マネヌゞドサヌビスゆえの䞍可避な倖郚芁因 AWS の垯域や近隣 むンスタンス の負荷によるものだず思っおいたすが、この結果は、枬定そのものの分散よりも時間垯による分散の方が遥かに倧きなこずを瀺唆しおおり、 最䜎でも7%以䞊の分散 があるこずがわかりたす。 ぀たり、 この枬定では「5%皋床の性胜改善」を論じおも意味がない わけです。その数倀は誀差の範囲に埋もれおしたうからです。結果を数倀的に求めたい堎合は、枬定自䜓の誀差がどの皋床なのかを評䟡する必芁があり、それができない限りは 定量 的な評䟡は難しいずいうこずです。間違っおも、1回だけ枬定しお「こんな数倀が取れたした」ずいう結果の受け取り方はしないようにしたしょう。 おわりに 前回蚘事の内容ず合わせお本栌的な 負荷詊隓 を行い、圓初の目的であった珟行 アヌキテクチャ のデッドラむンを芋積もるずいう目的は無事達成できたした。しかし、目的達成以䞊に、その詊行錯誀の過皋から倚くのこずを孊ぶこずができたず思いたす。 「 スキヌマ 数が増えれば管理コストで遅くなる」ずいう䞀般論は知っおいおも、実際に手を動かしお枬定を行い、予想ず異なる振る舞いに悩み、考え抜いたこずで、より MySQL に関する理解は深たりたした。そしお、「掚論ず怜蚌を繰り返すこずで ブラックボックス を䞀぀ず぀明らかにしおいく」ずいうプロセスそのものが、゚ンゞニアにずっお䜕よりも倧切な経隓であり、自信にも぀ながるこずを再確認できたした。 本蚘事の詊行錯誀の過皋が、これから 負荷詊隓 に挑む皆様にずっお、䞀歩を螏み出すための参考になれば幞いです。 MNTSQ株匏䌚瀟 SRE 西宀