<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>wan0ri Lab</title><link>https://wan0ri.com/</link><description>Recent content on wan0ri Lab</description><generator>Hugo</generator><language>ja-jp</language><lastBuildDate>Sun, 07 Jun 2026 21:00:00 +0900</lastBuildDate><atom:link href="https://wan0ri.com/index.xml" rel="self" type="application/rss+xml"/><item><title>AWSのセキュリティについて入門してみた</title><link>https://wan0ri.com/posts/2026/02/18/introduction-to-aws-security/</link><pubDate>Wed, 18 Feb 2026 21:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2026/02/18/introduction-to-aws-security/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;AWS セキュリティに入門するなら、いきなり &lt;code&gt;GuardDuty&lt;/code&gt; や &lt;code&gt;Security Hub&lt;/code&gt; から入るより、まずは &lt;code&gt;IAM&lt;/code&gt; / ネットワーク / 暗号化 / ログの4本柱を押さえた方が理解しやすいです。&lt;/li&gt;
&lt;li&gt;学習順としては、&lt;code&gt;IAM&lt;/code&gt; → &lt;code&gt;VPC&lt;/code&gt; / &lt;code&gt;Security Group&lt;/code&gt; → &lt;code&gt;KMS&lt;/code&gt; → &lt;code&gt;CloudTrail&lt;/code&gt; / &lt;code&gt;CloudWatch&lt;/code&gt; → &lt;code&gt;Security Hub&lt;/code&gt; / &lt;code&gt;GuardDuty&lt;/code&gt; の順が入りやすいと感じました。&lt;/li&gt;
&lt;li&gt;既存環境の是正対応では、「設定値を直す力」だけでなく、「なぜそれを直すのか」「本当に直すべきなのか」を説明する力も重要でした。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;p&gt;2026年4月から、大手損保向けの AWS 上に構築されたデータ蓄積・分析システムの運用保守案件にアサインされています。&lt;br&gt;
この案件では、経営統合に向けて既存 AWS 環境のセキュリティ是正を進める必要があり、私はその是正対応を 1 人で担当しています。&lt;br&gt;
対象サービスはかなり広く、たとえば以下のような AWS サービスが含まれています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GuardDuty&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;KMS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EKS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DynamoDB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Athena&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SQS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EMR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;S3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Glue&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SNS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Lake Formation&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Network Firewall&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CloudTrail&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EC2&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;EBS&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ECR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ELB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IAM&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Lambda&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CloudWatch&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VPC&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Security Hub&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;正直、最初から全部を深く理解していたわけではありません。&lt;br&gt;
むしろ、&lt;strong&gt;既存環境のセキュリティ是正を進める中で、何から理解すればよいかを後追いで整理していった&lt;/strong&gt; という方が実態に近いです。&lt;/p&gt;</description></item><item><title>Neovim操作方法チートシート</title><link>https://wan0ri.com/posts/2026/01/19/neovim-operate/</link><pubDate>Mon, 19 Jan 2026 21:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2026/01/19/neovim-operate/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;この記事は、VSCode から Neovim へ寄せるときに迷いやすい「操作の置き換え」をまとめたチートシートです。&lt;br&gt;
マウス前提の操作をキーボード中心にどう置き換えるかを主眼に、必要に応じてマウス操作も併記します。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="前提本記事の設定"&gt;前提（本記事の設定）&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Leader キー: &lt;code&gt;&amp;lt;Space&amp;gt;&lt;/code&gt;（スペース）&lt;/li&gt;
&lt;li&gt;エクスプローラ: neo-tree.nvim（&lt;code&gt;&amp;lt;C-b&amp;gt;&lt;/code&gt; トグル、&lt;code&gt;&amp;lt;leader&amp;gt;e&lt;/code&gt; フォーカス、&lt;code&gt;&amp;lt;leader&amp;gt;er&lt;/code&gt; カレントへ移動）&lt;/li&gt;
&lt;li&gt;検索: telescope.nvim（&lt;code&gt;&amp;lt;C-p&amp;gt;&lt;/code&gt;、&lt;code&gt;&amp;lt;leader&amp;gt;ff&lt;/code&gt;/&lt;code&gt;fg&lt;/code&gt;/&lt;code&gt;fb&lt;/code&gt; など）&lt;/li&gt;
&lt;li&gt;ターミナル: toggleterm.nvim（&lt;code&gt;&amp;lt;leader&amp;gt;tt&lt;/code&gt;/&lt;code&gt;tv&lt;/code&gt;/&lt;code&gt;tf&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;補完/LSP/診断: nvim-cmp + lspconfig（&lt;code&gt;gd&lt;/code&gt;/&lt;code&gt;gr&lt;/code&gt;/&lt;code&gt;K&lt;/code&gt;/&lt;code&gt;&amp;lt;leader&amp;gt;rn&lt;/code&gt;/&lt;code&gt;&amp;lt;leader&amp;gt;ca&lt;/code&gt;、&lt;code&gt;[d&lt;/code&gt;/&lt;code&gt;]d&lt;/code&gt;/&lt;code&gt;&amp;lt;leader&amp;gt;e&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;マルチカーソル: vim-visual-multi（&lt;code&gt;Ctrl-n&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;Git: lazygit（&lt;code&gt;&amp;lt;leader&amp;gt;gg&lt;/code&gt;）、neogit.nvim（&lt;code&gt;:Neogit&lt;/code&gt;）、diffview.nvim（&lt;code&gt;:DiffviewOpen&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="カーソル移動vscode--neovim"&gt;カーソル移動（VSCode → Neovim）&lt;/h2&gt;&lt;p&gt;よく使う順に。まずはここを覚えると体感が一気に上がります。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基本: &lt;code&gt;h&lt;/code&gt; 左 / &lt;code&gt;j&lt;/code&gt; 下 / &lt;code&gt;k&lt;/code&gt; 上 / &lt;code&gt;l&lt;/code&gt; 右&lt;/li&gt;
&lt;li&gt;単語単位: &lt;code&gt;w&lt;/code&gt; 次の単語先頭 / &lt;code&gt;e&lt;/code&gt; 単語末尾 / &lt;code&gt;b&lt;/code&gt; 前の単語先頭（大文字版 &lt;code&gt;W/E/B&lt;/code&gt; は記号無視で広めの単語）&lt;/li&gt;
&lt;li&gt;行頭・行末: &lt;code&gt;0&lt;/code&gt; 行頭 / &lt;code&gt;^&lt;/code&gt; 先頭の非空白 / &lt;code&gt;$&lt;/code&gt; 行末&lt;/li&gt;
&lt;li&gt;画面スクロール: &lt;code&gt;Ctrl-d&lt;/code&gt; 半ページ下 / &lt;code&gt;Ctrl-u&lt;/code&gt; 半ページ上 / &lt;code&gt;Ctrl-f&lt;/code&gt; 1ページ下 / &lt;code&gt;Ctrl-b&lt;/code&gt; 1ページ上 / &lt;code&gt;zz&lt;/code&gt; カーソル行を中央&lt;/li&gt;
&lt;li&gt;段落: &lt;code&gt;{&lt;/code&gt; 前の空行ブロック / &lt;code&gt;}&lt;/code&gt; 次の空行ブロック&lt;/li&gt;
&lt;li&gt;行ジャンプ: &lt;code&gt;gg&lt;/code&gt; 先頭 / &lt;code&gt;G&lt;/code&gt; 末尾 / &lt;code&gt;:42&lt;/code&gt; 42行へ&lt;/li&gt;
&lt;li&gt;1行内検索: &lt;code&gt;f{文字}&lt;/code&gt; 右へ移動 / &lt;code&gt;F{文字}&lt;/code&gt; 左へ移動 / &lt;code&gt;t{文字}&lt;/code&gt; 直前まで / &lt;code&gt;;&lt;/code&gt; 反復 / &lt;code&gt;,&lt;/code&gt; 逆反復&lt;/li&gt;
&lt;li&gt;検索移動: &lt;code&gt;/パターン&lt;/code&gt; → &lt;code&gt;n&lt;/code&gt; 次 / &lt;code&gt;N&lt;/code&gt; 前&lt;/li&gt;
&lt;li&gt;戻る/進む（ジャンプリスト）: &lt;code&gt;Ctrl-o&lt;/code&gt; 戻る / &lt;code&gt;Ctrl-i&lt;/code&gt; 進む&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="マウスの補助必要なら"&gt;マウスの補助（必要なら）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;一時的にマウス有効: &lt;code&gt;:set mouse=a&lt;/code&gt;（恒久化なら設定で &lt;code&gt;vim.opt.mouse = &amp;quot;a&amp;quot;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;端末（WezTerm）で“端末ネイティブ選択”をしたい場合は Shift を押しながらドラッグ&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="選択と編集vscode-のドラッグ選択--neovim-での置き換え"&gt;選択と編集（VSCode のドラッグ選択 → Neovim での置き換え）&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;ビジュアルモード（選択開始）: &lt;code&gt;v&lt;/code&gt; 文字 / &lt;code&gt;V&lt;/code&gt; 行 / &lt;code&gt;Ctrl-v&lt;/code&gt; 矩形（端末により &lt;code&gt;Ctrl-q&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;選択端の切替: &lt;code&gt;o&lt;/code&gt;（選択中に押すと反対側へ） / 直前の選択を再選択: &lt;code&gt;gv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;テキストオブジェクト（論理単位で選ぶ）
&lt;ul&gt;
&lt;li&gt;単語: &lt;code&gt;viw&lt;/code&gt;（内側の単語）/ &lt;code&gt;vaw&lt;/code&gt;（単語＋周辺空白）&lt;/li&gt;
&lt;li&gt;括弧/引用: &lt;code&gt;vi&amp;quot;&lt;/code&gt;, &lt;code&gt;va&amp;quot;&lt;/code&gt;, &lt;code&gt;vi)&lt;/code&gt;, &lt;code&gt;va)&lt;/code&gt; ほか多数&lt;/li&gt;
&lt;li&gt;段落: &lt;code&gt;vip&lt;/code&gt;（内側）/ &lt;code&gt;vap&lt;/code&gt;（空行含む）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;検索語を順に選ぶ: &lt;code&gt;gn&lt;/code&gt;（現在の検索語の次を選択）/ &lt;code&gt;gN&lt;/code&gt;（前）&lt;/li&gt;
&lt;li&gt;変換系のよく使うもの: &lt;code&gt;ciw&lt;/code&gt;（単語を変更）, &lt;code&gt;dw&lt;/code&gt;（単語削除）, &lt;code&gt;xp&lt;/code&gt;（文字入替）, &lt;code&gt;gU&lt;/code&gt;/&lt;code&gt;gu&lt;/code&gt;（大文字/小文字）, &lt;code&gt;&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;&lt;/code&gt;（インデント）&lt;/li&gt;
&lt;li&gt;コピー/貼り付け: &lt;code&gt;y&lt;/code&gt;（ヤンク）/ &lt;code&gt;p&lt;/code&gt;（貼り付け）/ &lt;code&gt;&amp;quot;+y&lt;/code&gt;（システムへ）/ &lt;code&gt;&amp;quot;+p&lt;/code&gt;（システムから）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="マルチカーソルvim-visual-multi"&gt;マルチカーソル（vim-visual-multi）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;カーソル下の一致を選択・次追加: &lt;code&gt;Ctrl-n&lt;/code&gt;（反復で複数） / 終了: &lt;code&gt;Esc&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;矩形に複数カーソル: &lt;code&gt;Ctrl-v&lt;/code&gt; → 縦に選んで &lt;code&gt;I&lt;/code&gt;（先頭に挿入）/&lt;code&gt;A&lt;/code&gt;（末尾に追記）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="ファイルフォルダ操作エクスプローラ-neo-tree"&gt;ファイル/フォルダ操作（エクスプローラ: neo-tree）&lt;/h2&gt;&lt;h3 id="開く移動"&gt;開く/移動&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;トグル: &lt;code&gt;&amp;lt;C-b&amp;gt;&lt;/code&gt; / フォーカス: &lt;code&gt;&amp;lt;leader&amp;gt;e&lt;/code&gt; / カレントへ: &lt;code&gt;&amp;lt;leader&amp;gt;er&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;フォルダ展開/閉: &lt;code&gt;Enter&lt;/code&gt; / &lt;code&gt;Backspace&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="作成"&gt;作成&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;a&lt;/code&gt; → 入力欄に名前を打って確定&lt;/li&gt;
&lt;li&gt;ディレクトリ作成は末尾に &lt;code&gt;/&lt;/code&gt; を付ける（例: &lt;code&gt;docs/&lt;/code&gt;）。中間ディレクトリも作成可（例: &lt;code&gt;docs/guides/&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="コピー移動リネーム削除"&gt;コピー/移動/リネーム/削除&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;コピー: &lt;code&gt;c&lt;/code&gt; → 貼り付け先で &lt;code&gt;p&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;カット（移動）: &lt;code&gt;x&lt;/code&gt; → 貼り付け先で &lt;code&gt;p&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;リネーム: &lt;code&gt;r&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;削除: &lt;code&gt;d&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;パスをコピー: &lt;code&gt;y&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="プラグインを使わない作成も可"&gt;プラグインを使わない作成も可&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;新規ファイル: &lt;code&gt;:edit パス&lt;/code&gt; → &lt;code&gt;:w&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ディレクトリ: &lt;code&gt;:!mkdir -p パス&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="検索とジャンプtelescope--標準"&gt;検索とジャンプ（telescope + 標準）&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;ファイル: &lt;code&gt;&amp;lt;C-p&amp;gt;&lt;/code&gt; または &lt;code&gt;&amp;lt;leader&amp;gt;ff&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;全文grep: &lt;code&gt;&amp;lt;leader&amp;gt;fg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;バッファ: &lt;code&gt;&amp;lt;leader&amp;gt;fb&lt;/code&gt; / コマンドパレット: &lt;code&gt;&amp;lt;leader&amp;gt;sp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;直前/次のファイルへはバッファ操作: &lt;code&gt;:ls&lt;/code&gt; → &lt;code&gt;:b 番号&lt;/code&gt; / &lt;code&gt;:bnext&lt;/code&gt; / &lt;code&gt;:bprevious&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="画面分割ウィンドウ操作vscode-の複数エディタ"&gt;画面分割・ウィンドウ操作（VSCode の複数エディタ）&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;分割: &lt;code&gt;:vsplit&lt;/code&gt;（縦）/ &lt;code&gt;:split&lt;/code&gt;（横）&lt;/li&gt;
&lt;li&gt;移動: &lt;code&gt;Ctrl-w&lt;/code&gt; を押してから &lt;code&gt;h/j/k/l&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;サイズ調整: &lt;code&gt;Ctrl-w&lt;/code&gt; &lt;code&gt;&amp;gt;&lt;/code&gt;/&lt;code&gt;&amp;lt;&lt;/code&gt;（幅）/ &lt;code&gt;+&lt;/code&gt;/&lt;code&gt;-&lt;/code&gt;（高さ）/ &lt;code&gt;=&lt;/code&gt;（均等）&lt;/li&gt;
&lt;li&gt;閉じる: &lt;code&gt;:q&lt;/code&gt;（ウィンドウ）/ &lt;code&gt;:bd&lt;/code&gt;（バッファ）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="タブvscode-のタブ"&gt;タブ（VSCode のタブ）&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;新規タブ: &lt;code&gt;:tabnew&lt;/code&gt; / 次/前: &lt;code&gt;gt&lt;/code&gt; / &lt;code&gt;gT&lt;/code&gt; / タブ一覧: &lt;code&gt;:tabs&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="ターミナルtoggletermと横2分割"&gt;ターミナル（ToggleTerm）と「横2分割」&lt;/h2&gt;&lt;h3 id="基本"&gt;基本&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;下パネル: &lt;code&gt;&amp;lt;leader&amp;gt;tt&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;右パネル（縦分割）: &lt;code&gt;&amp;lt;leader&amp;gt;tv&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;フロート: &lt;code&gt;&amp;lt;leader&amp;gt;tf&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Lazygit: &lt;code&gt;&amp;lt;leader&amp;gt;gg&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ターミナル内からノーマルへ: &lt;code&gt;Esc&lt;/code&gt; または &lt;code&gt;jk&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="横2分割で並べる縦に2つ"&gt;横2分割で並べる（縦に2つ）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;方法1（ToggleTerm だけで）&lt;/p&gt;</description></item><item><title>Neovim MCP サーバーの役割りやインストール手順</title><link>https://wan0ri.com/posts/2026/01/17/neovim-mcp-server-installation/</link><pubDate>Sat, 17 Jan 2026 21:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2026/01/17/neovim-mcp-server-installation/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;この記事では、Neovim から Codex TUI（MCP クライアント）経由で使う「MCP サーバー」の導入手順をまとめます。&lt;br&gt;
名称、入手先、必要トークン/キーの取得方法、&lt;code&gt;servers.json&lt;/code&gt; と &lt;code&gt;.env&lt;/code&gt; の具体例、検証方法までを一通り記載します。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="導入した-mcp-サーバー一覧"&gt;導入した MCP サーバー（一覧）&lt;/h2&gt;&lt;h3 id="context7ドキュメント検索"&gt;context7（ドキュメント検索）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/context7" target="_blank" rel="noopener noreferrer"&gt;https://github.com/context7&lt;/a&gt;

