2026年9月26日土曜日

Platform Engineering Kaigi 2026

2026年9月26日(土)に中野で開催された Platform Engineering Kaigi 2026 に参加してきました。全体の感想と、聴講したセッションのまとめです。

概要

  • 今年も参加しました。昨年と同じく Early Bird チケット(2,000円)を購入。
  • 今年は「Platform City」という企画があり、自己紹介のホームページ作成から CI/CD までをやってくれるプラットフォームが用意されていました。AI で開発のハードルがだいぶ下がったこともあり、面白い試みだったと思います。
  • https://citymap.core.paas.jp/?_gl=1*1vgtjjf*_ga*MTE2MDY0NTUzNC4xNzkwMjMwNjcw*_ga_HNMDVRJB0L*czE3OTA4MTc1MDUkbzExJGcwJHQxNzkwODE3NTA1JGo2MCRsMCRoMA..
  • 会場は中野。久しぶりだったので、駅の出口を間違えました。

全体の感想

  • Wi-Fi:会場の Wi-Fi はありましたが、Room を移動すると切れてしまいます。モバイル Wi-Fi を持っていって正解でした。
  • 記録方法:発表は PC でリアルタイムに録音し、Claude で要約しました。今回は土曜日で仕事をする必要もなかったので、次回からは PC を持っていかなくてもよさそうです。
  • 楽しみ1. 屋台:たこ焼きとクレープでした。以前はお酒もありましたが、今回は Red Bull に。Red Bull の大型コンテナ(?)は初めて見ました。
  • 楽しみ2. マッサージ:「エンジニアは手をよく使うから」ということで、今回は手(手首?)をほぐしてもらいました。今までにない刺激でした。

聴講セッション

1. 基調講演:Architecting Agentic Development Platforms

登壇:Kaspar von Grünberg 氏(『Thinking in Platforms』著者)|セッションページ

要点:モデルの競争ではなく、プラットフォームの競争

  • モデルはコモディティ化した。AI 導入後の生産性の差を生むのは、モデルを生産的に動かすプラットフォーム。今後はエージェントも「新しいユーザー」として迎える必要がある。
  • エージェント主導開発には 0〜4 の段階がある。レベル3(人間がオーケストレーター)が最大の転換点。レベル2以下が 72% を占め、「Copilot を入れたから十分」は危険。
  • エージェントは制御ループとして設計する(豊田佐吉の自働化が原点)。ハルシネーションは「最初のイテレーション」と捉え、決定論ゲート(CI など)と確率論ゲート(評価)を通ったものだけを人に渡す。
  • Agentic Development Platform は Tooling(既存の IDP)/Paths(ゴールデンパス)/Agent infrastructure(推論基盤・ハーネス・ガバナンス)の 3 レイヤー。
  • 始め方は /pr-review(読み取り専用でリスクがほぼない)→ /validate-change(ハイブリッド)の順に、2〜3人の小チームで磨く。
  • 質疑応答では「全社展開を急ぐと失敗する(あえて減速する)」「モデルアグノスティックに設計すべき」という話が印象的でした。

2. プラットフォームの複雑性はどこに住むべきか

12:15-12:45 Room A|登壇:小野 大器 氏(enechain)|セッションページ

要点:認知負荷は消えず、プラットフォームへ移る。それでも引き受ける価値がある

  • PR ごとに環境を複製するプレビュー基盤「prothea」(GKE 上の Kubernetes Controller)の設計事例。
  • マニフェスト生成方式ではなく、クラスター内で複製する Controller 方式を採用。ベースマニフェストは変更しない。
  • 設定は段階的なインターフェースにする。普段は紐付け情報だけを書き、導出結果は困ったときに見る窓口にする。
  • Preview ID と Istio(GAMMA)を使ったヘッダーベースのルーティングで、プロダクトをまたいだプレビューも可能。
  • 学び:必要な複雑さを引き受けられる技術を選ぶ/境界は引き直してよい/既存の概念を混ぜない/インターフェースに力を入れる。

3. AI Slopを生まないPlatform Service設計:価値仮説と効果測定ってどうやるの?

13:00-13:30 Hall|登壇:木嶋 幸子 氏(レッドハット)|セッションページ

要点:AI Slop は「低品質」ではなく、受け手の期待値とのずれ

  • 受け手の 42% は、AI Slop を渡してきた相手を信用しなくなる。Platform に置き換えるとアダプションが下がる。
  • AI を使っても渡す側の責任は変わらない。リレーのバトンパス(ハンドオフ)が大事。
  • 4象限(目的への貢献度 × 受け手の負荷)で緑ゾーンを目指す。まず貢献度を上げ、次に負荷を下げる。

成果物の4象限マトリクス

目的への貢献度 \ 受け手の負荷 負荷:低 負荷:高
貢献度:高 緑ゾーン(目標)
目的に効き、受け手も楽
グレー
良いものでも期待値がずれると AI Slop 扱いされる
貢献度:低 グレー
楽だが目的に効かない
グレー
効かないうえに負荷も高い
  • いきなり緑に入ることはほぼない。まず貢献度を確保して(上へ)、それから負荷を下げる(左へ)。
  • 貢献度が低い状態から上げるのは難しい。戦略の誤りは取り返しがつきにくいので、リリース前に検証する。
  • グレーに入っても即中止にはせず、まず原因を分析する。
  • 価値仮説(因果チェーン)を立て、VSM でハンドオフを観測する。正常系(効果)と異常系(副作用)をビジネスのテストケースとして測る。
  • 検討記録が GitHub で公開されています。
感想
  • 「成果物の4象限マトリクス」がとても良かった。もやもやしていたことが可視化・具体化できました。
  • Value Stream Management も良かった。今後の問題解決にぜひ適用したいです。

4. そのTerraform、マージした後も本当に安全か分かっていますか

