有名テック企業の技術ブログを、ひとつのフィードで。
フィード
912件
はじめに こんにちは。Merpay の Payment & Customer Platform で会計システムを開発・運用する Accounting チームで Backend Engineer をしている @mewuto です。本記事は「Merpay & Mercoin Tech Openness Month 2026」の11日目の記事です。 マネージドサービスの世代移行では、コードを変えていなくても、デフォルト値の違いだけでシステムの振る舞いが変わることがあります。ある月末の早朝、Cloud Functions の世代移行が引き金となり、会計イベント基盤で Cloud Pub/Sub のメッセージが大量に滞留して、新規の会計データを Cloud Spanner に登録できないインシデントが発生しました。 直接の引き金は、Cloud Functions を 1st gen から 2nd gen(Cloud Run ベース)へ移行した際に --max-instances を明示しておらず、Cloud Functions のスケール上限が「無制限」(1st genのデフォルト)から「100」(2nd genのデフォルト)へ下がっていたことでした。しかし、障害がここまで大きくなったのは、この引き金だけが原因ではありません。本記事では、1000万件規模に達したこの滞留がどのように構成されていたのかを解き、Cloud Run と Spanner、そして監視の各面で私たちが講じた対策を紹介します。 1. 月末ピークを支える会計イベント基盤 まず、私たちが運用する会計システムの全体像を説明します。会計システムは、社内の各マイクロサービスが発行する「会計イベント」を集約し、Cloud Spanner に記録する基盤です。この会計データは送信元サービスとの突合(リコンサイル)を経て、加盟店への精算や月次の会計締めといった後続処理の前提となるため、取りこぼしや遅延がそのまま業務影響に直結します。 会計システムは、次のような非同期パイプラインでイベントを受け取ります。送信元から Spanner までは、おおむね一方向の流れです。 この構成には、今回の障害を理解するうえで重要な特性が2つあります。 1つ目は、すべてが非同期で動く点です。送信元マイクロサービスはイベントを発行したら応答を待たず、配信・リトライ・整合性の担保はすべて Pub/Sub 以降のパイプラインが引き受けます。特に Pub/Sub から Cloud Run へは push 型サブスクリプションで配信されるため、消費側である Cloud Run とその書き込み先である Spanner の処理能力が、そのままパイプライン全体のスループット上限になります。 2つ目は、負荷が月末に集中する点です。業務特性上、月末・月初の締めに合わせてトランザクションが一気に押し寄せ、平常時の数十倍のスパイクが発生します。 なお、Pub/Sub に届いたメッセージは、この push 経路とは別の pull 型サブスクリプションを通じて Dataflow でも読み取られ、並行して Cloud Storage(GCS)に保存されています。本処理の滞留に巻き込まれないこのGCS上のデータを使って後の復旧を行いました。 2. 1000万件の滞留はどのように発生したか インシデントは、ある月末最終営業日の早朝に発生しました。6時ごろから Pub/Sub のメッセージが滞留しはじめ、Spanner への新規登録が滞り、新規の会計データを受け付けられない状態が断続的に続きました。復旧と再発を繰り返し、収束までには丸一日以上を要しました。 データロスそのものは、先ほど触れた GCS上のデータから再投入することで回避できました。しかし会計システムでは、送信元サービスと DB の間でリコンサイルが完了したものだけが、加盟店精算や月次の会計締めといった後続処理の対象になります。Spanner に登録されなかったデータはリコンサイル未完了のまま残り、締め日と重なったことで、広範な業務影響につながりました。 この滞留が深刻なのは、一度始まるとインフラリソースの上限でスケールが頭打ちになり、処理が遅れるほど次の処理も遅れる悪循環に陥るからです。会計イベントが高い密度で連続して到着すると、maxInstances=100 で頭打ちになった Cloud Run はこれを処理しきれません。上限まで張り付いた Cloud Run が一斉に書き込むことでSpanner CPUが逼迫し、1件あたりの書き込み時間が延びます。すると、処理スループットがさらに落ち、滞留はさらに進んでしまいます。やがて滞留時間が eventMaxAge(メッセージを破棄するまでの最大保持期間)を超えると、メッセージは Spanner に登録されないまま破棄されてしまいます。 3. きっかけは 2nd gen 移行時のデフォルト値 滞留の原因を調べていくと、今回の障害の1ヶ月以上前に行った Cloud Functions の世代移行に行き着きました。私たちは関数を 1st gen から 2nd gen(Cloud Run ベース)へ移行しましたが、このときデプロイコマンドで --max-instances を明示しておらず、世代によってデフォルト値が変わることを、十分に意識できていませんでした。 この「明示しなかった」ことが、スケール上限を静かに引き下げていました。下の表のとおり、1st gen ではスケール上限が実質無制限だったのに対し、2nd gen のデフォルトは 100 です。 Version デフォルトの max instances 1st gen 無制限(プロジェクト Quota の範囲) 2nd gen 100 コードもデプロイスクリプトも変えていないのに、移行しただけで上限が実質無制限から 100 へ下がっていたことになります。平常時は 100 インスタンスで十分だったため、移行後しばらくは問題が表面化せず、私たちの体感としては、ある月末に突如として障害が発生したように見えました。しかし実際は、監視がなかったために気づけていなかっただけでした。インシデント後に振り返ると、上限に迫っていた月もあり、水面下ではすでに限界の兆候が出ていたのです。 4. 障害を大きくした複数の要因 --max-instances の指定漏れは引き金ではありましたが、それだけでこの障害を説明することはできません。調べてみると、2nd gen移行後に迎えた月末のピーク時でも条件はほとんど同じだったからです。maxInstances は 100、eventMaxAge も同じ 21分で、トラフィック総量も3時間で約17〜19GBとほぼ変わりませんでした。 それでも、そのときは障害は起きていませんでした。決定的に違ったのは、トラフィックの「連続性」です。合間に負荷が落ち着く「谷」があり、その短い谷の間に Spanner CPU と未 ack の蓄積がリセットされていました。ところが障害当日は、この谷がないままピークが約40分間続き、システムは回復の契機をついに得られませんでした。 整理すると、今回の障害は3つの要因が連鎖して成り立っていました。まず maxInstances=100 という処理上限が滞留を発生させ、次にトラフィックの連続性がその滞留を固定化し、最後に eventMaxAge によるメッセージのドロップ、という連鎖により実害となりました。いずれか1つでも条件が違えば被害の規模は抑えられたはずで、だからこそ私たちは対策を1箇所に絞らず、制御できる Cloud Run と Spanner の両方に講じることにしました。 5. Cloud Run の処理能力を引き上げる 最初に着手したのは、直接の引き金となった Cloud Run のスケール上限です。あわせて、1インスタンスあたりの処理効率も見直しました。本番に反映した設定値は次のとおりです。 パラメータ 変更前 変更後 max-instances 100(暗黙のデフォルト) 1000 min-instances 0(暗黙のデフォルト) 1 concurrency 1(暗黙のデフォルト) 10 cpu デフォルト 1 memory 512MB 1Gi ここでの本質は、値を大きくしたこと以上に、必要なパラメータをすべて明示したことにあります。今回の引き金は、暗黙のデフォルト値に依存していたことでした。そこで max-instances をはじめとするスケール関連のパラメータをデプロイ定義に明示し、世代やデフォルトの変化に左右されない状態にしました。あわせて、 min-instances を 1 にしてコールドスタートを避け、concurrency を 10 に引き上げ、それに見合うよう CPU とメモリも増強しています。なお max-instances は、書き込み先である Spanner への負荷も考えて、一度に上げきらず段階的に引き上げました。 concurrency を 1 から引き上げることができたのは、ハンドラ内の共有状態を見直し、複数リクエストを同時に処理しても安全だと確認できたためです。Spanner クライアントは内部にコネクションプールを持ち並行アクセスに耐えられます。また、その初期化は sync.Once によって一度だけ行われます。こうした前提を確かめたうえで同時処理数を増やし、必要なインスタンス数そのものを抑えました。 6. Spanner autoscaler の監視軸を Total CPU へ広げる Cloud Run の処理能力を上げると、今度は書き込み先である Spanner がボトルネックになります。Spanner には以前から autoscaler を導入していましたが、障害のさなか、CPU が高負荷であるにもかかわらず PU(Processing Unit。Spanner の計算容量の単位)はまったくスケールアップしていませんでした。autoscaler があるのにスケールしない、という一見不可解な状況でした。 原因は、autoscaler が見ていた指標にありました。Spanner の CPU 使用率には High / Medium / Low の優先度があり、その合算が Total CPU 使用率です。当時の autoscaler は High priority CPU 使用率だけを見ており、障害時は Total CPU が 100% に張り付く一方で、High priority 単体では閾値(60%)の超過が続かず、起動条件を満たしませんでした。この死角は月末ピークに限りません。たとえばリアルタイム性を求めない大量データの削除ジョブは、意図的に Low priority で実行するため、Total は高いのに High は低いという同じ状態を容易に作り出します。優先度別の CPU だけを見る監視は、こうしたケースを構造的に取りこぼすのです。 しかし、これは autoscaler の設定変更では解決できませんでした。当時、OSSである mercari/spanner-autoscaler には、Total CPU でスケールする機能そのものが存在しなかったからです。そこで、OSS をメンテナンスしている SREチームに相談し、High priority と Total を OR 条件で評価する dual CPU scaling mode を実装してもらい、v0.8.0 として取り込みました。設定した閾値は次のとおりです。 パラメータ 変更前 変更後 Total CPU 閾値 (監視なし) 70% High priority CPU 閾値 60% 55% Total CPU 閾値は新設で、これが今回の障害への直接の対策です。値は Google Cloud の throughput 最適化推奨(〜70%)に合わせました。High priority CPU 閾値は、より早くスケールアップを起動するために 60% から 55% へ下げています。下げすぎると平常時のコストが増えるため、より低い 50% 等は避け、Google Cloud の推奨(regional で 65% 以下)に収まる 55% を選びました。これにより autoscaler は High priority(55%) と Total(70%) のいずれかが閾値を超えればスケールアップするようになり、「Total は逼迫しているのに High が閾値未満だからスケールしない」という障害時の挙動は原理的に起こらなくなりました。 また、スケール条件に加えて、運用面でも2つの調整を行いました。1つは、早朝5時から7時のスケールダウンを禁止することです。月末ピークの立ち上がりで、せっかく確保した PU が削られてしまうのを防ぎます。もう1つは、スケジュールによる事前の PU 確保です。月末早朝や大規模なリコンサイルが走る月など、負荷が読める期間にはあらかじめ PU を確保しておき、リアクティブなスケールアップだけに頼らない構えにしました。 7. 上限への接近を検知する監視を整える 今回の障害には、予兆を早期に捉える機会も、発生を即座に検知する機会も逃してしまったという課題があります。スケール上限に達してもそれを知らせるアラートがなく、水面下で限界に近づいていた兆候も見逃していました。そこでDatadog に2つの監視を追加しました。 1つは、Cloud Run のインスタンス数が上限の 40% / 60% に達した時点で警告・重大として通知する監視です。これにより、メッセージのドロップが始まる前に、上限への接近そのものを検知できます。もう1つは、Pub/Sub の未配信メッセージ数が閾値を超えたら通知する監視で、滞留という現象を直接とらえます。後者は誤検知を避けるため、当初は保守的な閾値から始め、定常状態の理解が進むにつれて段階的に下げていく方針にしています。 設定値を最適化するだけでは不十分です。これ自体は当たり前かもしれませんが、どれだけ良い値を入れても、その値への接近を検知できなければ、次の同じ障害は防げません。キャパシティの設計と、その接近を知る監視をセットで整える。この当たり前を当たり前に徹底することこそが大切だと、今回あらためて実感しました。特に、システムをゼロから作った当人ではなく、引き継いで運用していることのほうが多い現場では、こうした設定や前提を定期的に振り返り、整え直す文化を持つことが欠かせません。 8. まとめ 今回の障害は、1つのデプロイフラグを明示しなかったことから始まりました。しかし振り返れば、本当の教訓はもっと一般的なところにあります。 第一に、マネージドサービスの世代移行では、自分が変更していないデフォルト値こそが、負荷時に問題として表面化します。スケール上限・並行数・タイムアウトのような、平常時には効かないパラメータは、移行のたびに明示しておくべきでした。第二に、障害の原因は単一とは限りません。「直近の月は同じ条件で起きなかった」という事実がトラフィックの連続性という決定的な要因を浮かび上がらせたように、トリガー・増幅・被害化のそれぞれに目を向けることが再発防止には欠かせません。そして第三に、設定の最適化と、その状態に気づくための監視は、つねに一体で考える必要があります。 マネージドサービスの便利なデフォルト値は、平常時には何も語りません。だからこそ、ピーク時に初めて顔を出すその振る舞いを、移行のたびに確かめておく価値があります。この記事が、みなさんが運用するサービスの設定を今一度見直す、ひとつのきっかけになれば幸いです。 次の記事は um(うめ)さんです。引き続きお楽しみください。
こんにちは。ファインディのPlatform開発チーム(以降、SREチーム)でSREを担当している原(こうじゅん)です。 SREチームでは、AWSのユーザーとGitHubのアカウントの管理をTerraformで運用しています。Slackから申請が来たらTerraformのコードを書き換えてPRを出す、という作業を以前はDevinで自動化していました。 しかしDevinのアカウント利用形態が変わったことをきっかけに、この自動化基盤をClaude Code Actionsへ移行しました。同じようにAIツールでインフラ運用を自動化している方や、Claude Code Actionsの実践的な使い方を探している方に向けて、移行に至った判断理由と、移行後のアーキテクチャについて紹介します。 背景:Devinでアカウント管理を自動化していた 移行を決めた理由 移行後のアーキテクチャ triageジョブ:申請の振り分け routine-prジョブ:ファイル編集とPR作成 escalateジョブ:自動処理できない申請の通知 まとめ 背景:Devinでアカウント管理を自動化していた ファインディでは、GitHub Teamへのメンバー追加やチームの作成・廃止といったアカウント管理を、SREチームがTerraformで運用しています。 エンジニア・ビジネスサイド問わず、メンバーがSlackから申請を出すと、SREがTerraformのコードを手動で書き換えてPRを出し、レビュー・マージ後にapplyされる、という流れでした。毎回決まったパターンのYAML編集とPR作成を手作業でやるのは、典型的なトイルです。 このトイルを減らすために、Devinを使って自動化していました。Slackの申請内容をDevinに渡すと、Terraformリポジトリのコードを編集してPRを作ってくれる仕組みです。この取り組みの詳細は次の記事で紹介しています。 tech.findy.co.jp 移行を決めた理由 きっかけは、2026年4月1日のDevinのアップデートです。このアップデートでDevinアカウントを持たないユーザーはSlackのDevinスレッドに参加できなくなりました。 アカウントを持たない申請者が@Devinに返信してもDevinが反応せず、「動かない」というSREへの問い合わせが増えました。コンプライアンス上、全メンバーへのアカウント一律払い出しも現実的ではありません。そのため、Devinからの移行に踏み切りました。 移行先の選定にあたっては、次の3つを重視しました。 全メンバーが個人アカウントなしで使えること — Anthropic公式の@Claude(Claude in Slack)も検討しましたが、各ユーザーにClaude Codeのアカウントが必要で同じ問題を抱えて断念しました 申請者の体験を変えないこと — Slack Workflowのフォームはそのまま、バックエンドだけを差し替えます チームのツール統一 — 開発チーム全体がすでにClaude中心の開発体制になっており、自動化基盤もそれに合わせました この選定理由より、今回のアーキテクチャとなりました。 移行後のアーキテクチャ 移行後の全体構成は次のとおりです。 flowchart TD subgraph Slack["Slack (申請者の体験)"] F1["メンバー申請"] F2["チーム申請"] Thread[("申請者スレッド")] end subgraph GHA["GitHub Actions"] Triage["triage\n(Claude Code Action)\n整合性チェック・名前解決"] RoutinePR["routine-pr\n(Claude Code Action)\nファイル編集・PR作成"] Escalate["escalate\n自動処理不可を通知"] end SRE["SRE承認・マージ"] Apply["terraform apply"] F1 -->|workflow_dispatch| Triage F2 -->|workflow_dispatch| Triage Triage -->|routine| RoutinePR Triage -->|escalate| Escalate RoutinePR -->|PR作成| SRE RoutinePR -->|受付通知| Thread Escalate -->|エスカレ通知| Thread SRE -->|マージ| Apply Apply -->|完了通知| Thread classDef unchanged fill:#e6f4ea,stroke:#34a853,stroke-width:2px,color:#1b5e20; class F1,F2,Thread unchanged style Slack fill:#f1f8f4,stroke:#34a853,stroke-width:2px; Slack Workflowからの申請がGitHub Actionsのworkflow_dispatchをトリガーし、正常系ではtriageジョブとroutine-prジョブの2つのClaude Code Actionが順に処理する構成です。 ただし、Slack Workflow Builderから直接workflow_dispatchは呼べません。間にSlack Custom Function(Deno Slack SDK)を挟み、GitHub APIへの認証付き呼び出しを行っています。 api.slack.com sequenceDiagram participant WF as Slack Workflow Builder participant CF as Custom Function<br/>(Deno Slack SDK) participant GH as GitHub API WF->>CF: フォーム入力値を渡す CF->>CF: GitHub App秘密鍵でJWT署名 CF->>GH: JWT → Installation Access Token取得 GH-->>CF: Token返却 CF->>GH: workflow_dispatch(Token認証) GH-->>CF: 202 Accepted 申請者から見ればSlackのフォームに入力するという体験は変わっていませんが、裏側ではCustom FunctionがGitHub Appの認証(JWT署名 → Installation Access Token取得)を行い、workflow_dispatchを呼び出しています。 triageジョブ:申請の振り分け 最初のClaude Code Actionは、申請内容の整合性チェックと振り分けを担当します。 チーム名の名前解決(申請者が入力した表記ブレを、リポジトリ上の実際のディレクトリ名に解決する)、申請内容のバリデーション、そしてroutine(自動処理可能)かescalate(自動処理不可)かの判定を行います。 triageとPR作成を1つのジョブにまとめることもできますが、責務と権限を分離するためにあえて分けました。triageジョブは--max-turns 5と少ないターン数に制限し、使えるツールもReadとBash(ls:*)だけに絞っています。判定がおかしければPR作成に進まない安全弁にもなります。 判定結果はJSON Schemaで構造化出力させるので、後続のジョブが確実にパースできます。 claude_args: | --max-turns 5 --allowedTools Read,Bash(ls:*) --json-schema '{"type":"object","properties":{"verdict":{"type":"string","enum":["routine","escalate"]},...}}' routine-prジョブ:ファイル編集とPR作成 triageでroutineと判定された申請は、2つ目のClaude Code Actionが処理します。TerraformリポジトリのYAMLファイルを編集し、ブランチを切ってPRを作成するジョブです。 --max-turns 20とターン数を多めに確保し、ファイル編集やgit操作、PR作成に必要なツールを許可しています。 claude_args: | --max-turns 20 --allowedTools 'Read,Edit,Write,Bash(git checkout:*),Bash(git add:*),...,Bash(gh pr create:*)' PR作成後はSlackの申請スレッドに受付通知を送り、SREがレビュー・マージすればterraform applyが走って完了通知が届きます。 escalateジョブ:自動処理できない申請の通知 triageが自動処理できないと判断した申請は、Slackの申請スレッドにその旨が通知されます。Claude Code Actionは使わず、シェルスクリプトで通知を送るだけのシンプルなジョブです。 まとめ Devinのアカウント利用形態変更をきっかけに、アカウント管理の自動化基盤をClaude Code Actionsへ移行しました。Claude Code Actionをtriage(振り分け)とroutine-pr(PR作成)の2段階に分け、それぞれの責務と権限を絞った設計にしています。 現在、GitHubアカウント管理の移行は完了し、運用を開始しています。今後はAWSのユーザー管理なども移行を進めていきます。 ファインディでは一緒に会社を盛り上げてくれるメンバーを募集中です。興味を持っていただいた方はこちらのページからご応募お願いします。 herp.careers
中部支店に勤務しているData Intelligence Unit Master Data Groupのウチウゾウです。 前回、前々回とご好評をいただいた「Nagoya Tech Talk」の第3弾、「Nagoya Tech Talk #3 〜CloudNative × Platform〜」を開催しました!sansan.connpass.comこれまでは「AI」をメインテーマに扱ってきましたが、第3回となる今回は「オブザーバビリティとプラットフォームエンジニアリング」に特化しました。おかげさまで、当日は多くのエンジニアの方にお集まりいただき、熱気あふれるイベントとなりました。 CTO笹川によるオープニング本記事では、当日の発表内容やイベントの熱量をコンパクトに振り返ります。 1:研究開発部の監視基盤 - DatadogからNew Relicに移行 speakerdeck.com弊社の上田より、研究開発部のEKS基盤(Circuit)の監視基盤をDatadogからNew Relicへ、わずか1カ月で移行した事例が紹介されました。EKS基盤(Circuit)の使われ方とDatadogのコスト体系の相性があまりよくなかったため、移行に踏み切ったそうです。この爆速移行の裏側には、AWSが提供するAI統合開発環境Kiroの活躍がありました。具体的には、Datadog上にあった4つのダッシュボードと160個のウィジェットをJSONにエクスポートして、KiroとNew Relic MCPを組み合わせ、半自動移行を成功させました。これまで大きなコストがかかっていた移行作業をAIによって比較的容易にできた点に驚かされました。 上田の発表 2:作るより難しい、使い続けてもらうこと - Platform as a Productの実践 speakerdeck.comSansanには、研究開発部のCircuitのほかに、Orbitという全社横断Platformも存在します。弊社の辻田より、開発者体験を向上させ、使いたいと思われるPlatformにするため、Orbitという社内開発基盤をProductとして捉えた実践についての紹介がありました。まず、フィードバックループによる改善を目指して、1対1のユーザーインタビューを通し、社内開発基盤のユーザーの解像度を地道に上げていったそうです。その後、RICEスコア(Reach/Impact/Confidence/Effort)により施策の優先順位を決め、初期構築時の複雑さを解消する施策を実施しました。その結果、導入時のオンボーディング時間は半減しました。AIでなんでもすぐに作れてしまうものの、作り物が増えてしまうと、どうしても運用負荷もじわじわと膨れていきます。開発者の生産性を高く保つためにも、開発者に選ばれ続けるためのPlatform as a Productの実践の大切さを学べるお話でした。 辻田の発表 3:アラート削減でチームの開発生産性を向上 speakerdeck.com研究開発部のEKS基盤(Circuit)の上で動いているサービスのSREである野首より、システム運用負荷を大幅に改善した取り組みに関する発表がありました。アサイン初期は、アラート発報のたびにSREがまず調査する対応を繰り返していたそうです。この運用負荷を下げるべく、研究員とSREの責任境界の明確化に取り組みました。この明確になった責任境界を運用に乗せるため、SREに属人化していたナレッジをRunbookに整理しました。また、調査方法については、Agent Skillsに落とし込むことで、運用の属人化を解消していったそうです。これらの取り組みに伴い、不要なアラートを削減していった結果、アラートの9割削減に成功したとのことでした。SREと開発の責任境界を明確にしつつも、それを運用に乗せるには協働するという姿勢と、知見の共有が大事であることを改めて感じさせられました。 野首の発表 終わりに 改めて、会場にお越しいただいた参加者の皆さま、本当にありがとうございました。名古屋のエンジニアコミュニティーをさらに盛り上げ、最先端の知見をシェアし合える場として、「Nagoya Tech Talk」は今後も継続して開催していきます。「名古屋のエンジニアコミュニティーに関心がある」という方は、ぜひconnpassグループのフォローをお願いします!また、Sansan中部支店では、このような刺激的な環境で共に挑戦するメンバーを募集しています。イベントのオープニングでCTOの笹川が登壇した際の「Sansan中部支店の紹介資料」も公開しておりますので、私たちの組織や開発環境に少しでも興味を持っていただけた方は、ぜひチェックしてみてください。 speakerdeck.comそれでは、次回のNagoya Tech Talkでお会いしましょう! Sansan技術本部ではカジュアル面談を実施しています Sansan技術本部では中途の方向けにカジュアル面談を実施しています。Sansan技術本部での働き方、仕事の魅力について、現役エンジニアの視点からお話しします。「実際に働く人の話を直接聞きたい」「どんな人が働いているのかを事前に知っておきたい」とお考えの方は、ぜひエントリーをご検討ください。
こんにちは、IT 基盤部の西崎です。主に DeNA がグローバルに展開するゲームのインフラ管理を担当しています。 今回は MySQL のバージョンアップによって社内で利用しているツールで障害が発生し、その調査をした時のことをまとめてみます。細かい話になりますが、大規模に MySQL を使っている環境ならではの経験を紹介できると思います。 この調査自体は数年前に行ったものなので、この後の対応などについても追って記事にできればと考えています。 何が起こったのか DeNA では MHA という MySQL の高可用性ツールを利用しています。
はじめに IT基盤部第一グループの大田です。 私はインフラエンジニアとして日々の運用を担当する一方で、ここ一年ほどはチームの長期施策の進捗管理にも関わるようになりました。自分が直接担当する施策について、複数の関係者と調整しながら課題を前に進めることには慣れていましたが、そうではない案件について、PM(プロジェクトマネージャー)として間接的に完了まで見届ける仕事にはまだ不慣れでした。 そこで私は、PM業務、その中でも特に進捗確認の部分をAIで楽にできないかと考えました。複数のプロジェクトが並行して進んでいると、どの施策が順調で、どこにテコ入れのフォローが必要なのかなどを把握し続けることが手間になります。AIが個々のプロジェクトの状況を整理してくれれば、その負担も減るのではないかと考えました。
株式会社 AbemaTV で SRE / Platform Engineer をしている 後藤(@r ...
はじめに こんにちは。WEARバックエンド部SREブロックの春日です。普段はWEARというサービスのSREとして開発・運用に携わっています。 本記事では、WEARのハイブリッド検索のリリースに伴い刷新した検索インデクシングシステム(以下、インデクサー)について、OpenSearch Ingestionを採用しようとした際にハマったポイントや、ベクトル検索のためのインデクサーを設計する上で工夫した点を中心に紹介します。 目次 はじめに 目次 背景 既存のインデクサーと刷新の動機 ベクトルデータの保持方法の検討 インデクサーの構成方針 BigQuery → S3 のデータ連携 日次更新の設計 差分更新の設計 初期設計:OpenSearch Ingestion+Lambdaプロセッサでのベクトル化とインデクシング 1万件の差分更新で表面化した問題 再設計:ベクトル化Lambdaを前段に出す 最終設計:S3+SQS+Lambdaで非同期にベクトル化とインデクシング ベクトル化Lambdaでの工夫 Bedrockのリージョン分散 1ファイル単位の処理量を制御する 出力形式と後処理 OpenSearch投入Lambdaでの工夫 external versionで古いデータで新しいデータを上書きすることを防止 処理完了後のファイル削除 Lambdaエラー時のファイル退避 非同期処理の完了待機 既存データに対する初回ベクトル化 結果 まとめ 背景 WEARでは、検索基盤としてAmazon OpenSearch Service(以下、OpenSearch)を利用しています1。これまでフリーワード検索ではタグマッチングを主軸としていましたが、タグが付与されていない検索ワードに対する検索結果の質と量に課題がありました。 これを改善するため、ベクトル検索と全文検索を組み合わせたハイブリッド検索(WEARではあいまい検索と呼んでいるため、以下「あいまい検索」と表記)をリリースすることになりました2。 あいまい検索のためにベクトル検索を導入するには、検索対象の各documentに対して、タイトル・説明文・タグなどを連結したテキストをベクトル化したフィールドを持たせる必要があります。しかしながら、既存のインデクサーでこのフローを実現するのは難しく、インデクサー自体を刷新することになりました。本記事ではその刷新の過程と、設計時に行った工夫を紹介します。 既存のインデクサーと刷新の動機 WEARではOpenSearchへのインデクシングをEmbulkを用いて行っていました。 embulk-input-bigqueryとembulk-output-elasticsearchなどを組み合わせ、BigQueryからOpenSearchへデータを連携する構成です。EmbulkのジョブはDigdagのworkflowで管理し、Amazon EKS(以下、EKS)上のJobとして実行していました3。インデクサーには差分更新と日次更新の2種類があり、それぞれ次の役割を持っていました。 差分更新:10分間隔で実行。直近で新規投稿・更新documentをインデクシングし、削除された投稿をindexから削除 日次更新:1日1回、新しいindexを作成して全件をインデクシングし、Blue/Greenでエイリアスを切り替える形で全件更新する。統計データなどの日次で更新すべき値はこのタイミングで反映 しかし、ベクトル検索の導入を検討するにあたり、この構成にはいくつかの課題がありました。 WEARで一番大きいコーディネートのindexは大量のdocumentを持っており、これらを毎日ベクトル化するのはコストと処理時間の両面で非現実的 BigQueryからOpenSearchへの連携中にベクトル化の処理を挟むのが困難 本対応の検討時点でEmbulkはすでにメンテナンスがされていない状態であり、長期的な保守性に不安 これらを踏まえ、ベクトル検索対応に必要な機能と、長期的な保守性の両方を満たす構成へとインデクサーを刷新する方針を決めました。 ベクトルデータの保持方法の検討 最初に取り組んだのが、ベクトルデータをどこに、どのタイミングで持たせるかという検討です。 既存の日次更新では新しいindexを毎日作成して全件インデクシングしていましたが、大量のデータを毎日全件ベクトル化するのは非現実的なため、ベクトルデータを別ストレージに保存しておく案を検討しました。しかし、ベクトル取得時のパフォーマンスやコスト面で見合わないと判断し、最終的には日次での全件更新そのものを廃止する方針を取りました。 毎日indexを全件更新するメリットの1つとして、indexの不整合が発生した場合に、日次での全件更新によって整合性を保つことができるという点がありました。これは例として、差分更新の失敗時のリトライで、古いデータで新しいデータが上書きされてしまうといった状況が挙げられます。全件更新を廃止するにあたり、この点をどう担保するのかが課題でしたが、後述する方法でdocumentのバージョニングを行うことで、不整合が発生しないようにしました。 新しい設計では、ベクトル化は差分更新のみで行い、日次更新では同じindexに対して日次で更新すべき値のみを上書きするように責務を分けました。これにより、ベクトル化を投稿の追加・更新時のみに限定でき、ベクトル化コストと処理時間の問題を回避できるようになりました。 インデクサーの構成方針 インデクサー刷新にあたって、ベクトル化を含む新しい構成として複数の選択肢を検討しましたが、OpenSearch Ingestionを軸とする構成を採用しました。判断のポイントは以下の通りです。 自前で運用する外部ツールは最小限にしたい(Embulkのように追加でメンテナンスが必要なツールを増やしたくない) データ抽出のSQLはバックエンドエンジニア、インデクサーのインフラ構築・運用はSREという責務分離を維持し、両者を疎結合にしたい AWS公式のLambdaプロセッサでベクトル化するパターンを参考にすれば、ベクトル化部分をLambdaへ切り出して柔軟に構成できそう これらを総合的に考慮し、Amazon S3(以下、S3)を起点としたAWS Lambda(以下、Lambda)の構成を方針として進めることになりました。 BigQuery → S3 のデータ連携 WEARではMicrosoft SQL ServerからBigQueryへリアルタイム連携をしており、インデクサー側もBigQueryからデータを取得しています。前述の通り、インデクサーはS3を起点としてデータを処理する設計を取っているため、BigQueryから取得したデータをS3に連携する必要があります。BigQueryから直接S3へ出力する機能はないため、いったんCloud Storage(以下、GCS)へ出力してからS3へ転送する形を取りました。 GCSへの出力にはBigQueryのEXPORT DATA文を利用しています。差分更新・日次更新いずれもJSON Lines形式でGCSへ出力するように記述しており、以下に差分更新を例にしたものを記載します。 EXPORT DATA OPTIONS( uri='gs://GCS_BUCKET/coordinates/diff/raw-data/YYYY/MM/DD/HH/mm/data_*.jsonl', format='JSON', overwrite=true ) AS -- 対象データを取得するクエリ ... uriにワイルドカード(*)を含めることで、BigQueryが出力サイズに応じて自動的に複数ファイルへ分割します。出力フォーマットはJSONを指定するとJSON Lines形式になります。 GCSからS3への転送方法は、差分更新と日次更新で異なるツールを使い分けています。 差分更新:rclone 日次更新:AWS DataSync(以下、DataSync) DataSyncは大量データの高速転送に適していますが、タスクの起動・実行に約5分かかります。差分更新は10分間隔で実行する上、ベクトル化のような時間のかかる処理も挟まるため、起動に時間のかかるDataSyncは許容できませんでした。差分更新ではデータ量がそこまで多くないこともあり、rcloneを採用しています。 日次更新の設計 日次更新では、もともとEmbulkで実装されていた全件更新を廃止し、差分更新と同じindexに対して統計データなどの日次で更新すべき値のみを上書きする方式に変更しました。日次更新の構成は以下の通りです。 日次更新はOpenSearch Ingestionを採用しており、S3に格納された全件データに対してS3 scanでOpenSearchへbulkでupsertしています。OpenSearch Ingestionのパイプライン定義の例は以下のとおりです。 version: 2 coordinates-daily-indexer: source: s3: acknowledgments: true delete_s3_objects_on_read: true scan: buckets: - bucket: name: ${BUCKET_NAME} filter: include_prefix: ["coordinates/daily/raw-data/"] aws: region: ap-northeast-1 sts_role_arn: ${STS_ROLE_ARN} codec: ndjson: {} processor: - delete_entries: with_keys: <s
はじめに こんにちは。プラットフォームSREブロックの酒部・高塚・亀井です。私たちは2026年5月14日〜15日に名古屋で開催された「クラウドネイティブ会議」に参加してきました。本記事では印象に残ったセッションをご紹介します! はじめに クラウドネイティブ会議とは セッションレポート キーノート:老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- GameDay:チームで挑むリアルな障害対応 100マイクロサービスのTerraform/Kubernetes管理地獄から抜け出すためのAI活用術 巨大組織の認知負荷をどう下げるか?ソフトバンクが描くCNAP×Backstageによるクラウドネイティブの新時代 生成AI時代に信頼性をどう保ち続けるか - Policy as Codeの実践 おわりに クラウドネイティブ会議とは クラウドネイティブ会議は、CloudNative Days、Platform Engineering Kaigi、SRE Kaigiという3つの大規模テックイベントで合同開催されたカンファレンスです。現地参加とオンライン参加のハイブリッド形式で開催され、会場の名古屋・中日ホール&カンファレンスには約1,000人が集まりました。 当日の様子は公式のXのポストまとめで見ることができます。現地の雰囲気を感じることができるので、ぜひご覧ください。 posfie.com セッションレポート ここからは各メンバーからのセッション紹介をお届けします。 キーノート:老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- kaigi.cloudnativedays.jp 高塚です。1日目のキーノートでは、パナソニックさんの事例として、巨大なモノリスだった老舗IoTサービスをマイクロサービス化した熱い話を聞くことができました。 見切り発車でマイクロサービス化を始めたものの、最初は問題が山積みだったとのことです。 老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- アーカイブ動画 6:19 より引用 かなり昔からあるAWSアカウントでしか見られない「ap-northeast-1b」アベイラビリティゾーンがあった話では会場もざわざわしていました。 老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- アーカイブ動画 7:00 より引用 GitLabの導入は、社内から「Gitを使うなんて危険だ、けしからん」という声も上がるなど「正直ここが一番しんどかった部分」だったそうです。しかし、そういった意見を軽々しく否定せず、既存のシステムがお客様に価値を提供してきたことに敬意を持ちながらクラウドネイティブ化を進めたそうです。 老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- アーカイブ動画 8:22 より引用 その結果、無事に本番稼働させることができました。 老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- アーカイブ動画 15:26 より引用 後半ではIoT特有のE2Eトレーシングについて紹介がありました。 組み込みデバイスはCPU・メモリが限られており、スパン送信による性能劣化を避けるため、OpenTelemetry SDKは利用せずにトレーシングを自前実装したとのことです。 老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- アーカイブ動画 23:45 より引用 また、IoTデバイスはネットワークやOSなどの問題が障害の原因になりやすいため、eBPFでカーネルイベントもスパンにしたそうです。 老舗IoTクラウドサービス組織の変革 -クラウドネイティブをはじめよう- アーカイブ動画 24:45 より引用 約30分の発表はとても濃い内容で、大変勉強になりました。また私もZOZOTOWNのマイクロサービス化を長年担当しているため、発表で赤裸々に語られる苦労話には「わかりみが深い〜」と100回くらい頷いていました。 本記事では主に技術的な話をご紹介しましたが、組織の文化を醸成する話や「これからクラウドネイティブをはじめる方へのメッセージ」などとても学びになる内容が盛りだくさんですので、ぜひアーカイブ動画をご覧ください。 GameDay:チームで挑むリアルな障害対応 酒部です。GameDayはKubernetes環境上で発生するさまざまな障害シナリオに対して、チームで協力して原因を特定し、復旧させるという内容でした。参加者にはKubernetes上で稼働するアプリケーション環境が提供され、その環境にはあらかじめ障害が仕込まれており、クエスト形式で出題される問題を解きながら、システムを正常な状態に復旧させていくという形式でした。 形式は約2時間のチーム対抗戦で、Kubernetes初心者から経験者まで幅広い層が集まっていました。ルールはシンプルで、障害を復旧することでスコアが加算され、最終的にチーム単位で順位が決まる仕組みです。 困ったときに相談できるメンターがサポートしてくれる体制に加え、行き詰まっても段階的なヒントで前に進める仕組みも用意されており、勝負というよりもチームで協力して問題に取り組む過程が重視されているように感じました。 題材として用意されていたのは、OpenTelemetry DemoをベースとしたECサイトのマイクロサービス構成です。各チームにCode Server、Grafana、Jaeger、Argo CD、GitHubリポジトリが一式与えられました。基本フローは障害を発見 → GitHub上で修正 → commit & push → Argo CDが自動同期 → Verifierによって各問題の合否判定する流れです。 問題の内容や解説は下記の公式記事に解説があるため、そちらをご覧ください。 kaigi.cloudnativedays.jp 結果としては、最後の問題だけ時間内に解くことができませんでした。ただ、作問者が解かせるつもりで作っていないと言うほどの難問で、ほとんどのチームが解けていなかったので、終了後残り時間で会場でも簡単な解説をしていただきました。解説後、会場のあちこちから「そういうことだったのか」「やられた」といった声が漏れ、参加者全員が思わず唸ってしまうような巧妙な問題設計だったことが印象に残っています。 観察・操作系のチュートリアル問題も用意されていて、Kubernetesに触り始めたばかりの方でも問題なく楽しめる内容で、自信を持っておすすめできるプログラムでした。 100マイクロサービスのTerraform/Kubernetes管理地獄から抜け出すためのAI活用術 kaigi.cloudnativedays.jp speakerdeck.com 酒部です。このセッションでは膨大なTerraformやKubernetesマニフェストファイルを扱う構成において、日々の運用をAIエージェントでどう解決するかがテーマでした。 セッションは大きく4パートで構成されており、以下のトピックが扱われました。 Linear × Codexで全マイクロサービスのTerraform / K8s manifest更新を効率化 AIによるレビューで200 PR/dayの50%を自動化 問い合わせ・トラブルシューティングをAIに任せる試み Production Readiness Check(PRC)のEvidence確認もAIにやってもらう 特に、2と4は弊チームと似た取り組みも紹介されており大変興味深い内容でした。 まず、PRレビューへのAI活用について、Notionに整理されたガイドラインがあるのに、サービス・人数の増加で存在を知らない人が増え、結果としてガイドラインが守られないという課題がありました。 解決アプローチはガイドラインをAIエージェントが利用できる形でリポジトリに降ろすというものです。Notionは引き続きSoT(Source of Truth)として残しつつ、AIレビュー用のコンテキストはリポジトリに固定する設計としました。 紹介されていた実例では、Codex ReviewがIAM設定の重複を「社内ガイドライン違反」として指摘し、参照したガイドラインのファイルパスまで引用していました。一般論ではなく、社内ルールに照らした具体的な指摘ができている点が印象的でした。AIに指摘されない部分こそが暗黙知であるという気づきから、「AIが指摘してこない部分」を観察することで、ドキュメント化されていない暗黙知が浮かび上がってくる、というメタな視点が学びになりました。 本番リリース前のProduction Readiness Check(PRC)の自動化ですが、開発者はEvidenceを集め、SREはその妥当性をチェックするという、両者にとって骨の折れるプロセスがあるそうです。ここで、いきなりAIに丸投げするのではなく、AIに任せるべき項目を選定したり、Code化できる項目はCIで自動チェックしたりするなど工夫されていました。 また、AIに任せるためフォーマットを整理する過程で、以前はやや解釈が難しいPRC項目もあったが、自動化に向けて解釈がブレないようにしたという副次効果も語られていました。AI活用のためのドキュメント整備が、人間にとっても理解しやすいドキュメント整備につながるとい
はじめに こんにちは。 IT 本部 IT 基盤部 第三グループの岡崎です。 IT 基盤部では、組織横断的にさまざまなサービス・プロダクトのインフラ運用を行っており、サービスの安定的な稼働や、運用の改善などに取り組んでいます。 本記事では、我々が利用検証を進めているオブザーバビリティツールの1つである Dynatrace において、取り込まれるログ内の個人情報をマスキングする方法についてご紹介します。 Dynatrace においてログをマスキングする方法としていくつかのアプローチが提供されていますが、それぞれのアプローチの特徴を把握し、自身の要件に合わせた選択ができるようになることを目的としています。
はじめに こんにちは、WEAR開発部SREブロックの木内です。普段はWEARのSREとして開発、運用に携わっています。 WEARは2013年にサービスを開始し長年オンプレミスで運用されてきましたが、過去にクラウド(AWS)へのシステムリプレイスを実施しています。その際にWebアプリのCDNとしてFastlyを採用し、オンプレミスからクラウドへの段階的な移行を実現しました。 採用の決め手は主に以下の点です。 パスベースのルーティングが可能(パスごとにオンプレミスとクラウドのオリジンを切り替えられる) ネイキッドドメインへの対応 設定変更の即時反映による迅速なロールバック リプレイスの詳細については、以下の記事をご参照ください。 techblog.zozo.com Fastlyを用いた構成はサービスを止めずに安全なリプレイスを実現するうえで大きく貢献しました。一方で、リプレイスが完了しFastlyを導入した当初の目的を達成した後も残り続けたことで、運用負荷やコストといった課題が顕在化してきました。 本記事では、過渡期の構成を整理し、CDNをFastlyからAmazon CloudFront(以下、CloudFront)へ移行してAWSに統一した取り組みを紹介します。 目次 はじめに 目次 移行の背景・課題 運用負荷 コスト 移行後の構成 移行方法 1. LP・アセットの切り替え 2. 動的コンテンツ・認証処理の切り替え 3. VCLの処理をCloudFront Functionsへ移行 4. DNS切り替え WAFの移行タイミング 設計のポイント 移行期間中のリクエスト CloudFront Functionsを用いたリダイレクト・リライト カスタムエラーレスポンス 得られた成果 運用のシンプル化 インフラコストの削減 まとめ 移行の背景・課題 移行前の構成は以下の通りです。 Fastlyをフロントに置き、バックエンドにALBとS3が2つずつ、計4つのオリジンを持つ構成です。 ALBは動的コンテンツの配信や認証処理を担っており、S3はLPやアセットなどの静的コンテンツを配信しています。S3の前段にはそれぞれ個別のCloudFrontを使用しています。 また、FastlyではVCL1を用いてCDN以外の多くの処理も担っていました。 パスごとのオリジン振り分け 各種ブロック(IP / ASN / リファラ など) Basic認証 メンテナンスモード リダイレクト / リライト 前述の通り、リプレイス時にFastlyを採用したことで安全にリプレイスを進めることができましたが、リプレイス完了後に以下のような課題が残りました。 運用負荷 AWSとFastlyの二重管理が運用負荷に繋がっていました。 WEARの大部分はすでにCloudFrontを使用しており、2つのCDNそれぞれでキャッチアップが必要だった AWSとFastlyそれぞれでユーザーやリソースの管理も必要だった 設定変更をWebチームとSREチームで担っていたため、Fastly専用のインフラリポジトリを別途設けており、CIの整備やレビューフローの維持など、リポジトリが増えることに伴う管理コストが発生していた コスト FastlyをAWSの前段に置く構成であるため、大きく分けて2種類のコストが発生していました。 Fastlyがユーザーへ配信するコスト AWSがFastlyへ配信するコスト また、LPやアセットはすでにCloudFrontを使用していたため、FastlyとCloudFront両方のCDNコストが発生していた点も見過ごせません。 これらの課題を解消するために、CDNをCloudFrontへ移行し、配信基盤をAWSへ統一する方針を取りました。 移行後の構成 移行後の構成は以下の通りです。 移行後は、ALB・S3など全てのバックエンドの前段に1つのCloudFrontを配置しています。 また、FastlyのVCLで実施していた処理はそれぞれ対応するAWSサービスへ移行しました。 処理 Fastly AWS CDN・配信 Fastly CDN CloudFront パスごとのオリジン振り分け VCL CloudFront(Cache Behavior) 各種ブロック(IP / ASN / リファラ など) VCL AWS WAF Basic認証 VCL AWS WAF メンテナンスモード VCL AWS WAF リダイレクト / リライト VCL CloudFront Functions Basic認証・メンテナンスモードはAWS WAF(以下、WAF)のカスタムレスポンス機能を利用して実装しています。WAFのルールにマッチしたリクエストに対して、任意のHTTPステータスコード・レスポンスヘッダー・レスポンスボディを返せる機能です。 セキュリティ関連の処理をWAFに集約することで一元管理できることに加え、CloudFront Functionsのコードサイズや実行時間の制限2を避けられる点も採用の理由です。 リダイレクト・リライトの実装にあたり、Lambda@EdgeとCloudFront Functionsを比較検討しました。Lambda@Edgeは複雑な処理や長時間実行に向いている一方、今回のような軽量なリダイレクト・リライト処理にはCloudFront Functionsが適しています3。加えてスケール量・リクエスト料金の面でも有利なため採用しました。 さらに、リダイレクト・リライトはルール数が多いため、jsをビルドしminify化しながらCloudFront Functionsのコードサイズ制限を超過しないよう工夫しています。CloudFront KeyValueStoreを使用したサイズ圧縮も検討しましたが、フロントとインフラ間の依存関係の簡素化や認知負荷の低減を優先したかったからです。 これらの設計を経て、FastlyのVCLに分散していたエッジ処理をAWS WAFとCloudFront Functionsに集約し、CDNレイヤーの機能をすべてAWS上で完結させる構成になりました。 移行方法 今回のCDN切り替えはフェーズを分けて段階的に実施しました。このCDNはPC・SP全体のトラフィックを受けているため、問題が発生すれば多くのユーザーへ影響が出てしまいます。そのため、いかにユーザー影響を出さず安全に移行するかが本プロジェクトの鍵でした。 具体的なフェーズは以下の通りです。Fastlyの後段にCloudFrontを配置し、影響範囲が小さいものから順にCloudFront経由へ切り替えていきました。 1. LP・アセットの切り替え 最初はS3で配信しているLPとアセットの切り替えです。静的コンテンツは影響範囲が限定的で切り戻しもしやすいため、最初の移行対象としました。 切り替えにあたっては全パスを網羅するURLリストを用意してスクリプトで動作確認を行い、移行前後の挙動に差異がないことを確認しながら実施しました。 2. 動的コンテンツ・認証処理の切り替え 次に動的コンテンツの配信や認証処理を担うALBの切り替えです。動的コンテンツの配信や認証処理を担っているため、CDN切り替えによるヘッダーやキャッシュの挙動の変化が配信や認証に影響を与えるリスクがありました。 影響範囲を抑えるためにALBは1台ずつ切り替え、バックエンドチームにも協力してもらい機能リグレッションがないことを確認しながら進めました。 加えて、ダークカナリアリリース(ユーザーには見えない形で一部のトラフィックを新しい構成に流し、本番環境で検証する手法)でCloudFront経由に流し、環境差異による不具合がないかも検証しました。 3. VCLの処理をCloudFront Functionsへ移行 続いてVCLのリダイレクト・リライト処理を移行しました。VCLの処理の中にはリクエスト元の国別判定をした上でリダイレクトをする処理もあり、障害につながりやすい部分であったため、切り戻しやすさを考慮して2段階に分けて対応しました。 動作確認はFastlyで実施していたリダイレクト・リライト処理を網羅的に検証できるスクリプトをWebチームが作成してくれており、そちらを活用し移行前後の挙動に差異がないことを確認しながら進めました。 スクリプトはDenoのテストフレームワークで書かれており、各パスへのリクエストに対してステータスコード・リダイレクト先・レスポンスヘッダーを検証します。さらにHTMLレスポンス内のJS・CSSアセットも正常に取得できるかまで確認しており、移行後の挙動を一通りカバーしています。 また、VCLの移行はオリジンの切り替えと異なり、パスごとに挙動が細かく異なります。そのため追加のスクリプトも作成し、リダイレクト・リライト対象外のパスへの影響がないか、Serverヘッダーでオリジンが意図した経路を通っているかを、1パスずつ地道に確認していきました。 4. DNS切り替え 最後にDNSを切り替えてFastlyからCloudFrontへ完全移行しました。 WEARはDNSにAkamaiを使用しています。今回、AkamaiのChange Listを活用し、ダウンタイムなしで切り替えを実現しました。 CloudFrontのIPアドレスは固定されていないため、FastlyのIPを登録していたAレコードからCloudFrontのドメインを指定するCNAMEへの変更が必要でした。しかしAレコードとCNAMEは共存できないため、削除と追加を別々に適用すると瞬断のリスクがありました。Change ListはDNSレコードの変更をまとめてアトミックに適用できる機能で、これを活用することでダウンタイムなしでの切り替えを可能にしています。切り替えは有事の際に素早く切り戻せるよう、事前にTTLを60秒に下げた上で実施しました。 また、次のセクションで詳しく説明しますが、ブロックやBasic認証等のWAF切り替えもこのタイミングで同時に実施しています。 WAFの移行タイミング WAFはIP・国・ASNなどの判定を、送信元IPまたはX-Forwarded-For(XFF)ヘッダーのIPアドレスを基に行います。 DNS切り替え前はFastlyがリクエストを受け取るため、AWS WAFから見るとアクセス元のIPがFastlyのIPになります。この状態でWAFを先に切り替えると、ブロックルールがFastlyのIPに対して適用され、意図しないブロックや、本来ブロックすべきリクエストが通過してしまうリスクがありました。 XFFを参照することでクライアントIPに基づく判定も可能ですが、以下の理由から採用しませんでした。 Fastlyの判定ロジックが送信元IPを利用しており、仕様を変えたくなかった DNS切り替え前後でXFFの内容が変わるため、切り替えのタイミングで挙動が変わることを避けたかった そのため、DNS切り替えまでの間はFastly側のブロック設定を残し、WAFの切り替えはDNS切り替えと同時に行いました。 なお、事前の動作確認はCloudFrontのFQDNに直接curlでアクセスして行いました。DNS切り替え前はFastlyがエンドポイントのため、CloudFrontのFQDNに対してアクセスする必要があります。CloudFrontはHTTPS接続時にHostヘッダーの値でSSL証明書を選択するため、Hostヘッダーにドメインを指定することで正しく証明書検証が行えます。 CloudFront FunctionsについてもDNS切り替え前はリクエストがFastlyを経由するため、リクエスト元の国別判定で同様の問題がありました。 こちらはWebチームが解決策を検討・実装してくれました。DNS切り替え前の移行期間中に限り、Fastly側でCloudFrontの仕様に準ずるヘッダーを付与しました。CloudFront Functions側でもそのヘッダーを参照することで、正しく地域判定が行える構成にしています。 設計のポイント ここまで移行の手順を紹介しました。次に、移行にあたって設計上特に考慮が必要だったCloudFrontのビヘイビア設計について紹介します。 CloudFrontでは、リクエストのURIパターンに応じて処理方法を定義
はじめに本記事では、弊社における「開発・運用分離(Dev/Ops分離)」の取り組みについて、LINE Platformの現場メンバーへのインタビューを通じて紹介します。一般的に、Dev/Ops分離はシ...
SIerで積んできた経験は、事業会社でも通用するのか。運用やインフラ、リリース対応といったスキルは、大規模サービスの現場でどう生きるのか。キャリアを広げたいと考えたとき、多くのエンジニアが一度はこうし...
はじめに こんにちは、ファインディ株式会社でエンジニアをしている中嶋(@nakayama__bird)です。現在は、新規プロダクトであるFindy Contextの開発に携わっています。 ファインディでは、これまでSREチームが担っていた新規プロダクトのクラウド環境の構築から監視体制の整備までを、プロダクト開発チーム主体で行う体制に切り替えました。 本記事では、私自身がFindy Contextの環境立ち上げを担当した経験を、アプリケーションエンジニアの視点で振り返ります。 経験の浅いエンジニアにとって、0→1のクラウド環境構築は不安の大きい領域です。「自分にできるのか」と思いながら着手した私が前に進めたのは、「SREチームが整えてくれた仕組み」と「自分で進めた学習」、その2つが揃っていたからでした。 それぞれがどう機能したかを、これから体験ベースでお伝えします。 はじめに Findy Contextとは 背景: なぜ開発チーム主体に切り替わったのか これまでの体制 直近の事情の変化 方針転換: 開発チームメインの立ち上げへ 着手前の筆者スペック 委譲のスコープと私が担当した範囲 AWSアカウント発行とインフラ構築基盤整備 インフラリソースの構築 デプロイと運用の整備 監視(Datadog)の整備 個人として取り組んだこと 今後の伸び代 自分側 仕組み側(汎用モジュール・テンプレートへの改善余地) まとめ Findy Contextとは Findy Contextは、ファインディが2026年4月にリリースしたAI駆動開発支援プロダクトです。 プロダクト開発で日々生まれる文脈(過去のPR、関連議論、意思決定の経緯)をAIが資産化し、開発の調整コストを削減することを目指しています。 Findy Contextが目指すのは、特定の個人に依存せず、少人数でも高い開発能力を維持できる組織運営です。 あちこちに散らばっていた判断のプロセスを蓄積していくことで、組織の経験を資産として再活用できる土台を作っていきます。 findy.co.jp このFindy Contextの本番運用基盤を、なぜSREチームではなく開発チームが主体で構築することになったのか。その背景から振り返ります。 背景: なぜ開発チーム主体に切り替わったのか これまでの体制 ファインディにはSREチームが存在し、共通インフラやプラットフォームの運用、汎用モジュール・テンプレートの整備、セキュリティ・コスト最適化など、全社横断の取り組みを担っています。 インフラはTerraformでコード管理されており、SREチームが提供する汎用モジュールやテンプレートもTerraformで書かれています。オブザーバビリティの基盤としてはDatadogを採用しています。 また、既存プロダクトでは、プロダクトの一部メンバーがEmbedded SREとして、次のような役割を担っています。 アプリケーション開発に必要なインフラの整備・管理 モニタリングの整備 SLOの振り返り 一方、新規プロダクトの立ち上げに関しては運用が異なっていました。 SREチームが環境を構築し、ある程度の運用準備を整えてからプロダクト開発チームに引き渡す形が基本でした。 直近の事情の変化 ここ1〜2年で、事業成長や生成AIの追い風もあり新規プロダクトの立ち上げ件数が大きく増えたことで、これまでの体制で支えられる範囲を超えつつありました。 一方で、SREチームの人員を急に増やすことは難しく、既存プロダクトの運用や全社横断の取り組みも並行して走り続けています。新規プロダクトすべての環境構築をSREチームが担う体制は、現実的に維持できなくなりつつありました。 方針転換: 開発チームメインの立ち上げへ そこで、新規プロダクトの立ち上げ〜運用フェーズを、プロダクト開発チームが主体となり、SREチームがイネーブリングを担う体制へと切り替わりました。 これは、「開発側にオーナーシップを置く」というEmbedded SREの発想を、新規プロダクトの立ち上げフェーズにも広げた形と言えます。 SREチームが整備した汎用モジュール・テンプレート・イネーブリング体制を活用して、開発チームが自分たちの責任で立ち上げる。Findy Contextの立ち上げは、この新方針の第一弾として位置付けられたプロジェクトでした。 着手前の筆者スペック 未経験からファインディに入社し、当時は経験1年のエンジニアでした。業務の中でAWSの一部サービスを使うことはあっても、新規プロダクトの基盤として複数のサービスを組み合わせて構築した経験はありませんでした。0→1のクラウド環境構築は今回が初めてです。 そんな中、Findy ContextのEmbedded SREとして配置されることになりました。 開発チーム主体で環境構築を進める新方針のもと、その構築をメインの担当者として進めてほしいと依頼されました。 着手当初はそもそも「何から手をつければいいのか」が分からない状態でした。設計の手順も、優先順位も、判断基準が自分の中にありませんでした。 委譲のスコープと私が担当した範囲 まず、SREチームと開発チームの担当範囲を整理しておきます。 領域 SREチームが担当 開発チームが担当 AWSアカウント Organizations管理 アカウントの発行、発行後の初期設定 インフラリソース(ネットワーク・アプリケーション基盤) 共通方針、汎用モジュール・TerraformのCI/CDテンプレートの整備 汎用モジュールを使ったリソース構築(足りないものは個別作成)、CI/CD設定 監視(Datadog) Datadogの管理 ダッシュボード・モニター作成、運用 開発チームが引き受けた範囲は、SREチームが整備してくれた仕組みを使って自分たちで構築する、という形でした。 今回のFindy Contextの環境構築は、約2ヶ月をかけて、主にSREチームが事前に分解してくれた作業を順に消化する流れで進めました。ここからは、その流れを4つのフェーズに分けて振り返ります。 AWSアカウント発行とインフラ構築基盤整備 最初のフェーズは、Findy Context用のAWSアカウントを作り、Terraformで管理する基盤を整える作業でした。次のようなタスクです。 新規AWSアカウントの作成 HCP TerraformへのProject作成 Terraform用のGitHub Actionsワークフロー作成 ファインディには、SREチームが整備した初期セットアップ用のテンプレートがあり、Terraform用リポジトリの初期化はこのテンプレートで一気に進められました。 CI/CD・Docker・Trivy・TFLintなど、新規プロダクトに必要な基盤が一式揃った状態でスタートできます。 それでも詰まったのは、GitHub ActionsからAWSへのOIDC認証の設定でした。 Identity ProviderやIAM Roleの信頼ポリシーといった、IAM周りのポリシー理解が必要になります。IAMポリシーの仕組みをこれまで触る機会がなく、何をどう書けば期待通りの認証が通るのかが最初は全く分かりませんでした。 乗り越え方は、既存プロダクトのTerraformリポジトリを参考に、動いている設定を「型」として読み解くことでした。 特に初期は、SREチームの方と毎日ペアで作業する時間を持ち、レクチャーを受けたり質問を重ねたりしながら、自分の中に手順のイメージを作っていきました。 インフラリソースの構築 ここからが、Findy Contextのインフラを実際に組み立てるフェーズです。約1ヶ月かけて、次のようなリソースを順に構築していきました。 ドメイン / DNS Zone / 証明書(ACM) ネットワーク(VPC・サブネット等) コンテナ関連(ECSなど) データベース関連(RDSなど) 認証(Cognito) 配信(CloudFront) ここで強く助けられたのが、SREチームが整備した汎用モジュールです。汎用モジュールの整備については、別の記事で詳しく紹介されています。 tech.findy.co.jp ネットワーク・コンテナ・データベースなど、アプリケーション開発で標準的に必要なリソースは汎用モジュールが用意されており、変数を指定するだけで適切な構成が組み上がります。 「ベストプラクティスを毎回ゼロから調べる」必要がない状態でスタートできることは、学習コストを大きく下げる効果がありました。 それでも詰まったポイントが2つありました。 1つ目は、汎用モジュールにないリソースの作り込みやレビューです。Findy Contextでは、まだ汎用モジュール化されていないリソースを複数扱う必要がありました。 作成だけでなくレビューも、構成のイメージが頭の中にないと判断が難しく苦労した部分でした。AWS公式ドキュメントに加え、Claudeを活用して疑問に思った箇所を一つずつ理解していくアプローチで進めました。 2つ目は、リポジトリ構成とリモートステートの設計です。新規のリソースを作成する際、AWSのサービスごとに細かくstateを分割しようとしていたところ、SREチームから「リモートステートを多用しすぎない設計」をお勧めされました。 stateを分けるほど依存関係が複雑化し、apply順や terraform_remote_state の管理コストが増えるためです。アプリケーションエンジニア側のみで進めていたら気付けなかった視点で、SREチームのレビューを受けながら進められた価値を強く感じました。 デプロイと運用の整備 インフラリソースが揃ったら、次はアプリケーションをデプロイし、運用できる状態にするフェーズです。デプロイ用ワークフローの作成、Operationコンテナ環境の整備、Cognitoとアプリケーションの繋ぎこみなどを進めました。 ここで助けになったのは、既存の他プロダクトでしっかり整備されていたCI/CDフローでした。 tech.findy.co.jp デプロイの流れもOperationコンテナの構成も、基本的に「ゼロから設計する」のではなく「動いているフローを参考にする」アプローチで進められたのです。 SREチームと既存プロダクトを担当するエンジニアの両方に相談しながら、Findy Contextの構成に合わせた設計を組み立てていきました。 監視(Datadog)の整備 環境構築のクローズ後、別途取り組んだのが監視体制の整備です。Datadogでのダッシュボード作成、モニター(アラート)設定、ログ・メトリクスの収集設計、エラー検知の整備などを進めました。 SREチームが整備したDatadog基盤のおかげで、「Datadog自体をどう使うか」は迷わずに進められました。 このフェーズで一番大きな気付きは、監視や可視化はインフラ側だけでは完結しないということです。 例えばログ・エラー検知の領域です。エラーを正しく追跡できるようにするには、アプリケーション側で例外ハンドリングの設定を見直す、ログの出し方を整える、といった作業が必要でした。 「Datadogでエラーが見えない」という現象の根本原因が、Datadogの設定ではなくアプリケーションコード側にあるケースがありました。 詰まったポイントは2つです。 1つ目は、アラートの閾値とチューニングです。「何を、どの値を超えたらアラートを出すべきか」は、各サービスや時期に応じて判断が必要で、最初は誤検知が頻発したり、逆に本来検知すべき異常を見逃したりしながら調整を重ねました。 2つ目は、ダッシュボード設計とアプリ側のログ整備です。「何を載せるか、載せないか」「どんなログをどう出せば後で追跡できるか」は、メトリクスやログの選定だけでなく、アプリケーションの実装と表裏一体でした。 SREチームや既存プロダクトを参考にしつつ、Findy Contextのコードにも並行して手を入れていきました。 監視整備は、「インフラだけ知っていればよい」という認識を一番大きく崩した経験でもありました。可視化の基盤として、アプリケーション側の知識が必要だと、身をもって学んだフェーズです。 個人として取り組んだこと ここまで「仕組みが整っていた」という話を中心にしてきましたが、それだけで踏み出せたかというと、そうではありません。仕組みを活かすには、自分側でも一定の準備が必要でした。 私が並行して取り組んだのは、AWS認定ソリューションアーキテクト アソシエイト(SAA)の取得でした。 試験勉強の中ではAWS公式の「AWS Hands-on for Beginners」も活用し、書籍だけでは身につきにくい「実際にコンソールを触ってリソースを作る」感覚も合わせて身につけました。 SAAを通して、実際の業務で使うサービスだけでなく、AWSの主要サービスとアーキテクチャパターンを体系的に学べたことで、知識の幅が広がりました。 例えばコスト最適化のように、業務で直接深掘りしていない領域でも、「ここはもう一歩改善できそう」という気付きを得られる引き出しが増えた感覚があります。 「環境が整っていれば誰でも自走できる」わけではなく、整った環境を活かすための準備として、自分の側に基礎知識のストックを作ることが、踏み出す上で大きな支えになりました。 今後の伸び代 今回の構築で見えてきた、自分側と仕組み側の伸びしろを書いておきます。 自分側 複数リポジトリにまたがるサービスならではのオブザーバビリティ設計です。 Findy Contextは複数リポジトリで構成されており、1リクエストが複数サービスを跨ぐ場合の追跡や、ログ・メトリクスの相関付けには、まだ改善の余地があります。 Datadog APMなどを活用しながら、どう設計していくかは今後の課題です。 仕組み側(汎用モジュール・テンプレートへの改善余地) ここまで書いてきたとおり、SREチームが整備してくれた汎用モジュールやテンプレートには、非常に大きな価値を感じています。その上で、実際に使ってみて感じた「もう一歩テンプレート化されると嬉しい」領域も挙げておきます。 初期セットアップ(AWSアカウントの発行 → OIDC認証 → Terraform / GitHub Actionsの初期構成)の部分は、想像以上に時間がかかった領域でした。 中核のリソース構築は汎用モジュールのおかげで早く進められた一方で、初期のアカウント・認証まわりは、まだ個別対応の比率が高めだと感じました。 ここがもう一段テンプレート化されると、次に立ち上げる新規プロダクトの初動はさらにスムーズになりそうです。 まとめ Findy Contextのクラウド環境立ち上げを通して、最も強く感じたのは、「仕組み」と「個人の学習」の両方が揃って初めて、経験1年のエンジニアでも0→1に踏み出せるということでした。 仕組み側(SREチーム)が整備してくれた汎用モジュール、テンプレート、既存プロダクトの参考実装、相談しやすいサポート体制など、これらが
こんにちは!Core SRE の 織田 英吾です。 キャディはクラウドネイティブ会議にブーススポンサーとして協賛させていただきました。 この記事では2日間にわたるイベント期間中のブースの様子や聴講したセッションの感想をレポートします。 ブース紹介 まずは、キャディのブースについて紹介します! ブースでは、 私たちが取り組む製造業にまつわる課題を知っていただくための「30秒図面捜索チャレンジ」を実施したり、おすすめの Claude Code Skills を共有するボードを用意しました。 以下が実際に寄っていただいた皆さんに記載いただいたボードの写真です。おすすめ Claude Code Skills や使っている機能などをたくさん教えていただきました。Skillsを自作されている方も多くいて、コードのレビューやドキュメント作成が特に多かった印象です。私が知らなかったものも多くあり、試してみようかと思いました。 私たちが提供している「製造業AIデータプラットフォームCADDi」のアプリケーションである「製造業データ活用クラウドCADDi Drawer」のデモも行いました。 デモを通して、製造業出身者や所属の方以外にも、製造業が抱える課題に共感していただくことができました。私自身、キャディが取り組んでいることの重要さを再認識することができた機会になりました。 名古屋での開催ということもあり、製造業出身や所属の方も多く、現場の声やペインポイントを聞くことができ、私が学ばせていただく機会にもなり、とてもいい経験をすることができました。 イベント期間中、キャディのブースに立ち寄ってくださった皆さん、本当にありがとうございました! 聴講したセッションの感想 次に、私や他のメンバーが聴講させていただいたセッションの感想を簡潔にまとめます。 「100マイクロサービスのTerraform/Kubernetes管理地獄から抜け出すためのAI活用術」 株式会社LegalOn Technologies の Ishigaki さんと Wada さんによる発表です。 本セッションでは、マイクロサービスの増加に伴って Kubernetes / Terraform 管理や設定変更、レビュー対応が大きな運用負荷になるという課題が紹介されました。その解決策として、定型的な変更作業は仕様化・ツール化し、AIエージェントを活用してサービス単位で並列に展開するアプローチが示されており、とても参考になりました。 特に印象的だったのは、AI にすべてを任せるのではなく、社内ルールやガイドラインを AI が参照しやすい形に整備し、人間が最終判断を担う設計にしていた点です。これにより、大量の PR 作成やレビュー支援、移行作業を効率化しながら、運用品質を保つ工夫がなされていました。 AI 活用の前提として、業務プロセスやナレッジを構造化しておくことの重要性を感じるセッションでした。(Core SRE 織田) 登壇資料: https://speakerdeck.com/markie1009/kubernetesguan-li-di-yu-karaba-kechu-sutamenoaihuo-yong-shu 「そのSLO99.9%、本当に必要ですか? 〜優先度付きSLOによる責任共有の設計思想〜」 株式会社TopotalのVTRyoさんによる発表を聴講しました。以前、SREKaigiでもVTRyoさんの発表を聞いたことがあり、とても参考になったので今回も楽しみにしていました。 本発表では、SREチームはDevチームのEnablingを突き詰めていくと、横断で複数のサービスを見ていくことが増えていくことで、運用しているサービスで何が起きているのか分からなくなる可能性が高くなりやすいと話されてました。実際に、VTRyoさんは自分たちはSREとしてサービスに価値を生み出していないのではないかと葛藤が生じたそうです。 そこで紹介されていたのが Product-Focused Reliability for SRE (PER)という考え方でした。自分は初めて聞いたのですが、SREチームが限られたリソースを有効活用するために、ビジネスやユーザーにとって最も重要なことに焦点を当て、優先順位をつけて活動することだそうです。 確かに、従来のSREはインフラやサービスそのものの安定稼働に責務を持つため、自然とDevはアプリケーション、SREはインフラという得意領域で境界が分かれてしまいやすいです。 PERの考え方は、SREの関心ごとをサービスからプロダクトの重要機能に移し、プロダクト中心にSLOを考えていくため、よりユーザーやビジネスの理解が重要になってきます。自分自身、SREが信頼性という指標を追っているのはユーザーの満足度とビジネスの成功度を上げるためだと思っているので、PERの考え方はとてもしっくりきました。 また、SREは日々のアラート対応に追われがちではありますが、それは本当にユーザーの離脱や解約につながっているのか?実は過剰な信頼性を追っているだけで、根本的な問題は機能不足なのではないか?、本セッションの問いとして投げかけられていた「そのSLO99.9%は本当に必要なのか」は定期的にSLOを見直す際に立ち返りたい問いだなと思いました。(Drawer SRE 松嶋) 登壇資料:https://speakerdeck.com/vtryo/is-that-99-dot-9-percent-slo-really-necessary-design-philosophy-of-shared-responsibility-through-prioritized-slos 登壇レポート キャディからCore SREチームリーダーの小林明斗が登壇しました。 セッション名:生成AI時代に信頼性をどう保ち続けるか - Policy as Codeの実践 概要:生成AIによりコードやIaCの変更量が増える中、人手のレビューやチェックリストだけでは信頼性リスクを見落としやすくなっています。本セッションで は、組織ポリシーのうち静的に評価できる項目をPolicy as CodeとしてCIや実環境に組み込み、人の認知に依存しすぎず信頼性を維持する設計を紹介し、ConftestやKyvernoの実装例を交え、誤検知対応、ポリシー強度、既存リソース是正など、形骸化しないガードレール運用の工夫を共有しました。 開発組織の変遷や SRE の歩みから入り Policy as Code の紹介、実装方法の共有がされていました。私はまだ入社3か月目なのもあり、キャディの私でも学べるセッションでした。 会場の席はほぼ埋まり、立ち見がでるほどの盛況ぶりで約100名の方が参加されていました! SNSでも「Policy as Code が気になっている」、「PRC のレビューが大変なのわかる」、「Policy 違反1,684件すべて修正完了はすごい」などたくさんの反響をいただきました。キャディから登壇者を出せてとても良かったです。 Policy 違反の Report に「Policy の詳細や OK/NG の例が記載された wiki」へのリンクを入れているらしい。これを見ることで Policy 違反が発生した時の開発者側での修正をやりやすくしてる感じっぽい。よさそう。#cloudnativekaigi #cloudnativekaigi_b— こたつ&&みかん (@kota2and3kan) 2026年5月15日 x.com 1684件すべて修正完了すごすぎる#cloudnativekaigi #cloudnativekaigi_b— yuki / ほにゃにゃ (@honyanyas) 2026年5月15日 x.com うおー、ポリシーに対するテストコード書けるのめっちゃいい。#cloudnativekaigi #cloudnativekaigi_b— たか (@_nogtk_) 2026年5月15日 x.com 登壇資料: speakerdeck.com 終わりに ブースへの来場・セッション聴講・懇親会と充実した2日間でした。クラウドネイティブな技術の進化は非常に速いですが、今回学んだ知見を業務に活かしていきたいと思います。 スピーカーの皆さん、参加者の皆さん、クラウドネイティブ会議の運営の皆さん、素晴らしいイベントをありがとうございました。今後もキャディは、技術的な挑戦や課題解決の知見を発信し、エンジニアコミュニティへの貢献を続けていきます。今後のイベントでもお会いできるのを楽しみにしています!
はじめに こんにちは、BASE株式会社 Product Governance(通称:プロガバ)チームの緒方です。 突然ですが、皆さんは「IT統制」や「ガバナンス」と聞いてどんなイメージを持ちますか? 「開発スピードが落ちそう」「お堅い事務作業」……そんな風に思われがちなこの領域ですが、我々は少し違います。 今回は、私たちがどのような思いで「守り」を「攻め」に変える仕組み作りをしているのか、その裏側をお話しします。転職を考えている方も、そうでない方も、新しいキャリアの形としてご一読いただければ幸いです。 3年半で激変した、BASEのIT統制を取り巻く環境 前回、IT統制についてのブログを投稿させていただいてから早くも3年半が経過しました。この間、BASEグループは目まぐるしい変化を遂げています。 2024年のwant.jp、2025年のEストアー、そして2026年のPortと、次々と新たな仲間がグループに加わりました。これだけでも大きな変化ですが、同時に「PAY.JP」や「YELL BANK」といった既存プロダクトも右肩上がりで業績を拡大しています。 (「事業計画及び成長可能性に関する事項」より抜粋) 事業が広がり、複雑さが増せば、当然ながらJ-SOX(内部統制報告制度)における評価対象範囲も年々広がっていきます。 「異なる文化やシステムを持つ組織でどこまで共通の整備を行っていくべきか?」 「急成長する決済・金融事業の信頼性をどう担保するか?」 BASEの行動指針である「Move Fast」を維持しながら、上場企業としての「信頼性」を鉄壁なものにする。この難易度の高いミッションに、かつてないほどやりがいと魅力を感じつつ、日々取り組んでいます。 結局、プロガバって何をする人? 私たちの役割を一言で言えば、「プロダクトの成長を支える、ガバナンスの設計者」です。 大きく2つのミッションがあります。 開発現場と監査の「最高の通訳」になる 監査法人や内部監査室が求める「守り」の基準と、エンジニアが求める「開発の自由度」。この一見相反する両者の間に立ち、エンジニアリングの知識をフル活用して「これなら現場も納得できるし、統制も取れる」という最適解を導き出します。 ガバナンスを「仕組み」で解決する(Engineering for Governance) プロダクト開発のスピード、業務スピードを落とさないためにも「手作業でチェックして承認をもらう」ような面倒なプロセスは徹底的に排除します。以下は主な具体例です。 GitHub Actions / GAS / Slack App を駆使した証跡取得の自動化 AI(LLM)を活用したシステムログチェックの効率化 非エンジニア部門に対する内部監査室と連携した業務フローの再構築やシステム化の提案 「ルールを押し付けるのではなく、ルールを意識しなくて済む仕組みを作る」ことを目指しています。 ここで働くからこそ得られる「プロガバの魅力」 IT×会計・監査のハイブリッドキャリア 技術スタック(AWS, GitHub, SQL等)を維持しながら、ビジネスの根幹である経理・法務・監査の深い知識が身につきます。この両方の言語を話せる人材は市場価値が極めて高く、エンジニアとしてのバックグラウンドを活かした「唯一無二の専門性」を築くことができます。 グループ全体を俯瞰する「鳥の目」が手に入る 特定の機能開発や一つのプロダクトにとどまらず、グループ各社の事業成長や組織構造を横断的に見渡すことができます。経営層に近い視点で「組織がどう動いているか」を肌で感じ、プロダクトを俯瞰して捉えられるのは、このポジションならではの醍醐味です。 AI時代だからこそ光る、代替不可能な「人間力」 昨今、多くの業務がAIに置き換わろうとしていますが、プロガバの仕事の核は「正解のない問いに、人間としての納得感を持って答えを出すこと」にあります。 ルールを自動化する仕組みはAIで作れるかもしれませんが、「このリスクをどう解釈し、開発現場の熱量を削がずにどう落とし込むか?」という高度な判断や、ステークホルダーとの泥臭い調整は、人間にしかできません。ここで得られる経験やビジネススキルは、AIには当面取って代わられない、一生モノの武器になると確信しています。 おわりに 現在、BASEグループではこの「Product Governance(プロガバ)」を一緒に作り上げていく仲間を強く求めています。 「ガバナンスの経験はないけれど、技術を使って組織を良くすることに興味がある」 「エンジニアとしての経験を、より経営に近い領域で活かしてみたい」 そんな思いをお持ちの方、まずは難しく考えず、カジュアルにお話ししてみませんか? 👉[Product Governance の求人詳細・応募はこちら] open.talentio.com また、プロガバ以外にも、BASEグループではさまざまな職種・ポジションで募集を行っています。 「BASEという会社そのものに興味が湧いた」という方も、ぜひ一度採用ページを覗いてみてください。 👉 [BASE株式会社 採用サイト] binc.jp
製造業データ活用クラウド CADDi Drawerで SREを担当している佐藤です。 組織編成でCADDi Drawer専任のSREになったことを機に、長らく後回しにしていたDatadog MonitorのTerraform構成の再設計に取り組みました。複数チームの監視設定が単一リソースに混在しOwnershipが不明確なため、各チームが自律的に改善に踏み出せない状態でした。 本記事ではこの課題をOwnership単位のモジュール設計で解消した経緯と学びについて紹介します。 監視設定が抱える課題 単一リソースに積み上がった監視設定 Ownershipの不在によるアラート改善の停滞 Ownershipを明確化したモジュール設計 監視ロジックとOwner情報の分離 Owner情報の一元管理 ふりかえり よかったこと 苦労したこと 移行の現状と今後 おわりに 監視設定が抱える課題 キャディでは監視基盤にDatadogを採用しており、Datadog Monitorはすべて監視用の単一リポジトリでTerraform管理しています。 歴史的経緯により、監視すべき内容については検討できていましたが、Terraformモジュールとしてどう実装するかは整理されていませんでした。 このためDatadog MonitorのTerraform実装には以下のような課題を抱えていました。 単一リソースに積み上がった監視設定 Terraformモジュールの設計が難しい問題です。適切な抽象化の粒度を見極め、再利用可能な形でモジュール化するには、対象サービスや組織の全体像が見えていることが前提になります。 組織・サービスが急成長していた時期には監視対象が次々と増え、その都度設計を見直す余裕はありませんでした。この結果、再利用可能な形でのモジュール化は採用せず、既存の設定を拡張する形で対応し続けていました。 具体的には、監視対象のサービスが増えるたびに新規のモジュールやモニターを構築することはせず、既存の単一のDatadog Monitorに対象サービスを追加し続けていました。 Terraformのdatadog_monitorリソースでは監視条件をquery属性の文字列として定義しますが、サービスが増えるたびにOR条件を追記し続けた結果、以下のような状態になっていました。 logs( "status:error (service:SERVICE_A OR (service:SERVICE_B -message:\"noisy message\") OR service:SERVICE_C OR service:SERVICE_D OR (service:SERVICE_E env:production))" ).index("*").rollup("count").last("5m") > 100 組織規模が小さく、サービス数が2-3個の場合やサービス固有の除外条件が含まれていない場合にはこれでも上手くいきました。 しかし、サービス数が増え、サービスごとの監視条件が混在するようになると、とたんにコードからどのサービスがどの条件を持つのか読み解くことが困難な状態になりました。 また、問題の兆候は認識していましたが、全サービスに影響する監視設定の一括整備に踏み切るきっかけがなく、他のタスクを優先していました。 Ownershipの不在によるアラート改善の停滞 この課題に向き合うきっかけになったのが2025年に実施した組織編成です。 それまで全社横断でインフラ運用を担っていたSREチームから、サービス・プロダクトごとにSREが配置される形になりました。 私はCADDi Drawer専任のSREとして新たな役割を担うことになりました。 CADDi Drawerの信頼性向上に絞った視点で改めて監視設定を見ると、Datadog Monitorの複雑化に関する問題が浮き彫りになりました。 複数サービスの設定が混在したクエリへの変更は、git diff や terraform planでも影響範囲が掴みにくい状態でした。 Ownerの異なるチームが同じリソースを共有しているため、「他チームに影響しないか」という懸念が変更の腰を重くし、細かな改善がどんどん後回しになっていました。 クエリ実装の複雑さ以上に懸念になっていたのがSlackのアラート通知先の混在でした。 Datadog MonitorのSlack通知チャンネルの動的切り替えはクエリ条件以上に複雑になるため、Datadog Monitorごとに単一のSlackチャンネルに通知していました。 その結果、複数チームのアラートが同じSlackチャンネルに通知され、関係のないアラートであっても内容を都度確認する割り込みコストが発生していました。 SREチームで議論した結果、これらの問題の根本は、どのチームがどのモニターに責任を持つかが不明確というOwnershipの欠如にあると判断しました。 複数チームのサービスが1つのdatadog_monitorリソースを共有しているため、変更が他チームに影響するかどうかを確認せずに修正できません。 自チームの判断だけで変更に踏み切れず、誰も主体的に手を入れられない状態になっていました。 Ownershipを明確化したモジュール設計 これらの課題を解決し、Datadog MonitorのOwnershipを明確化するため、監視項目のTerraformモジュールを設計して移行しました。 監視ロジックとOwner情報の分離 従来の構成では、通知先や担当チームといったOwner情報がモジュール内に直接書き込まれていました。Owner情報が埋め込まれた構造では、呼び出す側がチームごとの情報を渡す設計にはできません。 具体的には以下のような構成でした。 terraform/ ├── modules/ │ └── monitor/ │ └── shared_monitors/ # 全サービス・全チームの監視設定が混在 └── environments/ ├── <google_project>-development/ │ └── main.tf # shared_monitorsモジュールを呼び出す └── <google_project>-production/ └── main.tf # shared_monitorsモジュールを呼び出す terraform/environments/<environment>/main.tf が shared_monitors モジュールを呼び出す構成です。このモジュールに全サービス・全チームの監視設定が集中していたことが、前述の問題の原因でした。 そこでこのモジュールを解体し、監視ロジックの情報に限定したモジュールを作成した上で対象サービス・通知先・担当チームといったOwner情報は呼び出し元から変数として渡す設計にしました。 具体的には以下のような構成です。 terraform/ ├── main.tf # environments をモジュールとして呼び出す ├── teams.tf # チーム情報を一元定義 ├── modules/ │ └── common/ │ ├── cloudrun-service/ # Cloud Run監視モジュール │ ├── alloydb/ # AlloyDB監視モジュール │ └── ... └── environments/ └── <google_project>-production/ └── service-a/ ├── locals.tf # 通知先・Owner情報を定義 └── main.tf # 各監視モジュールをOwner情報と共に呼び出す 監視項目の種類( cloudrun-service など)ごとにモジュールを定義し、 terraform/environments/<environment>/<service>/ というサービス単位のディレクトリから呼び出す構成です。 モジュール自体は監視対象のプロダクトを知らず、呼び出すディレクトリごとにOwnershipが決まるため、「誰がどのアラートに責任を持つか」がディレクトリ構造として表れます。 各モジュールに対するOwner情報を変数として以下のように設定することで、サービスごと・モニターごとのOwnershipを明確化できました。 # environments/project-production/service-a/main.tf module "cloudrun_service_monitor" { source = "../../../modules/common/cloudrun-service" service_name = "service-a" env = "production" owner_teams = local.owner_teams mention_slack_alert_ch = local.mention_slack_alert_ch mention_oncall = local.mention_oncall } この設計により、変更の影響範囲が自チームのスコープに収まり、他チームを巻き込まずに監視設定を改善できるようになります。移行後はこの設計方針をドキュメントにまとめ、社内で共有しました。設計を標準として明文化することで、チーム間の設計認識を統一し、今後の監視設定追加・変更にも一貫したOwnership管理を維持できるようにしました。 Owner情報の一元管理 監視対象ごとにDatadog Monitorが分割されることで、移行後はモニターの総数が増えます。多数のモニターを管理するにはタグ情報が欠かせないため、各Datadog Monitorへのタグ付けを徹底しました。 タグ自体は従来から設計に組み込まれていましたが、今回の移行を機に全Datadog Monitorへの適用を強化しました。 タグ情報の適用を強化する上で、通知先Slackチャンネル、担当チーム名といったOwner情報を各サービスの設定に個別に書き込むと、組織変更のたびに全サービスのファイルを修正する必要があります。また、複数の場所に分散して書くとチーム名やSlackメンションの表記ゆれも起きやすくなります。 そこで、チーム情報を teams.tf に一元定義しました。Datadog Teamリソースもここで合わせて作成します。 # teams.tf locals { owner_teams = { team_alpha = { name = "Dept A > Group A > Team Alpha" mention = "<!subteam^XXXXX>" # @team-alpha tag = "team:team_alpha" } team_beta = { name = "Dept B > Group B > Team Beta" mention = "<!subteam^YYYYY>" # @team-beta tag = "team:team_beta" } } } resource "datadog_team" "teams" { for_each = local.owner_teams name = each.value.name handle = each.key } 組織変更が起きた場合も teams.tf の修正だけで全サービスに反映されます。 ふりかえり よかったこと サービスごとに通知先を変更できるようになったことがメリットとしては大きいです。 チームごとにSlackチャンネルを切り分け、CADDi Drawer 関連のアラートのみに集中できる環境を整えたことで、業務への割り込みを最小限に抑えることが可能になりました。 datadog_monitor がOwnerごとに分割されたことで、PRレビューの負荷も下がり、自チームの判断だけで変更をマージできるようになりました。 モジュール化に伴い監視条件を変数として宣言的に記述する構成になったことで、各サービスの監視条件を個別に把握できるようになった点も嬉しいポイントです。 苦労したこと 最も苦労したのはDatadog Monitor移行に伴うPRレビューです。Datadog Monitorリソースの分割には destroy / create が伴う上、クエリの整理も必要だったため、大量の terraform plan の差分が発生しました。これをすべて人手で確認しきることは難しく、想定以上にレビューに時間がかかってしまいました。 今回は利用できませんでしたが、AIを活用したリソース移行の半自動化や設定差分の検出など、効率化の仕組みを今後の大規模移行に活かしたいと考えています。 移行の現状と今後 作業リソースやスケジュールの都合上、通知頻度の高いコンポーネントの移行を優先して完了させました。旧モジュールへの新規追加を禁止することでこれ以上の複雑化を防ぎつつ、通知先のチャンネル分離も継続して進めています。一方、一部のDatadog Monitorはまだ解体する必要があるものが残っており、引き続き整備を進める予定です。 設計方針はドキュメントとして共有しているものの、まだ十分に浸透できているとは言えません。設計の定着にはドキュメントだけでなく、ポリシーチェックをCIに組み込むような仕組みも必要だと感じています。今後は組織横断チームと連携しながら検討を進めていきます。 おわりに 組織の自律性を高める上で、監視設定のOwnershipも合わせて整備することが重要だと実感しました。Ownershipが明確であれば、自チームの判断だけで監視設定を改善でき、変更のスピードも上がります。 今回は組織編成がきっかけとなり、長らく後回しにしていた構成の再設計に取り組むことができました。 普段から監視設定を気軽に修正できる状態を維持し、組織や役割が変化した際には監視のOwnershipが適切に機能しているかを継続的に見直していきます。
はじめにこんにちは。Service ReliabilityチームでSRE(Site Reliability Engineer)として働いているKi Cheol Cheonです。SREチームは、ユーザー...
こんにちは、SRE Team の大野です。製造業データ活用クラウド CADDi Drawer の Product SRE として、サービスの信頼性の向上や、効率的な運用を目指す取り組みを行っています。 今回は Cloud Run ワークロードの OOM 対応に Datadog Continuous Profiler を導入した際の、ハマりどころや学びについてのお話をします。 TL;DR はじめに Cloud Run での Profiler 選定 導入時のハードル 公式 Wrapper が使えない Profiler 単体で動かすための環境変数 プロファイリング結果の分析 プロダクト理解から見えた掛け算の構造 複数の観点で判断する 改善提案のまとめ方 Claude Code を活用した横断的調査 おわりに TL;DR Cloud Run ワークロードの OOM 調査にて Datadog Continuous Profiler を利用、Cloud Run 特有の導入ハードルや環境変数の罠があった Compare 機能でスパイク時のメモリ増分を特定し、複数要因の掛け算で消費が増大する構造を解明 問題指摘で終わらず、レイヤーごとの改善提案を開発チームに共有して意思決定を支援 はじめに CADDi Drawer は、図面や仕様・不具合情報といった複数種別のデータから、関連性や類似性をもとに検索・集約する機能を持っています。 先日、その処理を担う Cloud Run ワークロードが頻繁に OOM を起こすようになり、ユーザー体験に影響が出る程度のパフォーマンス劣化を引き起こしていました。ユーザー影響を抑えるためにひとまずメモリ増強によって延命したものの、根本原因は掴めておらず、再発リスクの見通しも立っていない状況でした。 原因を突き止めるためにメモリプロファイリングの手法を検討した結果、Datadog Continuous Profiler を試験的に導入してメモリ消費構造を可視化することにしました。Profiler の結果と他の観点での分析を組み合わせて問題構造を紐解き、具体的な改善提案にまとめることができました。現在は改善が実施され、問題は解消しています。 Cloud Run での Profiler 選定 Cloud Run でメモリプロファイリングをやろうとすると、案外「これだ」と言える整った手法がないことに気づきます。取りうる手段をミニマムに整理しました。 🤔 Google Cloud Profiler: Google Cloud ネイティブだが、そもそも Cloud Run がサポート対象外 🤔 自前での heap dump: Node.js の場合はヒープスナップショット取得中にリクエスト処理が停止するため、本番環境での実施はリスクが高い 💡 Datadog Continuous Profiler: 全社的に Datadog を利用しており親和性が高い、2026 年 3 月時点ではプレビュー扱いで追加費用なく試せた、APM なしで Profiler 単体で動作する 消去法的ではありますが、スピード重視で Datadog Continuous Profiler の導入を決めました。 導入時のハードル いざ決まったものの、Cloud Run への導入は一筋縄ではいきませんでした。 公式 Wrapper が使えない Datadog は Cloud Run 向けに Terraform Wrapper モジュール を提供しており、これを使えば簡単に導入できるはずでした。しかし 2 つの壁にぶつかります。 1 つ目は Cloud Run API バージョンの問題です。 Wrapper モジュールは Cloud Run API v2 を前提としていますが、対象ワークロードは API v1 で管理されていました。Google Cloud の移行手引き を参考に v1 → v2 への Terraform import で解消しましたが、API バージョン間でパラメータ記法の変更(annotation からの移行など)があり、単純な import だけでは済みません。意図しない差分からリソースの再作成を引き起こすとサービス断に直結するため、plan 結果を慎重に確認しながら進める必要がありました。 2 つ目は CI/CD フローとの衝突です。 このワークロードでは、Terraform の管理外で GitHub Actions がイメージタグを書き換えることでデプロイしていました。このため Terraform 側では lifecycle { ignore_changes } で image の変更を無視する必要があります。しかし利用時点の Wrapper モジュールではその設定ができず、apply のたびに意図しないイメージの巻き戻しが発生してしまいます。 この時点で Wrapper モジュールの利用を断念し、serverless-init をサイドカーコンテナとして直接組み込む方式に切り替えました。この構成では、アプリケーションコンテナ側で dd-trace ライブラリがプロファイルデータを収集し、サイドカーの serverless-init が Datadog Agent として Datadog へ中継する形になります。 Profiler 単体で動かすための環境変数 弊社ではトレーシングに OpenTelemetry 経由で Google Cloud Trace を利用しているため、Datadog APM や Tracing は使わずに Profiler だけを有効化したいという状況でした。 しかし dd-trace にはトレーシング関連の環境変数が DD_TRACE_ENABLED, DD_TRACING_ENABLED, DD_APM_TRACING_ENABLED と似た名前で複数存在します。たとえば DD_TRACE_ENABLED=false は dd-trace ライブラリ全体を停止するため Profiler も止まってしまいます。 今回は組み合わせを検証する時間が限られていたため、なるべく Trace を送信せず APM も止める方向で安全側に倒すよう、下記で設定していました。 # アプリケーションコンテナ(dd-trace を init() する側) DD_PROFILING_ENABLED=true # Continuous Profiler を有効化 DD_APM_TRACING_ENABLED=false # Agent → Datadog APM への転送を無効化 DD_TRACE_SAMPLE_RATE=0 # トレースのサンプリングを 0% に # serverless-init サイドカー(Datadog Agent 側) DD_PROFILING_ENABLED=true # Profiler データの受け付けを有効化 DD_APM_TRACING_ENABLED=false # Agent → Datadog APM への転送を無効化 # DD_ENV, DD_SERVICE 等のユニバーサルサービスタグは両コンテナに必要(後述の公式ドキュメント参照) 詳細はライブラリやイメージのバージョンによって挙動が変わりうるため、公式ドキュメント(共通設定)および Node.js 固有の設定 を参照のうえ、実環境での検証を組み合わせて確認することをおすすめします。 プロファイリング結果の分析 Profiler が動き始めると、公式ドキュメント にもある通り、ある時点でのメモリ内訳確認や Insight によるボトルネック候補の自動提示など、いくつかの分析手法が使えるようになります。 今回特に効いたのが Compare 機能です。平常時とメモリスパイク発生時の Heap Live Size を並べることで、単一時点では区別がつきにくいスパイクの原因が明確になります。 冒頭で触れた通り、対象のワークロードは複数種別のデータから関連性や類似性をもとに検索・集約する処理を担っており、そのためのメモリ消費が中心になっています。 実際に比較すると、スパイク時にしか出現しない関数(Compare では「Only in B」と表示)が見つかりました。図ではマスクしていますが、特定条件下での検索クエリ構築やレスポンス保持の関数群にヒープ増分が集中していることが、画面から読み取れるようになっています。 Datadog Profiler 実画面イメージ この結果から、「常にメモリを多く使っている処理」ではなく「特定条件下でメモリを大量消費する処理」が原因であるという仮説が立ちました。 プロダクト理解から見えた掛け算の構造 Profiler の結果はあくまで「何がメモリを消費しているか」を示すだけで、「なぜそうなるのか」「どう対処すべきか」は別の分析が必要です。該当する機能がどのようなものでどう使われているのかを、仕様書や機能説明資料、直近の開発プロジェクトの動向から読み解きました。 CADDi Drawer は、顧客ごとに異なる課題を幅広く解決するデータプラットフォーム上でデータの探索・分析を担うプロダクトです。大半の利用シーンではメモリ消費も穏やかに収まりますが、特定の利用パターンでは画面表示のための検索エンジンへのクエリが、非常に複雑になることがあります。 まさに今回はこの事象を踏んでおり、リクエストあたりの処理量が複数の要因の掛け算で増大するケースに該当していました。個々の要因は単体では大きな問題にならないものの、これらが重なった際にメモリ消費が一気に跳ね上がります。 Profiler の Compare で特定の処理にヒープ増分が集中していたのも、この掛け算構造がスパイク時に顕在化した結果です。 複数の観点で判断する 掛け算構造が見えてくると、Profiler の結果だけで判断するのは危ういことに気づきます。チームメンバーからのフィードバックも踏まえ、以下の観点を加えました。 リクエストパターン: 同時かつ並列でリクエストが集中していないか 単発では問題にならないメモリ消費も、並列度が上がると一気にスパイクに繋がる リードタイム: 処理がどれくらい長引くか 長時間メモリを保持し続けることで、GC が追いつかずメモリ消費が積み上がるケースがある 発生頻度: 頻繁に起こるのか、稀なのか 稀であっても、データ量の増加やリクエストパターンの変化で「いつ爆発してもおかしくない」構造なのかを見極める必要がある 改善提案のまとめ方 問題構造が見えたら、開発チームが動きやすい形で提案をまとめる必要があります。今回は以下のように整理しました。 レイヤーごとに対策を分ける フロントエンド・バックエンド・API スキーマ・インフラなど 掛け算構造のすべての要因に単一の対策では対処できないため 今回の例では、API スキーマへの入力制約の追加や、検索エンジンへのクエリ構造の改善といった対策を提案 実現しやすさ順に並べる まず実態把握、次にすぐ効果が出る対策 構造的な改善は並行して検討 プロダクト判断が必要な境界を明示する 仕様の見直しを伴う対策は SRE だけでは判断できない どの対策が技術的に閉じるもので、どの対策が PdM や開発チームとの議論を要するのかを切り分けることで、関係者ごとに次のアクションが見えやすくなる 「ここが問題です」で終わるのと「こういう構造なので、この順で対処するのがよさそうです」まで持っていくのとでは、開発チームへの引き継ぎのスムーズさが変わります。 Claude Code を活用した横断的調査 今回の調査では Claude Code も活用しました。大規模かつ変化が激しいシステムでは、コードベース全体を頭に入れるのは現実的ではありません。複数リポジトリにまたがるコードフロー(ロジックの流れ、リクエストの経路、コンポーネント間の繋がり、インフラ設定)を横断的に追う場面で非常に重宝しました。 既に一般的になりつつありますが、工夫していた点をいくつか挙げます。 MCP 経由で調査対象を広げる GitHub のソースコードだけでなく、Datadog のログやメトリクスにも MCP Server 経由でアクセス コードの静的な構造とランタイムの挙動を突き合わせる調査を、コンテキストを切り替えずに一連の流れで実行できた subagent でコンテキストウィンドウを節約 フロントエンド・バックエンド・検索サービスのリポジトリ群を横断する必要があり、各コンポーネントの詳細調査を subagent に委譲 メインのコンテキストウィンドウには要約だけが残るので、Compacting Conversation の頻度を減らすことが出来た ドキュメントの下書きに使う 問題を構造的に説明しようとすると、ファクトを並べるだけでも文量が膨らむ 下書きを作らせたうえで、自分の理解と照らし合わせながら論点を絞り込んだ おわりに Cloud Run ワークロードに Continuous Profiler を導入し、OOM の根本原因を特定して改善提案に繋げた一連の流れを紹介しました。 振り返ると、重要だったのは以下の 3 点です。 ツールを入れて終わりにしない Profiler の出力はあくまで出発点であり、そこからプロダクトの仕様・使われ方・リクエストパターンを重ね合わせて、問題の構造を明らかにする 総合的に判断する プロファイリング結果だけでなく、頻度・リードタイム・並列度といった実利用での観点を加えて、対処の優先度を見極める 改善提案まで持っていく 問題を指摘するだけでなく、具体的な対策案と優先度の根拠を示して、開発チーム全体の意思決定を支援する 「ボトルネックを見つけて改善する」という行為は一見シンプルですが、そこに至るまでの選定・導入・分析・提案のそれぞれに SRE としての判断と工夫が求められます。同様の課題に取り組む方にとって、本記事が何かしらの参考になれば嬉しいです。 キャディの Product SRE は、インフラの運用に閉じることなく、プロダクトの仕様理解から改善提案まで踏み込める環境にあります。今回のようにプロファイリングで得た技術的なファクトを起点に、開発チームや PdM と協働しながらプロダクト全体の信頼性を高めていく働き方に興味がある方は、ぜひ キャディの Tech 採用ページ をご覧ください! また、キャディは 5 月 14 日(木)から 5 月 15 日(金)の 2 日間、名古屋で開催されるクラウドネイティブ会議にブーススポンサーとして協賛します! 当日は SRE や Platform Engineering に関わるメンバーを中心に 7 名が現地の会場を訪れる予定です。 参加される方は、ぜひ会場のブースにも遊びにお越しいただけると嬉しいです。 caddi.tech
こんにちは!CADDi Drawer SREの松嶋です。 キャディは、5月14日(木)から5月15日(金)の2日間、名古屋で開催されるクラウドネイティブ会議にブーススポンサーとして協賛します! 当日は弊社のSREやPlatform Engineeringに関わるメンバーを中心に7名が現地の会場を訪れる予定です。 会場でお会いできるのを楽しみにしていますので、ぜひ弊社のブースに足を運んでいただけると嬉しいです。 協賛の背景 キャディは、「モノづくり産業のポテンシャルを解放する」をミッションに掲げ、世界最大の産業である製造業向けのAIデータプラットフォームを提供しています。 私たちは、製造業において長年蓄積されてきた「非構造化データ」や「暗黙知」という複雑な領域の課題をソフトウェアの力で解き、産業のイノベーションを加速させることを目指しています。 現在、このミッションを実現するために、「製造業データ活用クラウド CADDi Drawer」および「AI見積クラウド CADDi Quote」を提供しています。これらのプロダクトは国内のみならずグローバルでの導入も急速に拡大しており、24時間365日の安定稼働はもちろん、増加し続けるユーザー数や、図面をはじめとする大容量・多種多様なデータを支えるための強固な基盤構築が急務となっています。 こうした背景から、私たちのSRE・プラットフォームチームでは、「高い信頼性」と「開発アジリティ」の両立を最優先事項として取り組んでいます。 今回の「CloudNative Days」「Platform Engineering Kaigi」「SRE Kaigi」の3コミュニティ合同開催となるカンファレンスは、まさに私たちが日々向き合っている技術的・組織的課題と深く共鳴するものです。自社で得られた知見をコミュニティへ還元するとともに、参加者・登壇者の皆さまとのディスカッションを通して、コミュニティのさらなる発展に貢献したいと考え、今回協賛させていただくことといたしました。 SREチームリーダーの小林が登壇します! 弊社からはCentral SREのチームリーダーである小林(@akitok_)がDay2の11:10より「生成AI時代に信頼性をどう保ち続けるか - Policy as Codeの実践」というタイトルで登壇します。 当日の発表資料は、後日公開予定です。 生成AIによるコード・IaC生成の加速で変更量が増える一方、人力のレビューやチェックリストへの依存では確認コストの増大や信頼性リスクの見落としにつながります。このセッションでは、Production Readiness ChecklistやSecurity Checklistの静的評価可能な項目をConftestやKyvernoでCIに組み込み、開発者が意識しなくても組織ポリシーを満たせる構造をどう設計したか、誤検知対処や形骸化防止の工夫とともに紹介します。 ブースでは製造業において最重要な「図面」にまつわるクイズ企画を実施します! 今回、モノづくりの地である名古屋で開催されることに合わせ、キャディのブースでは、製造業において最も重要かつ複雑な「図面データ」をテーマにしたクイズを用意しております。私たちのプロダクトを通して向き合っている製造現場の課題を、クイズを通してぜひ楽しみながら体験してください。 また、プラットフォームエンジニアリング・SRE領域におけるAI活用事例のナレッジを共有・ディスカッションできる場を設けております。 ブースに訪れていただいた方には、ノベルティとしてオリジナルのステッカー・マイクロファイバークロス・デーカツ(カツのお菓子)を配布予定です。 イベントで人気のデータ活用カツ また、5月15日(金)の12:00〜13:30頃には、セッション登壇者の小林がブースに常駐しておりますので、登壇内容について質問がある方、ディスカッションしたい方、ゆるく雑談したい方など、ぜひお気軽にブースにお越しください! さいごに キャディでは、今回初めての協賛となります。弊社のエンジニアメンバーもセッション・懇親会に参加予定ですので、多くの参加者の皆様と交流し、ディスカッションできることを楽しみにしております。 私自身、久しぶりにゆかりのある名古屋に訪れることができるので嬉しく思います。 また、魅力的なセッションがとても多く、どのセッションを聴講するか悩ましいですが、特に「サンプリングは「作る」のか「使う」のか?分散トレースのコストと運用を両立する実践的戦略」、「実践AI SRE — AIワークロードの自律的パフォーマンスエンジニアリング」のセッションが個人的に楽しみです。 では、現地でお会いできるのを楽しみにしております。よろしくお願いいたします! キャディのSRE/Platform Engineering関連の記事 caddi.tech caddi.tech caddi.tech
こんにちは、 id:sezemi です。 実は昨年度 2025 年 4 月~ 2026 年 3 月まで自宅のマンションの理事をしていました。 そこで区分所有法の変更などがあり規約の改定をする必要があったのですが、 NotebookLM がいい感じに変更案を作ってくれて承認されました。 仕事だけでなくプライベートも AI 漬けです。 そして、アンドパッドも AI ネイティブなプロダクト "ANDPAD ナレッジ AI" をリリースしております。 業界特化の膨大なデータを蓄積してきたからこそのプロダクトなので、ぜひご期待ください。 ai.andpad.co.jp さて、アンドパッドでは、技術やプロダクト開発、組織に関するさまざまなカンファレンス・イベントでの登壇、開催や会場提供などを行っています。 今月もイベント情報をまとめてお知らせします。 ぜひご参加ください !! なお、開催状況により、満員となってしまっている場合、すでに受付を終了している場合がございます。 1. 登壇情報 | Mobile Mob #1 〜スマホのハードウェア制御の悩み・工夫を語り合う〜 開催日時 : 2026年5月12日(火) 19:15 ~ 21:15 会場 : Sansan株式会社 本社 イベントスペース 主催 : 株式会社ビットキー イベント概要 : モバイルアプリ開発者が現場で直面する悩みと工夫を、みんなでカジュアルに語り合う勉強会です。 第 1 回となる今回のテーマは「スマホのハードウェア制御」です。本イベントには、アンドパッドから 平池 拓也 が「Android アプリから 360 度カメラ「RICOH THETA」に接続して写真を撮影する」というタイトルで登壇予定です。 申込方法 : イベントページからお願いいたします。 bitkey.connpass.com 平池 拓也 @hiraike32 2019 年から Android アプリの開発に従事し、2024 年にアンドパッドに入社。施工管理アプリや社内共有 SDK の開発を担当。 平池 拓也 のテックブログ執筆記事 - Android アプリから 360 度カメラ「RICOH THETA」に接続して写真を撮影する - ANDPAD Tech Blog - 複数の Android アプリで使う社内 SDK の開発を整備する - ANDPAD Tech Blog 登壇タイトル: Android アプリから 360 度カメラ「RICOH THETA」に接続して写真を撮影する 2. 会場スポンサー | Tamachi.sre#4 開催日時 : 2026年5月19日(金) 19:00 ~ 21:00 会場 : 株式会社アンドパッド 主催 : Tamachi.sre イベント概要 : Tamachi.sre は田町らへんでやる SRE のオフライン勉強会です。 SRE のテーマであれば、何でも発表は OK のイベントです。 申込方法 : イベントページからお願いいたします。 tamachi-sre.connpass.com 3. 会場スポンサー | DroidKaigi.collect { #30@Tokyo } 開催日時 : 2026年5月29日(金) 19:00 ~ 22:00 会場 : 株式会社アンドパッド 主催 : DroidKaigi イベント概要 : DroidKaigi が主催する、 Android 技術情報の共有とコミュニケーションを目的としたイベントです。今回は「Google I/O 2026・KotlinConf 2026」をテーマということで、 Android 17 での Gemini Nano との統合や、 Kotlin で AI エージェントを構築するための専用フレームワーク Koog などなど、大注目ポイントが目白押しです。 最速で Recap しましょう! 本イベントに、アンドパッドは 会場スポンサー として協賛し、会場を提供いたします。 申込方法 : イベントページからお願いいたします。 droidkaigi.connpass.com まとめ 採用広報から 5 月に開催されるイベント情報をお届けしました。 4 月に協賛した RubyKaigi 2026 については別途レポート記事を準備中なので、ご期待ください。 では、イベントやカンファレンスでお会いできることを楽しみにしています ! また、アンドパッドでは技術コミュニティが大好きな採用広報を大歓迎しています。 広報の経験がない方でも Welcome です! カジュアル面談やご応募、お待ちしております。 hrmos.co
こんにちは、SREの千明です。2020年1月に入社して、気づけば7年目に突入しました。 最近SREでは、イベント参加やブログ執筆が活発になってきているので、私もそれに触発されて久しぶりにブログを執筆することにしました。前回ブログに投稿したのが2020年6月だったので、なんと約6年ぶりの投稿になります。 WordPressをShifterへ移行した話 - ANDPAD Tech Blog 今回は最近導入した「Lambdaランタイムの更新をRenovateで検知できるようにする仕組み」について紹介しようと思います。 こんな方におすすめの内容です 今回の内容は、以下のような方向けになっています。ぜひ参考にしていただけると幸いです。 TerraformでLambda関数の管理をしていて、ランタイム更新を自動検知したい方 Renovateをすでに導入済みでパッケージ自動検知、更新に利用されている方(Renovateの導入方法は説明しません) 複数のLambdaランタイムが混在しているリポジトリを管理されている方 何を解決したかったのか この仕組みを導入する以前は、以下の手順で定期的にLambdaランタイムの更新を実施していました。 Lambda runtimes(英語版)のページを定期的に確認して、EOLが間近に迫ったLambdaランタイムがないか確認する。 EOLが間近に迫ったLambdaランタイムがあった場合、該当のLambdaランタイムを使用しているLambda関数をリストアップする。 リストアップしたLambda関数を管理している開発チームへLambdaランタイムの更新を依頼する。 各開発チームが開発環境でLambdaランタイム更新の動作検証を行い、問題なければ本番環境でもLambdaランタイムの更新を実施する。 しかし、上記の方法では誰かが能動的にEOLの確認をしなければいけません。さらに、EOLを基準として動き始めるため、更新期間(移行のための準備期間)が短くなってしまいます。Lambda関数を多数管理している開発チームの場合は、期限日(Deprecation date)までに更新が間に合わないこともあります。 この課題を解決するために、新しいLambdaランタイムがリリースされたタイミングを自動検知して、更新を始められるようにしたいと考えました。まず、更新タイミングを自動検知することによって、能動的に確認をする必要がなくなります。そして、更新を始めるタイミングをEOLからリリースにすることで、更新期間を長く取ることができて、余裕を持ってLambdaランタイムを更新できます。 どうやって解決したのか 上記の課題を解決するために、Terraformで管理しているLambdaランタイムのバージョンをRenovateで検知して、新しいバージョンがリリースされたタイミングでPRを作成するようにしました。以下でその方法を解説します。 リポジトリ構成 今回サンプルとして用意したリポジトリは以下のような構成になっていて、lambda_function_rubyとlambda_function_nodejsの2つのLambda関数を管理しています。(ソースコードは別で管理されていて、今回の説明では割愛します) . ├── lambda_function_ruby │ └── main.tf ├── lambda_function_nodejs │ └── main.tf ├── modules │ └── lambda_function │ └── main.tf └── renovate.json いくつかのファイルについても、簡単に説明します。まず、modules/lambda_functionは、Lambda関数に必要なリソースを定義するモジュールで、runtimeの値を変数で受け取るようになっています。例えば、lambda_function_rubyでは、以下のようにモジュールを呼び出して、ランタイムの値を指定しています。 module "lambda" { source = "../../../modules/lambda_function" runtime = "ruby3.3" } また、すでにRenovateが導入されていて、その内容はrenovate.jsonによって設定されています。今回は、Terraformを更新するだけのシンプルな設定になっています。 { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "packageRules": [ { "matchManagers": ["terraform", "terraform-version"], "labels": ["dependencies", "terraform"] }, { "matchPackageNames": ["hashicorp/terraform"], "additionalBranchPrefix": "{{packageFileDir}}-", "groupName": "terraform-version", "versioning": "hashicorp" }, { "matchDatasources": ["terraform-provider"], "additionalBranchPrefix": "{{packageFileDir}}-", "groupName": "terraform-providers" } ] } 説明は以上にして、次からはlambda_function_rubyを使って、Lambdaランタイムの更新をRenovateで検知できるようにするための設定方法を解説していきます。 Terraform側の設定 まずは、Terraform側のLambda関数のリソース定義にあるruntimeへ、以下のようなRenovate用のコメントを追加します。このコメントを元にRenovateが更新対象を抽出します。 module "lambda" { source = "../modules/lambda_function" # renovate: datasource=endoflife-date depName=aws-lambda versioning=loose runtime = "ruby3.3" } Renovate側の設定 次に、renovate.jsonにcustomManagersを追加します。この設定によって、先ほど追加したコメントとLambdaランタイムの設定を抽出します。 { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "packageRules": ["...Terraformの設定(省略)..."], "customManagers": [ { "customType": "regex", "description": "Update AWS Lambda runtime in Terraform files", "managerFilePatterns": ["/.+\\.tf$/"], "matchStrings": [ "#\\s*renovate:\\s*datasource=(?<datasource>.*?) depName=(?<depName>.*?)( versioning=(?<versioning>.*?))?\\s*runtime\\s*=\\s*\"(?<packageName>[a-z]+)(?<currentValue>[0-9.]+)(\\.x)?\"" ], "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}" } ] } このとき、正規表現の名前付きキャプチャーグループで名前を付けた値が、後述するpackageRulesの設定で利用できます。 そして、ここが今回のミソなのですが、packageNameにrubyが入るようになっていて、この値もpackageRulesの中で利用できます。 次に、先ほどのcustomManagersで抽出した部分を修正するために、packageRulesに2つのルールを追加します。このリポジトリでは、RubyとNode.js、2つのLambdaランタイムを管理しているので、それぞれのLambdaランタイムに対してルールを追加しています。 { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": ["config:recommended"], "packageRules": [ "...Terraformの設定(省略)...", { "matchDatasources": ["endoflife-date"], "matchDepNames": <span class="synS
1. はじめに DRE(Data Reliability Engineering)グループ のつざきです。タイミーのデータエンジニアリング部で、BigQuery / dbt / Cloud Composer / Looker といったデータ基盤の開発・運用をしています。 DREチームでは 2026 年 2 月から、AWS が提唱する AI-DLC(AI-Driven Development Life Cycle)というワークフローを運用しています。きっかけは、 1 月末に AWS 主催の研修「Unicorn Gym」で3 日間 AI-DLC を体験したことでした。 AI-DLC 自体とタイミー全体への波及は同部の橋本さんが、Operations フェーズ(リリース後の検証)の独自構築については同じ DRE G の chanyou さんが、それぞれまとめています。 3日間のUnicorn Gymが1ヶ月で組織を変えた —— データで見るAI-DLC導入の波及効果(橋本さん) 「リリース後」に向き合うAI駆動開発の実践(chanyou さん) 本記事はこれらの続編的な位置づけで、「DREチーム が Inception と Construction フェーズで何を実装・運用しているか」に絞って書きます。 対象読者: AI-DLC を個人ではなく、チーム(モブ)で運用したい開発/データ基盤チーム この記事の目的: 公式の想定(単一プロジェクト/個人運用)を、複数リポジトリ・リモートモブ前提に翻訳した実装パターンを共有する 扱わないこと: Operations フェーズの詳細、全社展開の話、AI-DLC の一般解説 TL;DR DREチームは 2026 年 2 月から AI-DLC を運用中 実装: Workspace + CLAUDE.md 読み替え、Intent 単位の運用 モブ: 1 日 3 ~ 4 時間のフルリモートモブ。狙いは「フロー効率(承認ゲートで止まらない)」「キーパーソンに頼らない(新基盤導入や新メンバー受け入れに効く)」「AI 出力の欠陥を集合知で減らす」の 3 つ 3 ヶ月の結果: Intent 完了が月 14〜17 件で推移、PR 数は維持、サイクルタイムに劇的な変化は見えず 記事の立ち位置: 公式に書かれていない実装の隙間(Mob、複数リポジトリ、パス読み替え等)を自分たちで翻訳した事例として記録 2. AI-DLC をざっくり AI-DLC の全体像は既出記事に譲り、本記事で後から使う概念だけ押さえておきます。 本記事での用語の使い方 Intent: 1 つのゴール(例: あるデータソースを BigQuery で使えるようにする) Unit: Intent を疎結合に分解した作業単位(DDD の Subdomain 相当。例: Terraform 追加、DAG 実装など) Ritual: モブでの儀式的な作業(後述の Mob Elaboration / Mob Programming / Mob Testing) Workspace: ドキュメントとルールを置く専用リポジトリ フェーズと成果物の階層 AI-DLC には 3 つのフェーズがあります。 Inception: 要件分析・設計 Construction: 実装・テスト Operations: デプロイ・監視 3 つの Mob Ritual 各フェーズには対応する儀式(Ritual)が定義されています。 Mob Elaboration(Inception): 要件分析・分解を全員で Mob Programming(Construction): 実装を全員で Mob Testing(Construction): テストを全員で いずれも、公式推奨は「物理集合 + 共有スクリーン + ファシリテーター」です。 Human Oversight = Loss Function AI-DLC は AI が実行主体、人間は各ステップで検証・承認する構造です。公式ペーパーの表現が印象的で: "Each step serves as a strategic decision point where human oversight functions like a 'loss function' - catching and correcting errors early before they snowball downstream." 機械学習の損失関数のように、人間のレビューが早期にエラーを補正する、というメタファーです。後の章でモブワークの話をするときに効いてきます。 3. 公式ドキュメントに書かれていない実装ギャップ chanyou さんの記事では、awslabs/aidlc-workflows リポジトリで Operations フェーズがプレースホルダになっている話が出てきます。実は Inception と Construction の側にも、公式の文書と実装の間にいくつかのギャップがあります。 awslabs/aidlc-workflows の構成 原典の awslabs/aidlc-workflows は MIT-0 ライセンスで公開されている、マークダウンのルールファイル群です。 aidlc-rules/ ├── aws-aidlc-rules/ │ └── core-workflow.md # ワークフロー本体 └── aws-aidlc-rule-details/ ├── common/ # 共通ルール ├── inception/ # Inception 詳細 ├── construction/ # Construction 詳細 └── operations/ # プレースホルダ ギャップ 1: ルール実装に Mob の記述がない AI-DLC 公式ペーパーでは Mob Elaboration / Mob Programming / Mob Testing が中核の儀式として定義されています。しかし原典のルールファイル群を mob で grep してもヒットしません。実装部分は「個人と AI エージェントが 1 対 1 で対話しながら承認ゲートを通す」構造になっており、Mob は想定されていない書き方です。 ギャップ 2: 公式チュートリアルは個人開発の例 AWS 公式ブログの実践記事 Building with AI-DLC using Amazon Q Developer のサンプルは、単一 HTML ファイルの川渡りパズルを個人で作る例だけで、モブで回す実演は出てきません。 ギャップ 3: 複数リポジトリの扱いが明確でない 公式は単一プロジェクト前提です。データチームのように「1 つの機能を作るのに複数リポジトリにまたがる」ケースへの具体的な示唆はほぼありません。 理念と実装の翻訳が必要 つまり、公式ペーパーに書かれた「Mob ワーク」や「複数チームでの協調」を実際に動かすには、自分たちで翻訳する必要があります。DRE では、各ギャップに対応する形で次のように対処しています。 ギャップ 1(Mob がルールにない) → モブでの意思決定を組み込み(章 6) ギャップ 2(単一 Intent 想定) → Workspace + CLAUDE.md 読み替え(章 4) ギャップ 3(複数リポジトリが薄い) → 複数リポジトリを 1 Intent でまとめる(章 5) 次章から具体に入ります。 4. DRE の実装: Workspace + CLAUDE.md 読み替え AI-DLC を Claude Code で回すために、DRE では次の構成にしています。 全体像(先に結論) ルール階層: aidlc-rules/(上流)→ .claude/rules/(上書き)→ CLAUDE.md(入口) パス読み替え: aidlc-docs/requirements.md を aidlc-docs/intents/<YYYY-MM>/<intent_name>/inception/requirements.md に読み替え Intent 箱: Intent ごとに独立したディレクトリ(intents/<YYYY-MM>/<intent_name>/) 状態管理: aidlc-state.md に Status と Code Repositories を記録 スキル化: Intent ライフサイクルを Claude Code のスキルで操作 以下、理由と詳細を順に見ていきます。 なぜこの構成なのか awslabs のリポジトリは単一プロジェクト・単一 Intent 前提で書かれていて、1 つの aidlc-docs/ ディレクトリに成果物を蓄積する想定になっています。 一方で DRE は、Intent という単位で開発を進めていて、完了した Intent もそのまま保存しています(後述しますが 2026 年 3 月は 14 件の Intent が完了しました)。Intent ごとに独立したディレクトリが必要になるので、パス読み替えが不可欠です。 ルール階層(継承構造) aidlc-rules/: awslabs/aidlc-workflows の中身をそのまま取り込む。手動変更禁止、/aidlc-rules-update スキルで上流追従 .claude/rules/: プロジェクト固有のルール。aidlc-rules のオーバーライドや追加ルールを置く CLAUDE.md: エントリポイント。プロジェクト概要とディレクトリ規則を最小限に記述 上流は変えない。プロジェクト固有の振る舞いは派生側で足す。オブジェクト指向の継承に近い発想です。 [入口] CLAUDE.md ├─ 参照: aidlc-rules/ # 上流(awslabs 同期、変更禁止) └─ 参照: .claude/rules/ # 派生(DRE 固有、オーバーライド+追加) パス読み替えの例 awslabs のルールは、成果物の置き場として aidlc-docs/ というパスを前提に書かれています。DRE ではこれを Intent ごとのディレクトリに読み替えます。 公式: aidlc-docs/requirements.md DRE: aidlc-docs/intents/<YYYY-MM>/<intent_name>/inception/requirements.md この読み替えは .claude/rules/aidlc-workflow.md に定義してあり、Claude Code が実行時に解釈します。ルール本体(aidlc-rules/)は触らずに、パスだけ派生側で書き換える構成です。 Intent ディレクトリの構造 1 つの Intent のディレクトリはこういう構造です。 aidlc-docs/intents/<YYYY-MM>/<intent_name>/ ├── intent.md # Intent の目的・受け入れ基準 ├── aidlc-state.md # Intent の状態管理 ├── audit.md # 監査ログ ├── inception/ │ ├── requirements.md │ ├── stories.md │ └── ... └── construction/ └── <unit_name>/ ├── functional-design.md ├── code-generation.md └── ... aidlc-state.md のカスタマイズ Intent の進捗追跡に使う aidlc-state.md は、公式テンプレートをベースに少し拡張しています。 Status: OPEN / SUSPEND / CLOSED の 3 値を追加 Assignee: 担当者 Code Repositories: 複数のコードリポジトリのブランチ状態を記録 この Code Repositories セクションが次の章(複数リポジトリ運用)の鍵になります。 スキル化 Intent のライフサイクル管理は Claude Code のスキルとして定義しています。 /aidlc-intent-start: 新規 Intent 開始 /aidlc-intent-continue: 既存 Intent の再開 /aidlc-intent-save: 作業内容を PR 化してマージ /aidlc-rules-update: 上流(awslabs)への追従 chanyou さんの記事では /inception のように AI-DLC のワークフローそのものを呼び出すスキルが紹介されています。一方、DRE では「Intent というライフサイクルの入れ物」をスキル側で担う構成にしています。どちらも awslabs のルールに乗りつつ、スキルで扱う粒度が違う、という関係です。 5. 複数リポジトリを 1 Intent でまとめる DRE のようなデータ基盤チームでは、1 つの機能を作るのに複数のリポジトリが絡みます。 典型的なワーク 例えば「ある外部 SaaS のデータを BigQuery に自動転送するパイプラインを構築する」といった Intent だと、以下のようなリポジトリにまたがる変更が必要になります。 GCP Terraform リポジトリ: IAM やデータセットの定義 Composer インフラリポジトリ: Cloud Composer や Secret Manager の Terraform Composer DAG リポジトリ: Cloud Run Job と Airflow DAG のコード dbt リポジトリ: staging モデル これを 1 つの Intent としてまとめます。まず Inception フェーズで全体の要件・設計を固め、その後 Construction フェーズで各リポジトリに Unit を切って進めます。例えば DRE の 2026 年 2 月に動かしたあるパイプライン構築 Intent では、4 ユニット・60 ドキュメント・6 PR で完了しました(規模感の一例として)。 ブランチ戦略の 2 階建て ドキュメントとコードで別々のブランチ戦略を使い分けています。 Workspace リポ: session/<intent_name>/<hex> という短命ブランチ。スキル呼び出し単位で切って都度 main にマージ コードリポ: feature/<intent_name> という長命ブランチ。Intent が完了するまで維持 Workspace 側はドキュメントの進捗を小さくマージして積み上げ、コードリポ側は実装が揃ったタイミングで main に入れる、という二層構造です。 aidlc-state.md に Code Repositories を記録 1 つの Intent が複数リポジトリに触るので、どのリポのどのブランチで作業しているかを aidlc-state.md に記録しておきます。 <pre class="code lang-markdown" data-lang="markdown" data-unlink
はじめに こんにちは、バクラクビジネスカード開発チームの @budougumi0617 です。 先月まではテックリードでしたが、4月からエンジニアリングマネージャになりました。 バクラクビジネスカードは2022年からサービスを提供している法人様向けクレジットカードのサービスです。昨年2025年からはETCカードのサービス提供も開始しました。 prtimes.jp 本記事では、もともとクレジットカードの決済データを受け取ることのみを前提に設計されていたバクラクビジネスカードのデータベースに対して新しくETCカードの決済データも受け入れられるようにマイグレーションを行なったときに考えたことをまとめます。 要点を先に書くと、次の3つです。 約3年分の決済レコードが蓄積された本番DBに対して、サービス無停止でスキーマ変更を完了した テーブルごとに許容できるマイグレーション時間も考慮しながらマイグレーション戦略を切り分けた 本番DBの複製を利用した実データを用いたマイグレーション時間の事前計測 決済系プロダクトでスキーマ拡張を行うエンジニアや、歴史あるサービスでの大きめのDBマイグレーションを検討している方にとって、なんらかのヒントになれば幸いです。 背景:クレジットカードの体験をETCカードでも提供する バクラクビジネスカードは他のバクラクプロダクトと連携し、クレジットカードの決済から経費精算・経理担当の会計処理までのシームレスな体験を提供しています。 「クレジットカードの体験をETCカードでも提供してほしい」と希望するお客様の声は常にありました。 ここで、バクラクビジネスカードはプロセシングプラットフォームであるインフキュリオン様のXardを利用してクレジットカード機能を開発しています。 2025年、XardでETCカード発行サービスがリリースされることになり、バクラクでもETCカードを提供することとなりました。 インフキュリオン、「Xard(エクサード)」の新オプション機能として 「ETCカード発行」サービスを2025年7月より提供開始 - Infcurion, Inc. Xardを利用したETCカードは、通常のクレジットカードと同じAPIを用いてETCカードの発行および高速道路利用時の決済情報を受け取ることができます。 バクラクビジネスカードのオプションとしてETCカードを新たに提供するにあたり、技術的に大きな問いが1つありました。 それは、クレジットカードの決済情報を受け取ることのみを前提としたデータベースのテーブルをETCカードの決済情報にも対応させることでした。 クレジットカードの決済情報テーブルをETCカードの決済情報にも対応するように拡張する バクラクビジネスカードは、Xardからの決済リクエスト、Webhookを受信して決済を処理しています。 クレジットカード(以降クレカ)の決済情報とETCカードの決済情報では含まれる内容に違いがあることが事前にわかっていました。 クレカとETCの決済データの差分 ETCの決済データは、クレジットカードの決済データといくつかは共通している一方、各々で固有の項目もそれなりにあります。たとえば以下のとおりです。 共通の項目:決済金額、決済日時、カードを特定する情報 など クレカ固有の項目:加盟店名などISO8583に定義された情報 1 ETC固有の項目:入口・出口インターチェンジ名、車種 など 2 2つの大きな制約 今回のカード・決済情報テーブルのETCカード対応拡張に着手するうえで、外せない制約が2つありました。 1つ目は「無停止要件」です。クレジットカードはお客様が24時間365日利用するサービスです。 決済を止めるメンテナンスはどうしても回避できない場合を除き行わない方針で開発・運用をしています。 2つ目は「既存データ量」です。バクラクビジネスカードはリリースから3年以上が経過し、主要テーブルには3年分の決済情報のレコードが蓄積されていて、今この瞬間にも新たな決済明細のレコードが増え続けています。ローカルでは一瞬のマイグレーションも、本番では何分・何十分とかかる可能性がありました。 「無停止」と「3年分のレコードを抱えたテーブルへの変更」が同時に求められる、これが今回の出発点です。 テーブルがどのようなユースケースで利用されているか考える スキーマ拡張の議論を始めると、つい「どんなテーブル設計が綺麗か」「どんなマイグレーションが速いか」という話に目が向きがちです。 しかし、今回のETC対応で最初に行なったのは、そのテーブルが本番でどう利用されているかを見るという工程でした。同じスキーマ変更要件でも、当てるテーブルの利用パターンが違えば、許される操作の幅も大きく変わります。 バクラクビジネスカードの決済処理を構成するテーブルは、本番での利用のされ方が大きく異なる2系統に分けられました。 系統A:決済処理APIに紐づくテーブル 指定時間以内にレスポンスを返さなければ決済そのものが失敗扱いになる経路で参照・更新されます 既存処理に与える影響を限りなく小さくする必要があります 許されるのは「短時間で完了し、マイグレーション後もアプリの挙動に影響がない」スキーマ変更です 系統B:Webhook経由の非同期処理に紐づくテーブル 外部からのイベントを弊社システム内部のキュー経由で非同期に処理する経路で参照・更新されます 一時的な処理失敗はキューで再処理できる設計になっており、ある程度の処理遅延・実行時間を許容できる経路です サブテーブル分離など、踏み込んだスキーマ変更も現実的な選択肢になります この2系統を同列に扱ってしまうと、「全テーブルを最速で完了させる」という強い縛りが生まれ、選べる打ち手が極端に狭くなります。逆に切り分ければ、系統Aには本当に安全な操作だけを当て、系統Bには踏み込んだ変更を入れる、という割り当て方ができます。 非同期経路のWebhook基盤の設計については、過去の発表資料もあわせてご覧ください。 speakerdeck.com 系統ごとに違うアプローチを選ぶ テーブルの利用パターンが違うことを踏まえて、ETC対応のスキーマ拡張も系統ごとに違うアプローチをとりました。 系統A:既存テーブルに最小限の変更だけを当て新規テーブルを追加する API処理側のテーブルには、既存クエリの形を変えずに済む最小限のスキーマ変更だけを当てる方針にしました。 具体的には、決済の親テーブルに種別を見分けるカラムを1つ追加し、ETC固有の付随情報は新規テーブルに寄せる構造です。 もともと決済処理APIの内容を格納しておくテーブルは2つに分かれていました。 IDや受信時刻など基本的な情報を持つ親テーブル 金額や加盟店名などに関する詳細テーブル そこで詳細テーブルはクレカ用として既存のまま残し、ETC側は新規に別の詳細テーブルを用意することにしました。 判断軸はシンプルで、API処理側では次の3点が強く求められたからです。 既存クエリへの影響を最小化すること 明細表示や集計クエリを1テーブルで完結させること(UNIONや分岐を増やさない) マイグレーション中・マイグレーション後もアプリケーションが決済を処理し続けられること 修正前後の構造のイメージは次のとおりです。 flowchart LR subgraph Before["修正前"] A1["親テーブル"] A2["詳細情報テーブル"] A1 --> A2 end subgraph After["修正後"] B1["親テーブル</BR>種別カラム"] B2["詳細サブテーブル<BR/>(クレカ用とする)"] B3["ETC用詳細情報テーブル<BR/>(新規)"] B1 --> B2 B1 -- "種別=ETC のときのみ参照" --> B3 end Before --> After 系統B:共通テーブル+種別ごとのサブテーブルに分ける Webhook経由側のテーブルも最終的な形は似た構造になるように変更しました。 Webhookで受け取る生データは最初1テーブルで全てを含んでいました。 そのテーブルにETCカードのカラムを追加するとクレカ・ETCカードでお互いNULLになるカラムが増え、意味づけも複雑化します。 そこで、共通テーブルには種別を見分ける情報を持たせ、種別固有のデータはサブテーブルに完全分離する形にしました。修正前後の構造のイメージは次のとおりです。 flowchart LR subgraph Before["修正前"] A1["共通テーブル<br/>(クレカ用カラムも含む)"] end subgraph After["修正後"] B1["共通テーブル<br/>+ 種別カラム<br/>(クレカ用カラムは後日DROP)"] B2["クレカ用<br/>詳細サブテーブル<br/>(新規)"] B3["ETC用<br/>詳細サブテーブル<br/>(新規)"] B1 -- "種別=クレカ" --> B2 B1 -- "種別=ETC" --> B3 end Before --> After このアプローチではデータの扱いが大きく代わりますが、共通テーブルへのカラムのDROPを後日行えばマイグレーション実行直後でもアプリケーションは問題なく動きます。 そのかわり新設したサブテーブルへ過去分のデータをバックフィルする作業や、共通テーブル側の旧カラムを段階的に整理していく作業も発生します。 しかしこれらの「時間のかかるデータ移行作業」でも、Webhook経路の場合は移行作業中にWebhook処理が失敗してもキューの再実行によりリカバリが可能です。 同じ要件、違う実装手段 ETC対応で必要だった共通要件をまとめると、次の3つでした。 カード種別を識別する仕組み ETC固有データを保持する場所 既存クレカ機能への影響を出さないこと 同じ要件でも、系統Aと系統Bでは実装手段が違います。 系統A:既存テーブルの延長線上で考える 系統B:多少実行コストがかかってもデータを整理する 「全テーブルで同じマイグレーション戦法をとる」という方針を強引に当てはめないことで、それぞれのテーブルの本番利用に合った形に落とし込めました。 マイグレーションを当てる前に、実データ量で測る 設計のアプローチが決まっても、本番にそのまま実行してよいとは限りません。先に述べたとおり、ローカルでは一瞬で完了するDDLでも本番のデータ量では想定外のロックや所要時間で決済を巻き込むリスクがあります。 ここで効いてきたのが、本番相当の実データ量でDDLの実行時間を必ず検証するという工程でした。バクラクプロダクトでは、権限管理された本番クローン環境で、事前にDDLを検証できるBytebaseをフロントにおいたSQL実行基盤があります。詳しくは以下の記事をご覧ください。 tech.layerx.co.jp この基盤の上で、今回投入したいDDLを実際に流し、所要時間とロックの挙動を計測しました。チェックしたのは主に次の点です。 カラム追加が ALGORITHM=INSTANT で完了するか、それともテーブルコピーになるか 「カード種別を識別するカラム」の追加が高速で終わるか 新規インデックス追加にかかる時間と、その間の参照/更新への影響 DROP COLUMN にどれくらい時間がかかるのか 検証で得られた所要時間が、系統A・系統Bそれぞれの戦略を本番に当てて良いかの判断根拠になりました。 複製環境ではデータが消える DROP COLUMN も気兼ねなく試せたため、戦略の幅も広く取れます。何より「確かめはしたけれど本番で本当に問題ないか不安」という気持ちにならずに自信をもってマイグレーション実行日を迎えることができました。 実施と結果 運用中のテーブルへのマイグレーションのため、実際のマイグレーションではアプリケーションコードのCRUDが壊れないかをQAで確認しつつ、本番環境へメンテナンスタイムなしでマイグレーションを実施していきました。 結果として、今回の一連のスキーマ変更について、マイグレーション起因の決済失敗は確認されず、メンテナンスタイムなしで完了することができました。 2025年夏にリリースしてからとくに問題なくETCカードの処理も行えています。 学びと振り返り 今回のプロジェクトで、後続のスキーマ変更にも持ち越したい学びは次のあたりです。 マイグレーションアプローチは、テーブルの本番利用に合わせて選ぶ API処理に紐づくテーブルには軽量な変更を当て、Webhook経由のテーブルには踏み込んだ変更を当てる、という割り当てがETC対応で機能しました。同じ考え方は決済に限らず、ECの注文テーブルへの配送種別追加、SaaSの課金テーブルへの新プラン追加など、SLAの厳しい経路と緩い経路が同居するシステム全般に応用できます。 マイグレーションは実データ量で測ってから当てる ローカルでの感覚と本番でのふるまいは違います。本番相当のデータで実際に流して数字を見ることで、「この変更は適用できる」「この方針は見直すべき」という判断を実測値にもとづいて行えました。 本番クローン環境は意思決定の精度を上げる 単なる検証ツールではなく、設計や戦略そのものを引き返す根拠として機能しました。 おわりに クレジットカードは止められないからこそ、「止めずにどう変えるか」を設計・意思決定の中心に置く必要があります。今回はETCカードの追加という形で、その難しさと面白さに、設計と運用の両面から向き合いました。 テーブルの本番利用を見てからアプローチを決めること、そして実データ量で実行時間を必ず検証すること、この2つが今回の無停止マイグレーションを支えました。 バクラクビジネスカードを開発しているバクラク事業部のPayment開発部では今年もBPSP(Business Payment Service Provider)をリリースしたり、新たな決済サービスの開発を続けています。 <iframe src="https://hatenablog-parts.com/embed?url=https%3A%2F%2Fprtimes.jp%2Fmain%2Fhtml%2Frd%2Fp%2F000000607.000036528.html" title="バクラク請求書発行、カード払い可能な請求書を発行できる「カード決済オプション」リリース。債権回収業務の安定化をサポート。" class="embed-c
こんにちは、タイミーでSRE業務を担当している徳富(@yannKazu1)です。 先日、函館で開催されたRubyKaigi 2026に参加してきました。Ruby本体やパーサ、GC、JITといった「言語の中身」を深掘りするカンファレンスなので、普段アプリケーションコードよりインフラ寄りの仕事をしている自分が行って楽しめるのか、という気持ちも少しありました。ですが、結果としてとても楽しめたので、感想を書いておきます。 SREが行っても普通に楽しめた 普段の仕事はRailsアプリケーションを安定して動かしたり、スケールさせたり、観測したりすることが中心です。Ruby本体にコミットするわけでも、パーサを書くわけでもないので、専門外の話も多いだろうと思っていました。 実際、聞いていて全部の細部までは追えないセッションもありました。それでも、 普段ブラックボックスとして扱っているGCやランタイムの中身が、作っている人の口から語られる 他社のRails運用をやっている人たちと直接話せる このあたりだけでも参加する価値を感じました。 Day 1のブースが意外と良かった 参加されたことがある方ならわかると思いますが、Day 2以降はブース巡りが意外と慌ただしくなります。スタンプラリーで人が流れたり、セッションの合間で時間が限られていたり。 その点、Day 1はスタンプラリーがまだ始まっていないので、ブースが比較的空いていてゆっくり話せる時間帯でした。立ち寄っても順番待ちがほとんどなく、エンジニアの方とじっくり話せます。 RubyKaigiにはほぼ例外なくRailsを本番運用している会社さんがスポンサーとして出展しているので、SREとしては「他社さんのアーキテクチャや困りごとを直接聞ける場」として、これがかなり面白かったです。 Railsで完結する vs クラウドネイティブに振り切る 複数の会社さんと話して興味深かったのが、「Railsの中で完結させるか、AWSのマネージドサービスに切り出すか」の判断基準が会社によって全然違うことです。 Railsはよくできていて、ActiveJob + Sidekiq、ActiveStorage、ActionCableなどを組み合わせれば、大抵のユースケースはRailsの世界の中で完結します。わざわざクラウドネイティブなマネージドサービスに切り出さなくても、運用負荷を抑えながら回せるケースは多い。 一方で、「ジョブの遅延が事業KPIに直結するので、マネージドサービスに切り出して水平スケールを確実にしている」と話す会社さんもあれば、逆に「今のスタックで十分捌けているし、Rubyエンジニアが運用できる構成に揃えたほうが組織的に強い」と話す会社さんもありました。 技術選定の基準として挙がっていたのは、ざっくりこんな観点です。 スパイク耐性が事業上クリティカルかどうか 運用するチームのスキルセットと採用市場 コールドスタートを許容できるワークロードか RubyKaigiは登壇者も来場者もアプリケーションエンジニアが中心です。そのため、「Sidekiqのままいくか、それとも切り出すか」といったテーマひとつとっても、「アプリケーション側からどう見えているか、何が嬉しいかつらいか」という視点で語られていたのが印象的でした。 普段、SRE系のイベントで「インフラ側の都合」としてこの手の話を聞くことが多い私にとっては、この視点の対比が非常に新鮮に感じられました。 「正解は一つじゃない」と頭ではわかっていても、SRE目線とアプリ目線では同じ意思決定でも見えている景色が違います。両方の視点を持っておくことが大事だなと、改めて感じました。 AI活用の温度感 もう一つ、多くのブースでも話題になったのがAI活用です。プロダクトへの組み込み、社内開発フローへの導入、営業・カスタマーサポートへの活用と、レイヤーごとに状況が違っていて面白かったです。 特に印象に残ったのが、SmartBankさんのブースで展示されていた「スマートバンクで働くAI Agentたち」のポスターでした。アプリユーザー向けの「ワンバンフレンズ」(家計を読み解いて気づきを届けるAIアシスタント)、社内メンバー向けの「Ask! ワンバン」(自然言語で社内データを検索・分析する分析AI)、そして開発者向けの「Guardie」(エラーや異常を検知してログ・コード・変更履歴を横断して原因特定を支援する番犬AI)という三本立てで、ユーザー向け / 社内向け / 開発者向けの3レイヤーに対してそれぞれAIエージェントを配置しているのがすごく整理されていて印象的でした。 特にGuardieはSRE視点でめちゃくちゃ刺さりました。「2時間覚悟していた調査が10分で終わった」という社内の声が紹介されていて、これは障害対応におけるMTTR(平均復旧時間)を本質的に短縮しに行っている事例だなと。エラー検知 → ログ・コード・変更履歴を横断した原因特定までをAIに任せる、というのはこれから我々も作っていこうと思っていた仕組みが普通に動いていて、刺激を受けました。 ブース担当の方とは「どこまでをAIに任せて、どこから人間がやるべきか」「誤検知や暴走への安全装置をどう設計しているか」みたいな話までできて、こういう一次情報が聞けるのもRubyKaigiならではでした。 気になった話は、その後のアフターパーティでさらに深掘りできました。資料には載らない現場のリアルな知見が交換される場として、ブース + アフターパーティの組み合わせは、セッションと同じくらい価値があったと感じます。 参加したセッションを日ごとに振り返る ここからは、自分が参加して特に印象に残ったセッションを日ごとに紹介していきます。 Day 1: Exploring RuboCop with MCP (Koichi ITO さん) 1日目に聴いたのが、RuboCopコアチーム・MCP Ruby SDKチームメンバーのKoichi ITOさんによる、RuboCopとMCP(Model Context Protocol)を組み合わせる試みについてのセッションです。 これまでRuboCopは「人間」または「他のプログラム(CIなど)」をきっかけに実行されてきました。そこにAI時代になって、AIエージェントという新しい実行のきっかけが登場した、というのが導入の話です。生成AIとリンター/フォーマッターをどう組み合わせるか、Rubyで実装されたMCPサーバーをエージェントの隣で走らせるとどうなるか、という内容でした。 技術的には、 MCP SDKの構造(サーバーとクライアントそれぞれのSDKがあること) トランスポート層としてstdioとStreamable HTTPの2種類があり、用途で使い分けること HTTPトランスポートではセッション管理が肝になり、Pumaのようなスレッドモデル + シングルプロセス構成だと素直に動くが、複数プロセス・複数ホストに横断するとセッションの保持が難しくなること ただしStateless Mode(stateless: true)を使えば、Pumaの複数ワーカーやUnicornのような複数プロセス構成にも対応できること。ただしこれはリクエストごとに新しいtransportインスタンスが生成されるためMCP-Session-Idを共有できないという制約とのトレードオフであり、「セッション保持を諦める代わりに複数ワーカー/プロセスでもスケールできる」という割り切り あたりが特に勉強になりました。特にセッション管理の話は、MCPサーバーをWebアプリケーションとして本番に乗せようとすると、ロードバランサーやスケールアウトとの兼ね合いが出てくるという点で実践的でした。ステートフル/ステートレスをどう使い分けるかは、これから考えないといけないテーマになりそうです。 セッションの締めくくりで「LLMの確率的な性質を決定的なツールに組み込むことで、これまでの決定的なツールとは違う未来が描ける」という話があって、そこも印象的でした。MCPのサンプリング(サーバーがクライアント経由でLLM補完を要求する仕組み)や、Elicitation(実行中にユーザーへ追加情報を問い合わせる仕組み)といった機能は、ツールの形そのものを変えそうな予感があります。 スライド: https://speakerdeck.com/koic/exploring-rubocop-with-mcp Day 2: Chasing Real-Time Observability for CRuby (Shintaro Otsuka さん) 2日目で一番テンションが上がったのがこのセッション。CRubyの実行状態をリアルタイムに3D可視化するというツール「rrtrace」の話でした。 普通のプロファイラはサンプリングベースで、後から集計して結果を見る形が多いですが、このツールは「いまこの瞬間にCRubyの中で何が起きているか」を、複数スレッドのスタック状態として3次元空間にレンダリングしながら見せる、というアプローチです。デモを見せてもらった時、IRBに入力するたびにスタックが積み上がっていく様子がリアルタイムで見えて、純粋に「すごいものを見ている」という感覚になりました。 技術的なポイントとしては、 計測側のC拡張は軽量に保つ設計で、イベントの収集と転送に特化している イベントはTracePoint API(CALL / RETURN / INTERNAL_GC_ENTER / INTERNAL_GC_EXIT)や内部のスレッドイベント(INTERNAL_THREAD_EVENT、こちらはWindowsでは利用不可)から収集し、timestamp(60bit) + event type(4bit) + method id/thread id(64bit)の合計16バイトの構造体に統一 C拡張(計測側)とビジュアライザプロセスの間はOS管理の共有メモリ上のリングバッファで受け渡し ビジュアライザ側はCRubyのコアを使っていない別プロセスなので、可視化処理が重くてもCRubyの実行を直接ブロックしない スタックシミュレーションが重いため、Parallel Scanアルゴリズムで並列化している という設計でした。ただし、スライドのベンチマーク結果を見ると、rrtrace有効時は関数呼び出しスループットがplain CRubyの17%程度(73,417,127 → 12,760,131 calls/s、約5.9倍遅くなる)、Railsサーバーのrpsもplain CRubyの55%程度(203.19 → 110.84 rps)になるとのことで、計測のオーバーヘッド自体は「小さくない(not small)」とスライドでも明記されていました。TracePointフックのコストが支配的で、ここは今後の課題とのことです。 それでも、「重い処理を別プロセスに全部寄せる」というアーキテクチャの考え方は面白かったです。計測側はできるだけ軽く保って、分析・可視化は別のリソースでやる。この割り切りが設計をシンプルにしていて、きれいだなと思いました。 「現代のマシンは10コア以上あるのが普通で、CRubyが1コアで動くなら残りのコアを観測やビジュアライズに自由に使える」という、リソースの捉え方の話も新鮮でした。GVLがある世界での観測ツールの設計思想として、納得感が強かったです。 スライド: https://speakerdeck.com/whitegreen/chasing-real-time-observability-for-cruby Day 3: The Less-Told Story of Socket Timeouts (Misaki Shioi さん) 3日目に聴いたのがこれ。ソケットライブラリのタイムアウトの歴史と内部実装を、Issue/Commitを参照しながらRuby 4.0までの流れに沿って解説していくセッションでした。タイトルからして気になっていたけど、期待以上の内容でした。 Socket.tcp / TCPSocket.new には、 resolv_timeout (名前解決のタイムアウト) connect_timeout (接続のタイムアウト) そしてRuby 4.0で追加された open_timeout (全体のタイムアウト) の3種類があります。「この3つがなぜ必要で、どういう順番で導入され、どんな歴史的な紆余曲折があったのか」を、Issue/Commitを参照しながら丁寧に追っていく構成でした。 特に印象的だったのが、 まず Socket.tcp に connect_timeout が導入され、続いて Addrinfo.getaddrinfo への timeout および Socket.tcp への resolv_timeout が追加されたこと resolv_timeout と connect_timeout を両方指定しても、全体のタイムアウト時間は制御できない(複数アドレスに対して逐次接続を試行するため、合計時間が想定より長くなりうる) これらの問題を解決するために、Ruby 4.0で全体時間を管理する open_timeout が追加された という話の流れです。普段、HTTPクライアントのopen_timeout/read_timeout/write_timeoutを「だいたいこのくらい」で設定しがちですが、その下のレイヤーでは名前解決と接続が並行で走っていて、タイムアウトの組み合わせによっては想定と全然違う挙動になるということを、改めて意識させられました。 また、歴史的な経緯として特に面白かったのが、名前解決の中断可能化(interruptible)をめぐる話です。 getaddrinfo(3) はブロッキング呼び出しで、Ctrl+C でも中断できないという問題が長年ありました。これを解決するアプローチとして、まず2020年1月に「Addrinfo.getaddrinfo が timeout をサポートする」提案が行われ、そのパッチで Addrinfo.getaddrinfo に timeout、Socket.tcp に resolv_timeout が追加されると同時に、内部的に「GNU拡張のgetaddrinfo_a(3)が利用可能ならそれを使う」実装が入りました(getaddrinfo_a(3)はワーカースレッドで非同期に名前解決を行う仕組み)。その後2020年8月には「Make Socket.getaddrinfo interruptible」がマージされ、Socket.getaddrinfo の内部もこの getaddrinfo_a(3) を使うように利用範囲が拡張、さらに2020年9月には TCPSocket.new にも resolv_timeout / connect_timeout が追加され、名前解決を中断可能にする方向で進められました。 ところがその後、Rails ActiveJobの統合テストが失敗するようになったという報告が入り、調査の結果、fork後の子プロセスでgetaddrinfo_a(3)を呼び出すとハングすることが判明します。getaddrinfo_a(3)は内部で再利用可能なワーカースレッドを保持しているのですが、forkでコピーされる子プロセスにはワーカースレッドが存在しないにもかかわらず内部状態は「ワーカースレッドが待機中」のままになっており、これによってデッドロックが発生する、という仕組みでした。 2ヶ月以上の調査と回避策の検討を経て、最終的にはgetaddrinfo_a(3)の導入自体が撤回され、関連変更もrevertされました。その代わり、後に別アプローチとして「名前解決ごとに専用のpthreadを立ててgetaddrinfo(3)を実行する」方式(mameさん提案、ruby/ruby#8695)が採用され、こちらはrsock_getaddrinfo内に実装されることで、内部的にrsock_getaddrinfoを呼んでいるAddrinfo.getaddrinfoやSocket.getaddrinfoを含む幅広いメソッドで名前解決のブロッキング問題が解消された、という流れです。 外部API連携で「タイムアウトを設定したはずなのにハングする」「connect_timeoutを短くしたのに、複数アドレスがあるホストで合計時間が想定の何倍もかかる」みたいな経験がある人は少なくないと思いますが、まさにあれの背景にある話でした。タイムアウト設計の見直しや、Ruby 4.0以降はopen_timeoutを積極的に使っていくこと、テストでタイムアウト周りの挙動を確認しておくことなど、すぐに持ち帰れる学びがいくつもありました。 スライド: https://speakerdeck.com/coe401_/the-less-told-story-of-socket-timeouts リモートワーク時代の副次効果 もう一つ書いておきたいのが社内メンバーとの関係性の話です。 弊社エンジニアはリモートワーク中心で、普段の業務だと開発チームの全員と毎日話すわけではありません。SlackやZoomでは話すけれど、雑談ベースで「最近どう?」みたいな会話になりにくい人もいます。 それが、RubyKaigiで3日間一緒に過ごすと一気に距離が縮まります。一緒にセッションを聞いて、休憩中に「今のどう思った?」と話して、夜は飲みに行って、移動中に雑談する。この3日間の密度は、リモートでの数ヶ月分のコ