&lt;/p&gt;
&lt;h3 id="playwrightブラウザ自動操作"&gt;playwright（ブラウザ自動操作）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/microsoft/playwright" target="_blank" rel="noopener noreferrer"&gt;https://github.com/microsoft/playwright&lt;/a&gt;

&lt;/p&gt;
&lt;h3 id="filesystemローカルファイル操作"&gt;filesystem（ローカルファイル操作）&lt;/h3&gt;&lt;p&gt;ルート制限付きでローカルを参照します。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/github" target="_blank" rel="noopener noreferrer"&gt;https://github.com/github&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;必要: &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;&lt;/p&gt;
&lt;h3 id="terraformhashicorp-公式-mcp-server"&gt;terraform（HashiCorp 公式 MCP Server）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/hashicorp/terraform-mcp-server" target="_blank" rel="noopener noreferrer"&gt;https://github.com/hashicorp/terraform-mcp-server&lt;/a&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;イメージ: &lt;code&gt;hashicorp/terraform-mcp-server:0.3.0&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;必要: &lt;code&gt;TERRAFORM_CLOUD_TOKEN&lt;/code&gt;（Terraform Cloud のユーザ API トークン）&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;備考: 以降の手順では &lt;code&gt;~/.config/mcphub/servers.json&lt;/code&gt; と &lt;code&gt;~/.config/mcphub/.env&lt;/code&gt; を前提にします。Neovim 側は &lt;code&gt;&amp;lt;leader&amp;gt;as&lt;/code&gt; で &lt;code&gt;servers.json&lt;/code&gt; を開き、&lt;code&gt;&amp;lt;leader&amp;gt;ar&lt;/code&gt; で &lt;code&gt;.env&lt;/code&gt; を再読込できます。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id="トークン--api-キーの取得手順"&gt;トークン / API キーの取得手順&lt;/h2&gt;&lt;h3 id="github-用-github_token個人アクセストークン"&gt;GitHub 用 &lt;code&gt;GITHUB_TOKEN&lt;/code&gt;（個人アクセストークン）&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;GitHub にサインイン → 右上アイコン → Settings。&lt;/li&gt;
&lt;li&gt;左メニュー最下部の Developer settings → Personal access tokens。&lt;/li&gt;
&lt;li&gt;Fine-grained（推奨）または Classic を発行。最小権限で可。
&lt;ul&gt;
&lt;li&gt;参照のみで足りる場合は、対象リポジトリの「Contents: Read」など最小に絞る。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;生成したトークンを控え、&lt;code&gt;~/.config/mcphub/.env&lt;/code&gt; に保存（例は後述）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="terraform-cloud-用-terraform_cloud_token"&gt;Terraform Cloud 用 &lt;code&gt;TERRAFORM_CLOUD_TOKEN&lt;/code&gt;&lt;/h3&gt;&lt;ol&gt;
&lt;li&gt;Terraform Cloud にサインイン（&lt;code&gt;https://app.terraform.io&lt;/code&gt;）。&lt;/li&gt;
&lt;li&gt;User Settings → Tokens → Create an API token。&lt;/li&gt;
&lt;li&gt;生成されたトークンを控え、&lt;code&gt;~/.config/mcphub/.env&lt;/code&gt; に保存。
&lt;ul&gt;
&lt;li&gt;互換のため &lt;code&gt;TF_TOKEN_app_terraform_io&lt;/code&gt; へも同値を設定するとツール連携が楽です（例は後述）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;セキュリティ注意: トークンは Git 管理しないパス（&lt;code&gt;~/.config/mcphub/.env&lt;/code&gt;）に置き、権限は最小に。公開リポジトリへコミットしないこと。&lt;/p&gt;</description></item><item><title>Neovimプラグインまとめ</title><link>https://wan0ri.com/posts/2026/01/15/neovim-plugins/</link><pubDate>Thu, 15 Jan 2026 21:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2026/01/15/neovim-plugins/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;この記事では、現在の Neovim 構成で導入している主要プラグインを「用途別」に整理し、あわせて最小の操作方法（キーやコマンド）をまとめます。&lt;br&gt;
各プラグインの GitHub リンクも併記します。&lt;/p&gt;
&lt;h3 id="前提環境"&gt;前提・環境&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Plugin manager: lazy.nvim（ロックファイル運用）&lt;/li&gt;
&lt;li&gt;Terminal: WezTerm（フォント MesloLGS NF、背景は不透明運用）&lt;/li&gt;
&lt;li&gt;Leader キー: &lt;code&gt;&amp;lt;Space&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="お断り"&gt;お断り&lt;/h3&gt;&lt;p&gt;この記事では、プラグインの機能の詳細説明などには触れません。&lt;br&gt;
あくまで「導入しているプラグインの一覧と、主要な操作方法のまとめ」を目的としています。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="git--diff変更確認と操作"&gt;Git / Diff（変更確認と操作）&lt;/h2&gt;&lt;h3 id="lazygittui-クライアント"&gt;lazygit（TUI クライアント）&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;brew install lazygit
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="gitsignsnvim行単位差分"&gt;gitsigns.nvim（行単位差分）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/lewis6991/gitsigns.nvim" target="_blank" rel="noopener noreferrer"&gt;https://github.com/lewis6991/gitsigns.nvim&lt;/a&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;変更行のサイン表示、ステージやリセットなど。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="neogitmagit-ライク-ui"&gt;neogit（Magit ライク UI）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/NeogitOrg/neogit" target="_blank" rel="noopener noreferrer"&gt;https://github.com/NeogitOrg/neogit&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:Neogit&lt;/code&gt; で起動。コミット/branch/merge を対話的に。&lt;/p&gt;
&lt;h3 id="diffviewnvim差分履歴"&gt;diffview.nvim（差分/履歴）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/sindrets/diffview.nvim" target="_blank" rel="noopener noreferrer"&gt;https://github.com/sindrets/diffview.nvim&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;代表コマンド: &lt;code&gt;:DiffviewOpen&lt;/code&gt; / &lt;code&gt;:DiffviewClose&lt;/code&gt;。&lt;/p&gt;
&lt;h3 id="vim-fugitive低レベル-git"&gt;vim-fugitive（低レベル Git）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/tpope/vim-fugitive" target="_blank" rel="noopener noreferrer"&gt;https://github.com/tpope/vim-fugitive&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;&lt;code&gt;:G blame&lt;/code&gt; などのコマンドが使える。&lt;/p&gt;
&lt;h3 id="補足lazygit-の格納先と日本語化"&gt;補足（lazygit の格納先と日本語化）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Lazygit 自体は TUI（プラグインではなく外部ツール）。macOS 既定の設定パスは &lt;code&gt;~/Library/Application Support/lazygit&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;運用しやすいように &lt;code&gt;~/.config/lazygit&lt;/code&gt; に設定を置き、App Support 側はシンボリックリンクで統一。
&lt;ul&gt;
&lt;li&gt;コマンド例:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; mkdir -p ~/.config/lazygit
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ln -snf ~/.config/lazygit &lt;span class="s2"&gt;&amp;#34;~/Library/Application Support/lazygit&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;日本語化は &lt;code&gt;~/.config/lazygit/config.yml&lt;/code&gt; に次を追記:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;gui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ja&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;Neovim からの起動: &lt;code&gt;&amp;lt;leader&amp;gt;gg&lt;/code&gt;（toggleterm 経由のフロート表示）。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="markdown書く整えるプレビュー"&gt;Markdown（書く・整える・プレビュー）&lt;/h2&gt;&lt;h3 id="vim-table-mode表整形"&gt;vim-table-mode（表整形）&lt;/h3&gt;&lt;p&gt;&lt;a href="https://github.com/dhruvasagar/vim-table-mode" target="_blank" rel="noopener noreferrer"&gt;https://github.com/dhruvasagar/vim-table-mode&lt;/a&gt;

&lt;/p&gt;</description></item><item><title>ECS×EFS: 片方向SGでコンテナが起動しない事象の再現と解消</title><link>https://wan0ri.com/posts/2026/01/11/ecs-validate/</link><pubDate>Sun, 11 Jan 2026 16:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2026/01/11/ecs-validate/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;EFS&lt;/code&gt; は各 &lt;code&gt;AZ&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;アベイラビリティゾーン。リージョン内の独立区画。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 に「マウントターゲット（&lt;code&gt;ENI&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Elastic Network Interface。仮想NIC。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
）」を持つ。
&lt;ul&gt;
&lt;li&gt;例: ap-northeast-1a/1c にそれぞれ固定プライベート IP を持つ &lt;code&gt;ENI&lt;/code&gt; が存在。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ECS&lt;/code&gt; タスクは、配置された &lt;code&gt;AZ&lt;/code&gt; に近い EFS マウントターゲットへ &lt;code&gt;NFS&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;EFS が使うファイル共有プロトコル。標準ポート 2049/TCP。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 で接続する。
&lt;ul&gt;
&lt;li&gt;つまり、1a に居れば 1a の ENI、1c に居れば 1c の ENI へ向かうのが基本動作。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;今回の原因は「コンテナ側 &lt;code&gt;SG&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;セキュリティグループ。VPC の仮想ファイアウォール（stateful）。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 のアウトバウンドが片側の IP/CIDR のみ許可」だったこと。別 &lt;code&gt;AZ&lt;/code&gt; の ENI への &lt;code&gt;2049/TCP&lt;/code&gt; がブロックされ、マウント失敗。
&lt;ul&gt;
&lt;li&gt;デプロイ時に「許可されていない側の AZ/IP」への経路になり、&lt;code&gt;NFS&lt;/code&gt; 接続不可 → タスク起動失敗。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;対策は「&lt;code&gt;SG&lt;/code&gt; のアウトバウンドに“抜けていた側のサブネット &lt;code&gt;CIDR&lt;/code&gt;”を追加」し、両 &lt;code&gt;AZ&lt;/code&gt; のマウントターゲット到達を許可。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="概要"&gt;概要&lt;/h2&gt;&lt;p&gt;参画先で、&lt;code&gt;ECS&lt;/code&gt; にデプロイしたコンテナが「&lt;code&gt;EFS&lt;/code&gt; に接続できない」エラーで起動に失敗しました。&lt;br&gt;
この記事では、&lt;/p&gt;</description></item><item><title>2026年の目標</title><link>https://wan0ri.com/posts/2026/01/07/2026-goals/</link><pubDate>Wed, 07 Jan 2026 21:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2026/01/07/2026-goals/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;p&gt;昨年末、身内に不幸がありましたので、新年のご挨拶は控えさせていただきます。&lt;br&gt;
年始は厳しい寒さもあり、ゆっくり過ごしていました。&lt;/p&gt;
&lt;p&gt;ありきたりですが、新年の目標を書いていきたいと思います。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="2026-年の目標"&gt;2026 年の目標&lt;/h2&gt;&lt;p&gt;今年の目標を、表にまとめました。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;No.&lt;/th&gt;
					&lt;th&gt;目標&lt;/th&gt;
					&lt;th&gt;目標に関する説明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;早起きする&lt;/td&gt;
					&lt;td&gt;学習時間を捻出するため、朝型に切り替える。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;2&lt;/td&gt;
					&lt;td&gt;テックノート更新継続&lt;/td&gt;
					&lt;td&gt;これまでのキャリアで学んできたことや習得したいことをアウトプットし続けていく。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;3&lt;/td&gt;
					&lt;td&gt;資格取得&lt;/td&gt;
					&lt;td&gt;3 月までに AWS SAA、あとは順次 AWS SOA と AWS DVA を取得する。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4&lt;/td&gt;
					&lt;td&gt;監視ツールのインプット&lt;/td&gt;
					&lt;td&gt;Datadog や New Relic など、市場で採用されている監視ツールのインプットを進めていきたい。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;5&lt;/td&gt;
					&lt;td&gt;コードでのアウトプット&lt;/td&gt;
					&lt;td&gt;ブログ偏重を見直し、隔月 1 回はコードでのアウトプットを実施する。&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;6&lt;/td&gt;
					&lt;td&gt;転職する&lt;/td&gt;
					&lt;td&gt;2025 年は惨敗だったので、内定取れるよう自己研鑽を継続していく。&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id="1-早起き"&gt;1. 早起き&lt;/h2&gt;&lt;p&gt;これまでは、仕事から帰宅後に風呂や食事が終わった後に就寝するギリギリまで勉強をしていました。&lt;br&gt;