13:45-14:15 Room A|登壇:ハオ 氏(テナブルネットワークセキュリティジャパン)|セッションページ

要点:静的スキャンの緑チェック ≠ 安全

  • 「その権限が実際に使われているか」はコードの中にない情報なので、静的スキャンでは判断できない。
  • 検証例:どこにもアタッチしない IAM ロールは PR 時の IaC スキャンを通過し、apply 後の継続スキャンで「Inactive IAM Role」として検出された。
  • 静的スキャン(車検)と継続スキャン(ドライブレコーダー)の両方が必要。
  • 見つけたリスクはコンソールで直さず、コードに戻して直す(Cloud to Code)ことでドリフトを防ぐ。
  • AWS 標準機能なら、IAM Access Analyzer(未使用アクセス)や RoleLastUsed で検出し、Claude でコード修正の PR を作る形でも実現できそうです。
感想
  • どちらかと言えば製品の宣伝がメインでした。
  • 少なくとも今の仕事では代わりの手段(IAM Access Analyzer+Claude)があります。
  • セッションのタイトルに釣られた感じがしました。

5. Agentic Platform Engineering on AWS

15:15-15:45 Hall|登壇:松岡 雄地 氏、小西 杏典 氏(AWS ジャパン)|セッションページ

要点:AI エージェントに専門知識を持たせて、ゴールデンパスをスケールさせる

  • 課題:ゴールデンパスを作っても使い方が伝わらず、プラットフォームチームに問い合わせが集中する。
  • AWS が OSS の APEX(Agentic Platform Engineering eXperience)を公開。ECS/EKS 構築のノウハウを Skills・Steering・Rules として持ち、既存のコーディングエージェントに追加して使える。
  • デモ:ECS Architect → ECS Build で要件から ECS Managed Instances を推奨し、検証が通る Terraform を生成。ECS Operation Review では既存環境を 8 つの観点で診断(サーキットブレーカーの無効などを検出)。
  • ガードレールは、人とエージェントの両方に SCP / Permission Boundary / Config / CloudTrail で同じように適用する。
  • リポジトリ:aws-samples/sample-apex-skills
感想

仕事でぜひ活用してみたいです。次のコマンドで導入できるらしいです。

npx apex-skills

6. セルフサービスのオブザーバビリティ基盤をOpenTelemetryで作る

16:45-17:15 Hall|登壇:山口 能迪 氏(Grafana Labs)|セッションページ

要点:「人の判断から仕組みへ」

  • 計装漏れ(Propagator の設定漏れでトレースが切れる)や属性値のばらつき(production / prod / PRD)は、チームの怠慢ではなく仕組みの問題。
  • プラットフォームチームは 3 本柱でセルフサービスを作る。
    1. SDK ディストリビューション:デフォルト値を持たせて配布する
    2. Collector 層:Agent と Gateway の 2 層。PII・サンプリングの方針、OpAMP によるフリート管理
    3. セマンティック規約のレジストリ:OpenTelemetry Weaver で許可する値を定義する
  • どの柱も「配布して終わりにせず、CI で検査する」。
  • 生成 AI による障害調査でも、属性がそろっていることが調査時間とトークンの削減につながる。
  • 解説資料:OpenTelemetryで作るセルフサービステレメトリー基盤(Zenn Book・無料)
感想
  • 冒頭の「メトリクスなどの量が多すぎてコストを圧迫する」という話にとても共感しました。
  • 今の仕事ですぐに使う機会はないかもしれませんが、勉強として OpenTelemetry を実際に使ってみたいです。
  • 登壇者が翻訳した『入門 OpenTelemetry』も調べてみましたが、翻訳の評判はあまり良くないようです。ほかに参考になる本も探してみようと思います。

7. インフラとアプリの境界線と委譲の設計

登壇:鈴木 勝史 氏(スリーシェイク)|セッションページ

要点:境界は「機能」と「役割」の2つ。両者は連動するので継続的に引き直す

  • 機能の境界(プロビジョニング / デプロイ):値が変わるタイミングで管理する側を決め、値の受け渡し方(インターフェース)まで設計する。
    • 事故例:Terraform で apply したら Cloud Run のイメージが初期値に戻り、アプリが停止した。直接の原因は ignore_changes の漏れ、本当の原因は「イメージを誰が管理するか」を決めていなかったこと。
    • 受け渡し方式:手で転記 → 名前で引く → tfstate を参照(ecspresso / tfstate-lookup)→ ストアに書き出し。
  • 役割の境界(プラットフォームチーム / 開発チーム):委譲には 5 段階あり、唯一の正解はない。ゴールデンパスは強制せず、標準から外れる逃げ道を用意する。
  • 委譲で渡すものは「書く権限」「適用する権限」「結果への責任」。AI で書くのは楽になったが、結果への責任は人が持つ。
感想
  • 前から気になっていた論点でした。
  • 話はどちらかというと、アプリ開発者へ渡していく方向の内容でした。
  • 一方、今の仕事ではインフラ側でアプリケーションのコードも書いています。

まとめ

  • 今年は「AI エージェントをプラットフォームのユーザーとしてどう迎えるか」というテーマが全体を通して目立ちました。
  • 共通していたキーワード:ゴールデンパス、認知負荷、ガードレール(決定論的な検査)、境界とインターフェースの設計。
  • すぐに試したいのは、APEX Skills の導入と、IAM Access Analyzer による未使用 IAM ロールの棚卸しです。
  • 買いたい本:実践 プラットフォームエンジニアリング 開発者体験を加速するセルフサービス基盤の設計と構築

※ 各セッションの内容は、当日の録音を文字起こし・要約したものです。聞き取りの誤りや解釈を含む場合があります。

2026年9月15日火曜日

本人にしか見せない画像を、CDN 越しに届ける

本人にしか見せない画像を、CDN 越しに届ける
Engineering Blog