しかし、日中に仕事をこなして帰宅するまでに体力を消耗した状態での勉強は、かなりの労力を必要としました。&lt;/p&gt;
&lt;p&gt;2025 年の反省を活かして、今年からは早起きして学習時間を確保することにしました。&lt;/p&gt;
&lt;p&gt;2026 年 1 月 5 日 （月）の仕事始め以降、4:30〜5:00 に学習を開始しています。&lt;br&gt;
まだ 3 日しか経過していませんが、このペースで継続していきたいと思います。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;設定：平日 4:30 起床／5:00〜6:30 学習、土日は 6:00 起床で 1 時間。&lt;/li&gt;
&lt;li&gt;測定：&lt;code&gt;Streak&lt;/code&gt; とカレンダーで皆勤を可視化（週 5 日以上 ○）。&lt;/li&gt;
&lt;li&gt;代替案：寝不足時は 30 分だけに短縮し、翌日に調整する。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=cKdJNgFrmM0" target="_blank" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=cKdJNgFrmM0&lt;/a&gt;

&lt;/p&gt;</description></item><item><title>WezTerm 設定と VSCodeライクな Neovim 導入メモ</title><link>https://wan0ri.com/posts/2025/12/29/wezterm-and-neovim/</link><pubDate>Mon, 29 Dec 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/29/wezterm-and-neovim/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;WezTerm へ移行。軽快でカスタムしやすい。&lt;/li&gt;
&lt;li&gt;Cmd+Shift+F は「メニューを残した最大化」、Ctrl+Shift+F はネイティブ全画面。&lt;/li&gt;
&lt;li&gt;Neovim を VSCode 風に整備（Dashboard/Dropbar/Context/Toggleterm/Copilot/Lazygit）。&lt;/li&gt;
&lt;li&gt;インフラ用途（Terraform/YAML/Docker）重視。保存時フォーマットと LSP/診断まで最小構成。&lt;/li&gt;
&lt;li&gt;「VSCode ならどう押す？」の対応表を掲載。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="今回の設定リポジトリ"&gt;今回の設定リポジトリ&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Neovim: &lt;code&gt;https://github.com/wan0ri/neovim&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;WezTerm: &lt;code&gt;https://github.com/wan0ri/wezterm&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="neovim-を使ってみたいと思った理由"&gt;Neovim を使ってみたいと思った理由&lt;/h2&gt;&lt;p&gt;元々Vimを使うことに抵抗感が合ったのですが、以下の理由で使ってみたくなりました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;もともとターミナルが iTerm2 だったが、セッションが増えると動作が重く感じることがあった。&lt;/li&gt;
&lt;li&gt;GPU 描画で軽いという評判の WezTerm を試したかった。
&lt;ul&gt;
&lt;li&gt;公式: Wezterm: &lt;code&gt;https://wezfurlong.org/wezterm&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;公式: GitHub: &lt;code&gt;https://github.com/wez/wezterm&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ターミナルと VSCode を行き来するコンテキストスイッチを減らしたい。
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;CLI から “そのまま編集→コミット” まで完結させたい。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;参考: WezTerm のカスタマイズ記事（配色・UI の考え方）
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;https://zenn.dev/mozumasu/articles/mozumasu-wezterm-customization&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;備考: 端末エミュレータの性能はフォント・レンダリング設定・プラグイン状況で大きく変わるため、厳密なベンチは環境依存です。本記事では体感差と設定の工夫に主眼を置いています。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;私自身がインフラクラウド領域の人間なので、VimであったりCLIだったりは、この先この領域を主戦場とするためには避けて通れないものであると思います。&lt;br&gt;
ですので、今回はそれらのツールを使ってみて、あわよくば使いこなせたらいいかなと思い触れてみることにしました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="wezterm-の設定"&gt;WezTerm の設定&lt;/h2&gt;&lt;p&gt;今回のポイントは「VSCode/iTerm2 の癖を残しつつ、端末として軽快に」するために設定しました。&lt;br&gt;
主要な調整点は次の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;フォント/フォールバック
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;MesloLGS NF → Monaco → &amp;quot;Courier New&amp;quot; → Menlo&lt;/code&gt;、CJK/絵文字に &lt;code&gt;Hiragino Sans / Apple Color Emoji&lt;/code&gt; を追加&lt;/li&gt;
&lt;li&gt;フォントサイズ: 14pt、IME 有効化: &lt;code&gt;use_ime = true&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;透明度/背景
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;window_background_opacity = 0.65&lt;/code&gt;、&lt;code&gt;macos_window_background_blur = 10&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;タブ/見た目
&lt;ul&gt;
&lt;li&gt;タイトルバー簡素化（&lt;code&gt;window_decorations = &amp;quot;RESIZE&amp;quot;&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;アクティブタブをシアン &lt;code&gt;#00BCD4&lt;/code&gt; に統一（&lt;code&gt;format-tab-title&lt;/code&gt; で色付け）&lt;/li&gt;
&lt;li&gt;カーソル色もシアン（&lt;code&gt;cursor_bg/cursor_border/compose_cursor = &amp;quot;#00BCD4&amp;quot;&lt;/code&gt;）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;改行とウィンドウ最大化
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Option+Enter&lt;/code&gt; は「送信せず改行」: &lt;code&gt;SendString(&amp;quot;\n&amp;quot;)&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;ウィンドウ最大化（メニューバーは表示のまま）: &lt;code&gt;Cmd+Shift+F&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;独自イベント &lt;code&gt;toggle-maximize&lt;/code&gt; で &lt;code&gt;window:maximize()/restore()&lt;/code&gt; をトグル&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ネイティブ全画面（別スペース・メニュー非表示）: &lt;code&gt;Ctrl+Shift+F&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;そのほか
&lt;ul&gt;
&lt;li&gt;コピー/貼り付け: &lt;code&gt;Cmd+c / Cmd+v&lt;/code&gt;、タブ操作（&lt;code&gt;Cmd+t / Cmd+w / Ctrl+Tab&lt;/code&gt;）、リーダーキーは &lt;code&gt;Ctrl+q&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;端末の分割/操作: &lt;code&gt;LEADER+d&lt;/code&gt;（縦）/ &lt;code&gt;LEADER+r&lt;/code&gt;（横）/ &lt;code&gt;LEADER+h/j/k/l&lt;/code&gt;（移動）/ &lt;code&gt;LEADER+s&lt;/code&gt; → &lt;code&gt;h/j/k/l&lt;/code&gt;（サイズ調整）/ &lt;code&gt;LEADER+z&lt;/code&gt;（ズーム）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;設定例（keybinds の一部）&lt;/p&gt;</description></item><item><title>インフラ特化テックノートを GitHub Pages + MkDocs Material で立ち上げた話</title><link>https://wan0ri.com/posts/2025/12/28/technote/</link><pubDate>Sun, 28 Dec 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/28/technote/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;インフラ領域の学習・業務で得た知見を素早く引ける「自分用の決定版カンペ」を作りました。&lt;/li&gt;
&lt;li&gt;公開先: &lt;a href="https://wan0ri.github.io/note" target="_blank" rel="noopener noreferrer"&gt;https://wan0ri.github.io/note&lt;/a&gt;

 （ソースは GitHub、デプロイは GitHub Pages）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="採用の背景"&gt;採用の背景&lt;/h2&gt;&lt;p&gt;当初は Google Cloud 上で Wiki.js をコンテナ化して運用する構成を検討しておりました。&lt;br&gt;
理由としては、Terraform にもっと触れてスキルを上げたかったこと、また触れたことのない Google Cloud を扱ってみたかったことが大きな理由です。&lt;/p&gt;
&lt;p&gt;しかし、個人用途では次が重くなりがちであることで開発自体に負担がかかっていました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;毎月のクラウドコスト（常時稼働ストレージ、監視などの積み上げ）&lt;/li&gt;
&lt;li&gt;セキュリティ/パッチ適用、バックアップ、アップグレードなどの保守運用&lt;/li&gt;
&lt;li&gt;アプリケーションのライフサイクルに自分が束縛される&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;一方で GitHub Pages + MkDocs は&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;0 円〜低コスト（Pages + Actions の範囲で完結）&lt;/li&gt;
&lt;li&gt;変更は Git 管理 → PR ベースで安全に運用&lt;/li&gt;
&lt;li&gt;書く → プレビュー → 公開までが速い（Docs-as-Code）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;で構成できます。&lt;/p&gt;
&lt;p&gt;結論として、 &lt;strong&gt;コスト/保守性/スピードのバランス&lt;/strong&gt; から GitHub Pages + MkDocs Material を採用することにしました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="サイト構成インフラ特化将来の言語枠"&gt;サイト構成（インフラ特化＋将来の言語枠）&lt;/h2&gt;&lt;p&gt;インフラ特化のセクションを主軸に設計しました。&lt;/p&gt;</description></item><item><title>2025年を振り返る</title><link>https://wan0ri.com/posts/2025/12/24-2/looking-back-to-2025/</link><pubDate>Wed, 24 Dec 2025 23:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/24-2/looking-back-to-2025/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;本日仕事納めでした。&lt;br&gt;
本当は 常駐先は 29 日（月）まで仕事なのですが、明日 25 日は通院するため、そして 1 日飛ばして金曜日に出社は流石にしんどいので、連休を取ることにしました。&lt;/p&gt;
&lt;p&gt;はい、余談が過ぎたので、表題通り 2025 年の振り返りをしていきたいと思います。&lt;/p&gt;
&lt;p&gt;読んでいて気落ちする内容や、表現が直球すぎて不快に感じる部分もあると思いますが、自分の正直な気持ちを綴って今年の活動を締めくくりたいと思います。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="転職活動"&gt;転職活動&lt;/h2&gt;&lt;p&gt;今年を振り返るうえで、一番大きなトピックは転職活動だったと思います。&lt;br&gt;
以前書いた全敗記録の振り返りは以下になります。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://wan0ri.com/posts/2025/11/15/job-hunting-completely-halted/"&gt;https://wan0ri.com/posts/2025/11/15/job-hunting-completely-halted/&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;過去に書いた転職活動の記事には書いていなかったのですが、&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;そもそもなぜ転職をしようと思ったのか&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;というきっかけを書いていきたいと思います。&lt;/p&gt;
&lt;h3 id="ses-から自社開発企業への転職を考えた理由"&gt;SES から自社開発企業への転職を考えた理由&lt;/h3&gt;&lt;p&gt;SES という働き方そのものを否定するつもりはありません。&lt;br&gt;
実際、これまでインフラエンジニアとして経験を積めたのは、SES という環境で複数の現場に関わることができたからだと感じています。&lt;/p&gt;
&lt;p&gt;一方で、この働き方を続ける中で、「このままで良いのだろうか」という違和感が、徐々に大きくなっていきました。&lt;/p&gt;
&lt;p&gt;特に大きかったのは、自分の意思とは関係のない契約都合に、キャリアや働き方が左右され続ける点です。&lt;/p&gt;
&lt;p&gt;気に入った現場や、信頼できるメンバー、腰を据えて改善していきたいシステムに出会えても、「契約満了」という理由だけで離任せざるを得ないことが何度もありました。&lt;br&gt;
成果や意欲とは無関係に環境が変わることに、強いストレスを感じていました。&lt;/p&gt;
&lt;p&gt;また、自分の仕事が誰のために、どのように役立っているのかを実感しづらい点も理由の一つです。&lt;/p&gt;
&lt;p&gt;インフラとしてシステムは動いており、障害対応や改善も行っています。&lt;br&gt;
しかし、見えているのは常駐先の SIer までで、その先にいるエンドユーザーの存在を感じる機会はほとんどありませんでした。&lt;/p&gt;
&lt;p&gt;自分の仕事が社会にどう貢献しているのかを実感できないまま、タスクだけが消化されていく日々に、正直なところ虚しさを感じていました。&lt;/p&gt;
&lt;p&gt;作ったものが実際に使われ、改善され、その結果として次の価値につながっていく。&lt;br&gt;
そうした一連の流れを、自分の目で確認できる環境で働きたいと考えるようになりました。&lt;/p&gt;
&lt;p&gt;SES が悪いから転職したい、という単純な理由ではありません。&lt;br&gt;
自分がこの先エンジニアとしてどこに責任を持ち、何に価値を感じて働きたいのかを考えた結果、自社サービスを持つ企業で働くという選択肢に行き着きました。&lt;/p&gt;
&lt;h3 id="自己研鑽の推進"&gt;自己研鑽の推進&lt;/h3&gt;&lt;p&gt;インフラエンジニアとして今後のキャリアを考えたとき、オンプレミス中心の経験よりも、AWS をはじめとしたパブリッククラウドを軸にしたキャリアを歩んでいきたいと考えるようになりました。&lt;/p&gt;
&lt;p&gt;その考えから、2024 年以降は AWS の構築・保守案件を中心に参画してきました。&lt;br&gt;
なお、IT 業界へキャリアチェンジした当初は運用保守案件が中心でしたが、その中でも AWS の監視・運用業務に携わった経験があります。&lt;/p&gt;
&lt;p&gt;実際に転職活動を始める前に、各企業の技術スタックを調査してみたところ、構築には Terraform、監視には Datadog や New Relic、AWS の基盤としては ECS/EKS ＋ Fargate といったサーバーレス構成を採用している企業が多数を占めていることが分かりました。&lt;/p&gt;</description></item><item><title>『SREの知識地図』の著者を招いてお送りするSREの旅〜 Road3 システムの状態を観測する を視聴して【イベント感想】</title><link>https://wan0ri.com/posts/2025/12/24-1/sre-knowledgemap-road3/</link><pubDate>Wed, 24 Dec 2025 21:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/24-1/sre-knowledgemap-road3/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;観測の目的は「ユーザー体験の保護」と「変更速度の維持」の両立。&lt;/li&gt;
&lt;li&gt;会話の中心は &lt;code&gt;SLI&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Service Level Indicator。ユーザー価値に直結する計測指標。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
/&lt;code&gt;SLO&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Service Level Objective。SLI に対する目標値。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 と &lt;code&gt;Error Budget&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;SLO を満たしつつ“許される失敗の枠”。意思決定の通貨。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
。症状ベース監視へ寄せる。&lt;/li&gt;
&lt;li&gt;メトリクス/ログ/トレースの役割分担と、&lt;code&gt;cardinality&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;ラベル組み合わせの種類数。増え過ぎるとコスト/クエリが悪化。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 管理・サンプリング・&lt;code&gt;PII&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Personally Identifiable Information。個人を特定できる情報。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
対策。&lt;/li&gt;
&lt;li&gt;アラートは SLO 由来＋ &lt;code&gt;Runbook&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;障害時の標準手順。前提/確認/軽減/深掘り/フォローを定型化。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
紐付け。ノイズは抑制/集約/遅延で削減。&lt;/li&gt;
&lt;li&gt;ダッシュボードは監視盤と探索盤を分離し、リリースと観測を結合（&lt;code&gt;Release Health&lt;/code&gt;) &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;リリース単位で健全性を追うビュー。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;2025 年 12 月 22 日（月）に開催された連続講義『SRE の知識地図』の著者を招いてお送りする SRE の旅〜 Road3 システムの状態を観測する を視聴しました。&lt;br&gt;
視聴メモと、実務に転用できそうなポイントを残します。&lt;/p&gt;</description></item><item><title>【AWS社協力】ECS最新アップデートと導入事例セッション 〜私たちはこう使っている〜 参加メモ【イベント感想】</title><link>https://wan0ri.com/posts/2025/12/21/latest-ecs-updates-case-studies-session/</link><pubDate>Sun, 21 Dec 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/21/latest-ecs-updates-case-studies-session/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;2025 年 12 月 18 日（木）開催「【AWS 社協力】ECS 最新アップデートと導入事例セッション 〜私たちはこう使っている〜」に参加。&lt;/li&gt;
&lt;li&gt;個人的ハイライト：
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Service Connect&lt;/strong&gt; の段階導入（名前解決/通信分離/ロールバック設計）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capacity Providers&lt;/strong&gt; での Fargate/EC2 混在運用とコスト・可用性のバランス取り。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CodeDeploy Blue/Green&lt;/strong&gt; + 観測性（CloudWatch/OTel）で“安全に速く”を両立。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;本記事は私的メモ。参加者限定資料は非公開につき、引用・画像・添付は一切なし。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;以下の connpass ページのイベントに参加しました。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://asiaquest.connpass.com/event/376288/" target="_blank" rel="noopener noreferrer"&gt;https://asiaquest.connpass.com/event/376288/&lt;/a&gt;

&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主催：アジアクエスト（AWS 社協力）&lt;/li&gt;
&lt;li&gt;開催日時：2025 年 12 月 18 日（木）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="免責と公開ポリシー"&gt;免責と公開ポリシー&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;本記事は当日の視聴と自分の理解に基づく要点メモです。&lt;/li&gt;
&lt;li&gt;参加者限定で配布された資料は公開されていないため、スクリーンショットやスライド抜粋、配布ファイルのアップロード等は一切行いません。&lt;/li&gt;
&lt;li&gt;議事の正確性は主催・登壇者の公式発表を優先します。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="当日のトピック公開情報に影響しない範囲の要約"&gt;当日のトピック（公開情報に影響しない範囲の要約）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;ECS の最近のアップデート動向と活用パターン&lt;/li&gt;
&lt;li&gt;事例に基づく運用・移行・コスト最適化の勘所&lt;/li&gt;
&lt;li&gt;デプロイ戦略（ローリング/Blue-Green）と観測性の標準化&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="視聴メモ非機密一般化した学び"&gt;視聴メモ（非機密・一般化した学び）&lt;/h2&gt;&lt;h3 id="1-設計と運用の接点ecs-文脈"&gt;1) 設計と運用の接点（ECS 文脈）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;まず“テンプレと観測性”を標準化：タスク定義の &lt;code&gt;logConfiguration&lt;/code&gt;、リソース制限、タグ、暗号化、ALB/SG 命名などを共通化。&lt;/li&gt;
&lt;li&gt;レビューはテンプレ差分で行う：&lt;code&gt;taskdef/servicedef&lt;/code&gt; とデプロイ設定（最小/最大ヘルシー、失敗時ロールバック）を PR で確認。&lt;/li&gt;
&lt;li&gt;小さく検証：単一サービスで &lt;strong&gt;Service Connect&lt;/strong&gt; を試し、名前解決/通信分離/段階導入のクセを掴む。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-自動化まわりcicd-と-iac"&gt;2) 自動化まわり（CI/CD と IaC）&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;IaC に観測性を同梱：Container Insights/OTel Collector/ダッシュボードを最初から一式で配布。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blue/Green&lt;/strong&gt; の標準化：&lt;strong&gt;CodeDeploy&lt;/strong&gt;（またはデプロイメント回りの既定）を IaC で強制し、ヘルスチェックと自動ロールバックを有効化。&lt;/li&gt;
&lt;li&gt;変更頻度で境界を切る：サービス定義とネットワーク/ALB/共有リソースは分離し、影響半径を限定。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="3-品質セキュリティのガードレール"&gt;3) 品質・セキュリティのガードレール&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;タスク実行ロール/タスクロールの最小権限化、シークレットは &lt;strong&gt;Secrets Manager&lt;/strong&gt;/&lt;strong&gt;SSM&lt;/strong&gt; を参照で注入。&lt;/li&gt;
&lt;li&gt;ECR スキャン/署名、イメージタグ戦略（イミュータブル）を徹底し、展開ミスを抑止。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Drift 検知&lt;/strong&gt;（GUI 変更の監査）と差分の PR 化で“単一の真実”を IaC に戻す。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="4-gui-と-iac-のすり合わせ"&gt;4) GUI と IaC のすり合わせ&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;コンソール検証 →IaC への取り戻しを前提設計。監査ログで変更検知 → 差分抽出 →PR 作成の自動化を検討。&lt;/li&gt;
&lt;li&gt;命名/タグ/環境変数/ポート命名などの“人が触る部分”を標準化して、すれ違いを最小化。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="後に試してみたいこと自分向け"&gt;後に試してみたいこと（自分向け）&lt;/h2&gt;

&lt;details class="code-collapse" &gt;
 &lt;summary&gt;（コピペ可）&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ ] Service Connect をステージングの 1 サービスで段階導入（名前解決/通信分離/ロールバック検証）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ ] Capacity Providers の標準方針を策定（Fargate/Fargate Spot/EC2 の使い分け指針 + コスト試算）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ ] Blue/Green を IaC で強制（失敗時自動ロールバック/ヘルスチェック既定/監視連携）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ ] GUI 変更 → IaC 反映のミニ PoC（監査イベント検知 → 差分抽出 → GitHub PR 作成）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;[ ] 観測性の既定（FireLens/OTel/ダッシュボード）をテンプレ化し、全サービスへ段階適用
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;

&lt;hr&gt;
&lt;h2 id="まとめ現時点の所感"&gt;まとめ（現時点の所感）&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;“人と運用を含めた設計”と“コードへ戻す運用”が鍵。スライドのテクニックに寄り過ぎず、現場の運用フローから逆算して IaC を位置付ける。&lt;/li&gt;
&lt;li&gt;まずは小さな自動化と検査を導入し、テンプレート差分をレビュー対象に。GUI の便利さは活かしつつ、最終的な資産はコードで一元管理する。&lt;/li&gt;
&lt;li&gt;参加者限定資料は非公開のため、本記事では引用・添付なし。必要に応じて公開情報に合わせてタイトル/日時などのみ後日更新する。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>参加したイベントをただ聞いているだけでは意味が無い【学びを業務に導入した話】</title><link>https://wan0ri.com/posts/2025/12/17/introducing-learning/</link><pubDate>Wed, 17 Dec 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/17/introducing-learning/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;これまで参加したイベントのまとめ。&lt;/li&gt;
&lt;li&gt;学びから、「ただ聞いているだけでは何の意味もない」と思い、実際に業務にて実践した話。&lt;/li&gt;
&lt;li&gt;客先常駐という制約もあり導入出来ないものも多々あったが、その中でも導入を推進したものを記録として残す。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;p&gt;今まで個人学習を継続的に推進していきましたが、レベルの高いエンジニアの方や企業がどの様に業務を推進しているのかを知りたくなりました。&lt;br&gt;
そこで、2025 年 11 月から本格的に connpass で開催しているイベントに参加することにしました。&lt;/p&gt;
&lt;p&gt;当ブログのイベントをまとめたものは以下になります。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://wan0ri.com/category/#event"&gt;https://wan0ri.com/category/#event&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;イベントに参加するごとにブログへまとめを進めてきましたが、 &lt;strong&gt;ただブログに書いているだけでは何の意味も無い&lt;/strong&gt; と感じたため、実際に常駐先にどのような形で導入したのかをまとめていきたいと思います。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="推進したものリスト"&gt;推進したものリスト&lt;/h2&gt;&lt;p&gt;以下、これまで書いてきた記事から、実際に導入・推進してきたものになります。&lt;br&gt;
客先のエンドユーザー様の意向や常駐先での導入が諸々の理由で実現不可能となったものもあり、実現不可能になったものは導入できませんでした。&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id="どろんこ-ai-話綺麗じゃない-ai-の苦労話-を視聴してイベント感想"&gt;どろんこ AI 話〜綺麗じゃない AI の苦労話〜 を視聴して【イベント感想】&lt;/h3&gt;&lt;p&gt;&lt;a href="https://wan0ri.com/posts/2025/11/27/forkwell-doronko-ai-impressions/"&gt;https://wan0ri.com/posts/2025/11/27/forkwell-doronko-ai-impressions/&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;2025 年 12 月時点での常駐先には Microsoft Copilot（以降、Copilot） が用意されておりました。&lt;br&gt;
作業時にエラーや不明点がでてきた際に各々で Copilot を利用して問題解決していくのですが、各自での Copilot の使い方が統一されていないこともあり &lt;strong&gt;出力された回答にばらつきが生じてしまいました。&lt;/strong&gt;&lt;br&gt;
そこで、以下の利用方法に絞って Copilot に対するテンプレートを作成することで問題解決に対する回答精度を向上させることを導入することにしました。&lt;/p&gt;


&lt;details class="code-collapse" &gt;
 &lt;summary&gt;1. AWS 関連エラー/警告の解説・原因分析・解消法テンプレ&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;以下は AWS に関する問題です。 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;再現環境・ログ・条件を整理して説明して、「原因の可能性」「すぐ試せる切り分け手順」「根本原因対応」を返してください。 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;出力は Markdown 形式で構成してください。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### 1. 問題の概要
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; サービス/リソース: &amp;lt;例: ECS Fargate サービス&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; リージョン: &amp;lt;例: ap-northeast-1&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 権限/ロール: &amp;lt;IAM Role info&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 発生タイミング: &amp;lt;Deploy/Update/Scale&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### 2. 入力した操作とレスポンス
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 実行したコマンド/操作: 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;aws CLI or console 操作&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 得られたエラーメッセージ:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;Error log snippet, up to 10 lines&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### 3. 現在までに試したこと
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; &amp;lt;例: タスク定義コンテナ再ビルド, セキュリティグループ確認など&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**要求される出力**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;1.&lt;/span&gt; &lt;span class="gs"&gt;**推定原因（根拠付き）**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;2.&lt;/span&gt; &lt;span class="gs"&gt;**試すべき切り分け手順（ステップ）**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;3.&lt;/span&gt; &lt;span class="gs"&gt;**最終的な解決手順例**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;4.&lt;/span&gt; &lt;span class="gs"&gt;**関連ドキュメントや CLI 例**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;