本人にしか見せない画像を、CDN 越しに届ける

モバイルアプリのアイテム登録機能に、CloudFront Signed Cookie を使った画像配信基盤を導入した話。設計と実装、そして途中で踏んだ3つの地雷について書きます。

Rails / AWS S3 / CloudFront Backend & Platform 読了時間 目安 8分

モバイルアプリでは、アイテムを写真つきで登録すると、ポイントや保証の管理に使えるようになっている。今回、この「アイテム写真」と「購入証明書の写真」を、これまでのような単純な公開URLではなく、本人だけがアクセスできる形で配信する基盤を作り直しました。この記事では、その設計と、実装中に遭遇した細かいけれど厄介な問題についてまとめます。

01背景と要件

これまで、アイテム画像はクライアントが用意した URL 文字列をそのまま DB に保存するだけの、ごく単純な仕組みでした。しかしアプリの機能拡張にともない、次の要件が持ち上がりました。

Requirements
  • → 画像はサーバー経由でアップロードするクライアントから直接ストレージを叩くのではなく、アプリサーバーが受け取って保存する形にしたい(バリデーションと保存先を一元管理するため)。
  • → ユーザー自身の画像しか見えないアイテム写真・購入証明書は個人情報に近い。URLを知っていれば誰でも見られる状態は避けたい。
  • → 配信はCDN経由にするS3から直接返すのではなく、CloudFrontでキャッシュ・配信したい。ただし「認証つきCDN配信」と「本人限定」を両立させる必要がある。
  • → モバイル側の実装コストを抑えるリクエストのたびに署名付きURLを都度発行するのではなく、ログインしてしまえばあとは普通に画像を読み込めるようにしたい。

「本人限定」と「CDN配信」を両立させる方法として選んだのが、CloudFront の Signed Cookie です。署名付きURLだと画像1枚ごとに個別の署名が要りますが、Signed Cookie ならログイン時に一度発行しておけば、あとはブラウザ(アプリ)が自動的にCookieを送ってくれるので、モバイル側の実装がシンプルになります。

02アーキテクチャ

全体は「アップロード」と「配信」の2つの流れに分かれます。どちらも同じ非公開S3バケットとRailsアプリサーバーを経由しますが、認証の仕組みが異なります。

LANE 1 — アップロード LANE 2 — 配信 iOS アプリ URLSession app-server Rails API ImageUploadService CloudFront Signed Cookie 検証 S3 非公開バケット ① POST /items(multipart: image) ② image_url + Set-Cookie put_object ③ GET 画像URL(Cookie同梱) 画像 / 403 署名検証OKの時だけ originへフェッチ
図: アップロード(レーン1)と配信(レーン2)は同じS3バケットを共有するが、認証の仕組みは別。配信側の検証はCloudFrontのエッジで行われ、アプリのコードは一切関与しない。

ポイントは、Signed Cookie の検証ロジックはアプリケーションコードのどこにも存在しないということです。Railsが担うのはCookieを「正しく発行する」ことだけで、検証はすべてCloudFrontのエッジ側で完結します。裏を返すと、発行時にちょっとでも間違った値を作ってしまうと、それを検出できるのは実際にCloudFrontにリクエストが飛んだ瞬間だけ、ということでもあります。これが後述するハマりどころに直結します。

Cookieはいつ発行するか

Signed Cookie は次の2つのタイミングで発行・再発行します。

  • ログイン時 — 毎回無条件に新規発行する
  • 認証つきAPI呼び出し時 — リクエストに3種類のCookieが揃っていない場合だけ、その場で再発行する(アプリの再インストールでCookieだけ消えた場合の保険)

有効期限は10年という、一見雑に見える値を設定しています。アプリ側のログイントークン自体に有効期限の概念が無いため、それに合わせて「実質失効しない」十分に長い値にした、という判断です。

03実装したこと

アップロードAPIは、これまでの「クライアントがURL文字列を送る」方式から、「クライアントが画像バイナリそのものを multipart で送る」方式に変更しました。サーバー側は受け取った画像を検証してS3に保存し、DBには完全なURLではなくS3キーだけを保持します。レスポンスを組み立てる際に、このS3キーをCloudFront配信URLへ変換します。

users/{user_id}/items/{item_id}/case/{timestamp}-{random}.jpg
users/{user_id}/items/{item_id}/purchase_certificate/{timestamp}-{random}.jpg

DBに完全なURLではなくキーだけを持たせたのは、配信ドメインを後から変更しやすくするためです。実際、開発環境と本番環境で配信ドメインが異なるため、この一段のクッションが効いています。

04ハマったところ

設計自体は素直でしたが、実装の過程で3つ、地味に厄介な問題にぶつかりました。いずれも「ローカル開発環境には本物のCloudFrontが無い」ことが発見を遅らせた共通点です。

全画像 403

ワイルドカードは「カスタムポリシー」でないと効かない