&lt;details class="code-collapse" &gt;
 &lt;summary&gt;2. コンテナデプロイ用シェル生成テンプレ&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;私は以下の AWS 環境でコンテナをデプロイしたいです。 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;指定した要件に応じた &lt;span class="gs"&gt;**bash シェルスクリプト（CLI）**&lt;/span&gt; を生成してください。 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;生成するシェルには必ずエラー処理・ログ出力・必要な前提条件チェックを含めてください。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## デプロイ要件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;1.&lt;/span&gt; 対象サービス: &amp;lt;ECS&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;2.&lt;/span&gt; クラスター/サービス名: &amp;lt;名前&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;3.&lt;/span&gt; イメージリポジトリ: &amp;lt;ECR URI&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;4.&lt;/span&gt; タスク定義/Pod spec の要点:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; CPU/Mem
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; ポート
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;-&lt;/span&gt; Env vars
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;5.&lt;/span&gt; IAM Role / Policies: &amp;lt;Role 名&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 必須要件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 成功/失敗時の exit code を返す
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 実行ログを適切に CloudWatch Logs へ送る
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 既存タスクが存在する場合は確認と警告表示
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gs"&gt;**期待出力**&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 実行可能な &lt;span class="sb"&gt;`deploy.sh`&lt;/span&gt; コード
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 実行例（引数付き）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 必要な前提条件チェック一覧
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;



&lt;details class="code-collapse" &gt;
 &lt;summary&gt;3. CI/CD（デプロイ時）エラー解析テンプレ&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;以下は CI/CD のビルド/デプロイエラーに関する情報です。 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;GitHub Actions などの YAML、ログ、失敗したステップ名を提示します。 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;原因の可能性と修正案を &lt;span class="gs"&gt;**可能な限りステップごとに整理して Markdown で返してください**&lt;/span&gt;。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;---
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### 1. 環境
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; CI/CD ツール: &amp;lt;GitHub Actions / CodePipeline / Jenkins …&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; AWS リソース: &amp;lt;サービス名/リージョン&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### 2. 失敗ステップ
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; ステップ名: &amp;lt;例: Build/Deploy&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 関連 YAML/Workflow:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &amp;lt;該当部分の 20 ～ 40 行以内の抜粋&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### 3. エラーログ
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&amp;lt;エラーのログ（最大 15 行）&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;

&lt;h4 id="導入したことによる効果"&gt;導入したことによる効果&lt;/h4&gt;&lt;ul&gt;
&lt;li&gt;テンプレ化することにより、問題箇所のみを抽出できるので AI への問い合わせのハードルが下がった。&lt;/li&gt;
&lt;li&gt;テンプレ化することにより回答精度のばらつきが減った。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id="freee-技術の日-2025-参加メモイベント感想"&gt;freee 技術の日 2025 参加メモ【イベント感想】&lt;/h3&gt;&lt;p&gt;&lt;a href="https://wan0ri.com/posts/2025/12/03/freee-technology-day-2025/"&gt;https://wan0ri.com/posts/2025/12/03/freee-technology-day-2025/&lt;/a&gt;

&lt;/p&gt;</description></item><item><title>Cloud Monitoringでエラーログを感知してSlack通知させる機能をTerraformで管理する</title><link>https://wan0ri.com/posts/2025/12/07/cloud-monitoring-and-sending-slack-notifications-with-terraform/</link><pubDate>Sun, 07 Dec 2025 15:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/07/cloud-monitoring-and-sending-slack-notifications-with-terraform/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;個人学習として、Cloud Monitoring でエラー検知時に Slack 通知する仕組みを Terraform で構築&lt;/li&gt;
&lt;li&gt;知見のメモとして公開&lt;/li&gt;
&lt;li&gt;AWS や Azure でも概ね同様の流れで構築可能だが未検証&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="terraform-での実装"&gt;Terraform での実装&lt;/h2&gt;&lt;p&gt;まずは、以下の Terraform リソース構成を用意します。&lt;br&gt;
なお、記述内容については Slack の設定に関するものだけを掲載します。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;❯ tree
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── main.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── modules
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── monitoring
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── main.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ ├── outputs.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;│ └── variables.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── outputs.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── providers.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── terraform.tfvars
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;├── variables.tf
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;└── versions.tf
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="modulesmonitoring-の実装"&gt;./modules/monitoring の実装&lt;/h3&gt;&lt;p&gt;modules 配下の実装は以下のようにしました。&lt;/p&gt;


&lt;details class="code-collapse" &gt;
 &lt;summary&gt;./modules/monitoring/main.tf&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;###########################################################
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Cloud Monitoring
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;###########################################################
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Cloud Monitoring API 有効化
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;google_project_service&amp;#34; &amp;#34;monitoring&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; project&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;project_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; service&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;monitoring.googleapis.com&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# HTTPSアップタイム監視と通知
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;google_monitoring_uptime_check_config&amp;#34; &amp;#34;https&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Wiki HTTPS 稼働時間チェック&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;10s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;300s&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # HTTPチェックの設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;http_check&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;443&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; use_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; validate_ssl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 監視対象のリソース設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;monitored_resource&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;uptime_url&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt; { host&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;host&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 依存関係の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; depends_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;google_project_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;monitoring&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Slack通知チャネルの作成
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;google_monitoring_notification_channel&amp;#34; &amp;#34;slack&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Slack - Wiki Alerts&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;slack&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Slack通知チャネルの設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; channel_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;slack_channel&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Slack通知チャネルの認証トークン
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;sensitive_labels&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; auth_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;slack_token&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 依存関係の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; depends_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;google_project_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;monitoring&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# アラートポリシーの作成
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;google_monitoring_alert_policy&amp;#34; &amp;#34;uptime_down&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Wiki HTTPS Uptime Down Alert&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # どれか1つでも condition が true ならアラート発火
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; combiner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OR&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 通知チャネルの設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; notification_channels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;google_monitoring_notification_channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # アラート条件の定義
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;conditions&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Uptime Check Failed&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # Uptimeチェックが失敗した場合にアラートを発火
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;condition_threshold&lt;/span&gt; {&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # フィルターの設定(メトリックタイプとチェックID)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt; &amp;#34;resource.type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;\&amp;#34;uptime_url\&amp;#34; AND metric.type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;\&amp;#34;monitoring.googleapis.com/uptime_check/check_passed\&amp;#34; AND metric.label.\&amp;#34;check_id\&amp;#34;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;${google_monitoring_uptime_check_config.https.uptime_check_id}\&amp;#34;&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 比較演算子の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; comparison&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;COMPARISON_LT&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 閾値の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; threshold_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 評価期間の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;300s&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # トリガー条件の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;trigger&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # アラート発火のトリガー条件を設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;aggregations&lt;/span&gt; {&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 集計期間の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; alignment_period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;60s&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # シリーズごとのアライナーの設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; per_series_aligner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ALIGN_NEXT_OLDER&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 依存関係の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; depends_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;google_monitoring_uptime_check_config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;https&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Scheduler 失敗アラートポリシーの作成
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;google_monitoring_alert_policy&amp;#34; &amp;#34;scheduler_failed&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Scheduler Job Failed Alert&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; combiner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;OR&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; notification_channels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;google_monitoring_notification_channel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;slack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 通知のレート制限設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;alert_strategy&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;notification_rate_limit&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; period&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;300s&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # アラート条件の定義
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;conditions&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; display_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Cloud Scheduler error logs&amp;#34;&lt;/span&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # ログベースの条件を使用して、Cloud Schedulerのエラーログを監視
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;condition_matched_log&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; filter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt; &amp;#34;resource.type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;\&amp;#34;cloud_scheduler_job\&amp;#34; AND resource.labels.job_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;~\&amp;#34;gce-(stop-nightly|start-morning)\&amp;#34; AND severity&amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;ERROR&lt;/span&gt;&lt;span class="err"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; }&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt; # 依存関係の設定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; depends_on&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;google_project_service&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;monitoring&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;



&lt;details class="code-collapse" &gt;
 &lt;summary&gt;./modules/monitoring/variables.tf&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;##################################
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 入力変数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;##################################
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Google Cloud プロジェクトID
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;project_id&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Google Cloud プロジェクトID&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Uptime監視対象のホスト名またはIP
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;host&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Uptime監視対象のホスト名またはIP&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Uptime監視対象のDNS名またはIPアドレス
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dns_name&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;監視対象のDNS名またはIPアドレス&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# TechSnapのSlackチャンネル
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;slack_channel&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;TechSnap Alert Management Slack channel&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# TechSnapのSlackトークン
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;slack_token&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;通知を監視するためのTechSnap Slackトークン&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;string&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;



&lt;details class="code-collapse" &gt;
 &lt;summary&gt;./modules/monitoring/outputs.tf&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-hcl" data-lang="hcl"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;##################################
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 監視モジュールの出力
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;##################################
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Cloud プロジェクトID（参照用）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;project_id&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;project_id&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 監視対象のDNS名またはIPアドレス（参照用）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;dns_name&amp;#34;&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt; value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;dns_name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;

&lt;h3 id="infra-ルート配下での実装"&gt;infra ルート配下での実装&lt;/h3&gt;&lt;p&gt;infra ルート配下での実装は以下のようにしました。&lt;br&gt;
（Slack の定義に関わる箇所のみ）&lt;/p&gt;</description></item><item><title>AWS IaCナレッジ共有LT会 参加メモ【イベント感想】</title><link>https://wan0ri.com/posts/2025/12/06/aws-iac-knowledge-sharing-lt-event/</link><pubDate>Sat, 06 Dec 2025 11:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/06/aws-iac-knowledge-sharing-lt-event/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;2025 年 12 月 5 日（金）開催の「【アジアクエスト × シスラボ】現場で活きる！AWS IaC ナレッジ共有 LT 会」を視聴。&lt;/li&gt;
&lt;li&gt;個人的ハイライトは「③IaC Analyzer」「④Amazon Connect × GUI と IaC の両立」。現場での“人と運用を巻き込む IaC”の解像度が上がった。&lt;/li&gt;
&lt;li&gt;①CDK は開発体験の良さと組織スケールのバランス設計が肝。② サーバレスは境界設計（責務分割）と観測性の IaC 化が要点。&lt;/li&gt;
&lt;li&gt;直近アクション：既存 Terraform/CFN へ静的検査を組み込み、GUI 変更 →IaC 反映の小さな実験を回す。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;2025 年 12 月 5 日（金）開催の「【アジアクエスト × シスラボ】現場で活きる！AWS IaC ナレッジ共有 LT 会」を視聴しました。&lt;br&gt;
以下のページで案内されています。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://asiaquest.connpass.com/event/376064/" target="_blank" rel="noopener noreferrer"&gt;https://asiaquest.connpass.com/event/376064/&lt;/a&gt;

&lt;/p&gt;
&lt;h3 id="免責"&gt;免責&lt;/h3&gt;&lt;p&gt;本記事は当時のイベント視聴に基づく私的メモであり、登壇資料の直接引用・転載は行っていません。&lt;/p&gt;
&lt;h3 id="当日のアジェンダ"&gt;当日のアジェンダ&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;①LT1 使って感じた CDK のリアル&lt;/li&gt;
&lt;li&gt;②LT2 AWS サーバレスにおける IaC の活用&lt;/li&gt;
&lt;li&gt;③LT3 IaC Analyzer で始める Well-Architected なインフラづくり&lt;/li&gt;
&lt;li&gt;④LT4 Amazon Connect で考える GUI と IaC の両立&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="視聴メモ"&gt;視聴メモ&lt;/h2&gt;&lt;h3 id="lt1-使って感した-cdk-のリアル"&gt;①LT1 使って感じた CDK のリアル&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;良かった点：型安全（TypeScript）とコンストラクト再利用で“実装 → 標準化”の循環を回しやすい。小さく始めて段階的に抽象化できる。&lt;/li&gt;
&lt;li&gt;つまずきがちな点：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bootstrap&lt;/code&gt; 周りの権限とアセット公開（S3/ECR）を正しく設計しないと多環境で詰まりがち。&lt;/li&gt;
&lt;li&gt;命令的ロジックを詰め込みすぎるとテンプレートの可読性が低下（CloudFormation の diff が追いづらい）。&lt;/li&gt;
&lt;li&gt;チーム内でコンストラクトの抽象度が揃わないと、拡張しづらい“島”ができる。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;運用のコツ：
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cdk synth&lt;/code&gt; の成果物（CFN）をレビュー対象に入れる。CI で &lt;code&gt;cdk diff&lt;/code&gt; を必ず回す。&lt;/li&gt;
&lt;li&gt;共有コンストラクトに既定（タグ/暗号化/ログ保持）を強制するラッパーを用意。&lt;/li&gt;
&lt;li&gt;併用前提の判断基準を明文化（例：ガバナンス強い基盤は Terraform、アプリチームは CDK）。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;所感：個人/小規模では “速さ” が圧倒的メリット。組織スケールでは“テンプレートの見える化”とガードレール設計が先に必要。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="lt2-aws-サーバレスにおける-iac-の活用"&gt;②LT2 AWS サーバレスにおける IaC の活用&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;PDF で掲載されていたサーバーレスのサービス（Lambda， DynamoDB， Step Functions）を、実務扱ったことが無かったため新鮮な気持ちで臨めることが出来た&lt;/li&gt;
&lt;li&gt;本項では CloudFormation で半自動化する手法を紹介されていたが、Terraform でさらなる自動化を推進できるのではと感じた。&lt;/li&gt;
&lt;li&gt;境界設計が最重要（関数の責務、Step Functions での“オーケストレーション vs コレオグラフィ”）。&lt;/li&gt;
&lt;li&gt;観測性は最初から IaC に同梱（ログ保持期間、分散トレーシング、メトリクス/ダッシュボード）。後付けは高コストになりやすい。&lt;/li&gt;
&lt;li&gt;権限は最小化をテンプレ化（Lambda 毎の IAM ポリシー、DynamoDB の条件付き権限、S3 バケット暗号化/ブロック Public）。&lt;/li&gt;
&lt;li&gt;デプロイ単位は“変更頻度”基準で分割（テーブルと関数のライフサイクルを切り離す）。&lt;/li&gt;
&lt;li&gt;ツール選定の指針：CloudFormation/SAM（ネイティブ連携）、CDK（開発速度/抽象化）、Terraform（マルチアカウント一元管理）。&lt;/li&gt;
&lt;li&gt;所感：まずは PoC で“1 フロー + 1 DB + 監視”の最小構成を IaC 化し、運用の痛みを早めに洗い出すのが良さそう。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="lt3-iac-analyzer-で始める-well-architected-なインフラづくり"&gt;③LT3 IaC Analyzer で始める Well-Architected なインフラづくり&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;AWS Well-Architected フレームワークに関する紹介（AWS SAA でも出題される）
&lt;ul&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;セキュリティ&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start="2"&gt;
&lt;li&gt;信頼性&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start="3"&gt;
&lt;li&gt;パフォーマンス効率&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start="4"&gt;
&lt;li&gt;コスト最適化&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start="5"&gt;
&lt;li&gt;運用上の優秀性&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;ol start="6"&gt;
&lt;li&gt;サステナビリティ（持続可能性）&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;私も存じ上げなかった &lt;strong&gt;IaC Analyzer&lt;/strong&gt; の説明があった
&lt;ul&gt;
&lt;li&gt;対応コードは Terraform, CloudFormation, CDK&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;参考になりそうな資料を探してみたところ、以下がヒットした&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a href="https://github.com/aws-samples/well-architected-iac-analyzer?utm_source=chatgpt.com" target="_blank" rel="noopener noreferrer"&gt;https://github.com/aws-samples/well-architected-iac-analyzer?utm_source=chatgpt.com&lt;/a&gt;

&lt;/p&gt;</description></item><item><title>freee 技術の日 2025 参加メモ【イベント感想】</title><link>https://wan0ri.com/posts/2025/12/03/freee-technology-day-2025/</link><pubDate>Wed, 03 Dec 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/03/freee-technology-day-2025/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;2025 年 11 月 30 日（日）開催の「freee 技術の日 2025」を視聴（インフラ/ SRE 関連セッションのみ）。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SLO&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Service Level Objective。SLI に対する“目標値”。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 / &lt;code&gt;エラーバジェット&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;SLO を満たしつつ“許される失敗の枠”。意思決定の通貨。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 を“止める規則”ではなく &lt;strong&gt;意思決定のものさし&lt;/strong&gt; として運用する姿勢が腑に落ちた。&lt;/li&gt;
&lt;li&gt;認知負荷を下げる仕組み（押すだけダッシュボード、最小アラート、例外運用の雛形）が継続の鍵。&lt;/li&gt;
&lt;li&gt;設計は &lt;strong&gt;「前提 → 概算 → 手筋 → 検証」&lt;/strong&gt; の反復。観測可能性と段階的リリースを最初から設計に入れる。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;2025 年 11 月 30 日（日）に開催された「freee 技術の日 2025」を視聴しました。&lt;br&gt;
以下のページで案内されています。&lt;/p&gt;</description></item><item><title>大規模システム設計の基礎 を視聴して【イベント感想】</title><link>https://wan0ri.com/posts/2025/12/01/fundamentals-of-large-scalesystem-design/</link><pubDate>Mon, 01 Dec 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/12/01/fundamentals-of-large-scalesystem-design/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;システム設計は「要件・制約 → 規模見積もり → ボトルネック仮説 → 対策/検証」の反復。言語化した前提を常に更新するのが肝。&lt;/li&gt;
&lt;li&gt;規模感は“目分量”ではなく、概算（&lt;code&gt;QPS&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Queries Per Second。1 秒あたりに処理するリクエスト数。システム規模を見積もる基本指標。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 / &lt;code&gt;スループット&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;単位時間あたりに処理できる量（件数・データ量）。処理能力の指標。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 / ストレージ容量 / 帯域 &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;1 秒あたりに送受信できるデータ量。ネットワークの太さの指標。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
）で会話する。数字があると優先順位とトレードオフが立つ。&lt;/li&gt;
&lt;li&gt;スケール戦略は段階的に（垂直 &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;1 台あたりの CPU/メモリ/IO を増やす拡張。シンプルだが上限がある。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 → 水平 &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;台数を増やして負荷を分散。運用は複雑になるが上限が高い。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
 → 分割 &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;機能やデータを小さな単位に分ける（サービス分割/パーティショニング）。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
）。キャッシュ &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;繰り返し使う結果を一時保存し、読み出しを速くする仕組み。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
、非同期化 &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;応答を待たずに裏で処理する設計（ピーク平準化）。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
、キュー &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;要求を一時的に貯めて順次処理するバッファ。スパイク吸収に有効。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
、シャーディング &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;データを複数分割（シャード）に分けて並列処理/分散。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
、&lt;code&gt;冪等性&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;同じ操作を何度実行しても最終結果が変わらない性質。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
で詰まりを逃がす。&lt;/li&gt;
&lt;li&gt;可用性 &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;システムが利用可能な時間の割合（例: 99.9%）。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
・&lt;code&gt;一貫性&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;複数の副本やサービス間で“同じ事実”を同時に見られる度合い。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
・コスト・複雑性はトレードオフ。&lt;code&gt;SLO&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Service Level Objective。ユーザー体験の“目標品質”。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
を置き、過不足を検証で詰める。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;2025 年 11 月 29 日（土）に開催された Recursion 主催の『大規模システム設計の基礎』を視聴しました。&lt;/p&gt;</description></item><item><title>『SREの知識地図』の著者を招いてお送りするSREの旅 〜 Road2: 信頼性を定義して組織で運用する を視聴して【イベント感想】</title><link>https://wan0ri.com/posts/2025/11/29/sre-knowledgemap-road2/</link><pubDate>Sat, 29 Nov 2025 14:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/11/29/sre-knowledgemap-road2/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SLO&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Service Level Objective。SLI に対する &lt;strong&gt;“目標値”&lt;/strong&gt; 。例: 28 日間で成功率 99.9% 以上。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
/&lt;code&gt;エラーバジェット&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;SLO を満たしつつ &lt;strong&gt;“許される失敗の枠”&lt;/strong&gt; 。意思決定の通貨。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
は“止める/止めない”の絶対規則ではなく、現実的な意思決定のためのものさし。&lt;/li&gt;
&lt;li&gt;導入は小さく（パイロット → 可視化 → 会話）から。いきなり&lt;code&gt;バーンレート&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;エラーバジェットの“消費速度”で鳴るアラート。上級運用向け。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
等の上級運用に飛ばない。&lt;/li&gt;
&lt;li&gt;認知負荷を下げる仕組みづくり（押すだけのダッシュボード/アラート/手順）が継続のカギ。&lt;/li&gt;
&lt;li&gt;四半期〜年次で見直し。サポートチケットや &lt;code&gt;NPS&lt;/code&gt; &lt;span class="anno" data-anno&gt;
 &lt;button type="button" class="anno__btn" data-icon="*" aria-label="注釈" aria-expanded="false"&gt;
 &lt;span class="anno__text"&gt;注釈&lt;/span&gt;
 &lt;/button&gt;
 &lt;span class="anno__pop" role="tooltip" tabindex="-1"&gt;Net Promoter Score。推奨度ベースの満足度指標。&lt;/span&gt;
 &lt;/span&gt;

&lt;script&gt;
 (function(){
 if (window.__annoInit) return; window.__annoInit = true;
 function updateBodyClass(){
 const anyOpen = document.querySelector('[data-anno] .anno__btn[aria-expanded="true"]');
 document.body.classList.toggle('anno-open', !!anyOpen);
 }
 document.addEventListener('click', function(e){
 const isBtn = e.target.closest &amp;&amp; e.target.closest('[data-anno] .anno__btn');
 const all = document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]');
 if (isBtn){
 e.preventDefault();
 const btn = e.target.closest('[data-anno] .anno__btn');
 const expanded = btn.getAttribute('aria-expanded') === 'true';
 all.forEach(b =&gt; { if (b!==btn) { b.setAttribute('aria-expanded','false'); } });
 btn.setAttribute('aria-expanded', expanded ? 'false' : 'true');
 updateBodyClass();
 if (!expanded) {
 const pop = btn.nextElementSibling;
 if (pop &amp;&amp; pop.classList.contains('anno__pop')) {
 setTimeout(()=&gt;{ try{ pop.focus(); }catch(_){} }, 0);
 }
 }
 } else {
 all.forEach(b =&gt; b.setAttribute('aria-expanded','false'));
 updateBodyClass();
 }
 });
 document.addEventListener('keydown', function(e){
 if (e.key === 'Escape') {
 document.querySelectorAll('[data-anno] .anno__btn[aria-expanded="true"]').forEach(b=&gt;{
 b.setAttribute('aria-expanded','false');
 try { b.focus(); } catch(_){ }
 });
 document.body.classList.remove('anno-open');
 }
 });
 })();
&lt;/script&gt;