問題Signed Cookieには「既定ポリシー(canned policy)」と「カスタムポリシー(custom policy)」の2種類があります。既定ポリシーはCookieに有効期限しか含まず、CloudFront側が実際にリクエストされたURLからポリシーを再構築して検証します。今回のように users/{user_id}/* というワイルドカードを含むリソースを許可したい場合、既定ポリシーでは検証時に組み立て直したURLと署名対象が一致せず、問答無用で403になります。

気づいたきっかけローカル開発環境には実物のCloudFrontが存在しないため、この不具合はテストでは一切表面化しませんでした。実際にAWS SDKのCookie発行ロジックを手元で動かし、生成された署名を実際に検証してみて初めて気づく類の問題です。

解決ポリシーJSONを自前で組み立てて明示的に渡す、カスタムポリシー方式に切り替えました。発行されるCookie名も変わる(CloudFront-Expires → CloudFront-Policy)ため、関連するテストやドキュメントもあわせて追随させています。

署名が壊れる

フレームワークがCookieの値を勝手にURLエンコードしていた

問題CloudFrontの署名付きの値は、通常のBase64ではなく+ = / をそれぞれ - _ ~ に置き換えた独自形式です。ところがRailsのCookie機構は、値をSet-Cookieヘッダーに書き出す際、内部でURLエンコード処理を通しており、この~が%7Eに変換されてしまっていました。

送出された値
Signature=abc~def~ghi
実際にブラウザが保持する値
Signature=abc%7Edef%7Eghi

ブラウザ(アプリ)はSet-Cookieの値をそのままのバイト列として保存・送信するだけなので、%7Eは~には戻りません。CloudFront側はこの3文字を「1文字の~」として解釈できず、署名データの一部を読み違えて検証に失敗します。

気づいたきっかけログインもアイテム登録も普通に成功し、返ってくる画像URLも一見正しく見えるため、切り分けが非常に難しい壊れ方でした。実際にレスポンスの生ヘッダーを見て、値の中に%7Eが複数含まれていることに気づいたのがきっかけです。

解決RailsのCookie機構(フレームワークによる自動エスケープ)を経由せず、Set-Cookieヘッダーの文字列を自前で組み立てて直接レスポンスに載せるように変更しました。値自体はエスケープが不要な文字種(英数字と- _ ~のみ)だったため、素通しで問題ありません。

型が壊れる

multipartはネストしたJSONを運べない

問題購入証明書登録APIでは、画像とあわせてAI判定結果(金額や店舗名などを含むJSONオブジェクト)も送る必要があります。しかし multipart/form-data は「名前=文字列」しか運べないため、ネストしたオブジェクトを送ろうとすると、数値が文字列になり、nullが空文字になり、空配列がキーごと消える、という劣化が起こります。

ブラケット記法(NG)
ai_result[price]=1200
ai_result[dates][]=
JSON文字列化(OK)
ai_judgment_result=
{"price":1200,"dates":[]}

解決ネストしたオブジェクトをブラケット記法に分解するのではなく、JSON文字列化した1つのフィールドとして送ってもらい、サーバー側でパースする方式に統一しました。型情報を保ったまま運べるうえ、クライアント側の実装もシンプルになります。

05まとめ

今回いちばん学びが大きかったのは、「検証ロジックがアプリの外(CDNのエッジ)にある」仕組みは、ローカル環境だけでは正しさを保証できないということです。3つのハマりどころのうち2つは、実際にAWSのSDKを手元で動かして署名やヘッダーを1バイトずつ確認するまで発覚しませんでした。テストが通っていても、境界の外側で何が起きているかは別の話だと痛感しました。

一方で、Signed Cookieという仕組み自体は、いったん正しく発行できてしまえばアプリ側の実装は驚くほどシンプルです。画像1枚ごとに署名付きURLを発行する必要も、モバイル側で特別なヘッダーを付け回す必要もありません。ログインという1点さえ正しく作り込めば、あとは普段どおりの画像読み込みで「本人限定配信」が実現できる。地味ですが、良い設計だったと思っています。

この基盤は現在、社内のモバイルアプリチームと連携しながら実際のクライアント実装を進めているところです。実機での検証がまとまり次第、続報を書きたいと思います。

Rails AWS CloudFront Signed Cookie S3 アーキテクチャ

2026年9月3日木曜日

Amazfit HELIO STRAP購入

■発端

加齢のせいか、最近、眠りが浅くなってきた気がする。

色々調べてみたところ、世の中には睡眠の質を計測するアプリがあることを知った。

それで、良さそうなものを入れてみたのだが、以下のような難点があった。

・寝る前にアプリを起動する必要がある
・家族と一緒に寝ているせいか、自分には身に覚えのない「いびき」や「咳」が記録されていた
・一応、録音もされていたが、確認するためには課金が必要だった

 

 

 

ちょうどAmazonセールでポイントも貯まっていたので、何かしら睡眠を計測できる装置を買うのも良いかと思うようになった。

調べてみると、睡眠計測専用の装置もあるらしいが、数万円台のものが多く、かなり高い。

一般的なのはスマートウォッチで、安いものなら1万円未満のものもあった。

ただ、別に画面を見ながら操作をしたいわけでもないし、中国製の端末を身につけて、自分の個人データを計測することには少し抵抗がある。

そこで、最終的にたどり着いたのが「スマートバンド」というものだった。

 

■購入

初めはスマートリングも考慮した。

ただ、これもまあまあ高かったし、そもそも普段から指輪を使う習慣がない。

あまり指輪をつけたくないし、個人的には見栄えもあまり好みではない。

スマートバンドで調べると、GoogleとAmazfitあたりに絞られた。

サイズ感などはGoogleのものが良さそうだったのだが、最近発売されたばかりということもあってか、どこも品薄で売り切れ状態。

しかも、計測自体はできるものの、計測値を評価するためには課金が必要になるようだった。

一方、AmazfitはちょうどAmazonセールで10%ほど安くなっていた。

しかも、今まで貯めていたAmazonポイントで全額支払える。

ということで、Amazfit HELIO STRAPを購入した。

 

■Amazfit HELIO STRAP しばらく使ってみての感想

注文してからは、結構早めに発送された。

・今まで使っていた睡眠測定アプリと並行して使ってみた。寝る前にアプリを立ち上げなくても済むのは助かる。ただし、周りの音を拾う機能がないため、以前記録されていた咳・いびきについては、結局謎のままになってしまっている

・腕に何かを付けて寝たことがないので、これを付けたまま寝られるかが心配だったが、意外と普通に寝られた。ただし、ある程度しっかり手首につけておかないと、起きたときに緩くなっていた。今度、余裕があったら大きめのバンドを買って、足に付けても良いかもしれない

・HYBRIDCHARGEで総合的な「元気度」のようなものが分かるのは助かる。数値をどこまで信頼して良いのかは分からないが、自分が客観的にどのくらい疲れているのかが見えるのは面白い

・モデル名に「FIT」がついていることもあり、運動項目が充実している。普段、あまり運動していない自分でも、これを見ていると運動をして数値化したい気持ちにさせられる。運動する前に何の運動をするか選ぶようになっているが、残念ながら弓道はなかった。代わりにアーチェリーを選択している

・運動項目にスイミングもあるので、おそらく防水性能もあるのだと思う。ただ、高い機器を水の中に入れるのはやはり怖い。一応、旅行中にプールで使ってみたが、今のところは大丈夫そう

・一応、課金メニューは存在するが、確かに使わなくても特に支障はなさそう。睡眠健康評価は少し気になるので、どこかで一度、有効化してみるかもしれない

ちなみに、買ったタイミングで一番最初に気づいたのは子どもだった。

その後は旅行だったこともあり、ずっと付けていたが、誰も特に気づかなかった。

そういえば、着替えるときに妻が気づいていたような……。

2026年9月1日火曜日

「開発環境は止まっているはずなのに」— AWS Configコスト急増の犯人を追いつめた話

TL;DR

  • コスト削減のため夜間・週末に自動で止めているはずの開発環境で、AWS Configの利用料金だけが跳ね上がった
  • 「起動・停止の瞬間にコストが増える」という自然な仮説は、調査したら半分正解・半分不正解だった
  • 本当の犯人は、環境の停止処理がECSサービスの Application Auto Scaling を止めていなかったことによる、丸4日間ぶっ通しのタスク起動失敗ループだった
  • 最初の修正案もレビューで「まだ不十分」と指摘され、AWSの正式な「Auto Scalingの一時停止」機能に作り直すことになった

以下、調査の一部始終と、そこから得た教訓をまとめる。


背景

とあるWebサービスのdevelop/staging環境は、コスト削減のために夜間・週末は自動で止まるようになっている。具体的には、Lambda + EventBridge Schedulerで組んだ仕組みで、

  • ECS Fargateサービスを desiredCount = 0 にスケールダウン
  • RDS Auroraクラスタを停止
  • NAT用・踏み台用のEC2インスタンスを停止

という3点セットを実行し、平日朝に元へ戻す。よくある構成だと思う。

このスケジュールをしばらく無効化していた期間があり、再度有効化したところ、AWS Configの利用料金(Configuration Item記録件数に応じた課金)が急増した。「起動・停止イベントが増えたから、その分Configの記録も増えたのだろう」と考えるのが自然だし、実際そういう外部の事例もいくつか見つかる。

というわけで、原因を確定させ、直すことにした。

第一の仮説:起動・停止のたびにENIが作り直される

ECS Fargateをawsvpcネットワークモードで使っていると、タスクが再作成されるたびに新しいENI(Elastic Network Interface)が払い出される。これ自体はAWS::EC2::NetworkInterfaceというリソースタイプとしてAWS Configに記録される対象で、実際にいくつかのブログ記事でも「ECSタスクの頻繁な入れ替わりでConfigコストが跳ね上がった」という事例が紹介されている。

これを検証するために、調査専用の一時的なCloudTrail(管理イベントのみ、単一リージョン)を新しく作り、既存の設定には一切手を触れずに構築した。ついでにAWS Configのselect-resource-config(Advanced Query)やget-discovered-resource-counts、CloudWatchメトリクス(AWS/Config名前空間のConfigurationItemsRecorded)も使って、実際に何が記録されているかを定量的に追いかけた。

起動イベントの瞬間(数十分のウィンドウ)を見てみると、確かに記録の内訳はこうなっていた。

リソースタイプ件数比率
EC2::NetworkInterface最多約3割
EC2::SecurityGroup次点約2割弱
ECS::Service約1割強
EC2::Instance(NAT/踏み台)1割弱
その他(RouteTable/EIP/RDS等)残り

想定通り、ENI(と、それに付随するSecurityGroupやRouteTableなどの周辺リソース)が最多だった。「やっぱりこれが原因か」と一瞬納得しかけた。

ここで、地味に役立った教訓が一つ。select-resource-config(Advanced Query)は「各リソースの最新の状態」しか返さない。過去のある時点で何が記録されたかを調べようとしても、そのリソースがその後さらに変化していれば、古いタイムスタンプでの記録はヒットしない。「昨日の停止イベントを調べようとしたら0件だった」と一瞬焦ったが、原因はこれだった。過去の特定時点の記録を追うには、リソースIDを指定して履歴を直接取得できるAPI(get-resource-config-history)や、Config配信チャネル先のS3にある設定履歴ファイルを使う必要がある。

しかし、これでは説明がつかない

起動・停止イベント単体の影響は数十件程度で、確かに存在はする。しかし、Cost Explorerでコスト推移を見ると、環境が完全に停止しているはずの期間(週末含む、丸4日間)もコストが下がるどころか上がり続けていた。

もし原因が「起動・停止イベントに伴う一過性のENI churn」なら、停止直後にスパイクしてすぐ収まるはずだ。しかし実際は、停止期間中ずっと高止まりし、日を追うごとにむしろ微増していた。これは「一過性のイベント」の説明では無理がある。「別の何かが継続的に発生している」と考えるべきだった。

寄り道:見当違いの仮説を検証して捨てる

真っ先に疑ったのは、「現在Configが管理しているリソースの総数」を見たときに突出して多かった、あるレガシーなリソースタイプ(過去に別件のコスト急増の原因になったことがあるもの)だった。しかし、そのリソースタイプ単体のCloudWatchメトリクスを見ると、直近ではほとんど動きがなかった。「総数が多い」ことと「直近の変化率が高い」ことは別物だと、指摘されて初めて気づいた。仮説は却下。

次に、「Config全体の記録件数」と「ENI(EC2::NetworkInterface)単体の記録件数」を日次で比較してみると、両者はほぼ同じ形(ベースラインの約15〜18倍に跳ね上がり、その後も高止まり)をしていた。しかも比率がずっと一定(全体のうち常に2〜3割程度)。ここでようやく「ENIが本命で間違いない、ただし発生源は起動・停止イベントとは別」という当たりがついた。

犯人探し:CloudTrailで「誰が」ENIを作っているかを追う

CloudTrailでCreateNetworkInterfaceイベントのdescriptionフィールドを見ると、完全に停止しているはずの1日分の記録が、すべてECSタスクのアタッチメントを示す形式(arn:aws:ecs:...:attachment/<uuid>)だった。しかもユニークなIDが数百件並んでいて、「同じ理由で1回だけ大量発生した」のではなく「何かが延々とタスクを再作成し続けている」ことを示していた。

不思議なことに、明示的なRunTask呼び出しは1日たった数件(既知の定期実行ジョブ分だけ)しかなかった。ここで学んだのが、ECSサービスがスケジューラ経由で内部的にタスクを再配置する場合、RunTaskではなくTaskCreatedという別の管理イベントとして記録されるということ。実際にTaskCreatedイベントの件数を見ると、RunTaskの数十倍あった。

ここから、作成されたENIの「サブネットID」と「セキュリティグループID」の組み合わせを集計し、既知のリソース(AZごとに2つのサブネットに分散配置されるのが通例)と突き合わせることで、犯人サービスを絞り込んだ。最終的にaws ec2 describe-security-groupsでセキュリティグループ名を引いて、内部向けAPIサービスであることが判明した。

根本原因:Auto Scalingが停止と綱引きしていた

該当サービスのdescribe-servicesのイベントログを見て、ようやく全貌が見えた。

(service ...) was unable to place a task. Reason: ResourceInitializationError:
unable to pull secrets or registry auth: unable to retrieve secret from asm:
There is a connection issue between the task and AWS Secrets Manager.
... context deadline exceeded.

タスクが起動すらできず、コンテナ定義に紐づいたSecrets Managerのシークレット取得でタイムアウトしていた。原因はシンプルで、環境停止時にNAT用EC2インスタンスも一緒に止めているため、プライベートサブネットからインターネット(Secrets Managerのパブリックエンドポイント)へ抜ける経路が失われていたからだ。

ではなぜタスクを起動しようとしていたのか。該当サービスにはApplication Auto ScalingがMinCapacity=1で登録されており、環境停止処理がこのAuto Scalingの設定に一切触れていなかった。停止処理はUpdateService(desiredCount=0)でサービスを一旦0にするが、Auto Scalingは自分の管理範囲(Min1〜Max2など)を守ろうとして、定期的にdesiredCountを1へ戻そうとする。

  • Auto Scalingがタスクを1つ起動しようとする
  • NATが止まっているのでSecrets Manager取得に失敗し、約13分でタイムアウト
  • ECSが即座に次のタスクを試みる
  • 以下、環境停止期間中(今回のケースでは最大4日弱)ずっと、13〜27分間隔で繰り返し

サービスのイベントログのタイムスタンプを丹念に追うと、この失敗ループは環境の起動処理でNATが復旧した直後にピタリと止まっていた。ここまで来ると、もう疑いようがない。

一度目の修正:レビューで「まだ足りない」と言われる

最初に考えた修正は素直なものだった。停止時にAuto ScalingのMinCapacityを0にし、起動時に元へ戻す。IAM権限もDescribeScalableTargets/RegisterScalableTargetの2つだけで済むし、実装もシンプル。コードレビューに出した。

すると、こういう指摘が返ってきた。

MinCapacityを0にするだけでは、Auto Scalingに登録済みの「スケジュールドアクション(毎日決まった時刻にMin/Maxを書き換えるアクション)」に上書きされてしまうのでは?

確認してみると、まさにその通りだった。当該サービスには「毎朝の営業開始前にスケールアウトする」「毎晩の営業終了後にスケールインする」という、Auto Scaling自体のスケジュールドアクションが別途設定されていた。これは環境の起動停止スケジュールとは完全に独立して毎日動く。つまり:

  • 環境を停止した直後の夜、Auto Scaling側の「スケールイン」アクションがMinCapacityを1に戻す
  • 環境の起動処理より前に、Auto Scaling側の「スケールアウト」アクションがMinCapacityを1に戻す(NAT起動よりも先に)

MinCapacityを直接書き換える方式では、この2つの独立したスケジュールに対して常に後出しじゃんけんで負ける構造だった。しかも、TerraformでこのMinCapacityを管理している場合、Lambdaが書き換えた値は次回のterraform applyで元に戻されてしまう(driftの元)という副作用まで指摘された。

二度目の修正:AWS標準の「一時停止」機能を使う

Application Auto Scalingには、まさにこの用途のための機能がある。RegisterScalableTargetAPIのSuspendedStateだ。MinCapacity/MaxCapacityの値には一切触れず、「動的スケーリング」と「スケジュールドアクション」の両方を丸ごと一時停止・再開できる。

# 停止時
autoscaling.register_scalable_target(
    ServiceNamespace="ecs",
    ResourceId=resource_id,
    ScalableDimension="ecs:service:DesiredCount",
    SuspendedState={
        "DynamicScalingInSuspended": True,
        "DynamicScalingOutSuspended": True,
        "ScheduledScalingSuspended": True,
    },
)

# 起動時(すべてFalseに戻すだけ)
autoscaling.register_scalable_target(
    ServiceNamespace="ecs",
    ResourceId=resource_id,
    ScalableDimension="ecs:service:DesiredCount",
    SuspendedState={
        "DynamicScalingInSuspended": False,
        "DynamicScalingOutSuspended": False,
        "ScheduledScalingSuspended": False,
    },
)

これなら、Auto Scaling側のスケジュールドアクションも含めて確実に止まるし、Min/MaxCapacityの値そのものは触らないのでTerraformとのdriftも起きない。IAM権限も変わらず同じ2つで済んだ。

あわせて、「環境起動後のサービス安定化待ちがタイムアウトした場合でも、Auto Scalingを止めたまま放置しない」ようにtry/finallyでresume処理を保証するようにした。一時停止したまま忘れられると、今度は逆に「業務時間中の正常なオートスケールが働かない」という別の障害になりかねないからだ。

教訓

  1. 「怪しいものが大量にある」と「直近悪化した原因である」は別物。総数のスナップショットだけで判断せず、時系列のレート(メトリクス)で裏を取る。
  2. AWS Configの「今のリソース状態」を返すAPIと、「過去の履歴」を返すAPIは別物。過去のある時点を調べたいなら履歴系のAPI(あるいは配信されたログファイル)を使う。
  3. CloudTrailの「明示的なAPI呼び出し」と「サービス内部の自動処理」は別のイベント名で記録されることがある。RunTaskが少ないからといって、タスクがあまり起動していないとは限らない。
  4. 「止める」処理を書くときは、それを上書きしうる自動化(Auto Scaling、他のスケジュール、外部の監視ツールによる自動修復など)が他にないか、必ず洗い出す。今回は「環境の起動停止スケジュール」と「Auto Scaling自身のスケジュール」という、独立に育った2つの自動化が噛み合っていなかったことが根本原因だった。
  5. コードレビューは通す。一度目の修正で「動くはず」と思っていたが、実際にはterraformの中身まで確認しないと見抜けない落とし穴があった。一人で完結させず、指摘をもらえる体制は大事。

似たような「開発環境の自動停止」を運用している人の参考になれば幸いです。

2026年8月16日日曜日

子どもの誕生日プレゼント

子どもの誕生日プレゼント

準備など

子どもの誕生日が近くなると、何が欲しいのか聞いているが、その日の気分によって欲しいものが変わることが多い。

最終的に、最近ブームになっているマインクラフトのおもちゃの剣が欲しいと言った。

ちなみに、その前は友達が持っていたトミカの連結バスが欲しかったらしい。また、妻は勉強にもなる遊びができるレゴが良いらしい。

まずは本命である剣をネットで探してみたら、まあまあ高かった。

そんな中、Yahoo!ショッピングのある店がAmazonより3分の2くらいの値段で販売していたので、購入することにした。Amazonではないので発送期間が少し心配ではあったが、誕生日の6日前だったので、まあ大丈夫だろうと思っていた。

妻があげたかったレゴは、イオンで安く売っていたので別途購入。

ついでに、マインクラフトのアイスクリームケーキも欲しかったらしく、こちらも注文した。

トミカ購入

だいぶ前に小田原図書館で子ども向けイベントのチラシを見つけ、8/9(日)だったので申し込んでいた。

参加費は200円で、場所は小田原駅から近い公民館だった。

初めは市外からの申し込みだったので、あまり期待していなかったが、受付済みの連絡をもらった。前日になるとSMSで改めて案内が来るなど、意外としっかりしている感じだった。

子どもは最初、あまり行く気がなさそうだったが、「トミカを買ってあげる」ことで、何とか行くことができた。

最近はあまり外に出かけたくないらしく、物で釣るしかなくなってきた感じがする。

公民館は駅から近かったが、結構年季の入った建物だった。

参加者は全部で10組くらいで、比較的小さな子どもが多かった。

それでも、うちの子としては工作もできて、まあまあ満足だったらしい。

ちなみに、小田原市の何かしらの取り組みらしく、ゲストの人たちが後ろで大勢見学していた。

そのあとは、いつもの子育てサロンと図書館、バーガーキングに行って、最後に約束していたトミカを買った。

配送遅延

誕生日が間近になっても、プレゼントの発送連絡が来ない。


店に連絡してみると、その途端にステータスが「発送済み」になっていた。なんだか怪しい。

どうやら海外からの発送だったらしい。確かに購入する際に、発送目安の案内をちゃんと読んでいなかった。こちらの確認不足でもあるが、商品ページのどこにも海外からの発送とは書かれていなかった。

幸い、子どもは今回いろいろ買ってもらったこともあってか、多少遅れても問題ないらしい。

結局、プレゼントが届いたのは誕生日を過ぎた8/16だった。

購入してから一週間以上かかっている。海外発送としては早い方なのかもしれないが、誕生日プレゼントだったことを考えると、あまりすっきりしない感じだった。


2026年8月12日水曜日

「このメンバーで大丈夫か?」と言われたときに、何をどう動かしたか

背景

あるとき、上層部の会議で新しいプロジェクトの担当割りについて「大丈夫か?」という懸念が出た。 特定のメンバーが新しいプロジェクトを主導することになったタイミングで、である。

現場ではすでに、社内ルールの整備・1on1・報告書の提出依頼といった対応が進められていた。 しかし、それらを重ねても上層部の不安はなかなか払拭されなかった。

この経験を通じて考えたこと、実際に取った対応を、一般化した形で残しておく。

気づいたこと:「人を信じてもらう」では不安は消えない

1on1・報告書・ルール整備は、いずれも「本人の行動を改善する/可視化する」ためのアプローチだった。 しかし振り返ると、上層部が求めていたのはそれとは少し違う種類の安心感だったのではないかと思う。

  • 1on1は主観的なやり取りで、外部には見えない
  • 本人が書く報告書は、恣意性が入りやすく信頼の証明にはなりにくい
  • ルール整備は属人化リスクを下げる仕組みだが、「この人で大丈夫か」への直接的な回答にはならない

つまり、「本人を信じてほしい」という路線だけでは、組織が求める「仕組みとして担保されている」という安心材料にならないということだった。

そこで、アプローチの重心を「人への信頼」から「仕組みへの信頼」に移すことを考えた。

具体的に取ったアプローチ

1. 懸念の正体を具体化する

「大丈夫か?」という発言は抽象的だった。技術力なのか、コミュニケーションなのか、過去の経緯なのか。 まずは懸念の中身を関係者間で言語化することから始めた。曖昧なままだと、何をやっても「まだ不安」と言われ続けてしまう。

2. 「合意形成」を先に固める

技術的な対応内容そのものよりも、「その対応レベルで良い」ということについて、関係する事業側の担当者が納得・合意しているかどうかの方が本質的だと考えた。

対応が完了していても、その基準について事業側の合意が取れていない状態で報告だけが出ると、一方的な連絡で終わってしまい、報告後も同じ構造の懸念が再燃しかねない。 そのため、正式な報告の前に、関係者との間で「このリスク許容レベルで進める」という合意を明示的に取ることを優先した。

口頭だけでなく、誰が・何に合意したかが後から分かる形で残すようにした。

3. コミュニケーションの実態を、本人を介さずに確認する

本人が頑張っていても、周囲とのコミュニケーションの取り方によって、些細な話でも上位者へのエスカレーションが起きやすい状態になっている可能性を考えた。

これは本人の能力の問題ではなく、関係者との情報のやり取りの構造の問題として捉えた。 そこで、調整が可能な立場の人に、関係者側が実際どう感じているかをヒアリングしてもらう、という動きを提案した。 自分から直接動くと、本人の頭越しに評判を探るような形になりかねないため、権限のある人に依頼する形を取った。

4. 人選・役割の見直しは、別の文脈で、タイミングを分けて考える

対応を進める中で、そもそも本人の得意な仕事のスタイルと、今回のタスクの性質が噛み合っていない可能性も見えてきた。 これは技術的な問題とは次元の違う話であり、扱いを一段慎重にする必要があると考えた。

  • 直近の懸念対応(合意形成・報告)と同時に進めない。混ぜると「指摘を機に外された」という見え方になりやすい
  • 「向いていない」ではなく「本人の強みをより活かせる場がある」というプラスの文脈で伝えられるかを重視する
  • 決定権のない自分が直接本人に伝えるのではなく、権限を持つ人に判断してもらう

5. 提案は「選択肢」として渡す

関係者に何かを依頼するときは、「やってください」ではなく「一つの案として、採用するかどうかも含めてお任せします」という伝え方を意識した。

理由は、進め方や本人への伝え方について、自分より状況を把握している人がいる場合、その人の判断を尊重した方が結果的にうまく進むことが多いためである。 指示ではなく選択肢を渡すことで、相手も動きやすくなる。

まとめ

「大丈夫か?」という不安に対して、本人の頑張りをアピールするだけでは効果が薄い場面がある。 そのときに有効だったのは、次の3つの視点だった。

  1. 人への信頼ではなく、仕組みへの信頼に置き換える(合意形成、段階的な権限拡大、レビュー体制など)
  2. 懸念の背景にある構造(コミュニケーションの取り方など)を、本人を介さずに確認する
  3. 技術的な対応と、人選・役割の見直しは、時間軸を分けて別々に進める

組織の中で「この人は大丈夫か」という空気が生まれたとき、個人の弁明や努力だけで払拭しようとすると長期化しやすい。 仕組みとして「何かあっても組織は対応できる」という状態を作ることの方が、結果的に速く、かつ本人にとってもフェアな解決に近づくと感じている。


2026年7月23日木曜日

AWS Certified Data Engineer - Associate 合格

 

背景

前回の受験で申し込んでいたCloud Licenseの期限がまだ残っていたので、有効活用するために何か簡単そうな試験に挑戦しようと思った。

残っている試験はAssociateかSpecialtyのどちらかだが、Specialtyはまだ難しそうだった。

Data Engineerは今の仕事とはあまり縁がなさそうではあるが、一応AWS Glueを使ったことがあり、全く経験がないわけでもない。

「これも落ちたら仕方ない」という気持ちで挑戦することにした。

勉強した書籍・サービス

  • Cloud Licenseのみを利用し、分からないところはインターネットで記事を検索して学習した。

  • RedshiftやEMR関連はほぼゼロから勉強するような感じだったが、これまでのAWSの経験があるからか、それ以外は比較的スムーズに理解できた。間違えた問題の復習も2周目まで終えることができた。

  • Cloud Licenseの有料オプションが切れたタイミングで試験日を決定。もともとは金曜日に受験する予定だったが、午前中に空きがなかったため、仕方なく木曜日にした。

  • ネット上でData Engineer試験の内容をよくまとめた記事を見つけたので、最後はそれを使って復習した。しかし、読んでいると分からない内容が次々と出てきて、試験直前まで焦っていた。

受験日

  • 朝早めに家を出て、いつもの喫茶店で試験対策のまとめ記事を読みながら最後の勉強をした。

  • 試験会場で席に座ると、左斜め前の人がCCNAらしき試験を受けていた。最初は気にならなかったが、その人が考えるたびに頭に手を乗せる動作を繰り返していて、視界に入るたびに気が散ってしまった。仕方なく見えないように姿勢を変えていたが、それでも時々視界に入ってしまった。

  • 十数問を見直し対象としてチェックし、ちょうど制限時間いっぱいで試験を終えた。

結果

  • 少し自信はあったものの、点数はこれまでと同じように合格ラインを少し上回る程度だった。とりあえず合格できたので良しとしよう。

     


     

次はどうするか

  • Cloud Licenseも期限切れになったので、今度こそAWS認定試験はしばらく休もうと思う。

  • 本も読みたいが、以前から周りに「簡単だから取れる」と勧められている簿記の勉強にも挑戦してみたい。