&lt;style&gt;
 .anno{ display:inline-block; position:relative; }
 
 .anno__btn{
 --anno-cyan: var(--h2-accent-light, #06b6d4);
 --anno-cyan-dark: var(--h2-accent-dark, #22d3ee);
 display:inline-block; cursor:pointer; margin-left:0.12em; padding:0 .02em;
 background:none; border:none; color:var(--anno-cyan);
 font-size:.85em; line-height:1; vertical-align:text-top; text-decoration:none;
 touch-action:manipulation; position:relative;
 }
 .dark .anno__btn{ color:var(--anno-cyan-dark); }
 .anno__btn:hover{ text-shadow:0 0 0 currentColor; }
 .anno__btn:focus-visible{ outline:0; box-shadow:0 0 0 2px currentColor; border-radius:3px; }
 
 .anno__btn::after{
 content:""; display:inline-block; width:0.45em; height:0.45em;
 border-radius:9999px; background: currentColor; translate: 0 -0.05em;
 }
 
 .anno__btn[data-icon]::after{
 content: attr(data-icon); width:auto; height:auto; border-radius:0; background:none; font-weight:700; font-size:1em; line-height:1; transform: translateY(-0.08em);
 }
 
 .anno__text{ position:absolute; width:1px; height:1px; padding:0; margin:-1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap; border:0; }
 .anno__pop{
 position:absolute; z-index:1000; min-width:220px; max-width:320px; padding:0.6rem 0.75rem;
 background:#111; color:#fff; border-radius:6px; box-shadow:0 4px 16px rgba(0,0,0,.25);
 transform:translate(-10%, .25rem); top:1.6rem; left:0; display:none; font-size:.85em; outline:none;
 }
 .anno__btn[aria-expanded="true"] + .anno__pop{ display:block; }
 
 body.anno-open { overflow:hidden; }
 body.anno-open::before{
 content:""; position:fixed; inset:0; background:rgba(0,0,0,.35); z-index:900; display:block;
 }
 
 @media (max-width: 640px){
 .anno__btn[aria-expanded="true"] + .anno__pop{
 position:fixed; top:50%; left:50%; transform:translate(-50%,-50%);
 max-width:92vw; width:auto; max-height:70vh; overflow:auto; padding:0.9rem 1rem; border-radius:10px;
 }
 }
&lt;/style&gt;
と併置して SLO の厳しさ/緩さを調整。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;2025 年 11 月 27 日（木）に開催された連続講義『SRE の旅』の Road2「信頼性を定義して組織で運用する」を視聴しました。&lt;br&gt;
視聴メモと、実務に転用できそうなポイントを残します。&lt;/p&gt;</description></item><item><title>どろんこAI話〜綺麗じゃないAIの苦労話〜 を視聴して【イベント感想】</title><link>https://wan0ri.com/posts/2025/11/27/forkwell-doronko-ai-impressions/</link><pubDate>Thu, 27 Nov 2025 09:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/11/27/forkwell-doronko-ai-impressions/</guid><description>&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;「綺麗じゃない」実践知が山盛り。課題定義・責任境界・計測を先に決めることが成否を分けると再確認。&lt;/li&gt;
&lt;li&gt;生成 AI は“使いどころ”の設計が命。UX 起点で仮説 → プロトタイプ → 学習ループを高速に回す文化が要。&lt;/li&gt;
&lt;li&gt;モデル/外部 API への依存は前提。互換レイヤ・フェイルセーフ・コスト監視をプロダクション設計に埋め込む。&lt;/li&gt;
&lt;li&gt;「学びの KPI」を置くと探索が楽になる。小さく泥臭く試し、ユーザー接点で早めに擦るのが結局近道。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="参加イベント"&gt;参加イベント&lt;/h2&gt;&lt;p&gt;2025 年 11 月 25 日（火）に Forkwell 社より主催された、『どろんこ AI 話〜綺麗じゃない AI の苦労話〜』を視聴しました。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://jobs.forkwell.com/events/dsa0_8s94bl" target="_blank" rel="noopener noreferrer"&gt;https://jobs.forkwell.com/events/dsa0_8s94bl&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;視聴した感想や、自身に置き換えて役に立てそうだと思ったことを書いていきたいと思います。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="印象に残ったこと"&gt;印象に残ったこと&lt;/h2&gt;&lt;h3 id="1-前提を揃える"&gt;1. 前提を揃える&lt;/h3&gt;&lt;p&gt;まず、登壇全体を通じて強く感じたのは、AI プロジェクトほど「前提を揃えること」が効くという点でした。&lt;br&gt;
解くべき課題を丁寧に言葉にし、PoC を卒業したと見なす基準を最初に置いておく。ここには精度やコストだけでなく、説明可能性、運用に必要な手間、組織としての責任の持ち方まで含めるべきだという示唆がありました。&lt;br&gt;
意思決定の単位 『 &lt;strong&gt;誰が何をいつ承認するのか&lt;/strong&gt; 』 を明文化しておくと、試行錯誤が迷走せず、泥臭い実験を健全な速度で続けられるのだと理解しました。&lt;/p&gt;
&lt;h3 id="2-llm-の応答はアプリケーションのいち機能として扱うべき"&gt;2. LLM の応答はアプリケーションのいち機能として扱うべき&lt;/h3&gt;&lt;p&gt;フロントエンドと生成 AI の接点についても多くの学びがありました。&lt;br&gt;
LLM の応答は“そのまま見せるテキスト”ではなく、アプリケーションのいち機能として扱うべきです。&lt;br&gt;
ストリーミング表示は待ち時間の心理的負担を軽くし、キャンセルや再実行は失敗を前提にした健全な操作感を生みます。&lt;br&gt;
さらに、要約や根拠の提示を UI に組み込むことで、幻覚や遅延がユーザー体験を損なうリスクを和らげられると感じました。&lt;/p&gt;
&lt;h3 id="3-抗わない設計"&gt;3. 抗わない設計&lt;/h3&gt;&lt;p&gt;技術選定の観点では、モデルや外部 API の進化速度に抗わない設計が重要だと再確認しました。&lt;br&gt;
プロバイダ適配のレイヤを用意し、プロンプト資産はバージョンを明確に管理する。&lt;br&gt;
コスト・品質・レイテンシの三点を儀式化してダッシュボードで見える化し、劣化や偏りを早期に捕捉できるようにしておく。&lt;br&gt;
これらは &lt;strong&gt;“差し替えが常態”&lt;/strong&gt; である時代の基本動作だと感じます。&lt;/p&gt;</description></item><item><title>学習の一環で個人開発していたら請求額が凄いことになってアゴが外れかけた</title><link>https://wan0ri.com/posts/2025/11/23/personal-development-almost-bankrupt/</link><pubDate>Sun, 23 Nov 2025 10:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/11/23/personal-development-almost-bankrupt/</guid><description>&lt;hr&gt;
&lt;h2 id="tldr"&gt;TL;DR&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;学習目的の個人アプリを Google Cloud で運用していたところ、月額が 1 万円を超えていることに気付きました。&lt;/li&gt;
&lt;li&gt;緊急対応として DNS 以外の GCP リソースを停止・削除し、以後は Cloudflare を中心に再構築する方針に切り替えました。&lt;/li&gt;
&lt;li&gt;代替は Cloudflare と Netlify を比較し、Next.js との相性と固定費の低さから Cloudflare を選定しました。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img
 src="https://wan0ri.com/images/posts/personal-development-almost-bankrupt/2025-11-19-20-35-22@0.5x.jpg"
 alt="GCP 請求スナップショット (2025-11-19 20:35)"
 loading="lazy"
 decoding="async"
 class="content-image"
/&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="背景なぜ-google-cloud-で始めたか"&gt;背景（なぜ Google Cloud で始めたか）&lt;/h2&gt;&lt;p&gt;ポートフォリオ作成と学習を目的に、インフラから CI/CD、監視まで一通りを触ることを狙って始めた個人開発を行っていました。&lt;br&gt;
採用技術は Google Cloud を中心に、Cloud Run、Artifact Registry、Cloud Scheduler、Logging/Monitoring を Terraform で管理していました。&lt;br&gt;
アプリは Next.js ベースで、将来的な SSR やバッチ実行も見据えていました。&lt;br&gt;
Google Cloud を採用した理由は、今まで触ったことがなかったので学習をきっかけに触ってみたかったのと、ドキュメントを読んでいて機能がシンプルで理解しやすく感じたからです。&lt;/p&gt;
&lt;h2 id="何が起きたか請求が-1-万円超"&gt;何が起きたか（請求が 1 万円超）&lt;/h2&gt;&lt;p&gt;ある日、何気なく請求ダッシュボードを確認したところ、当月見込みが 1 万円を超えていました。&lt;br&gt;
個人開発としては継続が重い水準だと判断し、即座にコスト抑制に舵を切りました。&lt;br&gt;
参考までに、JST 2025-11-19 20:35 時点のスナップショットは以下のとおりです。&lt;/p&gt;</description></item><item><title>2025年の転職活動が散々たる結果で終えたインフラエンジニアの棚卸し記録【完全敗北】</title><link>https://wan0ri.com/posts/2025/11/15/job-hunting-completely-halted/</link><pubDate>Sat, 15 Nov 2025 10:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/11/15/job-hunting-completely-halted/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;2025 年、私は SES 企業で働くインフラエンジニアとして、自社開発企業への転職を目指しました。&lt;br&gt;
結果として、選考に進んだ企業すべてに落ちました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="1-活動背景と技術スタック"&gt;1. 活動背景と技術スタック&lt;/h2&gt;&lt;p&gt;まず、2025 年時点での私のスタックは以下の通りです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;AWS（実務 4 年）&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VPC / EC2 / S3 / RDS / Aurora&lt;/li&gt;
&lt;li&gt;ECS / Lambda / Route53&lt;/li&gt;
&lt;li&gt;CloudWatch / IAM / KMS&lt;/li&gt;
&lt;li&gt;ElastiCache / Secrets Manager&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;インフラ・運用&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux / Windows Server&lt;/li&gt;
&lt;li&gt;VMware / Docker&lt;/li&gt;
&lt;li&gt;Zabbix / JP1&lt;/li&gt;
&lt;li&gt;Shell / Ansible / Jenkins&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;ネットワーク&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;L2〜L3 / VLAN / QoS&lt;/li&gt;
&lt;li&gt;障害対応 / 設計・運用&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;スタックだけを見ると、インフラ周りの運用・構築を幅広く経験しているように見えると思います。&lt;br&gt;
ただし、このあと触れる「敗因」の部分に繋がりますが、&lt;strong&gt;深さより広さのキャリア&lt;/strong&gt; であることが課題でした。&lt;/p&gt;</description></item><item><title>サイトポリシー</title><link>https://wan0ri.com/info/policy/</link><pubDate>Sat, 15 Nov 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/info/policy/</guid><description>&lt;p&gt;本サイトのコンテンツ利用方針は以下のとおりです。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;引用は出典明記のうえ適切な範囲でご利用ください。&lt;/li&gt;
&lt;li&gt;画像・図表・ソースコードの無断転載はお控えください。&lt;/li&gt;
&lt;li&gt;掲載情報の正確性には努めていますが、内容を保証するものではありません。&lt;/li&gt;
&lt;li&gt;予告なく内容の変更・削除を行うことがあります。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;免責事項については「免責事項」をご参照ください。&lt;/p&gt;</description></item><item><title>プライバシーポリシー</title><link>https://wan0ri.com/info/privacy/</link><pubDate>Sat, 15 Nov 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/info/privacy/</guid><description>&lt;p&gt;当サイト（以下「本サイト」）では、ユーザーの個人情報および行動情報を以下の方針に基づき適切に取り扱います。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;取得する情報&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;アクセス解析（Google Analytics 4）による匿名のトラフィックデータ&lt;/li&gt;
&lt;li&gt;広告配信（Google AdSense）に伴う Cookie 等の識別子&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="2"&gt;
&lt;li&gt;利用目的&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;サイトの利便性向上、コンテンツ改善のための統計分析&lt;/li&gt;
&lt;li&gt;広告の最適化と不正防止&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;Google Analytics 4 について&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;本サイトは Google 提供の GA4 を用いて匿名のトラフィックデータを収集します。&lt;/li&gt;
&lt;li&gt;取得データは Google のプライバシーポリシーに基づいて管理されます。&lt;/li&gt;
&lt;li&gt;詳細: &lt;a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer"&gt;https://policies.google.com/privacy&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;Google AdSense について&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;第三者配信事業者の広告を掲載し、Cookie 等によりユーザーに応じた広告を表示する場合があります。&lt;/li&gt;
&lt;li&gt;パーソナライズ広告の無効化: &lt;a href="https://adssettings.google.com/" target="_blank" rel="noopener noreferrer"&gt;https://adssettings.google.com/&lt;/a&gt;

&lt;/li&gt;
&lt;li&gt;事業者のポリシー: &lt;a href="https://policies.google.com/technologies/ads" target="_blank" rel="noopener noreferrer"&gt;https://policies.google.com/technologies/ads&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;Cookie の管理&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;ブラウザ設定から Cookie の保存を拒否できます。拒否時は一部機能が制限される場合があります。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="6"&gt;
&lt;li&gt;外部リンク&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;外部サイトでの個人情報の取り扱いについて、本サイトは責任を負いません。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;改定&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;本ポリシーは予告なく改定される場合があります。改定後は本ページにて公表します。&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>免責事項</title><link>https://wan0ri.com/info/disclaimer/</link><pubDate>Sat, 15 Nov 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/info/disclaimer/</guid><description>&lt;p&gt;本サイトに掲載する情報は、執筆時点の知見に基づくものであり、完全性・正確性・最新性を保証するものではありません。内容の利用により生じたいかなる損害についても、当方は一切の責任を負いかねます。必ず公式ドキュメントや一次情報をご確認のうえ、ご自身の判断と責任にてご利用ください。&lt;/p&gt;</description></item><item><title>広告について</title><link>https://wan0ri.com/info/ads/</link><pubDate>Sat, 15 Nov 2025 00:00:00 +0900</pubDate><guid>https://wan0ri.com/info/ads/</guid><description>&lt;p&gt;本サイトでは、運営継続のため Google AdSense 等の広告を掲載しています。広告配信事業者は、Cookie 等を使用してユーザーの興味に応じた広告を表示する場合があります。パーソナライズ広告を無効化する設定は以下を参照してください。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;パーソナライズ広告: &lt;a href="https://adssettings.google.com/" target="_blank" rel="noopener noreferrer"&gt;https://adssettings.google.com/&lt;/a&gt;

&lt;/li&gt;
&lt;li&gt;事業者のポリシー: &lt;a href="https://policies.google.com/technologies/ads" target="_blank" rel="noopener noreferrer"&gt;https://policies.google.com/technologies/ads&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;アフィリエイトリンクを含む場合は、リンクに「sponsored」「nofollow」を付与しています。&lt;/p&gt;</description></item><item><title>巷で噂の CodeX CLIに触れてセッティング諸々トライしてみた</title><link>https://wan0ri.com/posts/2025/09/21/codex-settings/</link><pubDate>Sun, 21 Sep 2025 10:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/09/21/codex-settings/</guid><description>&lt;h2 id="はじめに"&gt;はじめに&lt;/h2&gt;&lt;p&gt;以前書いた記事で ChatLLM の API を使ってバイブコーディングの環境を構築しました。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://wan0ri.com/posts/2025/09/06/vscode-vibecoding/"&gt;https://wan0ri.com/posts/2025/09/06/vscode-vibecoding/&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;使い始めた当初は AI に壁打ちしながらコーディングできていたのですが、以下の問題点が出てきました。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;チャットを入力してレスポンスまでかなりの時間がかかった&lt;/li&gt;
&lt;li&gt;拡張機能の Continue がかなりの頻度でエラーになってしまいチャットでの壁打ちが困難&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;他のサービスに切り替えることを検討するなかで Claude Code を視野に入れましたが、ここ最近精度があまりよろしくないと他のユーザーからの声が挙がっていたので、早々に候補から外しました。&lt;/p&gt;
&lt;p&gt;Qiita や Zenn などを眺めていたところ、ChatGPT を提供している OpenAI から &lt;strong&gt;CodeX&lt;/strong&gt; という AI エージェントがかなりいいという記事を多数見かけましたので、遅ればせながら試してみました。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://developers.openai.com/codex/cloud" target="_blank" rel="noopener noreferrer"&gt;https://developers.openai.com/codex/cloud&lt;/a&gt;

&lt;/p&gt;
&lt;p&gt;以降、私が設定した内容になります。&lt;br&gt;
以前 Cursor を使用していて、その中で AI と壁打ちでチャットできるようルールを作ったことがあるので、そのルールを踏襲して CodeX にもルールの定義付けをしようと思います。&lt;/p&gt;
&lt;h2 id="説明しないこと"&gt;説明しないこと&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;CodeX の概要説明やインストール、導入までの手順&lt;/li&gt;
&lt;li&gt;CodeX に対しての詳細説明※あくまで CodeX のルールなどの設定内容と実行手順にフォーカス&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="設定agentsmd"&gt;設定（AGENTS.md）&lt;/h2&gt;&lt;p&gt;ユーザー配下に.codex があることを確認し、その中に &lt;strong&gt;AGENTS.md&lt;/strong&gt; を作成する。&lt;br&gt;
これは現在のプロジェクト内を解析する説明書のようなものになります。&lt;/p&gt;


&lt;details class="code-collapse" &gt;
 &lt;summary&gt;AGENTS.md&lt;/summary&gt;
 &lt;div class="code-collapse__content"&gt;
 &lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gh"&gt;# AGENTS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;最終更新: 2025-10-03 00:00:00
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 基本指示
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; CodeX CLI エージェントは初回応答から日本語で返答し、ユーザーが別の言語を明示した場合のみ切り替える。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 説明は平易な日本語で、必要な識別子やコードは英語で表記する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 運用スタイル
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 基本は &lt;span class="sb"&gt;`codex`&lt;/span&gt; コマンド単体で起動し、用途に応じて都度 &lt;span class="sb"&gt;`-p &amp;lt;profile&amp;gt;`&lt;/span&gt; や &lt;span class="sb"&gt;`--prompt`&lt;/span&gt; を指定する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; プロファイル切替ラッパー（&lt;span class="sb"&gt;`codex-infra`&lt;/span&gt; など）は廃止し、&lt;span class="sb"&gt;`codex`&lt;/span&gt; コマンドに集約する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 設定変更後は Codex CLI を再起動するか、TUI で &lt;span class="sb"&gt;`:config reload`&lt;/span&gt; を実行して反映させる。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## プロファイル一覧
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### infra-tf (profile: `infra`)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **目的** : Terraform / Kubernetes / Docker / CI レビューに特化。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **呼び出し例** : &lt;span class="sb"&gt;`codex -p infra --prompt &amp;#34;@tf:rules&amp;#34;`&lt;/span&gt;。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **備考** : インフラ構成レビューは「目的 → 差分 → 影響 → ロールバック → 検証」で整理する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### docs-scribe (profile: `docs`)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **目的** : ADR / 設計資料 / 要件定義の骨子生成。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **呼び出し例** : &lt;span class="sb"&gt;`codex -p docs --prompt &amp;#34;@docs:adr title=\&amp;#34;...\&amp;#34;&amp;#34;`&lt;/span&gt;。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **備考** : Markdown 構造化と簡潔な要約を優先する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### full-stack-rev (profile: `full`)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **目的** : BE / FE / DB / CI を横断したレビュー。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **呼び出し例** : &lt;span class="sb"&gt;`codex -p full --prompt &amp;#34;@fe:rules&amp;#34;`&lt;/span&gt;。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **備考** : アプリとインフラの相互影響を棚卸ししてから提案する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;### quality-ops (profile: `quality`)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **目的** : コマンド安全性・セキュリティ・テスト生成のスポットチェック。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **呼び出し例** : &lt;span class="sb"&gt;`codex -p quality --prompt &amp;#34;@review:commands&amp;#34;`&lt;/span&gt;。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; **備考** : 機微情報と再現性、重大リスクを最優先で精査する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## プロンプトプリセット
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; プリセット本文は &lt;span class="sb"&gt;`config.toml`&lt;/span&gt; の &lt;span class="sb"&gt;`[prompt_presets]`&lt;/span&gt; に定義される。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; 追加・更新時は diff を確認しつつ &lt;span class="sb"&gt;`:config reload`&lt;/span&gt; で反映する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; よく使うプリセットは &lt;span class="sb"&gt;`codex --prompt &amp;#34;@name&amp;#34;`&lt;/span&gt; 形式で直接呼び出す。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## MCP サーバー
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; context7: Context7 ドキュメント参照。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; playwright: Playwright ブラウザ操作。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; filesystem: ローカルファイル参照。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; github: GitHub リポジトリ参照（&lt;span class="sb"&gt;`GITHUB_TOKEN`&lt;/span&gt; を環境変数で渡す）。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;-&lt;/span&gt; terraform: HashiCorp 公式 Terraform MCP Server。&lt;span class="sb"&gt;`docker run --rm -i hashicorp/terraform-mcp-server:0.3.0`&lt;/span&gt; で起動し、Terraform Registry からプロバイダ情報を取得できる。Terraform Cloud / Enterprise を使う場合は &lt;span class="sb"&gt;`TERRAFORM_CLOUD_TOKEN`&lt;/span&gt; などを &lt;span class="sb"&gt;`env`&lt;/span&gt; で明示すること。ローカル開発環境での利用を前提とし、外部公開時はアクセス制御を追加する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gu"&gt;## 運用ルール
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;1.&lt;/span&gt; 日常作業は &lt;span class="sb"&gt;`codex`&lt;/span&gt; コマンドで開始し、必要に応じて &lt;span class="sb"&gt;`-p`&lt;/span&gt; でプロファイルを切り替える。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;2.&lt;/span&gt; プロファイルの使い分けが煩雑になった場合はプリセット側で段階的に吸収する。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;3.&lt;/span&gt; 有用だった会話はプロファイル紐付けのメモとして本ファイルに追記し、再利用できる形に整理する。
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
 &lt;/div&gt;
&lt;/details&gt;

&lt;h2 id="設定configtoml"&gt;設定(config.toml)&lt;/h2&gt;&lt;p&gt;次に &lt;strong&gt;config.toml&lt;/strong&gt; の設定を行います。&lt;br&gt;
これは、CodeX の設定ファイルになります。&lt;/p&gt;</description></item><item><title>ChatLLM APIとVSCode拡張機能Continueを組み合わせてVibe Coding環境を構築した話</title><link>https://wan0ri.com/posts/2025/09/06/vscode-vibecoding/</link><pubDate>Sat, 06 Sep 2025 10:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/09/06/vscode-vibecoding/</guid><description>&lt;h2 id="概要"&gt;概要&lt;/h2&gt;&lt;p&gt;本記事では、ABACUS.AI が提供する ChatLLM の API を VSCode 拡張機能「Continue」に連携させ、バイブコーディング環境を整備した一連の流れをまとめます。&lt;br&gt;
当初は Cursor ルール用に作成した .mdc ファイルを参照する方式を考えましたが、最終的には &lt;strong&gt;全ルールを config.yaml に直書きし、API キーもローカルの config.yaml に直接埋め込む&lt;/strong&gt; 方式に落ち着きました。&lt;/p&gt;
&lt;h2 id="経緯"&gt;経緯&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Cursor の Pro プランを利用していたが、毎月の上限を使い切ることが多かった。&lt;/li&gt;
&lt;li&gt;既に契約していた Abacus.AI の ChatLLM API を活用できないか検討。&lt;/li&gt;
&lt;li&gt;ChatLLM にはエディタ連携機能がないため、&lt;strong&gt;VSCode 拡張「Continue」&lt;/strong&gt; をフロントエンドに利用。&lt;/li&gt;
&lt;li&gt;最初は .mdc にルールを分割し参照する構成を考えたが、将来的に不要になるため廃止。&lt;/li&gt;
&lt;li&gt;また、環境変数経由で API キーを渡す方式は &lt;strong&gt;GUI 環境の VSCode でうまく読み込まれない&lt;/strong&gt; ことが判明。&lt;/li&gt;
&lt;li&gt;結果として、&lt;strong&gt;個人利用では config.yaml に API キーを直書き&lt;/strong&gt; する方法が最も安定することが分かった。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="対応"&gt;対応&lt;/h2&gt;&lt;h3 id="1-chatllm-api-キー発行"&gt;1. ChatLLM API キー発行&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;Abacus.AI 管理画面から RouteLLM API キーを発行。&lt;/li&gt;
&lt;li&gt;GUI 経由の VSCode では ~/.zshrc などから環境変数が引き継がれなかったため、今回は &lt;strong&gt;config.yaml に直書き&lt;/strong&gt; する運用とした。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2-continue-の設定ファイル"&gt;2. Continue の設定ファイル&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;VSCode 拡張 Continue は、ユーザーごとに &lt;strong&gt;~/.continue/config.yaml&lt;/strong&gt; を参照する。&lt;/li&gt;
&lt;li&gt;今回はここに AI モデル定義とルールを直書き。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;設定例（抜粋）:&lt;/p&gt;</description></item><item><title>CloudWatchのメトリクスフィルターを論理演算で組み立てるまでの記録</title><link>https://wan0ri.com/posts/2025/09/05/cloudwatch-metrics-filter/</link><pubDate>Fri, 05 Sep 2025 10:00:00 +0900</pubDate><guid>https://wan0ri.com/posts/2025/09/05/cloudwatch-metrics-filter/</guid><description>&lt;h2 id="前置き"&gt;前置き&lt;/h2&gt;&lt;div class="ads ads--in-article not-prose" style="margin: 20px 0;"&gt;
 &lt;ins class="adsbygoogle"
 style="display:block; text-align:center;"
 data-ad-client="ca-pub-5953476910958882"
 data-ad-slot="3725574318"data-ad-format="fluid"
 data-ad-layout="in-article"
 data-full-width-responsive="true"&gt;&lt;/ins&gt;
 &lt;script&gt;(adsbygoogle = window.adsbygoogle || []).push({});&lt;/script&gt;
&lt;/div&gt;&lt;p&gt;参画案件の情報を伏せる意味合いもあり、アプリケーション名や文字列については実際のものとは違うものになります。&lt;/p&gt;
&lt;h2 id="背景とやらかし"&gt;背景とやらかし&lt;/h2&gt;&lt;p&gt;現在参画している「AIX から AWS への全面リプレイス案件」で、CloudWatch Logs のメトリクスフィルターを設定する作業がありました。&lt;br&gt;
現行 AIX 環境では、オンラインアプリケーション ABC 系のエラー検知に以下の 2 つのルールが設定されています。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ABC[0-9]{4}E&lt;/li&gt;
&lt;li&gt;CV[0-9]{4}E&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;さらに例外として CV9006E と CV9007E が出力された場合は検知から除外する必要があります。&lt;br&gt;
この条件をそのまま CloudWatch に写経しようとして、フィルターは「正規表現を連結するもの」だと勘違いしたまま構築を進めたのが今回のやらかしの発端でした。&lt;/p&gt;
&lt;h2 id="cloudwatch-フィルターパターンの素朴なルール整理"&gt;CloudWatch フィルターパターンの素朴なルール整理&lt;/h2&gt;&lt;p&gt;CloudWatch Logs のフィルターパターンは、まず「文字列の出現有無を論理演算で組み合わせる」仕組みになっています。&lt;br&gt;
スペースで区切った語句は AND、語句の前に - を置くと NOT、語句の前に ? を置くと OR（いずれかが含まれていればマッチ）という扱いになります。&lt;br&gt;
これらはプレーンテキストにも JSON ログにも共通で、JSON の場合は { $.field = &amp;ldquo;value&amp;rdquo; } のようにフィールドを指定できるのが追加要素です。&lt;br&gt;
正規表現はあくまで補助的に %&amp;hellip;% で囲んだ部分として書けますが、1 パターンにつき最大 2 つまでという制限がありますし、使わなくても目的を満たせるなら論理演算だけで完結させた方が運用は楽です。&lt;/p&gt;
&lt;p&gt;今回のケースでは「ABC または CV で始まり、4 桁の数字と E が続く」という条件をどうしても 1 つの正規表現で書きたいので &lt;strong&gt;%((ABC|CV)[0-9]{4}E)%&lt;/strong&gt; までは許容するとしても、除外条件にまで正規表現を使ってしまうとクォータを超えてしまいます。&lt;br&gt;
このルールを理解しないまま以下のように 4 本の正規表現を含んだパターンを書いた結果、テスト画面で&lt;/p&gt;</description></item><item><title>About</title><link>https://wan0ri.com/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://wan0ri.com/about/</guid><description>&lt;img src="icon.png"
 alt="icon"
 class="avatar-img"
 style="display:block;margin:0;width:20%;max-width:20%;border-radius:50%;height:auto;"&gt;


&lt;h2 id="profile"&gt;Profile&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Name&lt;/strong&gt;: wan0ri&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Job&lt;/strong&gt;: インフラクラウドエンジニア, SRE&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Favorite&lt;/strong&gt;: キャンプ, 野球観戦&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="introduction"&gt;Introduction&lt;/h2&gt;&lt;p&gt;自動車製造業の設備配管の施工管理からIT業界へキャリアチェンジし、現在はクラウドインフラ構築・保守・運用を専門にしています。&lt;br&gt;
金融・保険業界の高セキュリティ要件対応、データセンター運用、クラウドネイティブ環境まで、幅広い領域で実務経験を積んできました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="cases-ive-experienced"&gt;Cases I&amp;rsquo;ve experienced&lt;/h2&gt;&lt;p&gt;AIXからAWSへの大規模移行プロジェクトや、銀行システムの新規・追加構築、保守運用、SRE業務に従事し、複雑な技術課題の調査・改善を担当。&lt;br&gt;
可用性向上、運用工数削減、デプロイの安定化など、信頼性向上に直結する改善を継続してきました。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="contribution"&gt;Contribution&lt;/h2&gt;&lt;p&gt;製造業で培った「ムリ・ムダ・ムラ削減」の視点をIT運用に応用し、運用設計・標準化・自動化を推進し、現場に根付く改善を積み重ね、安定した技術基盤づくりに貢献しています。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="strengths"&gt;Strengths&lt;/h2&gt;&lt;p&gt;強みは傾聴力・協調性・実行力で、関係者と協力しながら改善を前に進めることができます。&lt;br&gt;
これらを活かし、社会的意義のあるプロダクトをSREとして支えていきたいです。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="ambition"&gt;Ambition&lt;/h2&gt;&lt;p&gt;医療・福祉や教育など、社会課題が残る領域で、人々の生活を支えるプロダクトの信頼性向上に貢献したいと考えています。&lt;br&gt;
私のビジョンは「誰もが前向きに人生を満喫できる社会」を実現すること。&lt;br&gt;
そのためには、根幹を支える技術基盤の安定性が不可欠であり、SREとしてその役割を果たしたいと考えています。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="caution"&gt;Caution&lt;/h2&gt;&lt;p&gt;本ブログの内容・発言は個人の見解であり、所属組織の見解ではありません。&lt;/p&gt;</description></item><item><title>Info</title><link>https://wan0ri.com/info/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://wan0ri.com/info/</guid><description>&lt;h2 id="サイトポリシー--プライバシー"&gt;サイトポリシー / プライバシー&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wan0ri.com/info/privacy/"&gt;プライバシーポリシー&lt;/a&gt;

&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wan0ri.com/info/policy/"&gt;サイトポリシー&lt;/a&gt;

&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wan0ri.com/info/disclaimer/"&gt;免責事項&lt;/a&gt;

&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wan0ri.com/info/ads/"&gt;広告について&lt;/a&gt;

&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>