<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>随心笔记 Blog</title>
        <link>https://tanzhuo.xyz/ja/</link>
        <description>随心笔记 Blog</description>
        <lastBuildDate>Thu, 06 Aug 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>ja</language>
        <item>
            <title><![CDATA[AI Room:本当に計算しているニューラルネットワークの中へ]]></title>
            <link>https://tanzhuo.xyz/ja/2026/08/06/ai-room</link>
            <guid>https://tanzhuo.xyz/ja/2026/08/06/ai-room</guid>
            <pubDate>Thu, 06 Aug 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[新しいプロジェクト AI Room をオープンソース化しました。ブラウザ内に構築したニューラルネットワークの 3D 空間で、MLP・CNN・ミニ Transformer がすべて実際に学習・推論され、どのノードも開いて検算できます。]]></description>
            <content:encoded><![CDATA[<p>新しいプロジェクト <a href="https://github.com/tan-zhuo/ai-room" target="_blank" rel="noopener noreferrer" class="">AI Room</a> をオープンソース化しました。ニューラルネットワークを 3D 空間に配置し、中に入ってノード単位で観察できるものです。オンラインデモ:<a href="https://ai-room-phi.vercel.app/" target="_blank" rel="noopener noreferrer" class="">ai-room-phi.vercel.app</a>。</p>
<p>よくある「ニューラルネットワーク解説アニメーション」との本質的な違いが一つあります。<strong>これはアニメーションではなく、本物の計算です</strong>。ページ読み込み時に、各ネットワークは固定シードでブラウザ内で実際に学習されます。画面に表示される値——活性化値、アテンション重み、softmax 確率——はすべて順伝播の本当の結果です。どのノードでも開いて、自分で検算できます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ作ったのか">なぜ作ったのか<a href="https://tanzhuo.xyz/ja/2026/08/06/ai-room#%E3%81%AA%E3%81%9C%E4%BD%9C%E3%81%A3%E3%81%9F%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ作ったのか への直接リンク" title="なぜ作ったのか への直接リンク" translate="no">​</a></h2>
<p>ニューラルネットワークを解説する図や動画はたくさん見てきましたが、大半は「イメージ図」の域を出ません。矢印、ボックス、グラデーション、作り物の数字、演出された流れ。私はずっと「検算できる」バージョンが欲しかったのです——各ニューロンの入力、重み、バイアス、活性化関数が目の前に並び、Σ で足し合わせた数が画面で光っている数とぴったり一致する。嘘だと思うなら、電卓を叩けばいい。</p>
<p>それが AI Room になりました。純粋なフロントエンドで、バックエンドなし、実行時のネットワーク通信もなし。学習も推論もすべてあなたのブラウザの中で行われます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="中身">中身<a href="https://tanzhuo.xyz/ja/2026/08/06/ai-room#%E4%B8%AD%E8%BA%AB" class="hash-link" aria-label="中身 への直接リンク" title="中身 への直接リンク" translate="no">​</a></h2>
<p><strong>MLP(多層パーセプトロン)</strong>:3 つのガウス分布クラスタを分類します。データフローの粒子が重み付きの接続に沿って層ごとに流れ、アニメーションの順序がそのまま計算の順序です。</p>
<p><strong>CNN(畳み込みネットワーク)</strong>:パターン(縦線/横線/対角線/リング)を分類します。受容野のウィンドウは計算順序どおりに入力上をスライドします。カーネルは 2 モード——手作りの Sobel 風エッジ検出カーネル(解釈可能。全結合ヘッドのみ学習)と、ランダムノイズから手書きの逆伝播でエンドツーエンドに学習されるカーネル。さらにドローモードでは、入力グリッドに自分でパターンを描き、ネットワーク全体が一筆ごとにリアルタイムで分類する様子を見られます。S/M/L の 3 スケールがあり、切り替えるとその場で再学習されます。</p>
<p><strong>ミニ Transformer(文字レベル)</strong>:構造的に忠実な Transformer ブロックです——トークナイザ → 埋め込み → 正弦波位置エンコーディング → マルチヘッド因果アテンション(2 ヘッド、出力射影付き)→ 残差 + LayerNorm → フィードフォワード → 残差 + LayerNorm → 出力 softmax。順伝播も逆伝播も(LayerNorm と残差の勾配を含めて)手書き実装で、読み込み時に小さなコーパスで約 2.5 秒学習します。2 つのヘッドの 8×8 アテンション行列が行ごとに点灯し、Add &amp; Norm のセルを開くと μ、σ、γ、β までの完全な計算式が見えます。「連続生成」を押すと本物の自己回帰デコードが始まります:サンプリングされた文字がコンテキストに追加され、ウィンドウがスライドし、パイプライン全体が再実行され、テキストが 1 文字ずつ流れ出てきます——実際の LLM の書き方と同じです。温度スライダー(0.2–1.4)でサンプリング分布をリアルタイムに制御できます。</p>
<p><strong>言語識別(AI アプリケーション)</strong>:何でも入力してみてください。テキストは 8 つの解釈可能な統計量(ラテン文字の割合、CJK の割合、かなの割合など)に変換され、学習済みの MLP が 中文 / English / 日本語 を判定します。これは「計算が本物である」ことの直接的な証明でもあります:あなたの入力、その数値、その予測——一本の鎖として端から端まで追えます。</p>
<p>各層のタイトルをクリックすると解説が開きます:何をしているのか、なぜネットワークに必要なのか、平易な言葉でのたとえ——中国語・英語・日本語の三言語対応で、解説中はその層が 3D 内で光り、他は暗くなります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="gpt-のふりはしません">GPT のふりはしません<a href="https://tanzhuo.xyz/ja/2026/08/06/ai-room#gpt-%E3%81%AE%E3%81%B5%E3%82%8A%E3%81%AF%E3%81%97%E3%81%BE%E3%81%9B%E3%82%93" class="hash-link" aria-label="GPT のふりはしません への直接リンク" title="GPT のふりはしません への直接リンク" translate="no">​</a></h2>
<p>README には「プロダクションモデルとの違い」という専用セクションがあります:Transformer ブロックは 1 つだけ、2 ヘッド、d=12、文字レベルのトークン。学習はサンプル単位の素朴な SGD。CNN は畳み込み+プーリング 1 段のみ。言語識別は現代的な埋め込みではなく、あえて手作りの統計特徴を使っています。表示される数学は本物ですが、規模は本物ではありません。簡略化した点を正直に列挙するほうが、「これが ChatGPT の仕組みです」と装うよりも価値があると考えています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術スタック">技術スタック<a href="https://tanzhuo.xyz/ja/2026/08/06/ai-room#%E6%8A%80%E8%A1%93%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF" class="hash-link" aria-label="技術スタック への直接リンク" title="技術スタック への直接リンク" translate="no">​</a></h2>
<p>Vite + React + TypeScript + React Three Fiber + Drei + Zustand。<code>npm run sanity</code> はすべてのネットワークを全スケールで学習させ、精度を検証します。</p>
<p>コードは <a href="https://github.com/tan-zhuo/ai-room" target="_blank" rel="noopener noreferrer" class="">github.com/tan-zhuo/ai-room</a> にあります。ぜひ遊んでみてください。issue も歓迎です。</p>]]></content:encoded>
            <category>AI</category>
            <category>フロントエンド</category>
        </item>
        <item>
            <title><![CDATA[コンテナクラウドからインテリジェントクラウドへ]]></title>
            <link>https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji</link>
            <guid>https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji</guid>
            <pubDate>Fri, 03 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[もはや単一のエージェントでは足りません。本記事では Kubernetes の上に AI 専用に設計されたコントロールプレーンを構築する——Kubernetes AI OS の六層アーキテクチャと進化の道筋を提案します。]]></description>
            <content:encoded><![CDATA[<p>AI エージェント（Agent）システムが今日まで発展してきた結果、単一のエージェントではもはや足りなくなりました。私たちに必要なのは、<strong>マルチエージェントの協調</strong>、<strong>観測可能な実行トレース</strong>、<strong>安全な副作用ガバナンス</strong>、そして分散環境での信頼性の高いスケジューリングです。</p>
<!-- -->
<p>OpenClaw は gateway、tools、sessions、ローカルでのマルチエージェントルーティングにおいて優れた性能を発揮していますが、大規模な協調、ノード間スケジューリング、オペレーターへの可視性という面ではまだ弱点があります。一方、golutra のようなオーケストレーションと trace の能力は、貴重な補完を提供してくれます。</p>
<p>OpenClaw の gateway runtime をベースに、私はひとつの進化アーキテクチャを提案します。<strong>Kubernetes を堅牢な土台とし、その上に AI 専用に設計されたコントロールプレーンを構築する</strong>——これを <strong>Kubernetes AI OS</strong> と呼びます。Kubernetes を置き換えるのではなく、インフラストラクチャ層として位置づけ、エージェントランタイム、実行スケジューリング、ケイパビリティディスカバリー、副作用ガバナンス、ノード間協調といった能力を新たに加え、真にオペレーターに向き合った実行システムを作るのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-04-Gemini_Generated_Image_xonbpixonbpixonb-c7b4a8c8648cceb82c3292ec7ce19fe8.webp" width="1600" height="873" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜこのアーキテクチャが必要なのか">なぜこのアーキテクチャが必要なのか<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E3%81%AA%E3%81%9C%E3%81%93%E3%81%AE%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜこのアーキテクチャが必要なのか への直接リンク" title="なぜこのアーキテクチャが必要なのか への直接リンク" translate="no">​</a></h2>
<p>OpenClaw は現在、channels、tools、sessions、ローカルのマルチエージェントルーティングにおいて強力ですが、大規模な協調、実行トレースの監査、ノード間の配置についてはまだ明らかな弱点があります。</p>
<p>私たちが融合させたいのは次の三つです。</p>
<ul>
<li class="">OpenClaw のサウスバウンド統合と agent runtime</li>
<li class="">golutra 的な実行トレースと可視化の能力</li>
<li class="">Kubernetes のライフサイクル管理、スケジューリング、障害復旧の能力</li>
</ul>
<p>核心となる理念は一言に尽きます。<strong>実行は可視化でき、検査でき、帰属を特定できなければならない</strong>。明確な実行台帳がなければ、マルチエージェントシステムは容易にブラックボックスと化してしまいます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="設計目標">設計目標<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E8%A8%AD%E8%A8%88%E7%9B%AE%E6%A8%99" class="hash-link" aria-label="設計目標 への直接リンク" title="設計目標 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">単一ノード内でのマルチエージェントの明確な協調をサポートする</li>
<li class="">ノードをまたぐワークフロー単位のスケジューリングをサポートする</li>
<li class="">ノードの自動参加・隔離・復旧を可能にする</li>
<li class="">小規模モデルでロール推薦を補助するが、高リスクの意思決定は必ずポリシープレーンを通す</li>
<li class="">意味のあるすべてのアクションを統一実行台帳に記録する</li>
<li class="">コントロールプレーン、実行プレーン、可観測性プレーン、ポリシープレーンを明確に分離する</li>
</ul>
<p><strong>やらないこと</strong>：Kubernetes ネイティブのスケジューリングを置き換えない。エージェント同士の無制限な自由チャットを許可しない。権限昇格やトポロジー変更といった高リスク操作をモデルに直接決定させない。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="六層アーキテクチャの概要">六層アーキテクチャの概要<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E5%85%AD%E5%B1%A4%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E6%A6%82%E8%A6%81" class="hash-link" aria-label="六層アーキテクチャの概要 への直接リンク" title="六層アーキテクチャの概要 への直接リンク" translate="no">​</a></h2>
<p>システムは六つの層に分かれ、責任の境界は明確です。</p>
<ul>
<li class=""><strong>Southbound Gateway Layer</strong>：OpenClaw の channel・tool・session 接続能力をそのまま活用する</li>
<li class=""><strong>Execution Kernel</strong>：中核となる実行層で、Run → Flow → Step → SideEffect の全ライフサイクルを管理する</li>
<li class=""><strong>Cluster Mesh</strong>：ノード登録、ケイパビリティの公開、step のスケジューリングとセキュリティ隔離を担当する（ノード間の自由なチャットは推奨しない）</li>
<li class=""><strong>Observability Plane</strong>：統一実行台帳として、runs、flows、steps、side effects の可視化ビューを提供する</li>
<li class=""><strong>Cognition Plane</strong>：小規模モデルで workload の識別、ロール推薦、状態サマリーを行う</li>
<li class=""><strong>Policy Plane</strong>：セキュリティ承認、secret のスコープ管理、side-effect ガバナンス、信頼チェックを担当する</li>
</ul>
<p>コントロールプレーンは「何を実行すべきか、どこで実行するか、どのポリシーで実行するか」を決定し、実行プレーンは実際の実行を担って trace を生成します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="単一ノードのマルチエージェントランタイム">単一ノードのマルチエージェントランタイム<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E5%8D%98%E4%B8%80%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E3%83%9E%E3%83%AB%E3%83%81%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0" class="hash-link" aria-label="単一ノードのマルチエージェントランタイム への直接リンク" title="単一ノードのマルチエージェントランタイム への直接リンク" translate="no">​</a></h2>
<p>ノードが一つしかなくても、複数ロールの協調実行をサポートすべきです。推奨する基本ロールは次のとおりです。</p>
<ul>
<li class="">Planner（計画）</li>
<li class="">Executor（実行）</li>
<li class="">Reviewer（レビュー）</li>
<li class="">Watcher（監視）</li>
<li class="">Specialist（ドメインエキスパート）</li>
</ul>
<p>すべてのアクションはまずローカルの ledger に報告され、その後コントロールプレーンで集約されることで、実行トレースの明確な可視性が確保されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ノード間スケジューリングチャットではなく-step-ベースで">ノード間スケジューリング：チャットではなく Step ベースで<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E3%83%8E%E3%83%BC%E3%83%89%E9%96%93%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%83%81%E3%83%A3%E3%83%83%E3%83%88%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F-step-%E3%83%99%E3%83%BC%E3%82%B9%E3%81%A7" class="hash-link" aria-label="ノード間スケジューリング：チャットではなく Step ベースで への直接リンク" title="ノード間スケジューリング：チャットではなく Step ベースで への直接リンク" translate="no">​</a></h2>
<p>ノード間協調のカギは、ノード間で自由にメッセージを転送させるのではなく、<strong>Step を単位としてスケジューリングする</strong>ことです。これによって初めて、所有権の明確さ、キャンセル可能性、監査可能性が保証されます。</p>
<p>スケジューラは、必要なツール、モデルの可用性、ハードウェアリソース、locality、信頼レベル、tenant のアフィニティといった要素を総合的に考慮します。</p>
<p>ノードの状態には joining、ready、degraded、quarantined、draining などがあり、高リスクノードの自動隔離をサポートします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="中核プリミティブexecution-ledger実行台帳">中核プリミティブ：Execution Ledger（実行台帳）<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E4%B8%AD%E6%A0%B8%E3%83%97%E3%83%AA%E3%83%9F%E3%83%86%E3%82%A3%E3%83%96execution-ledger%E5%AE%9F%E8%A1%8C%E5%8F%B0%E5%B8%B3" class="hash-link" aria-label="中核プリミティブ：Execution Ledger（実行台帳） への直接リンク" title="中核プリミティブ：Execution Ledger（実行台帳） への直接リンク" translate="no">​</a></h2>
<p>これはシステム全体で最も重要な部分です。すべてのイベント（run、flow、step、side effect、policy など）は runId に紐づけられ、統一的にクエリ可能な台帳を形成します。</p>
<p>オペレーターは次の問いに簡単に答えられるべきです。</p>
<ul>
<li class="">どの agent が実行しているのか？</li>
<li class="">どんな副作用が発生したのか？</li>
<li class="">現在どのステップで止まっているのか？</li>
<li class="">この失敗は安全にリトライできるのか？</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="kubernetes-との関係">Kubernetes との関係<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#kubernetes-%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82" class="hash-link" aria-label="Kubernetes との関係 への直接リンク" title="Kubernetes との関係 への直接リンク" translate="no">​</a></h2>
<p>Kubernetes は引き続き、Pod のスケジューリング、liveness、service discovery、ストレージ、RBAC といったインフラストラクチャ能力を担当します。</p>
<p>AI OS 層は CustomResource（CRD）を通じて HiveRun、HiveFlow、HiveStepLease、HiveNode などのオブジェクトを表現し、Operator でリコンサイルロジックを実装します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="進化の道筋の提案">進化の道筋の提案<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E9%80%B2%E5%8C%96%E3%81%AE%E9%81%93%E7%AD%8B%E3%81%AE%E6%8F%90%E6%A1%88" class="hash-link" aria-label="進化の道筋の提案 への直接リンク" title="進化の道筋の提案 への直接リンク" translate="no">​</a></h2>
<p>小さな一歩から始めることをおすすめします。</p>
<ol>
<li class=""><strong>Phase 1</strong>：単一ノードで run ledger、flow graph、side-effect の可視化を整備する</li>
<li class=""><strong>Phase 2</strong>：単一クラスタの step scheduler とノード管理を実装する</li>
<li class=""><strong>Phase 3</strong>：軽量なロール推薦を追加する</li>
<li class=""><strong>Phase 4</strong>：適応的なクラスタ挙動を実現する</li>
</ol>
<p>最初の目標は完全な自律を追求することではなく、まず<strong>単一ノードで一級の可観測性を備えた AI ランタイム</strong>を作ることです。まず実行を可視化し、次に副作用をガバナンス可能にし、最後にノード間スケジューリングに取り組みます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/04/03/dang-ai-xue-hui-gan-zhi-zi-ji#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Kubernetes AI OS の目標は、Kubernetes を AI 時代のオペレーティングシステムの土台へと真に進化させることです。安全性と制御可能性を保った前提のもとで、マルチエージェントの協調能力を最大限に発揮させます。車輪の再発明はせず、OpenClaw のサウスバウンド接続、golutra 的な実行トレース、そして Kubernetes のスケジューリング・復旧能力を組み合わせます。設計全体はただ一つの主軸を巡っています——実行は可視化でき、検査でき、帰属を特定できなければならない。実装は一足飛びである必要はありません。単一ノードの実行台帳から始めて、段階的にノード間スケジューリングと適応的クラスタへと進んでいけばよいのです。</p>]]></content:encoded>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[OpenClaw アーキテクチャ設計]]></title>
            <link>https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji</link>
            <guid>https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji</guid>
            <pubDate>Thu, 02 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[OpenClaw は大規模言語モデルの推論能力とローカル実行環境を徹底的に分離しています。本記事では、全体アーキテクチャ・実行ロジック・セキュリティ機構という 3 つの観点から、このオープンソース AI エージェントツールを解説します。]]></description>
            <content:encoded><![CDATA[<p>多くの AI アプリケーションはいまだに「チャットによる対話」という表層にとどまっています。ユーザーが質問を入力し、モデルがテキストを返すだけで、ローカルデバイスに実際に触れて具体的なタスクを実行することはできません。OpenClaw（旧称 Clawdbot/Moltbot/Molty）はこの限界を打ち破りました。大規模言語モデルの「推論能力」とローカル実行環境を徹底的に分離することで、AI を「対話アシスタント」から「自律的に行動できるエージェント」へと進化させたのです。本記事では、Peter Steinberger 氏が開発した TypeScript/Node.js 製のこのオープンソースツールを、全体アーキテクチャ・実行ロジック・設計上のハイライトという 3 つの観点から解説します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="全体アーキテクチャ設計ハブアンドスポークレイヤード分離によるos-レベルのフレームワーク">全体アーキテクチャ設計：ハブ・アンド・スポーク+レイヤード分離による「OS レベル」のフレームワーク<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E5%85%A8%E4%BD%93%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E8%A8%AD%E8%A8%88%E3%83%8F%E3%83%96%E3%82%A2%E3%83%B3%E3%83%89%E3%82%B9%E3%83%9D%E3%83%BC%E3%82%AF%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%83%89%E5%88%86%E9%9B%A2%E3%81%AB%E3%82%88%E3%82%8Bos-%E3%83%AC%E3%83%99%E3%83%AB%E3%81%AE%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF" class="hash-link" aria-label="全体アーキテクチャ設計：ハブ・アンド・スポーク+レイヤード分離による「OS レベル」のフレームワーク への直接リンク" title="全体アーキテクチャ設計：ハブ・アンド・スポーク+レイヤード分離による「OS レベル」のフレームワーク への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-04-Gemini_Generated_Image_iy2svaiy2svaiy2s-1-f3d52510266b418bdf06cabe1bb30c7a.webp" width="1408" height="768" class="img_ev3q"></p>
<p>OpenClaw の中核となる設計哲学は「AI をプロンプトエンジニアリングの問題ではなく、インフラの問題として捉える」ことです。モデルは推論だけを担当し、状態管理・実行制御・セキュリティ保障・マルチチャネル連携はシステム側が引き受けます。アーキテクチャは<strong>ハブ・アンド・スポーク（Hub-and-Spoke）<strong>と</strong>レイヤード分離</strong>を組み合わせた方式を採用しており、コアロジックの集中管理と各コンポーネントの柔軟性を両立させています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハブアンドスポークgateway-が唯一のコントロールプレーン">ハブ・アンド・スポーク：Gateway が唯一のコントロールプレーン<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E3%83%8F%E3%83%96%E3%82%A2%E3%83%B3%E3%83%89%E3%82%B9%E3%83%9D%E3%83%BC%E3%82%AFgateway-%E3%81%8C%E5%94%AF%E4%B8%80%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%97%E3%83%AC%E3%83%BC%E3%83%B3" class="hash-link" aria-label="ハブ・アンド・スポーク：Gateway が唯一のコントロールプレーン への直接リンク" title="ハブ・アンド・スポーク：Gateway が唯一のコントロールプレーン への直接リンク" translate="no">​</a></h3>
<p>OpenClaw は **Gateway（ゲートウェイ）**を中心に据え、すべてのコンポーネント（クライアント、チャネルアダプター、Agent 実行器など）が Gateway を軸として放射状に接続されます。Gateway はシステムの「交通ハブ」であり、デフォルトではローカルループバックアドレス（127.0.0.1:18789）にバインドされ、ローカルデバイスからのアクセスのみを許可することで、データプライバシーを根本から保護します。その主な責務は次のとおりです。</p>
<ul>
<li class=""><strong>統一認証とセッション分離</strong>：ユーザーやデバイスごとにセッションを独立して管理し、データの相互汚染を防ぎます。</li>
<li class=""><strong>Lane Queue（レーン式シリアルキュー）</strong>：各セッションはデフォルトでタスクを直列実行し、マルチタスクの並行実行による状態の競合やログの混在を根本的に解消します（手動で並列モードを有効化することも可能です）。</li>
<li class=""><strong>メッセージルーティングとスケジューリング</strong>：標準化されたメッセージを対応する Agent へ正確に転送します。</li>
<li class=""><strong>ストリーミング対話のサポート</strong>：モデルの出力と「入力中」ステータスをリアルタイムに配信し、自然な会話体験を再現します。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="レイヤードアーキテクチャ外から内への能力分解">レイヤードアーキテクチャ：外から内への能力分解<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E3%83%89%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E5%A4%96%E3%81%8B%E3%82%89%E5%86%85%E3%81%B8%E3%81%AE%E8%83%BD%E5%8A%9B%E5%88%86%E8%A7%A3" class="hash-link" aria-label="レイヤードアーキテクチャ：外から内への能力分解 への直接リンク" title="レイヤードアーキテクチャ：外から内への能力分解 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw のレイヤードアーキテクチャは、ユーザーとの対話からインフラまで段階的に構成されており、各レイヤーの責務が明確で、それぞれ独立して拡張できます。</p>
<p>1）<strong>クライアント層：マルチエントリーの対話インターフェース</strong></p>
<p>ユーザーは専用アプリに依存することなく、次の方法で OpenClaw と対話できます。</p>
<ul>
<li class="">コマンドラインツール（CLI）：技術系ユーザーの素早いデバッグに最適</li>
<li class="">Web UI：ビジュアルな操作パネル</li>
<li class="">macOS メニューバーアプリ、iOS/Android Node：モバイルデバイス向けの軽量エントリー</li>
<li class="">サードパーティのチャットプラットフォーム：WhatsApp、Telegram、Discord など 20 以上の一般的なアプリに対応し、ユーザーは使い慣れたツールから直接指示を出せます。</li>
</ul>
<p>2）<strong>接続調整層：Gateway の「中枢機能」</strong></p>
<p>前述のとおり、Gateway は接続調整の中核であり、異種の入力をシステムが認識できる形式に変換し、セッションのライフサイクルを管理します。</p>
<p>3）<strong>チャネル抽象層：プラットフォームプロトコルの壁を突破</strong></p>
<p>チャットプラットフォームごとにメッセージプロトコルは大きく異なります（WhatsApp の Baileys プロトコル、Telegram の MTProto プロトコルなど）。**Channel Adapters（チャネルアダプター）**は「通訳者」の役割を担い、各プラットフォームのメッセージ（テキスト、画像、音声など）を OpenClaw 内部の標準形式に統一変換し、添付ファイルのダウンロード・キャッシュ・アクセス制御（「指定した連絡先からの指示のみ許可」など）を処理します。</p>
<p>4）<strong>コアロジック層：Agent の「思考と実行エンジン」</strong></p>
<p>コアロジック層は <strong>Agent Runner</strong> と <strong>Memory System</strong> で構成され、AI エージェントが「仕事をこなす」ための鍵となります。</p>
<ul>
<li class=""><strong>Agent Runner</strong>：各チャネルやグループを独立した Agent インスタンスにマッピングでき（マルチ Agent 連携をサポート）、RPC で Pi Agent Runtime を呼び出して「指示の受信→モデルの呼び出し→ツールの実行→結果の生成」という完全なループを実行します。</li>
<li class=""><strong>Memory System</strong>：セッション履歴、ユーザーの長期記憶、ツールの能力記述などのコンテキスト管理を担当し、モデルに意思決定の根拠を提供します。</li>
</ul>
<p>5）<strong>インフラ層：ローカル実行の「土台」</strong></p>
<p>インフラ層はシステム稼働の基礎能力を提供します。</p>
<ul>
<li class="">ローカル永続化：すべてのデータを Markdown と .jsonl 形式で保存（クラウドデータベースへの依存を回避）</li>
<li class="">WebSocket ストリーミング通信：クライアントと Gateway のリアルタイム接続を保証</li>
<li class="">サンドボックス実行：ツールのシステム権限を制限し、悪意ある操作を防止</li>
<li class="">定期タスク（Cron）と Webhook：能動的なタスク起動をサポート（毎朝のレポート配信など）</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="コアコンポーネントの関係各モジュールはどう連携するのか">コアコンポーネントの関係：各モジュールはどう連携するのか？<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E3%82%B3%E3%82%A2%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E9%96%A2%E4%BF%82%E5%90%84%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AF%E3%81%A9%E3%81%86%E9%80%A3%E6%90%BA%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="コアコンポーネントの関係：各モジュールはどう連携するのか？ への直接リンク" title="コアコンポーネントの関係：各モジュールはどう連携するのか？ への直接リンク" translate="no">​</a></h3>
<p>アーキテクチャをより明確に理解するために、コアコンポーネントの関係を次のように整理できます。</p>
<ol>
<li class=""><strong>Gateway</strong> は「スケジューリングセンター」として、クライアントやチャネルアダプターからのメッセージを受け取り、対応する Agent に振り分けます。</li>
<li class=""><strong>Agent Runner</strong> は Gateway の指示に基づき、モデルを呼び出して意思決定を生成し、ツール（ファイルの読み書き、ブラウザ操作など）を実行します。</li>
<li class=""><strong>Channel Adapters</strong> はサードパーティプラットフォームのメッセージを「翻訳」し、ユーザーがツールを切り替えることなく対話できるようにします。</li>
<li class=""><strong>Skills（スキルプラグイン）</strong>：<code>~/.openclaw/workspace/skills</code> ディレクトリに格納され、Agent が自律的に発見・インストール・呼び出しできます（ClawHub レジストリでのスキル共有もサポート）。</li>
<li class=""><strong>Nodes（デバイス側実行器）</strong>：macOS や iOS などのデバイス上で動作し、WebSocket 経由でローカルハードウェアの能力（音声入力、カメラ、画面録画など）を公開します。</li>
<li class=""><strong>Canvas</strong>：独立ポート（18793）で提供されるビジュアルワークスペースで、Agent が HTML/A2UI インターフェース（タスクの進捗バー、データダッシュボードなど）を生成できます。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実行ロジックagent-loop-と能動的スケジューリングによる仕事をこなす仕組み">実行ロジック：Agent Loop と能動的スケジューリングによる「仕事をこなす」仕組み<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E5%AE%9F%E8%A1%8C%E3%83%AD%E3%82%B8%E3%83%83%E3%82%AFagent-loop-%E3%81%A8%E8%83%BD%E5%8B%95%E7%9A%84%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%AB%E3%82%88%E3%82%8B%E4%BB%95%E4%BA%8B%E3%82%92%E3%81%93%E3%81%AA%E3%81%99%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="実行ロジック：Agent Loop と能動的スケジューリングによる「仕事をこなす」仕組み への直接リンク" title="実行ロジック：Agent Loop と能動的スケジューリングによる「仕事をこなす」仕組み への直接リンク" translate="no">​</a></h2>
<p>OpenClaw の中核的な価値は「AI に本当に仕事をさせる」ことにあり、これは<strong>循環可能な Agent Loop（エージェントループ）<strong>と</strong>能動的スケジューリング機構</strong>によって支えられています。前者はユーザーが起動するタスクを処理し、後者は無人での自律稼働を実現します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="典型的なメッセージ処理フロー入力から出力までの全経路">典型的なメッセージ処理フロー：入力から出力までの全経路<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E5%85%B8%E5%9E%8B%E7%9A%84%E3%81%AA%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E5%87%A6%E7%90%86%E3%83%95%E3%83%AD%E3%83%BC%E5%85%A5%E5%8A%9B%E3%81%8B%E3%82%89%E5%87%BA%E5%8A%9B%E3%81%BE%E3%81%A7%E3%81%AE%E5%85%A8%E7%B5%8C%E8%B7%AF" class="hash-link" aria-label="典型的なメッセージ処理フロー：入力から出力までの全経路 への直接リンク" title="典型的なメッセージ処理フロー：入力から出力までの全経路 への直接リンク" translate="no">​</a></h3>
<p>ユーザーの指示（「デスクトップにある直近 3 日間のファイルを整理してリストを作成して」など）は、次のような流れで処理されます。</p>
<p>1）<strong>メッセージの受信と標準化</strong></p>
<p>ユーザーが Telegram で指示を送信→Channel Adapter が Telegram のメッセージをシステム標準形式（テキスト内容、送信者情報、コンテキストを含む）に変換→標準化されたメッセージを Gateway の対応する Session Lane Queue に投入（直列実行で競合を回避）。</p>
<p>2）<strong>ルーティングと Agent の起動</strong></p>
<p>Gateway は設定（「main セッションはデフォルト Agent にバインド」「main 以外のセッションは @ で起動」など）に基づき、メッセージを対象の Agent にルーティングします。たとえばユーザーが家族グループで OpenClaw をメンションすると、Gateway は「家族アシスタント」Agent を起動します。</p>
<p>3）<strong>コンテキストの組み立て：Agent に重要な情報を「覚えさせる」</strong></p>
<p>Memory System は次の内容を自動的に組み立て、モデルへの入力コンテキストとします。</p>
<ul>
<li class="">現在のセッションの履歴メッセージ（Agent の「記憶喪失」を防止）</li>
<li class="">ユーザーの長期記憶（「ユーザーは Markdown でのファイル整理を好む」「デスクトップのパスは ~/Desktop」など）</li>
<li class="">システムプロンプト：AGENTS.md（Agent の行動基準、「ローカルツールを優先的に使用」など）、SOUL.md（性格設定、「簡潔で効率的」など）、TOOLS.md（ツールの能力記述、「file.read はローカルファイルを読み取れる」など）</li>
<li class="">インストール済みの Skills（「ファイル分類スキル」など）</li>
</ul>
<p>コンテキストが長すぎる場合、システムが自動的に圧縮・要約し、モデルへの入力がコンテキストウィンドウに収まるようにします。</p>
<p>4）<strong>モデルの呼び出しと意思決定の生成</strong></p>
<p>Agent Runner は設定された LLM（Claude、GPT、あるいは Llama 3 のようなローカルモデルに対応）を呼び出し、モデルはコンテキストに基づいて「ツールの実行が必要か？」を判断します。</p>
<ul>
<li class="">ツールが不要な場合：自然言語の返答を直接生成します（「整理が完了しました。リストはデスクトップに保存済みです」など）。</li>
<li class="">ツールが必要な場合：ツール呼び出しの指示を生成します（<code>{"name": "file.list", "parameters": {"path": "~/Desktop", "days": 3}}</code> など）。</li>
</ul>
<p>5）<strong>ツールの実行：タスク完了まで複数回のループ</strong></p>
<p>ツールの実行には<strong>ループ機構</strong>が採用されています。</p>
<ol>
<li class="">Agent Runner はサンドボックス内でツールを実行します（main セッションはデフォルトでホスト権限を持ち、main 以外のセッションは Docker サンドボックスによる隔離を強制できます）。</li>
<li class="">ツールが実行結果を返します（「5 個のファイルが見つかりました：report.pdf、photo.jpg...」など）。</li>
<li class="">結果がコンテキストに書き戻され、Agent Runner が再度モデルを呼び出し、さらなる操作が必要かを判断します（「リストを Markdown に変換すべきか？」など）。</li>
<li class="">モデルがタスク完了と判断するまで、上記のステップを繰り返します。</li>
</ol>
<p>6）<strong>出力と永続化</strong></p>
<p>最終結果は Gateway を通じて元のチャネル（Telegram など）にストリーミング配信されます。同時に、すべての対話記録、ツール実行ログ、記憶の更新はローカルの .jsonl と Markdown ファイルに書き込まれ、データの追跡可能性とクラウド非依存が保証されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="能動的稼働機構ai-の自律性の源泉">能動的稼働機構：AI の「自律性」の源泉<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E8%83%BD%E5%8B%95%E7%9A%84%E7%A8%BC%E5%83%8D%E6%A9%9F%E6%A7%8Bai-%E3%81%AE%E8%87%AA%E5%BE%8B%E6%80%A7%E3%81%AE%E6%BA%90%E6%B3%89" class="hash-link" aria-label="能動的稼働機構：AI の「自律性」の源泉 への直接リンク" title="能動的稼働機構：AI の「自律性」の源泉 への直接リンク" translate="no">​</a></h3>
<p>従来のチャットボットはユーザーによる起動が必要ですが、OpenClaw は<strong>能動的な稼働</strong>をサポートしており、ユーザーの指示なしにタスクを完了できます。その実現方法は次の 3 つです。</p>
<p>1）<strong>定期タスク（Heartbeat Scheduler/Cron）</strong></p>
<p>「毎朝 8 時に前日のメールを整理して要約を作成」「毎週日曜にデスクトップのファイルを外付けドライブにバックアップ」といった周期的なタスクを設定できます。Gateway は時間どおりに Agent を起こしてタスクを実行させ、結果は指定チャネル（ユーザーの WhatsApp など）に自動送信されます。</p>
<p>2）<strong>外部イベントトリガー（Webhook/Pub/Sub）</strong></p>
<p>OpenClaw は Webhook 経由で外部イベントを受信できます。Gmail の新着メール、GitHub Issue の作成、スマートホームデバイスのトリガーなどです。たとえば重要な仕事のメールを受信した際に、OpenClaw が自動的に要点を抽出してユーザーに通知できます。</p>
<p>3）<strong>マルチ Agent 連携</strong></p>
<p>Agent は組み込みツール（すべてのセッションを一覧する <code>sessions_list</code>、他の Agent にメッセージを送る <code>sessions_send</code> など）を通じて連携できます。たとえば「スケジュール管理 Agent」がユーザーの明日の会議を発見し、「ファイル整理 Agent」に会議資料の事前準備を通知する、といった具合です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="起動と稼働数ステップでデプロイ完了">起動と稼働：数ステップでデプロイ完了<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E8%B5%B7%E5%8B%95%E3%81%A8%E7%A8%BC%E5%83%8D%E6%95%B0%E3%82%B9%E3%83%86%E3%83%83%E3%83%97%E3%81%A7%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E5%AE%8C%E4%BA%86" class="hash-link" aria-label="起動と稼働：数ステップでデプロイ完了 への直接リンク" title="起動と稼働：数ステップでデプロイ完了 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw のデプロイは非常に軽量で、一般ユーザーでもすぐに使い始められます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 最新バージョンをグローバルインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">npm</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-g</span><span class="token plain"> openclaw@latest</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 初期化：チャットチャネル、LLM モデル、ワークスペースパスの設定をガイド</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openclaw onboard</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 常駐稼働：Gateway をシステムデーモンとして登録し、24/7 稼働を実現</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openclaw onboard --install-daemon</span><br></div></code></pre></div></div>
<p>完了後は、CLI、Web UI、またはチャットアプリから Gateway に接続すれば利用開始できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="主要な設計ハイライトとセキュリティ機構ローカルファーストのプライバシー保護">主要な設計ハイライトとセキュリティ機構：ローカルファーストのプライバシー保護<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E4%B8%BB%E8%A6%81%E3%81%AA%E8%A8%AD%E8%A8%88%E3%83%8F%E3%82%A4%E3%83%A9%E3%82%A4%E3%83%88%E3%81%A8%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E6%A9%9F%E6%A7%8B%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E3%81%AE%E3%83%97%E3%83%A9%E3%82%A4%E3%83%90%E3%82%B7%E3%83%BC%E4%BF%9D%E8%AD%B7" class="hash-link" aria-label="主要な設計ハイライトとセキュリティ機構：ローカルファーストのプライバシー保護 への直接リンク" title="主要な設計ハイライトとセキュリティ機構：ローカルファーストのプライバシー保護 への直接リンク" translate="no">​</a></h2>
<p>OpenClaw の設計は「ユーザーのコントロール権」と「プライバシーの安全性」を軸に展開されています。以下がその主なハイライトです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ローカルファーストデータは常にあなたの手の中に">ローカルファースト：データは常にあなたの手の中に<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AF%E5%B8%B8%E3%81%AB%E3%81%82%E3%81%AA%E3%81%9F%E3%81%AE%E6%89%8B%E3%81%AE%E4%B8%AD%E3%81%AB" class="hash-link" aria-label="ローカルファースト：データは常にあなたの手の中に への直接リンク" title="ローカルファースト：データは常にあなたの手の中に への直接リンク" translate="no">​</a></h3>
<p>すべてのデータ（セッション履歴、記憶、ツール実行ログ）はローカルデバイスに保存され、Gateway はデフォルトで 127.0.0.1（ローカルループバックアドレス）のみをリッスンし、クラウドへは一切データを送信しません。ネットワークが切断されても、OpenClaw はローカルタスク（ファイル操作、ローカルモデルの呼び出しなど）を正常に実行できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="セキュリティ隔離多層防御でリスクを回避">セキュリティ隔離：多層防御でリスクを回避<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E9%9A%94%E9%9B%A2%E5%A4%9A%E5%B1%A4%E9%98%B2%E5%BE%A1%E3%81%A7%E3%83%AA%E3%82%B9%E3%82%AF%E3%82%92%E5%9B%9E%E9%81%BF" class="hash-link" aria-label="セキュリティ隔離：多層防御でリスクを回避 への直接リンク" title="セキュリティ隔離：多層防御でリスクを回避 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw は多層の仕組みでシステムの安全性を保証します。</p>
<ul>
<li class=""><strong>DM ペアリング承認</strong>：サードパーティチャットプラットフォームからの DM による指示はユーザーの手動承認が必要です（Telegram ユーザーが <code>/pair</code> を送信すると OpenClaw が確認コードを送り、ユーザーが認証して初めて対話可能になります）。</li>
<li class=""><strong>サンドボックス実行</strong>：main 以外のセッションのツール実行はデフォルトで Docker サンドボックス内で行われ、ホストシステムへのアクセスが制限されます。</li>
<li class=""><strong>ツールブラックリスト</strong>：Agent による危険なツール（システムコマンドを実行する <code>system.exec</code> など）の呼び出しを禁止する設定が可能です。</li>
<li class=""><strong>明示的なコントロール</strong>：<code>/think high</code>（モデルの思考の深さを引き上げる）、<code>/verbose on</code>（ツール実行の詳細を表示する）などのコマンドで Agent の挙動を微調整できます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="拡張性プラグインアーキテクチャによるカスタマイズ">拡張性：プラグインアーキテクチャによるカスタマイズ<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E6%8B%A1%E5%BC%B5%E6%80%A7%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AB%E3%82%88%E3%82%8B%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA" class="hash-link" aria-label="拡張性：プラグインアーキテクチャによるカスタマイズ への直接リンク" title="拡張性：プラグインアーキテクチャによるカスタマイズ への直接リンク" translate="no">​</a></h3>
<p>OpenClaw のすべてのコアコンポーネントはホットスワップに対応しています。</p>
<ul>
<li class=""><strong>チャネル拡張</strong>：新しい Channel Adapter（WeChat、Signal など）を自作できます。</li>
<li class=""><strong>モデル拡張</strong>：任意の LLM を接続できます（標準インターフェースを実装するだけです）。</li>
<li class=""><strong>スキル拡張</strong>：ユーザーはカスタム Skills（「Obsidian ノートの自動同期」「PPT アウトラインの生成」など）を作成でき、Agent が Skill の契約ファイルを自律的に読み取って呼び出します。</li>
<li class=""><strong>メモリ拡張</strong>：Memory System をベクトルデータベース（Pinecone など）に置き換え、長期記憶の検索効率を高められます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="記憶システム4-層構造で-agent-が使うほど賢くなる">記憶システム：4 層構造で Agent が「使うほど賢くなる」<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E8%A8%98%E6%86%B6%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A04-%E5%B1%A4%E6%A7%8B%E9%80%A0%E3%81%A7-agent-%E3%81%8C%E4%BD%BF%E3%81%86%E3%81%BB%E3%81%A9%E8%B3%A2%E3%81%8F%E3%81%AA%E3%82%8B" class="hash-link" aria-label="記憶システム：4 層構造で Agent が「使うほど賢くなる」 への直接リンク" title="記憶システム：4 層構造で Agent が「使うほど賢くなる」 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw の記憶システムは 4 層に分かれており、Agent が履歴情報を効率的に活用できるようになっています。</p>
<ol>
<li class=""><strong>SOUL 層</strong>：Agent の性格と価値観（「ユーザーのプライバシー保護を最優先」「簡潔に回答する」など）</li>
<li class=""><strong>TOOLS 層</strong>：ツールの能力記述と使用方法</li>
<li class=""><strong>USER ベクトル記憶</strong>：ユーザーの長期的な好み（「紅茶よりコーヒーが好き」「よく使うファイルパス」など）。ベクトルストレージにより高速検索を実現</li>
<li class=""><strong>Session 短期記憶</strong>：現在のセッションの履歴メッセージ。自動圧縮により冗長性を回避</li>
</ol>
<p>システムは定期的に記憶の「圧縮（compaction）」を行い、重要な情報を残して重複を削除することで、Agent の「記憶」をより効率的にします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/04/02/openclaw-jia-gou-she-ji#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>OpenClaw は「OS レベル」のアーキテクチャによって、「AI はどうすれば本当に仕事をこなせるのか」という問いに答えました。Gateway が唯一のコントロールプレーンとして統一的にスケジューリングを行い、レイヤード設計によりチャネル・モデル・スキルをそれぞれ独立して置き換えられます。Agent Loop の複数回にわたるツールループと、定期タスクや Webhook といった能動的な仕組みの組み合わせにより、AI は指示に応答するだけでなく無人で稼働することもできます。そしてローカルファーストのストレージ戦略、サンドボックス隔離、ペアリング承認は、能力と安全性の間に明確な境界線を引いています。大規模言語モデルを実際のワークフローに組み込みたい人にとって、この設計には参考になる点が数多くあるはずです。</p>]]></content:encoded>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[Cursor の発展]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan</guid>
            <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Cursor の発展の軌跡：従来の IDE とコード補完ツールから話を始め、この AI ネイティブエディタの誕生背景、コア技術設計、成長状況、そして直面する課題を整理します。]]></description>
            <content:encoded><![CDATA[<p>Cursor はおそらくこの 2 年で最も成長の速い開発ツールの一つです。この記事ではその発展の軌跡を整理します。従来の IDE から話を始め、この AI ネイティブエディタが一体何をやってのけたのか、そしてどんな問題に直面しているのかを見ていきましょう。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ide-プログラミングツールの背景">IDE プログラミングツールの背景<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#ide-%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E8%83%8C%E6%99%AF" class="hash-link" aria-label="IDE プログラミングツールの背景 への直接リンク" title="IDE プログラミングツールの背景 への直接リンク" translate="no">​</a></h2>
<p>長い間、ソフトウェア開発は主に従来の IDE、例えば Visual Studio Code、IntelliJ IDEA、Eclipse に依存してきました。これらの IDE の中核的な能力はコード編集、シンタックスハイライト、自動補完、デバッグツールですが、本当のコードロジックは依然として <strong>プログラマー自身が書く</strong> 必要がありました。</p>
<p><strong>大規模言語モデル（LLM）の登場後</strong>、ソフトウェア開発は <strong>AI プログラミング時代</strong> に入り始めました。最初期の AI プログラミングツールには GitHub Copilot や Tabnine があり、主に <strong>コード補完の問題</strong> を解決するものでした。しかし Cursor の目標は単なる補完ではなく、こうでした。</p>
<blockquote>
<p><strong>AI ネイティブ（AI-Native）な開発環境を構築する。</strong></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cursor-の誕生">Cursor の誕生<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#cursor-%E3%81%AE%E8%AA%95%E7%94%9F" class="hash-link" aria-label="Cursor の誕生 への直接リンク" title="Cursor の誕生 への直接リンク" translate="no">​</a></h2>
<p>Cursor は <strong>Anysphere</strong> が開発した <strong>AI ネイティブコードエディタ（AI-Native IDE）</strong> です。会社は <strong>2022 年</strong> に設立され、創業者は 4 人の MIT 卒業生です。</p>
<ul>
<li class="">Michael Truell</li>
<li class="">Aman Sanger</li>
<li class="">Sualeh Asif</li>
<li class="">Arvid Lunnemark</li>
</ul>
<p>Cursor は <strong>2023 年に正式リリース</strong> されました。技術的には次のようなものです。</p>
<blockquote>
<p><strong>VS Code の Fork をベースにした AI IDE</strong></p>
</blockquote>
<p>ただし VS Code の土台の上に、AI コード生成、AI Debug、AI リファクタリング、AI によるコードベース検索など、大量の AI 機能を組み込みました。Cursor の目標は非常に明確です。</p>
<blockquote>
<p><strong>開発者が自然言語で直接コードを書けるようにする。</strong></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cursor-のコア技術設計">Cursor のコア技術設計<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#cursor-%E3%81%AE%E3%82%B3%E3%82%A2%E6%8A%80%E8%A1%93%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="Cursor のコア技術設計 への直接リンク" title="Cursor のコア技術設計 への直接リンク" translate="no">​</a></h2>
<p>Cursor の設計思想は主に 3 つの核心ポイントからなります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1ai-コード生成">1）AI コード生成<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#1ai-%E3%82%B3%E3%83%BC%E3%83%89%E7%94%9F%E6%88%90" class="hash-link" aria-label="1）AI コード生成 への直接リンク" title="1）AI コード生成 への直接リンク" translate="no">​</a></h3>
<p>Cursor は大規模言語モデルによってコード生成を実現しています。開発者が「Redis 分散ロックを実装して」と直接入力すれば、Cursor はコード、コメント、テストコードを自動生成します。これは従来の IDE の <strong>コード補完とはまったく異なります</strong>。</p>
<ul>
<li class="">従来の IDE：開発者がコードを書き、IDE が補完を支援する</li>
<li class="">Cursor：開発者が要件を記述し、AI がコードを生成する</li>
</ul>
<p>このスタイルは <strong>Vibe Coding</strong> と呼ばれています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2codebase-理解能力">2）Codebase 理解能力<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#2codebase-%E7%90%86%E8%A7%A3%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="2）Codebase 理解能力 への直接リンク" title="2）Codebase 理解能力 への直接リンク" translate="no">​</a></h3>
<p>Cursor の中核能力の一つは <strong>コードリポジトリ全体を理解する</strong> ことです。コードのベクトルインデックス、ファイル間の関係、依存関係を構築し、開発者は「このプロジェクトのユーザーログインのロジックはどこにある？」と直接質問すれば、Cursor が関連コードを特定してくれます。これは RAG + Code Embedding に似たアプローチです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3ai-コード修正能力">3）AI コード修正能力<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#3ai-%E3%82%B3%E3%83%BC%E3%83%89%E4%BF%AE%E6%AD%A3%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="3）AI コード修正能力 への直接リンク" title="3）AI コード修正能力 への直接リンク" translate="no">​</a></h3>
<p>Cursor は <strong>Smart Rewrite</strong> と呼ばれる能力をサポートしています。例えば「このモジュールを非同期に変えて」と言えば、Cursor は複数ファイルの自動修正、関数の自動調整、呼び出し関係の自動更新ができます。これは従来の IDE ではほぼ不可能なことです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cursor-の急成長">Cursor の急成長<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#cursor-%E3%81%AE%E6%80%A5%E6%88%90%E9%95%B7" class="hash-link" aria-label="Cursor の急成長 への直接リンク" title="Cursor の急成長 への直接リンク" translate="no">​</a></h2>
<p>Cursor の成長スピードは驚異的です。リリースから 2 年足らずで：</p>
<ul>
<li class="">ARR が <strong>3 億ドル</strong> を突破</li>
<li class="">資金調達額が <strong>10 億ドル</strong> を突破</li>
<li class="">企業評価額が <strong>300 億ドル</strong> に接近</li>
</ul>
<p>Cursor は <strong>最も成長の速い AI SaaS プロダクトの一つ</strong> にまでなりました。Nvidia、Stripe、Spotify など多くの企業が Cursor を使い始めています——Nvidia では <strong>3 万人以上のエンジニアが Cursor でコードを開発している</strong> そうです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cursor-の技術的課題">Cursor の技術的課題<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#cursor-%E3%81%AE%E6%8A%80%E8%A1%93%E7%9A%84%E8%AA%B2%E9%A1%8C" class="hash-link" aria-label="Cursor の技術的課題 への直接リンク" title="Cursor の技術的課題 への直接リンク" translate="no">​</a></h2>
<p>Cursor は非常に人気がありますが、学術研究はいくつかの問題も指摘しています。AI プログラミングアシスタントは開発速度を高める一方で、<strong>コードの複雑度と警告の数を増やす</strong> 可能性があるのです。</p>
<p>例えばある研究論文はこう指摘しています。</p>
<blockquote>
<p>AI プログラミングアシスタントを使うと、プロジェクトの開発速度は短期的に向上するが、コードの複雑度も上昇する。</p>
</blockquote>
<p>別の研究ではこんな発見もありました。</p>
<blockquote>
<p>AI コードアシスタントはコンテキスト内の誤ったコードの影響を受けやすく、その結果、欠陥のあるコードを生成してしまう。</p>
</blockquote>
<p>これは、AI プログラミングには依然として <strong>開発者によるレビューとコントロール</strong> が必要であることを示しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cursor-の今後の方向性">Cursor の今後の方向性<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#cursor-%E3%81%AE%E4%BB%8A%E5%BE%8C%E3%81%AE%E6%96%B9%E5%90%91%E6%80%A7" class="hash-link" aria-label="Cursor の今後の方向性 への直接リンク" title="Cursor の今後の方向性 への直接リンク" translate="no">​</a></h2>
<p>Cursor のビジョンは非常にラディカルです。将来のソフトウェア開発は「自然言語 → ソフトウェアシステム」になるかもしれません。開発フローはこう変わる可能性があります。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">要件</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">AI がコードを生成</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">AI がテストを生成</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">AI が自動デプロイ</span><br></div></code></pre></div></div>
<p>つまり、<strong>ソフトウェア開発は「コードを書く」ことから「要件を記述する」ことに変わるかもしれない</strong> のです。Cursor 公式はこんなビジョンまで打ち出しています。</p>
<blockquote>
<p>いつの日か、開発者は 50 行の擬似コードを書くだけで、2000 行の完全なコードを生成できるようになる。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/15/cursor-de-fa-zhan#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Cursor は VS Code を Fork するという方法で成熟したエディタの土台を素早く手に入れ、そこに LLM の能力をコード生成、リポジトリ理解、ファイル横断の修正へと組み込み、AI プログラミング爆発のタイミングという時間の窓をぴたりと捉えました。驚異的な成長の数字はこの道が正しかったことを物語っています。しかし研究が私たちに注意を促しているように、AI が生成するコードは複雑度の上昇といった新しい問題をもたらします。ツールがどれほど強力になっても、最終的にコード品質に責任を負うのは依然として開発者です。この点は、予見できる未来においておそらく変わらないでしょう。</p>]]></content:encoded>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[PolarDB のストレージ・コンピュート分離]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou</guid>
            <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[アーキテクチャの観点から PolarDB のストレージ・コンピュート分離設計を分析します。計算とストレージのデカップリングがどのように拡張性・ストレージコスト・高可用性の課題を解決するのかを、AWS Aurora や従来型 MySQL アーキテクチャと比較しながら解説します。]]></description>
            <content:encoded><![CDATA[<p>ビジネス規模の拡大に伴い、従来のリレーショナルデータベースアーキテクチャは、拡張能力の不足、高いストレージコスト、読み書き負荷の集中といったボトルネックを露呈するようになりました。クラウドベンダーはこれに対して新しいアーキテクチャパターンを設計しました。それが <strong>ストレージ・コンピュート分離（Storage-Compute Decoupling）</strong> です。本記事ではアーキテクチャの観点から PolarDB のストレージ・コンピュート分離設計を分析し、AWS Aurora および従来型 MySQL アーキテクチャと比較します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>PolarDB は Alibaba Cloud が提供するクラウドネイティブデータベースで、その中核となる設計思想の一つが <strong>計算層とストレージ層のデカップリング</strong> です。このアーキテクチャにより、データベースはより強い弾力的な拡張能力と、より高いリソース利用効率を備えます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-04-Gemini_Generated_Image_lr15jglr15jglr15-aaa1df73e131dae3dab907117c43cd3a.webp" width="1408" height="768" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="従来型-mysql-アーキテクチャの問題">従来型 MySQL アーキテクチャの問題<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#%E5%BE%93%E6%9D%A5%E5%9E%8B-mysql-%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="従来型 MySQL アーキテクチャの問題 への直接リンク" title="従来型 MySQL アーキテクチャの問題 への直接リンク" translate="no">​</a></h2>
<p>従来の MySQL アーキテクチャでは、データベースは通常 1 台のサーバー上で動作し、計算とストレージが同じマシンに同居します：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">MySQL Server</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> ├── CPU</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> ├── Memory</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> └── Local Disk      # データと計算が同じマシンに束縛される</span><br></div></code></pre></div></div>
<p>このアーキテクチャは初期のインターネット時代には十分でしたが、ビジネス規模が拡大するにつれ、いくつかの明白な問題が現れます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-ストレージの拡張が難しい">1) ストレージの拡張が難しい<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#1-%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%81%AE%E6%8B%A1%E5%BC%B5%E3%81%8C%E9%9B%A3%E3%81%97%E3%81%84" class="hash-link" aria-label="1) ストレージの拡張が難しい への直接リンク" title="1) ストレージの拡張が難しい への直接リンク" translate="no">​</a></h3>
<p>データベースのデータは通常ローカルディスクに保存されるため、データ量が増えた場合、ディスクのアップグレードやより大きなマシンへの交換しかできず、拡張能力に限界があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-読み取りスケールのコストが高い">2) 読み取りスケールのコストが高い<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#2-%E8%AA%AD%E3%81%BF%E5%8F%96%E3%82%8A%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%B3%E3%82%B9%E3%83%88%E3%81%8C%E9%AB%98%E3%81%84" class="hash-link" aria-label="2) 読み取りスケールのコストが高い への直接リンク" title="2) 読み取りスケールのコストが高い への直接リンク" translate="no">​</a></h3>
<p>MySQL でよく使われる拡張方式は <strong>マスター・スレーブレプリケーション</strong> です：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Master</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> ├── Slave1      # 各スレーブが完全なデータコピーを保持する</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> ├── Slave2</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> └── Slave3</span><br></div></code></pre></div></div>
<p>各リードオンリーノードは完全なデータコピーを必要とします。リードノードが増えるほどストレージコストは急速に増大し、データ同期の負荷も増していきます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-高可用性の実現が複雑">3) 高可用性の実現が複雑<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#3-%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7%E3%81%AE%E5%AE%9F%E7%8F%BE%E3%81%8C%E8%A4%87%E9%9B%91" class="hash-link" aria-label="3) 高可用性の実現が複雑 への直接リンク" title="3) 高可用性の実現が複雑 への直接リンク" translate="no">​</a></h3>
<p>マスターノードに障害が発生した場合、手動または自動でマスター・スレーブの切り替えを行う必要があり、その間にレプリケーション遅延が発生する可能性があるほか、データ整合性の担保にも追加の対応が必要です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="polardb-のストレージコンピュート分離アーキテクチャ">PolarDB のストレージ・コンピュート分離アーキテクチャ<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#polardb-%E3%81%AE%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%83%88%E5%88%86%E9%9B%A2%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" class="hash-link" aria-label="PolarDB のストレージ・コンピュート分離アーキテクチャ への直接リンク" title="PolarDB のストレージ・コンピュート分離アーキテクチャ への直接リンク" translate="no">​</a></h2>
<p>PolarDB の中核設計は、<strong>計算ノードとストレージノードの完全なデカップリング</strong> です。アーキテクチャの概略は次のとおりです：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          +--------------------+</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          |    Compute Node    |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          | (MySQL / PG / ORA) |   # 計算層：SQL 解析、実行、トランザクション</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          +---------+----------+</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          +---------+----------+</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          | Distributed Storage|   # ストレージ層：データ、ログ、レプリカ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          |     (PolarStore)   |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          +--------------------+</span><br></div></code></pre></div></div>
<p>計算ノードは SQL 解析、クエリ実行、トランザクション処理を担当し、ストレージ層はデータ保存、ログ管理、レプリカ複製を担当します。すべての計算ノードが同一の分散ストレージを共有するこのアーキテクチャは、<strong>Shared Storage Architecture</strong> と呼ばれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ストレージコンピュート分離は何を解決したのか">ストレージ・コンピュート分離は何を解決したのか<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%83%88%E5%88%86%E9%9B%A2%E3%81%AF%E4%BD%95%E3%82%92%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%9F%E3%81%AE%E3%81%8B" class="hash-link" aria-label="ストレージ・コンピュート分離は何を解決したのか への直接リンク" title="ストレージ・コンピュート分離は何を解決したのか への直接リンク" translate="no">​</a></h2>
<p>この設計は主に、従来のデータベースが抱えるいくつかの核心的な問題を解決します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-弾力的な拡張能力">1) 弾力的な拡張能力<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#1-%E5%BC%BE%E5%8A%9B%E7%9A%84%E3%81%AA%E6%8B%A1%E5%BC%B5%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="1) 弾力的な拡張能力 への直接リンク" title="1) 弾力的な拡張能力 への直接リンク" translate="no">​</a></h3>
<p>従来のデータベースでは計算能力を増やすにはサーバーのアップグレードが必要でしたが、PolarDB では計算ノードを直接追加できます：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Storage Layer</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   ↑</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Compute1      # 計算ノードの追加にデータコピーは不要</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Compute2</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Compute3</span><br></div></code></pre></div></div>
<p>すべてのノードが同一のデータを共有するため、読み取りスケール、分散計算、秒単位のスケールアウトが実現できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-ストレージコストの削減">2) ストレージコストの削減<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#2-%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%82%B3%E3%82%B9%E3%83%88%E3%81%AE%E5%89%8A%E6%B8%9B" class="hash-link" aria-label="2) ストレージコストの削減 への直接リンク" title="2) ストレージコストの削減 への直接リンク" translate="no">​</a></h3>
<p>従来の MySQL のリードノードには完全なデータコピーが必要ですが、ストレージ・コンピュート分離アーキテクチャでは複数の計算ノードが同じストレージシステムを共有するため、完全なデータの複製が不要になり、ストレージコストが大幅に下がります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-より強い高可用性">3) より強い高可用性<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#3-%E3%82%88%E3%82%8A%E5%BC%B7%E3%81%84%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7" class="hash-link" aria-label="3) より強い高可用性 への直接リンク" title="3) より強い高可用性 への直接リンク" translate="no">​</a></h3>
<p>PolarDB のストレージ層は通常、分散レプリカ機構を使用します：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Storage Node</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> ├── Replica1      # 複数レプリカでデータの永続性を保証</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> ├── Replica2</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> └── Replica3</span><br></div></code></pre></div></div>
<p>あるノードに障害が発生しても、システムは非常に高速に復旧できます。この設計により、自動障害復旧、高可用アーキテクチャ、データ永続性の保証が実現できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-極めて高い性能">4) 極めて高い性能<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#4-%E6%A5%B5%E3%82%81%E3%81%A6%E9%AB%98%E3%81%84%E6%80%A7%E8%83%BD" class="hash-link" aria-label="4) 極めて高い性能 への直接リンク" title="4) 極めて高い性能 への直接リンク" translate="no">​</a></h3>
<p>PolarDB のストレージ層は通常、分散ログ構造、並列 I/O、SSD クラウドストレージを採用しており、データベース性能を大幅に向上させられます。主なボトルネックはストレージ層から内部ネットワークの I/O 層へと移りました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="polardb-の弱点">PolarDB の弱点<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#polardb-%E3%81%AE%E5%BC%B1%E7%82%B9" class="hash-link" aria-label="PolarDB の弱点 への直接リンク" title="PolarDB の弱点 への直接リンク" translate="no">​</a></h2>
<p>ストレージ・コンピュート分離アーキテクチャには多くの利点がある一方で、いくつかの課題も存在します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-ネットワーク遅延">1) ネットワーク遅延<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#1-%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E9%81%85%E5%BB%B6" class="hash-link" aria-label="1) ネットワーク遅延 への直接リンク" title="1) ネットワーク遅延 への直接リンク" translate="no">​</a></h3>
<p>計算層とストレージ層が分離されているということは、データアクセスがネットワーク経由になるということであり、ローカルディスクと比べて一定の遅延が加わります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-アーキテクチャの複雑さ">2) アーキテクチャの複雑さ<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#2-%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E8%A4%87%E9%9B%91%E3%81%95" class="hash-link" aria-label="2) アーキテクチャの複雑さ への直接リンク" title="2) アーキテクチャの複雑さ への直接リンク" translate="no">​</a></h3>
<p>ストレージ・コンピュート分離アーキテクチャでは、分散ストレージシステム、ネットワークプロトコル、データ整合性機構の設計が必要となり、実装の難易度は従来のデータベースよりはるかに高くなります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-クラウドインフラへの強い依存">3) クラウドインフラへの強い依存<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#3-%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%A4%E3%83%B3%E3%83%95%E3%83%A9%E3%81%B8%E3%81%AE%E5%BC%B7%E3%81%84%E4%BE%9D%E5%AD%98" class="hash-link" aria-label="3) クラウドインフラへの強い依存 への直接リンク" title="3) クラウドインフラへの強い依存 への直接リンク" translate="no">​</a></h3>
<p>このアーキテクチャは高性能ネットワーク、分散ストレージ、クラウドプラットフォームのスケジューリング能力に大きく依存するため、通常はクラウド環境でのみ適用可能です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="polardb-vs-aws-aurora">PolarDB vs AWS Aurora<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#polardb-vs-aws-aurora" class="hash-link" aria-label="PolarDB vs AWS Aurora への直接リンク" title="PolarDB vs AWS Aurora への直接リンク" translate="no">​</a></h2>
<p>PolarDB と Aurora の全体的な設計思想は非常に近く、どちらも典型的な <strong>クラウドネイティブデータベースアーキテクチャ</strong> であり、<strong>ストレージ・コンピュート分離（Compute + Storage Decoupling）</strong> の設計を採用しています。</p>
<p>Aurora は計算ノードと分散ストレージクラスタを分離し、複数の計算ノードが同一のストレージシステムを共有します。アーキテクチャの概略：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          Writer Node</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">               |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       +-------+-------+</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       |               |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   Reader1         Reader2         # リードノードは下層ストレージを共有</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       |               |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   +-----------------------+</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   |   Distributed Storage |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   +-----------------------+</span><br></div></code></pre></div></div>
<p>Aurora の分散ストレージは通常、可用性向上のため複数のアベイラビリティゾーンをまたいでデプロイされます。しかし具体的な実装においては、両者には依然としていくつかの重要な違いがあります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-ストレージアーキテクチャ設計の違い">1) ストレージアーキテクチャ設計の違い<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#1-%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E8%A8%AD%E8%A8%88%E3%81%AE%E9%81%95%E3%81%84" class="hash-link" aria-label="1) ストレージアーキテクチャ設計の違い への直接リンク" title="1) ストレージアーキテクチャ設計の違い への直接リンク" translate="no">​</a></h3>
<p>Aurora の中核設計は <strong>ログ駆動ストレージ（Log Structured Storage）</strong> です。Aurora では計算ノードがデータベースページを直接書き込むのではなく、<strong>redo log</strong> をストレージノードに送信し、ストレージ層がそれらのログに基づいてデータページを再構築します。簡略化したフローは次のとおりです：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Aurora Compute Node</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Redo Log                    # ログのみを転送し、完全なデータページは転送しない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Distributed Storage Nodes</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Storage Node がデータページを再構築</span><br></div></code></pre></div></div>
<p>Aurora のストレージ層は通常 <strong>6 つのレプリカで構成</strong> され、<strong>3 つのアベイラビリティゾーン（AZ）</strong> に分散されます：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">AZ1 : Storage Node A / Storage Node B</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">AZ2 : Storage Node C / Storage Node D</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">AZ3 : Storage Node E / Storage Node F</span><br></div></code></pre></div></div>
<p>書き込み時には <strong>4/6 quorum</strong> を満たせばトランザクションをコミットできます。この設計にはいくつかの利点があります：</p>
<ul>
<li class="">書き込みはログの送信だけで済み、ネットワーク転送量が小さい</li>
<li class="">データベースページの完全な複製が不要</li>
<li class="">障害復旧の速度が速い</li>
</ul>
<p>一方、PolarDB のストレージ実装は少し異なります。PolarDB は <strong>共有分散ストレージ（Shared Storage）</strong> アーキテクチャを採用し、計算ノードは下層の <strong>PolarStore / PolarFS 分散ファイルシステム</strong> に直接アクセスでき、データベースページは引き続きページの形で保存されます。簡略化した構造は次のとおりです：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Compute Node (MySQL / PG / Oracle)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">PolarFS / PolarStore        # 分散ファイルシステム、ページ形式のストレージ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">↓</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Distributed Storage Nodes</span><br></div></code></pre></div></div>
<p>この設計は従来のデータベースのストレージ方式に近いため、データページ構造の互換性が保たれ、データベースカーネルへの改修が比較的少なくて済みます。ただし書き込みパスにおいては、ネットワーク転送のデータ量が Aurora のログ方式より大きくなる可能性があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-リードノードのアーキテクチャ">2) リードノードのアーキテクチャ<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#2-%E3%83%AA%E3%83%BC%E3%83%89%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" class="hash-link" aria-label="2) リードノードのアーキテクチャ への直接リンク" title="2) リードノードのアーキテクチャ への直接リンク" translate="no">​</a></h3>
<p>Aurora のリードノードは主に <strong>Reader Instance</strong> で、通常は読み取りのみを担当し、書き込みは <strong>Writer Node</strong> に集中します。一方 PolarDB は <strong>Reader Node + マスターノードによる共有ストレージ</strong> のモードをサポートし、複数のノードが共有ストレージに直接アクセスできるため、より柔軟な読み取りスケールが実現できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-エコシステムと互換性の戦略">3) エコシステムと互換性の戦略<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#3-%E3%82%A8%E3%82%B3%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%A8%E4%BA%92%E6%8F%9B%E6%80%A7%E3%81%AE%E6%88%A6%E7%95%A5" class="hash-link" aria-label="3) エコシステムと互換性の戦略 への直接リンク" title="3) エコシステムと互換性の戦略 への直接リンク" translate="no">​</a></h3>
<p>Aurora は AWS が深く改造したデータベースエンジンで、MySQL / PostgreSQL と互換性はあるものの、下層の実装は大きく変わっています。PolarDB はより <strong>ネイティブなデータベース互換性</strong> を重視しています：</p>
<ul>
<li class="">PolarDB MySQL</li>
<li class="">PolarDB PostgreSQL</li>
<li class="">PolarDB Oracle</li>
</ul>
<p>そのため多くのアプリケーションは、PolarDB への移行時の修正コストがより低く抑えられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/15/polardb-du-xie-fen-chi-jia-gou#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ストレージ・コンピュート分離の本質は、データベースの計算能力とストレージ能力を、独立して拡張できる 2 つの層に切り分けることです。PolarDB は共有分散ストレージによって、秒単位のスケールアウト、低コストの読み取りスケール、より強い高可用性を手に入れました。その代償がネットワーク遅延とアーキテクチャの複雑さの増大です。Aurora と比べると両者の思想は近いものの、PolarDB は共有ファイルシステム路線を採り、従来のデータベースのストレージ形態に近く互換性に優れています。一方 Aurora はログ駆動ストレージによって、書き込み時の転送量の小ささを手に入れました。この 2 つの路線のトレードオフを理解しておくことは、クラウドデータベースの選定評価に大いに役立ちます。</p>]]></content:encoded>
            <category>DB</category>
            <category>技術</category>
            <category>Linux</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[チームマネジメントの経験]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan</guid>
            <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[技術チームを数年率いて得た素朴な経験：まず物事をしっかり考え抜く、分担を明確にする、手放してチームを成長させる、プロセスで問題を減らす。そして、チームの雰囲気と長期的な視点が同じくらい重要である理由。]]></description>
            <content:encoded><![CDATA[<p>技術チームのマネジメントは非常に複雑な学問だと思っている人が多いのですが、自分でチームを率いるようになってから、実は多くのことは想像ほど複雑ではないと徐々に気づきました。技術チームのマネジメントとは、突き詰めれば「人を管理する」ことではなく、<strong>チームが安定して物事をやり遂げられるようにする</strong> ことなのです。</p>
<!-- -->
<p>チームを率い始めた頃は、私も回り道をしました。最初のうちは、自分の技術力さえ高ければ、多くのことは自分で手を動かせば解決できると思っていました。しかし後になって、チームの規模が大きくなると、一人では支えきれないことに気づきました。テックリードが本当にやるべきことは、チーム全体の能力を引き上げることであって、自分がチームで一番忙しい人になることではないのです。この数年間、技術マネジメントをやってきて、シンプルだけれどとても重要な経験をいくつかまとめられるようになりました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まず物事を考え抜いてからチームに任せる">まず物事を考え抜いてから、チームに任せる<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%81%BE%E3%81%9A%E7%89%A9%E4%BA%8B%E3%82%92%E8%80%83%E3%81%88%E6%8A%9C%E3%81%84%E3%81%A6%E3%81%8B%E3%82%89%E3%83%81%E3%83%BC%E3%83%A0%E3%81%AB%E4%BB%BB%E3%81%9B%E3%82%8B" class="hash-link" aria-label="まず物事を考え抜いてから、チームに任せる への直接リンク" title="まず物事を考え抜いてから、チームに任せる への直接リンク" translate="no">​</a></h2>
<p>技術チームの効率が低いのは、実はエンジニアの能力が足りないからではなく、物事自体が考え抜かれていないからであることが多いのです。以前私もこんな状況に遭遇しました。要件が来るとみんながすぐに開発を始め、開発の途中になって設計が不合理だと気づき、やり直しになるのです。</p>
<p>その後、私は徐々にある習慣を身につけました。プロジェクトを始める前に、必ず <strong>設計の考え方と全体の方針を考え抜く</strong> ことです。例えば、システムがどんな機能を作るのか、システムをおおよそどう分割するのか、コアモジュールをどう設計するのか、どこがボトルネックになり得るのか。これらのことが考え抜かれていなければ、チームが努力すればするほど、方向がずれていく可能性があります。</p>
<p>そこで今の私は、通常まずこれをやります。<strong>問題をはっきりさせてから、チームに仕事を割り当てる</strong>。こうすることで効率が上がるだけでなく、不要なやり直しも大きく減らせます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="チームの分担は必ず明確に">チームの分担は必ず明確に<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%83%81%E3%83%BC%E3%83%A0%E3%81%AE%E5%88%86%E6%8B%85%E3%81%AF%E5%BF%85%E3%81%9A%E6%98%8E%E7%A2%BA%E3%81%AB" class="hash-link" aria-label="チームの分担は必ず明確に への直接リンク" title="チームの分担は必ず明確に への直接リンク" translate="no">​</a></h2>
<p>技術チームでよくある問題の一つが、責任の所在が曖昧なことです。多くのシステムは一見みんなで作っているように見えますが、本当に問題が起きたとき、誰に聞けばいいのか誰も分からないのです。</p>
<p>やがて私は、チームがスムーズに回るためには、明確な責任の分担が不可欠だと気づきました。例えば、システムには必ず責任者が必要です。その人がすべてのコードを書く必要はありませんが、システムのアーキテクチャ、コアロジック、そしてシステムの稼働状況を把握していなければなりません。</p>
<p>私は通常、コアとなるシステムごとに Owner を一人置きます。例えばあるサービス、あるモジュール、あるいはある基盤コンポーネントです。こうしておけば、システムに問題が起きたとき、チームはすぐに責任者を見つけられますし、お互いに責任を押し付け合うことも避けられます。責任が明確になると、チームの効率は目に見えて向上します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="テックリードは何でも自分でやるべきではない">テックリードは何でも自分でやるべきではない<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%83%86%E3%83%83%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%89%E3%81%AF%E4%BD%95%E3%81%A7%E3%82%82%E8%87%AA%E5%88%86%E3%81%A7%E3%82%84%E3%82%8B%E3%81%B9%E3%81%8D%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84" class="hash-link" aria-label="テックリードは何でも自分でやるべきではない への直接リンク" title="テックリードは何でも自分でやるべきではない への直接リンク" translate="no">​</a></h2>
<p>チームを率い始めた頃、私はつい自分で多くのコードを書いてしまいがちでした。自分で書いたほうが確かに速いし、安心できるからです。しかし時間が経つと、このやり方は実はチームの成長にとって良くないと分かってきます。テックリードが重要なことをすべて自分でやってしまうと、チームメンバーは成長しにくくなり、長期的にはチームがますます特定の一人に依存するようになってしまいます。</p>
<p>その後、私は自分のやり方を少しずつ調整しました。今の私はどちらかというと、<strong>チームメンバーを設計や意思決定に参加させる</strong> ことを重視しています。単に実行だけを任せるのではありません。例えばシステム設計の議論では、私一人で決めるのではなく、みんなに参加してもらいます。このやり方は最初こそ少し時間がかかるかもしれませんが、長期的に見ればチームの能力はどんどん強くなっていきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プロセスは縛るためではなく効率を上げるためのもの">プロセスは縛るためではなく、効率を上げるためのもの<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%81%AF%E7%B8%9B%E3%82%8B%E3%81%9F%E3%82%81%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E5%8A%B9%E7%8E%87%E3%82%92%E4%B8%8A%E3%81%92%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE%E3%82%82%E3%81%AE" class="hash-link" aria-label="プロセスは縛るためではなく、効率を上げるためのもの への直接リンク" title="プロセスは縛るためではなく、効率を上げるためのもの への直接リンク" translate="no">​</a></h2>
<p>プロセスと聞くと、作業量が増えるだけだと感じるエンジニアは多いものです。しかし実際には、成熟したチームには必ず基本的なプロセスが必要です。例えば私たちのチームには、通常いくつかの固定の工程があります：</p>
<ol>
<li class="">要件レビュー</li>
<li class="">技術設計</li>
<li class="">コードレビュー</li>
<li class="">テスト検証</li>
<li class="">リリース</li>
</ol>
<p>これらのプロセスの目的は複雑さを増すことではなく、問題を減らすことです。例えばコードレビューは潜在的なバグを事前に発見でき、技術設計はアーキテクチャの問題を回避でき、テストプロセスは本番障害を減らせます。プロセスが安定してくると、チームの協働は格段にスムーズになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="テックリードは長期的な問題に目を向ける必要がある">テックリードは長期的な問題に目を向ける必要がある<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%83%86%E3%83%83%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%89%E3%81%AF%E9%95%B7%E6%9C%9F%E7%9A%84%E3%81%AA%E5%95%8F%E9%A1%8C%E3%81%AB%E7%9B%AE%E3%82%92%E5%90%91%E3%81%91%E3%82%8B%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8B" class="hash-link" aria-label="テックリードは長期的な問題に目を向ける必要がある への直接リンク" title="テックリードは長期的な問題に目を向ける必要がある への直接リンク" translate="no">​</a></h2>
<p>チームは毎日要件の処理に追われがちで、目の前のことにばかり意識が向きやすいものです。しかし技術的負債に長期間目を向けないでいると、システムはどんどんメンテナンスしづらくなっていきます。そこで私は通常、一定の時間を確保してシステムアーキテクチャについて考えるようにしています。例えば：</p>
<ul>
<li class="">システムに性能上のボトルネックがないか</li>
<li class="">アーキテクチャの調整が必要ではないか</li>
<li class="">新しい技術コンポーネントの導入が必要ではないか</li>
</ul>
<p>これらのことは短期的には効果が見えないかもしれませんが、長期的にはシステムの安定性に大きな影響を与えます。テックリードは今日の問題を解決するだけでなく、<strong>システムの将来の発展の方向性</strong> も考えなければなりません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="チームの雰囲気は実はとても重要">チームの雰囲気は実はとても重要<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%83%81%E3%83%BC%E3%83%A0%E3%81%AE%E9%9B%B0%E5%9B%B2%E6%B0%97%E3%81%AF%E5%AE%9F%E3%81%AF%E3%81%A8%E3%81%A6%E3%82%82%E9%87%8D%E8%A6%81" class="hash-link" aria-label="チームの雰囲気は実はとても重要 への直接リンク" title="チームの雰囲気は実はとても重要 への直接リンク" translate="no">​</a></h2>
<p>技術チームの効率は技術力だけでなく、チームの雰囲気にも左右されます。チームメンバーの間に信頼が欠けていると、多くのことが難しくなります。</p>
<p>私自身が特に重視しているのは、チームメンバーが技術的な問題を自由に議論できるようにすることです。人が違えば考え方も違うことが多く、そうした議論からより良い解決策が生まれることがよくあります。また、私は過度に厳格な管理のやり方もあまり好きではありません。技術チームには比較的オープンな環境のほうが向いていて、みんなが物事をやり遂げることに集中できるようにするのがよいのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術マネジメントもまた一つの成長">技術マネジメントもまた一つの成長<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E6%8A%80%E8%A1%93%E3%83%9E%E3%83%8D%E3%82%B8%E3%83%A1%E3%83%B3%E3%83%88%E3%82%82%E3%81%BE%E3%81%9F%E4%B8%80%E3%81%A4%E3%81%AE%E6%88%90%E9%95%B7" class="hash-link" aria-label="技術マネジメントもまた一つの成長 への直接リンク" title="技術マネジメントもまた一つの成長 への直接リンク" translate="no">​</a></h2>
<p>エンジニアからテックリードへの転身は、実はとても大きな変化です。以前はコードにだけ集中していればよかったのが、今はチーム、プロジェクト、そしてシステム全体に目を配る必要があります。最初は慣れないかもしれませんが、やがて技術マネジメントもまた新しい挑戦なのだと分かってきます。それには技術力だけでなく、思考力とコミュニケーション力も必要です。</p>
<p>私にとって、チームを率いる最大の収穫は、チームメンバーが絶えず成長し、同時にシステムも絶えず進歩していく姿を見られることです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/15/tuan-dui-guan-li-jing-yan#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>この数年間、技術チームのマネジメントをやってきて、私の最大の実感は実にシンプルです。技術チームのマネジメントとは複雑な経営学ではなく、エンジニアの集団が <strong>一緒に物事をやり遂げられる</strong> ようにすることです。目標が明確であってこそ、チームは無駄な力を使わずに済みます。分担が明確であってこそ、問題が起きたときに引き受ける人がいます。プロセスが安定していてこそ、協働は運任せになりません。そこにチーム内の信頼が加われば、多くのことは自然とスムーズに進んでいくものです。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[マイクロサービスアーキテクチャ設計]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji</guid>
            <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[モノリシックアーキテクチャのボトルネックから出発し、マイクロサービスのコア設計原則、全体アーキテクチャの構成、重要な設計課題を整理し、その拡張能力、導入の難しさ、適用範囲についても考察します。]]></description>
            <content:encoded><![CDATA[<p>モノリシックなアプリケーションがある程度まで肥大化すると、分割はほぼ避けて通れない話題になります。この記事では、マイクロサービスアーキテクチャの由来、コアとなる設計原則、全体アーキテクチャの構成を整理し、それがもたらす新たなコストについても考えます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜマイクロサービスアーキテクチャが生まれたのか">なぜマイクロサービスアーキテクチャが生まれたのか<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%81%AA%E3%81%9C%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%8C%E7%94%9F%E3%81%BE%E3%82%8C%E3%81%9F%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜマイクロサービスアーキテクチャが生まれたのか への直接リンク" title="なぜマイクロサービスアーキテクチャが生まれたのか への直接リンク" translate="no">​</a></h2>
<p>初期のソフトウェアシステムでは、ほとんどのアプリケーションが <strong>モノリシックアーキテクチャ（Monolithic Architecture）</strong> を採用していました。すべての機能モジュールが同一のアプリケーションプロセス内で動作し、たとえばユーザーシステム、注文システム、決済システム、在庫システムなどが 1 つのプロジェクトにパッケージされ、一括でデプロイされます。このアーキテクチャはシステム規模が小さいうちは非常にシンプルで分かりやすく、開発効率も高いものでした。</p>
<p>しかしビジネスが成長し続けるにつれ、モノリシックアーキテクチャの問題が次第に表面化してきます。システムのコードはどんどん膨れ上がり、1 つのプロジェクトが数十万行、あるいは百万行を超えるコードを含むこともあり、どんな小さな修正でもシステム全体の再ビルドと再デプロイが必要になります。同時に、機能モジュール間の結合が強く、1 つのモジュールの問題がシステム全体の稼働に影響を及ぼしかねません。アクセス量が増えても、特定のホットスポットモジュールだけをスケールさせることはできず、全体をスケールアウトするしかなく、リソースの利用効率は非常に低くなります。</p>
<p>こうした背景の中で、マイクロサービスアーキテクチャは次第に大規模システムの主流な設計方式となっていきました。マイクロサービスの核心となる考え方は、<strong>巨大なシステムを複数の独立したサービスに分割し、各サービスが明確なビジネス能力を担い、独立して開発・デプロイ・スケールできるようにする</strong> ことです。これによりシステムの複雑さを下げるだけでなく、保守性と拡張能力も向上させることができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスアーキテクチャのコア設計思想">マイクロサービスアーキテクチャのコア設計思想<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E3%82%B3%E3%82%A2%E8%A8%AD%E8%A8%88%E6%80%9D%E6%83%B3" class="hash-link" aria-label="マイクロサービスアーキテクチャのコア設計思想 への直接リンク" title="マイクロサービスアーキテクチャのコア設計思想 への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-04-Gemini_Generated_Image_qjw17xqjw17xqjw1-40a2fd0d4db35c47974e2ed1357238bd.webp" width="1408" height="768" class="img_ev3q"></p>
<p>マイクロサービスアーキテクチャは、単にシステムを多くの小さなサービスに分割することではありません。より重要なのは、<strong>ビジネス能力（Business Capability）を軸にシステムを設計する</strong> ことです。各サービスは明確な責務の境界を持ち、独立して動作できる必要があります。</p>
<p>マイクロサービス設計では、通常いくつかのコア原則に従います。</p>
<ul>
<li class=""><strong>単一責任</strong>: 各マイクロサービスは 1 つのコアビジネス領域だけを担当します。たとえばユーザーサービス、注文サービス、在庫サービスなどです。これによりサービスの責務の混乱を避けられ、チームの分業と協働もしやすくなります。</li>
<li class=""><strong>サービスの自律性</strong>: 各マイクロサービスは自分専用のデータベースとデータモデルを持ち、複数のサービスでデータベースを共有することを避けます。これによりサービス間の強い依存を減らし、システムをより疎結合にできます。</li>
<li class=""><strong>独立デプロイ</strong>: 各サービスは他のサービスに影響を与えることなく、独立してリリース・アップグレードできます。この点は大規模なチームにとって特に重要で、開発効率を大幅に高めることができます。</li>
<li class=""><strong>インターフェース駆動の通信</strong>: サービス間は API やメッセージングシステムを通じて通信し、データベースへの直接アクセスやコードベースの共有は行いません。</li>
</ul>
<p>これらの原則が組み合わさって、マイクロサービスアーキテクチャの基礎を構成しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスシステムの全体アーキテクチャ設計">マイクロサービスシステムの全体アーキテクチャ設計<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E5%85%A8%E4%BD%93%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="マイクロサービスシステムの全体アーキテクチャ設計 への直接リンク" title="マイクロサービスシステムの全体アーキテクチャ設計 への直接リンク" translate="no">​</a></h2>
<p>実際のシステムでは、完全なマイクロサービスアーキテクチャは通常、単なるサービス分割ではなく、複数の基盤コンポーネントで構成されます。</p>
<p>システムの最も外側には通常 <strong>API Gateway（ゲートウェイ層）</strong> があります。ゲートウェイはシステムの統一された入口として、クライアントのリクエストを処理し、認証、レート制限、ログ記録、ルーティングなどの機能を提供します。ゲートウェイを通すことでバックエンドサービスの複雑さを隠蔽し、クライアントは単一の統一されたインターフェースだけを相手にすればよくなります。</p>
<p>ゲートウェイの後ろには <strong>ビジネスサービス層</strong> があります。この層は複数のマイクロサービスで構成されます。たとえばユーザーサービス、注文サービス、商品サービス、決済サービスなどです。各サービスは独立したビジネス能力を担い、API や RPC を通じて相互にやり取りします。</p>
<p>サービス同士がお互いを発見し通信できるようにするため、システムには通常 <strong>サービスレジストリとディスカバリの仕組み</strong> が導入されます。よく使われるのは etcd、Nacos、Consul などのコンポーネントです。サービスは起動時にレジストリに登録され、他のサービスはレジストリを通じて対象サービスのアドレスを取得できます。</p>
<p>高い並行性が求められるシステムでは、非同期通信を実現するために <strong>メッセージキュー（MQ）</strong> も導入されるのが一般的です。たとえば Kafka、RabbitMQ、RocketMQ などです。メッセージキューは、ピークカット、サービスの疎結合化、イベント駆動アーキテクチャの構築などに利用できます。</p>
<p>データ層については、各マイクロサービスが通常自分専用のデータベースを持ちます。たとえば MySQL、PostgreSQL、あるいは NoSQL データベースです。パフォーマンス向上のために Redis をキャッシュ層として使い、データベースへの負荷を減らすこともあります。</p>
<p>最終的にシステム全体は通常 <strong>コンテナプラットフォーム（たとえば Kubernetes）</strong> 上で動作し、コンテナオーケストレーションによってサービスの自動デプロイ、スケールアウト、障害復旧を実現します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスアーキテクチャにおける重要な設計課題">マイクロサービスアーキテクチャにおける重要な設計課題<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E9%87%8D%E8%A6%81%E3%81%AA%E8%A8%AD%E8%A8%88%E8%AA%B2%E9%A1%8C" class="hash-link" aria-label="マイクロサービスアーキテクチャにおける重要な設計課題 への直接リンク" title="マイクロサービスアーキテクチャにおける重要な設計課題 への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャは多くの利点をもたらす一方で、新たな複雑さも持ち込みます。システム設計時には、いくつかの重要な課題に特に注意を払う必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-サービス分割の粒度">1) サービス分割の粒度<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#1-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E5%88%86%E5%89%B2%E3%81%AE%E7%B2%92%E5%BA%A6" class="hash-link" aria-label="1) サービス分割の粒度 への直接リンク" title="1) サービス分割の粒度 への直接リンク" translate="no">​</a></h3>
<p>サービスの分割が粗すぎると、システムは結局大きなモノリスのままになってしまいます。逆に細かすぎると、サービスの数が多くなりすぎてシステムの複雑さが増します。一般的には、技術モジュール単位ではなく、ビジネスドメインを境界として分割することが推奨されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-サービス間の通信方式">2) サービス間の通信方式<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#2-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E9%96%93%E3%81%AE%E9%80%9A%E4%BF%A1%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="2) サービス間の通信方式 への直接リンク" title="2) サービス間の通信方式 への直接リンク" translate="no">​</a></h3>
<p>同期呼び出しには通常 HTTP や gRPC を使い、非同期通信はメッセージキューで実現します。高並行なシステムでは、大量の同期呼び出しがサービス間に複雑な呼び出しチェーンを形成する可能性があるため、通信パターンを適切に設計する必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-分散トランザクション">3) 分散トランザクション<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#3-%E5%88%86%E6%95%A3%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3" class="hash-link" aria-label="3) 分散トランザクション への直接リンク" title="3) 分散トランザクション への直接リンク" translate="no">​</a></h3>
<p>モノリシックなシステムでは、データベースのトランザクションでデータの一貫性を保証できます。しかしマイクロサービスアーキテクチャでは各サービスが独立したデータベースを持つため、従来のトランザクション機構をそのまま使うことはできません。そのため、通常は <strong>結果整合性（Eventual Consistency）</strong>、Saga パターン、あるいは分散トランザクションフレームワークを用いてデータ一貫性の問題を解決します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-システムの安定性">4) システムの安定性<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#4-%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E5%AE%89%E5%AE%9A%E6%80%A7" class="hash-link" aria-label="4) システムの安定性 への直接リンク" title="4) システムの安定性 への直接リンク" translate="no">​</a></h3>
<p>マイクロサービスアーキテクチャでは、サービス数の増加は障害点の増加を意味します。そのためレート制限、サーキットブレーカー、デグレードなどの仕組みを導入する必要があります。たとえばサービスガバナンスフレームワークや Service Mesh によってトラフィック制御を実現します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスアーキテクチャの拡張能力">マイクロサービスアーキテクチャの拡張能力<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E6%8B%A1%E5%BC%B5%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="マイクロサービスアーキテクチャの拡張能力 への直接リンク" title="マイクロサービスアーキテクチャの拡張能力 への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャの最大の利点の一つが <strong>弾力的な拡張能力</strong> です。</p>
<p>従来のモノリシックアーキテクチャでは、ある機能モジュールがシステムのボトルネックになった場合、たとえば注文サービスへの負荷が高くなりすぎた場合、システム全体をスケールアウトするしかありません。一方マイクロサービスアーキテクチャでは、注文サービスだけを拡張できます。たとえば複数のインスタンスを追加してロードバランシングを行う、といった具合です。</p>
<p>クラウドネイティブ環境では、Kubernetes がシステム負荷に応じてサービスインスタンス数を自動的に調整できます。トラフィックのピーク時にはサービスノードを自動でスケールアウトし、トラフィックが減れば自動でスケールインすることで、リソース利用効率を高めます。このオンデマンドな拡張能力により、マイクロサービスアーキテクチャはインターネットの高並行シーンに非常に適しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスアーキテクチャの課題">マイクロサービスアーキテクチャの課題<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E8%AA%B2%E9%A1%8C" class="hash-link" aria-label="マイクロサービスアーキテクチャの課題 への直接リンク" title="マイクロサービスアーキテクチャの課題 への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャは多くの問題を解決してくれますが、万能ではありません。モノリシックアーキテクチャと比べると、マイクロサービスシステムはより複雑になります。</p>
<ul>
<li class=""><strong>運用の複雑さの増加</strong>: システム内に数十、場合によっては百を超えるサービスが存在し、それぞれが独自のデプロイフローと監視体系を持つため、しっかりした DevOps プラットフォームのサポートが必要になります。</li>
<li class=""><strong>サービスガバナンスの問題</strong>: サービス数が増えるにつれ、サービス間の呼び出し関係は非常に複雑になり、サービスガバナンスシステムによる管理が必要になります。</li>
<li class=""><strong>分散システム固有の問題</strong>: たとえばネットワーク遅延、データ一貫性、サービス依存などです。これらの問題はモノリシックなシステムではほとんど存在しませんが、マイクロサービスシステムでは重点的に解決しなければなりません。</li>
</ul>
<p>したがって、システム規模が小さいうちはモノリシックアーキテクチャのほうがシンプルで効率的なことが多く、マイクロサービスアーキテクチャは <strong>中〜大規模システム</strong> に向いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-jia-gou-she-ji#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャの本質は、<strong>サービスの分割と自律性</strong> によってシステムの複雑さを下げ、拡張性と保守性を高めることにあります。これによりシステムはビジネスの成長に合わせて柔軟に拡張でき、チームの並行開発も可能になり、大規模なインターネットシステムの発展をより良く支えられるようになります。</p>
<p>しかしマイクロサービスは単なる技術のアップグレードではなく、サービスレジストリ、API ゲートウェイ、メッセージングシステム、監視システム、コンテナプラットフォームといった周辺インフラを必要とします。完全な技術体系のサポートがあって初めて、マイクロサービスアーキテクチャは本当の価値を発揮できるのです。</p>
<p>実際のシステム設計では、アーキテクトはビジネス規模、チームの能力、システムの複雑さを総合的に判断し、最も適したアーキテクチャパターンを選ぶべきであり、やみくもにマイクロサービス化を追い求めるべきではありません。本当に優れたアーキテクチャ設計とは、技術的に最も複雑な方式ではなく、<strong>複雑さと得られる価値のバランスを取れる方式</strong> なのです。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[L1・L2 二段キャッシュの設計]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun</guid>
            <pubDate>Sun, 15 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[L1（ローカル）キャッシュと L2（Redis）キャッシュによる階層アーキテクチャを紹介。アクセスの流れ、二段のキャッシュがそれぞれ解決する問題、整合性と容量制御、そして代表的なキャッシュ更新戦略について。]]></description>
            <content:encoded><![CDATA[<p>データベースが耐えられなくなったとき、誰もが最初に思いつくのは Redis の導入です。その Redis も苦しくなってきたら、いよいよ多段キャッシュの出番です。この記事では、最もよく使われる L1 + L2 キャッシュの設計について語ります。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ多段キャッシュが必要なのか">なぜ多段キャッシュが必要なのか<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%81%AA%E3%81%9C%E5%A4%9A%E6%AE%B5%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ多段キャッシュが必要なのか への直接リンク" title="なぜ多段キャッシュが必要なのか への直接リンク" translate="no">​</a></h2>
<p>インターネットシステムでは、ビジネス規模の拡大に伴い、データベースがシステムの性能ボトルネックになりがちです。大量のリクエストが直接データベースにアクセスすると、高いレイテンシを招くだけでなく、データベースへの負荷が過大になり、コネクションの枯渇やクエリの遅延といった問題まで発生します。そのため、ほぼすべての高並行システムは <strong>キャッシュ（Cache）</strong> を導入してシステム性能を高めています。</p>
<p>最も一般的なキャッシュ方式は、Redis を統一キャッシュ層として使うことです。クライアントのリクエストはまず Redis にアクセスし、キャッシュにヒットすればそのままデータを返します。ヒットしなければデータベースにアクセスし、結果をキャッシュに書き込みます。この方式だけでもデータベースの負荷を大幅に下げられます。</p>
<p>しかしシステム規模がさらに拡大すると、Redis だけに頼っていては新たな問題が出てきます。例えば高 QPS の場面では、大量のリクエストが同時に Redis にアクセスすることで、ネットワークのオーバーヘッドと Redis の CPU 負荷が発生します。また、特定のホットデータが頻繁に読まれる場合、毎回ネットワーク越しに Redis にアクセスするため、余分なレイテンシも生じます。</p>
<p>これらの問題を解決するために、多くの大規模システムは <strong>多段キャッシュアーキテクチャ（Multi-Level Cache）</strong> を導入しています。その中で最も一般的なパターンが <strong>L1 キャッシュ + L2 キャッシュの設計</strong> です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="l1l2-キャッシュの基本アーキテクチャ">L1・L2 キャッシュの基本アーキテクチャ<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#l1l2-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" class="hash-link" aria-label="L1・L2 キャッシュの基本アーキテクチャ への直接リンク" title="L1・L2 キャッシュの基本アーキテクチャ への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-04-Gemini_Generated_Image_a0c1wqa0c1wqa0c1-f8f48454a1f04bb95342ba3fc5447ebd.webp" width="1408" height="768" class="img_ev3q"></p>
<p>L1・L2 キャッシュは本質的に <strong>キャッシュの階層アーキテクチャ</strong> であり、通常システムには二層のキャッシュを設計します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="l1-キャッシュ一次キャッシュ">L1 キャッシュ（一次キャッシュ）<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#l1-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E4%B8%80%E6%AC%A1%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5" class="hash-link" aria-label="L1 キャッシュ（一次キャッシュ） への直接リンク" title="L1 キャッシュ（一次キャッシュ） への直接リンク" translate="no">​</a></h3>
<p>L1 キャッシュは通常 <strong>アプリケーションプロセスの内部</strong> に置かれ、<strong>ローカルキャッシュ（Local Cache）</strong> とも呼ばれます。例えば：</p>
<ul>
<li class="">Go：bigcache / ristretto</li>
<li class="">Java：Caffeine / Guava Cache</li>
<li class="">Node：LRU Cache</li>
</ul>
<p>L1 キャッシュの特徴は次の通りです。</p>
<ul>
<li class="">アクセスが極めて高速（メモリアクセス）</li>
<li class="">ネットワーク通信が不要</li>
<li class="">各サービスインスタンスが自分専用のキャッシュを持つ</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="l2-キャッシュ二次キャッシュ">L2 キャッシュ（二次キャッシュ）<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#l2-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E4%BA%8C%E6%AC%A1%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5" class="hash-link" aria-label="L2 キャッシュ（二次キャッシュ） への直接リンク" title="L2 キャッシュ（二次キャッシュ） への直接リンク" translate="no">​</a></h3>
<p>L2 キャッシュは通常 <strong>分散キャッシュシステム</strong> で、最も一般的なのは Redis や Memcached です。すべてのサービスインスタンスが同一のキャッシュシステムを共有します。</p>
<p>L2 キャッシュの特徴は次の通りです。</p>
<ul>
<li class="">複数ノードで共有できる</li>
<li class="">データの整合性がより良い</li>
<li class="">容量がより大きい</li>
<li class="">分散デプロイをサポートする</li>
</ul>
<p>システム全体のアクセスの流れは通常、クライアントのリクエスト → サービスプロセス → L1 キャッシュを照会 → ミスなら L2 キャッシュを照会 → それでもミスならデータベースを照会、となります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="l1l2-キャッシュのアクセスフロー">L1・L2 キャッシュのアクセスフロー<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#l1l2-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%95%E3%83%AD%E3%83%BC" class="hash-link" aria-label="L1・L2 キャッシュのアクセスフロー への直接リンク" title="L1・L2 キャッシュのアクセスフロー への直接リンク" translate="no">​</a></h2>
<p>実際のシステムにおける一回のリクエストの典型的なアクセスフローは次の通りです。</p>
<p>まず、リクエストがアプリケーションサービスに入ると、システムは優先的に <strong>L1 キャッシュ</strong> を照会します。データが存在すればそのまま結果を返します。このケースはレイテンシが最も低く、通常はマイクロ秒レベルで済みます。</p>
<p>L1 キャッシュがミスした場合は、続けて <strong>L2 キャッシュ（Redis）</strong> を照会します。Redis にデータが存在すれば結果を返すとともに、そのデータを L1 キャッシュに書き込み、以降のリクエストがローカルキャッシュに直接ヒットできるようにします。</p>
<p>Redis にもデータがなければキャッシュミスです。このときシステムはデータベースにアクセスする必要があります。データベースが結果を返した後、システムは Redis と L1 キャッシュの両方に書き込み、新しいキャッシュデータを構築します。</p>
<p>フロー全体はこう理解できます。</p>
<p>L1 Cache → L2 Cache → Database</p>
<p>この方式により、大部分のリクエストはキャッシュ層で受け止められ、データベースへの頻繁なアクセスを回避できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ二段のキャッシュを設計するのか">なぜ二段のキャッシュを設計するのか<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%81%AA%E3%81%9C%E4%BA%8C%E6%AE%B5%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%92%E8%A8%AD%E8%A8%88%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ二段のキャッシュを設計するのか への直接リンク" title="なぜ二段のキャッシュを設計するのか への直接リンク" translate="no">​</a></h2>
<p>L1 キャッシュと L2 キャッシュはそれぞれ異なる問題を解決しており、両者を組み合わせることでシステム性能を大幅に向上させられます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-の負荷を下げる">Redis の負荷を下げる<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#redis-%E3%81%AE%E8%B2%A0%E8%8D%B7%E3%82%92%E4%B8%8B%E3%81%92%E3%82%8B" class="hash-link" aria-label="Redis の負荷を下げる への直接リンク" title="Redis の負荷を下げる への直接リンク" translate="no">​</a></h3>
<p>高並行システムでは、すべてのリクエストが Redis にアクセスすると、Redis 自体が新たなボトルネックになる可能性があります。L1 キャッシュによって、大量のホットデータをアプリケーションのメモリ内で直接返せるため、Redis へのリクエスト数を減らせます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ネットワークオーバーヘッドを減らす">ネットワークオーバーヘッドを減らす<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%98%E3%83%83%E3%83%89%E3%82%92%E6%B8%9B%E3%82%89%E3%81%99" class="hash-link" aria-label="ネットワークオーバーヘッドを減らす への直接リンク" title="ネットワークオーバーヘッドを減らす への直接リンク" translate="no">​</a></h3>
<p>Redis へのアクセスにはネットワーク通信が必要です。レイテンシがわずか 1ms だとしても、高 QPS の場面では無視できないオーバーヘッドになります。一方 L1 キャッシュはプロセス内アクセスであり、ネットワーク呼び出しよりはるかに高速です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="システムのスループットを高める">システムのスループットを高める<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%97%E3%83%83%E3%83%88%E3%82%92%E9%AB%98%E3%82%81%E3%82%8B" class="hash-link" aria-label="システムのスループットを高める への直接リンク" title="システムのスループットを高める への直接リンク" translate="no">​</a></h3>
<p>L1 キャッシュによって、Redis やデータベースの負荷を増やすことなく、システムはより多くのリクエストを処理できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ホットデータへのアクセス効率を高める">ホットデータへのアクセス効率を高める<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%83%9B%E3%83%83%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF%E3%81%B8%E3%81%AE%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E5%8A%B9%E7%8E%87%E3%82%92%E9%AB%98%E3%82%81%E3%82%8B" class="hash-link" aria-label="ホットデータへのアクセス効率を高める への直接リンク" title="ホットデータへのアクセス効率を高める への直接リンク" translate="no">​</a></h3>
<p>多くのビジネスシーンでは、少数のホットデータが頻繁にアクセスされます。例えば人気商品、ユーザー情報、設定データ、レコメンド結果などです。L1 キャッシュを使えば、これらのホットデータをローカルで直接ヒットさせられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="l1-キャッシュ設計で注意すべき問題">L1 キャッシュ設計で注意すべき問題<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#l1-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E8%A8%AD%E8%A8%88%E3%81%A7%E6%B3%A8%E6%84%8F%E3%81%99%E3%81%B9%E3%81%8D%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="L1 キャッシュ設計で注意すべき問題 への直接リンク" title="L1 キャッシュ設計で注意すべき問題 への直接リンク" translate="no">​</a></h2>
<p>L1 キャッシュの性能は優れていますが、新たな問題ももたらします。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-データの整合性">1) データの整合性<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#1-%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E6%95%B4%E5%90%88%E6%80%A7" class="hash-link" aria-label="1) データの整合性 への直接リンク" title="1) データの整合性 への直接リンク" translate="no">​</a></h3>
<p>L1 キャッシュは各サービスインスタンス内に存在するため、インスタンスごとのキャッシュデータが同期していない可能性があります。データが更新されたとき、適切なキャッシュ無効化の仕組みがなければ、古いデータを読んでしまうかもしれません。この問題を解決するために、通常は <strong>キャッシュ無効化戦略</strong> を採用します。例えばデータ更新時に、メッセージキューや Pub/Sub の仕組みで全サービスインスタンスに通知し、ローカルキャッシュをクリアさせます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-キャッシュ容量の制御">2) キャッシュ容量の制御<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#2-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E5%AE%B9%E9%87%8F%E3%81%AE%E5%88%B6%E5%BE%A1" class="hash-link" aria-label="2) キャッシュ容量の制御 への直接リンク" title="2) キャッシュ容量の制御 への直接リンク" translate="no">​</a></h3>
<p>L1 キャッシュはアプリケーションのメモリ内に存在するため、キャッシュデータが多すぎると大量のメモリを占有し、システムの安定性に影響します。そのため通常は LRU や LFU などの戦略でキャッシュサイズを制限します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-キャッシュペネトレーションとキャッシュブレイクダウン">3) キャッシュペネトレーションとキャッシュブレイクダウン<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#3-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%83%9A%E3%83%8D%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%83%96%E3%83%AC%E3%82%A4%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3" class="hash-link" aria-label="3) キャッシュペネトレーションとキャッシュブレイクダウン への直接リンク" title="3) キャッシュペネトレーションとキャッシュブレイクダウン への直接リンク" translate="no">​</a></h3>
<p>大量のリクエストが存在しないデータに同時にアクセスすると、データベースを直撃する可能性があります。通常はブルームフィルターやミューテックスロックの仕組みで防御します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュ更新戦略の設計">キャッシュ更新戦略の設計<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E6%9B%B4%E6%96%B0%E6%88%A6%E7%95%A5%E3%81%AE%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="キャッシュ更新戦略の設計 への直接リンク" title="キャッシュ更新戦略の設計 への直接リンク" translate="no">​</a></h2>
<p>多段キャッシュシステムでは、キャッシュ更新戦略が非常に重要です。代表的な戦略には次のものがあります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-cache-asideキャッシュアサイド">1) Cache Aside（キャッシュアサイド）<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#1-cache-aside%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%A2%E3%82%B5%E3%82%A4%E3%83%89" class="hash-link" aria-label="1) Cache Aside（キャッシュアサイド） への直接リンク" title="1) Cache Aside（キャッシュアサイド） への直接リンク" translate="no">​</a></h3>
<p>アプリケーションがまずキャッシュを照会し、ミスならデータベースを照会してからキャッシュに書き込みます。最も一般的なパターンです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-write-throughライトスルー">2) Write Through（ライトスルー）<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#2-write-through%E3%83%A9%E3%82%A4%E3%83%88%E3%82%B9%E3%83%AB%E3%83%BC" class="hash-link" aria-label="2) Write Through（ライトスルー） への直接リンク" title="2) Write Through（ライトスルー） への直接リンク" translate="no">​</a></h3>
<p>アプリケーションがデータベースに書き込むと同時にキャッシュも更新し、キャッシュとデータベースのデータの同期を保証します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-write-backライトバック">3) Write Back（ライトバック）<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#3-write-back%E3%83%A9%E3%82%A4%E3%83%88%E3%83%90%E3%83%83%E3%82%AF" class="hash-link" aria-label="3) Write Back（ライトバック） への直接リンク" title="3) Write Back（ライトバック） への直接リンク" translate="no">​</a></h3>
<p>アプリケーションはキャッシュにのみ書き込み、キャッシュシステムが非同期でデータベースに書き込みます。この方式は性能に優れますが、実装は複雑です。</p>
<p>インターネットシステムでは、実装がシンプルで安定していることから、大多数のビジネスが <strong>Cache Aside パターン</strong> を採用しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="l1l2-キャッシュアーキテクチャの適用シーン">L1・L2 キャッシュアーキテクチャの適用シーン<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#l1l2-%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E9%81%A9%E7%94%A8%E3%82%B7%E3%83%BC%E3%83%B3" class="hash-link" aria-label="L1・L2 キャッシュアーキテクチャの適用シーン への直接リンク" title="L1・L2 キャッシュアーキテクチャの適用シーン への直接リンク" translate="no">​</a></h2>
<p>多段キャッシュアーキテクチャは通常、次のような場面に適しています。</p>
<ul>
<li class=""><strong>読み取りが高並行なシステム</strong>：商品詳細ページ、ユーザー情報照会、設定の読み取りなど。</li>
<li class=""><strong>ホットデータへのアクセスが頻繁</strong>：ランキング、レコメンドリストなど。</li>
<li class=""><strong>データベースの負荷が大きい</strong>：キャッシュ層によってデータベースへのクエリ回数を大幅に減らせます。</li>
</ul>
<p>金融取引システムのように、リアルタイムの整合性要求が非常に高いシステムでは、通常は多段キャッシュを使わず、データベースに直接アクセスします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/15/wei-fu-wu-yi-er-ji-huan-cun#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>L1・L2 キャッシュの核心となる考え方は、階層で受け止めることです。ローカルキャッシュが最もホットなトラフィックを飲み込み、Redis がインスタンス間で共有されるデータを引き受け、データベースは本当のキャッシュミスだけを処理します。このアーキテクチャはアクセスレイテンシとバックエンドの負荷を大幅に下げられますが、その代償として複数レプリカの整合性問題を持ち込むため、無効化通知、容量制御、ペネトレーション対策とセットで設計する必要があります。多段キャッシュを導入する価値があるかどうかは、システムの読み書き比率と整合性要求次第です。整合性に敏感な場面では、シンプルな方式のほうがかえって安全です。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
            <category>Redis</category>
        </item>
        <item>
            <title><![CDATA[ETCD を探る]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[分散協調の実際のニーズから出発して etcd の位置づけと Raft による強整合性の原理を紹介し、クラスタ構築と etcdctl での検証の過程を記録。協調の場面における Redis とのトレードオフも比較します。]]></description>
            <content:encoded><![CDATA[<p>この記事を書くきっかけは、Kubernetes 関連の知識を整理していたときに、etcd という名前に何度も出くわしたことでした。クラスタの状態はその中に保存され、サービスディスカバリもそれに依存し、少なくない設定センターの基盤も etcd です。ブラックボックスとして扱うより、単独で取り出して一度構築し、整理してみたほうがよいと考えました。分散システムでは、次のような問題によく遭遇します。</p>
<!-- -->
<ul>
<li class="">サービスノード間で設定を共有する必要がある</li>
<li class="">システムにサービスディスカバリが必要</li>
<li class="">分散ロックには協調のためのセンターが必要</li>
<li class="">クラスタには整合性のある状態ストレージが必要</li>
</ul>
<p>これらの問題は、本質的にはどれも <strong>信頼できる分散協調システム</strong> を必要としています。</p>
<p>単一マシンの時代なら、これらのニーズはデータベースのテーブル一つ、あるいはファイル一つで解決できました。しかしノードが増えると、問題はこう変わります。複数のレプリカの間で「現在の状態」についてどう合意するのか？誰の言い分が正なのか？あるノードが落ちた後、データはまだ信頼できるのか？これこそが分散協調システムが答えるべき問いです。</p>
<p>そして現代のクラウドネイティブ体系で最もよく使われるコンポーネントが <strong>etcd</strong> です。</p>
<p>例えば：</p>
<ul>
<li class="">Kubernetes</li>
<li class="">CoreDNS</li>
<li class="">service mesh</li>
<li class="">分散設定センター</li>
</ul>
<p>これらのシステムの基盤はいずれも etcd に依存しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="etcd-とは何か">etcd とは何か<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#etcd-%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B" class="hash-link" aria-label="etcd とは何か への直接リンク" title="etcd とは何か への直接リンク" translate="no">​</a></h2>
<p>簡単に言えば：</p>
<p><strong>etcd は高信頼の分散 Key-Value データベースです。</strong></p>
<p>しかし、普通のデータベースとの最大の違いは：</p>
<p><strong>「分散協調」のために設計されている点です。</strong></p>
<p>この位置づけがトレードオフを決めています。大量データや極限のスループットを追求するのではなく、「保存したデータの一つ一つが信頼できること」を追求します。したがって、メタデータ、設定、状態といった小さくてもクリティカルなデータの保存に適しており、業務用データベースとして使うのには向いていません。</p>
<p>主な特徴は次の通りです。</p>
<ul>
<li class="">強整合性（Strong Consistency）</li>
<li class="">分散クラスタのサポート</li>
<li class="">Watch による監視機構</li>
<li class="">トランザクションのサポート</li>
<li class="">リース（Lease）機構</li>
</ul>
<p>これらの特性の組み合わせが実に面白いのです。</p>
<ol>
<li class="">
<p>Watch により、クライアントは特定の key やプレフィックスの変化を購読できます。設定を一つ変えれば全ノードがリアルタイムに感知でき、ポーリングは不要です。</p>
</li>
<li class="">
<p>Lease は TTL 付きの「ハートビート証憑」に相当します。key はリースに紐付けることができ、クライアントが更新を止めると key は自動的に削除されます。サービス登録と障害ノードの除外はこれによって実現されています。</p>
</li>
<li class="">
<p>トランザクション（Txn）は compare-and-swap のセマンティクスを提供します。「この key が存在しなければ書き込む」をアトミックに完了でき、これが分散ロックの基礎です。</p>
</li>
</ol>
<p>多くの分散システムが etcd を次の用途に使っています。</p>
<ul>
<li class="">サービスレジストリ</li>
<li class="">設定センター</li>
<li class="">分散ロック</li>
<li class="">Leader 選出</li>
</ul>
<p>例えば Kubernetes は <strong>クラスタ状態</strong> 全体を etcd に保存しています。kube-apiserver が etcd を直接読み書きする唯一のコンポーネントで、他のコントローラーはすべて apiserver の Watch 機構を通じて間接的に状態変化を感知します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="強整合性raft-合意アルゴリズム">強整合性（Raft 合意アルゴリズム）<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#%E5%BC%B7%E6%95%B4%E5%90%88%E6%80%A7raft-%E5%90%88%E6%84%8F%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0" class="hash-link" aria-label="強整合性（Raft 合意アルゴリズム） への直接リンク" title="強整合性（Raft 合意アルゴリズム） への直接リンク" translate="no">​</a></h2>
<p>etcd の内部では <strong>Raft 合意アルゴリズム</strong> が使われています。</p>
<p>Raft の核心となる考え方は：</p>
<p>クラスタ全体から一つの <strong>Leader ノード</strong> が選出されます。</p>
<p>すべての書き込みリクエストは Leader を経由しなければなりません。</p>
<p>流れはおおよそ次の通りです。</p>
<p>クライアント → Leader → 過半数のノードへ同期 → コミット成功</p>
<p>そして：</p>
<p><strong>半数を超えるノードが書き込みを確認したとき</strong></p>
<p>にのみ、データは本当にコミットされたことになります。</p>
<p>ここをもう少し細かく見てみましょう。Raft では各ノードは三つの役割のいずれかにあります。Leader、Follower、Candidate です。正常運転時は Leader が一つだけ存在し、すべての Follower に周期的にハートビートを送信します。ある Follower がタイムアウト時間内にハートビートを受け取れないと、自身を Candidate に変えて選挙を開始し、過半数の票を得たノードが新しい Leader になります。</p>
<p>書き込みはログレプリケーション（Log Replication）で行われます。Leader はまず書き込み操作を自身のログに追記し、それからすべての Follower に並列に送信します。<strong>多数派</strong>（majority、つまり半数超）のノードがこのログを永続化した時点で、Leader はそれをコミット済みとマークし、クライアントに応答します。</p>
<p>「多数派」という設計こそが強整合性の鍵です。任意の二つの多数派集合は必ず交わりを持つため、たとえ Leader がクラッシュしても、新しく選出された Leader は必ずコミット済みのデータをすべて含んでおり、書き込みが失われることはありません。</p>
<p>これは、etcd クラスタのノード数として奇数（3、5、7）が推奨される理由の説明にもなります。</p>
<ul>
<li class="">3 ノードなら 1 台の故障を、5 ノードなら 2 台の故障を許容できる</li>
<li class="">4 ノードでも許容できる故障は 1 台のみ（多数派に 3 台必要）。フォールトトレランスは 3 ノードと同じなのに、同期のオーバーヘッドが一つ増えるだけ</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="etcd-クラスタの構築">etcd クラスタの構築<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#etcd-%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AE%E6%A7%8B%E7%AF%89" class="hash-link" aria-label="etcd クラスタの構築 への直接リンク" title="etcd クラスタの構築 への直接リンク" translate="no">​</a></h2>
<p>デプロイ環境：</p>
<p>オペレーティングシステム：</p>
<p>Debian 11.5.0</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-etcd-のダウンロード">1 etcd のダウンロード<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#1-etcd-%E3%81%AE%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89" class="hash-link" aria-label="1 etcd のダウンロード への直接リンク" title="1 etcd のダウンロード への直接リンク" translate="no">​</a></h3>
<p>GitHub から etcd のバイナリファイルをダウンロードします。</p>
<p><a href="https://github.com/etcd-io/etcd/releases" target="_blank" rel="noopener noreferrer" class="">https://github.com/etcd-io/etcd/releases</a></p>
<p>release ページにはプラットフォームごとの圧縮パッケージが用意されているので、対応するアーキテクチャ（linux-amd64 など）を選んでダウンロードすれば OK です。etcd は単一の静的バイナリで、追加のランタイム依存がありません。これがデプロイの簡単さの理由の一つです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-etcd-のインストール">2 etcd のインストール<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#2-etcd-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="2 etcd のインストール への直接リンク" title="2 etcd のインストール への直接リンク" translate="no">​</a></h3>
<p>解凍後、以下のファイルを次の場所に配置します。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">/usr/local/bin</span><br></div></code></pre></div></div>
<p>主なファイルは：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">etcd</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">etcdctl</span><br></div></code></pre></div></div>
<p>二つのファイルの役割分担は明確です。<code>etcd</code> はサーバープロセスで、<code>etcdctl</code> はコマンドラインクライアントです。以降のすべての照会、書き込み、メンテナンス操作は後者を通じて行います。<code>/usr/local/bin</code> に置くのは PATH に入れるためで、どのディレクトリからでも直接呼び出せるようになります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-実行権限の設定">3 実行権限の設定<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#3-%E5%AE%9F%E8%A1%8C%E6%A8%A9%E9%99%90%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="3 実行権限の設定 への直接リンク" title="3 実行権限の設定 への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 解凍したファイルには実行ビットが付いていないことがあるので、手動で付与する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">chmod</span><span class="token plain"> +x /usr/local/bin/etcd</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">chmod</span><span class="token plain"> +x /usr/local/bin/etcdctl</span><br></div></code></pre></div></div>
<p>ここまでで、単一ノードのバイナリの準備が整いました。クラスタモードでは、各ノードで上記のインストール手順を繰り返した上で、起動パラメータでそれぞれの名前、リッスンアドレス、初期クラスタメンバーリストを宣言します。ノード間は 2380 ポートで相互通信し、クライアントは 2379 ポートでアクセスします。私の環境では TLS を有効にしており、証明書は <code>/opt/etcd/ssl</code> にまとめて置いてあるため、以下の検証コマンドにはすべて証明書のパラメータを付ける必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-クラスタの検証">4 クラスタの検証<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#4-%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AE%E6%A4%9C%E8%A8%BC" class="hash-link" aria-label="4 クラスタの検証 への直接リンク" title="4 クラスタの検証 への直接リンク" translate="no">​</a></h3>
<p>etcdctl を使ってデータを確認できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># クラスタ内のすべての key を列挙する（key のみ表示、value は表示しない）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --cacert/--cert/--key：TLS 相互認証に必要な CA 証明書とクライアント証明書</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --endpoints：3 つのノードをすべて書いておくと、クライアントが自動的に負荷分散とフェイルオーバーを行う</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token assign-left variable" style="color:hsl(207, 82%, 66%)">ETCDCTL_API</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">3</span><span class="token plain"> etcdctl </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--cacert</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/opt/etcd/ssl/ca.pem </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--cert</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/opt/etcd/ssl/server.pem </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--key</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/opt/etcd/ssl/server-key.pem </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--endpoints</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token string" style="color:hsl(95, 38%, 62%)">"https://172.24.93.151:2379,https://172.24.93.149:2379,https://172.24.93.150:2379"</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">get / </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--prefix</span><span class="token plain"> --keys-only</span><br></div></code></pre></div></div>
<p>コマンドが結果を返せば、クライアントからクラスタまでの経路、証明書、多数派の状態がすべて疎通していることを意味します。<code>--prefix</code> はプレフィックスマッチを表し、<code>/</code> と組み合わせると全 key の走査に相当します。</p>
<p>key の削除：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># プレフィックスで削除する。空のプレフィックス "" はすべての key にマッチする —— 本番環境では要注意</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token assign-left variable" style="color:hsl(207, 82%, 66%)">ETCDCTL_API</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">3</span><span class="token plain"> etcdctl </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--cacert</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/opt/etcd/ssl/ca.pem </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--cert</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/opt/etcd/ssl/server.pem </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--key</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/opt/etcd/ssl/server-key.pem </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--endpoints</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token string" style="color:hsl(95, 38%, 62%)">"https://172.24.93.151:2379,https://172.24.93.149:2379,https://172.24.93.150:2379"</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">del </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--prefix</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">""</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p><code>del --prefix ""</code> は全件削除です。この etcd が Kubernetes のバックエンドとして動いている場合、このコマンドはクラスタ状態全体を消去するのと同じことになります。実行前に必ず対象環境を確認してください。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ分散協調には-redis-より-etcd-が適しているのか">なぜ分散協調には Redis より etcd が適しているのか<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#%E3%81%AA%E3%81%9C%E5%88%86%E6%95%A3%E5%8D%94%E8%AA%BF%E3%81%AB%E3%81%AF-redis-%E3%82%88%E3%82%8A-etcd-%E3%81%8C%E9%81%A9%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ分散協調には Redis より etcd が適しているのか への直接リンク" title="なぜ分散協調には Redis より etcd が適しているのか への直接リンク" translate="no">​</a></h2>
<p>多くの人がこう尋ねます。</p>
<p>Redis でも分散ロックはできるのでは？</p>
<p>確かにできます。</p>
<p>しかし Redis は <strong>AP システム</strong> であり、etcd は <strong>CP システム</strong> です。</p>
<p>簡単に理解するなら：</p>
<p>Redis は <strong>可用性と性能</strong> を重視します。</p>
<p>etcd は <strong>整合性と信頼性</strong> を重視します。</p>
<p>ロックの場面に具体化すると、違いは障害時の挙動に現れます。Redis のマスター・スレーブレプリケーションは非同期です。マスターノードがロックを書き込んだ後、同期される前にダウンすると、スレーブがマスターに昇格した時点でロックは「消えて」しまい、二つのクライアントが同時にロックを保持する可能性があります。Redlock 方式はこの問題の緩和を試みていますが、その安全性についてはコミュニティで議論が続いています。一方 etcd の書き込みは多数派の確認を経なければ成功とならず、Leader の切り替えでコミット済みデータが失われることはないため、障害シナリオでもロックのセマンティクスは成立し続けます。</p>
<p>代償はもちろん性能です。一回の書き込みに多数派同期一巡とディスクへの永続化が必要で、レイテンシもスループットも Redis には遠く及びません。したがって結論は「どちらが優れているか」ではなく、場面による使い分けです。キャッシュ、カウンター、まれな失効を許容できる軽量ロックには Redis を、設定、Leader 選出、厳密な相互排他が必須のロックには etcd を使いましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>構築と利用の過程で、記録しておく価値のあるポイントがいくつかあります。</p>
<ol>
<li class="">
<p>環境変数 <code>ETCDCTL_API=3</code> を忘れないこと。古いバージョンの etcdctl はデフォルトで v2 API を使いますが、v2 と v3 のデータは完全に分離されています。API バージョンを間違えると「確かに書き込んだのに見つからない」という見かけ上の不具合が起きます。比較的新しいバージョンではデフォルトが v3 になっていますが、スクリプト内では明示的に宣言しておいて損はありません。</p>
</li>
<li class="">
<p><code>--endpoints</code> には全ノードを書いておくことを推奨します。一つのノードだけ書いた場合、そのノードが故障するとクライアントは即座に接続を失います。全部書いておけば、クライアントは健全なノードに自動的に切り替えられます。</p>
</li>
<li class="">
<p>証明書のパスと SAN を一致させること。TLS を有効化した後は、証明書内の IP・ドメイン名が endpoints で実際に使うアドレスをカバーしていなければならず、そうでないとハンドシェイク段階で拒否されます。</p>
</li>
<li class="">
<p>ノード数は奇数を保つこと。理由は前述の Raft の部分で説明した通りです。偶数ノードはフォールトトレランスを高めず、同期コストを増やすだけです。</p>
</li>
<li class="">
<p>etcd はディスクレイテンシに敏感です。Raft のログはコミットのたびに fsync が必要で、ディスクが遅いと書き込みレイテンシが直接跳ね上がり、Leader 選挙の揺らぎを引き起こすことさえあります。条件が許すならできるだけ SSD を使いましょう。</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/14/da-jian-etcdji-qun#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>etcd は本質的に「性能を整合性と引き換えにする」典型的な設計です。Raft の多数派コミットにより、いかなる時点でも読み取ったコミット済みデータは信頼でき、Watch、Lease、トランザクションといった機構が「信頼できる状態ストレージ」をさらにサービスディスカバリ、Leader 選出、分散ロックの汎用的な土台へと押し上げています。構築自体は複雑ではありません——バイナリ二つと一組の起動パラメータだけです。本当に頭を使うべきなのは、TLS 証明書、ノード設計、そして適用範囲の見極めです。Redis との役割分担をしっかり考え、それぞれが得意とする場面で使うほうが、どちらが強いかを論争するよりずっと意義があります。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Flink を理解する]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[バッチ処理からストリーム処理への進化を出発点に、Flink が解決するリアルタイム計算・状態管理・耐障害性・イベントタイムの問題と、Stream First、State Driven、Exactly Once という核心的な設計思想を整理します。]]></description>
            <content:encoded><![CDATA[<p>ビッグデータシステムの発展の過程には、ずっと一つの核心的な問題がありました。<strong>データはますます増えるのに、処理速度はますます遅くなる。</strong> この記事ではバッチ処理から話を始め、Flink がなぜ登場したのか、いったい何を解決したのかを整理します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="バッチ処理からストリーム処理へ">バッチ処理からストリーム処理へ<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#%E3%83%90%E3%83%83%E3%83%81%E5%87%A6%E7%90%86%E3%81%8B%E3%82%89%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0%E5%87%A6%E7%90%86%E3%81%B8" class="hash-link" aria-label="バッチ処理からストリーム処理へ への直接リンク" title="バッチ処理からストリーム処理へ への直接リンク" translate="no">​</a></h2>
<p>従来のデータ処理は、そのほとんどが<strong>オフライン処理</strong>でした。毎晩ジョブを 1 回実行し、ユーザー行動を集計し、広告データを計算し、レポートを生成し、ビジネス指標を分析する。この方式は <strong>Batch Processing(バッチ処理)</strong> と呼ばれ、代表的なツールには次のようなものがあります。</p>
<ul>
<li class="">Hadoop MapReduce</li>
<li class="">Hive</li>
<li class="">Spark</li>
</ul>
<p>これらのシステムは<strong>大量の過去データ</strong>の処理に向いています。しかしインターネットの発展にともない、多くのビジネスで<strong>リアルタイムなデータ処理</strong>が必要になってきました。たとえば:</p>
<ul>
<li class="">リアルタイム不正検知</li>
<li class="">リアルタイムレコメンド</li>
<li class="">リアルタイム監視</li>
<li class="">リアルタイム広告入札</li>
<li class="">リアルタイムログ分析</li>
</ul>
<p>これらのシーンには共通の特徴があります。<strong>データを「生成されると同時に処理」しなければならず</strong>、翌日まで待つことはできません。そこで新しい計算モデルが登場しました。<strong>Stream Processing(ストリーム処理)</strong> です。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-04-Gemini_Generated_Image_xvg9q2xvg9q2xvg9-d0ff4be7b9e2ef01d55dbbf821b03ac4.webp" width="1600" height="873" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-flink-が必要なのか">なぜ Flink が必要なのか<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#%E3%81%AA%E3%81%9C-flink-%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ Flink が必要なのか への直接リンク" title="なぜ Flink が必要なのか への直接リンク" translate="no">​</a></h2>
<p>初期のリアルタイム計算システムは主に Storm と Spark Streaming に依存していましたが、これらのシステムにはいくつかの問題がありました。</p>
<p>Storm はストリーム処理システムではあるものの、開発が複雑で、耐障害性はそこそこ、状態管理も困難でした。Spark Streaming は安定してはいるものの、その本質は<strong>マイクロバッチ処理(Micro Batch)</strong> です。つまり、本当の意味でのストリーム処理ではありません。たとえば:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">每1秒处理一批数据</span><br></div></code></pre></div></div>
<p>このモデルでは、多くのシーンで依然としてリアルタイム性が足りません。そこで Flink が登場しました。その目標は明確です。<strong>本物のストリーム処理システムを構築すること。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="flink-は何を解決したのか">Flink は何を解決したのか<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#flink-%E3%81%AF%E4%BD%95%E3%82%92%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%9F%E3%81%AE%E3%81%8B" class="hash-link" aria-label="Flink は何を解決したのか への直接リンク" title="Flink は何を解決したのか への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-リアルタイムデータ処理">1) リアルタイムデータ処理<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#1-%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%87%E3%83%BC%E3%82%BF%E5%87%A6%E7%90%86" class="hash-link" aria-label="1) リアルタイムデータ処理 への直接リンク" title="1) リアルタイムデータ処理 への直接リンク" translate="no">​</a></h3>
<p>Flink は本当の意味での<strong>ミリ秒級のリアルタイム計算</strong>を実現できます。ユーザーが商品をクリックする例で見てみましょう。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">点击 → Kafka → Flink → 实时推荐</span><br></div></code></pre></div></div>
<p>この一連の流れは、<strong>数十ミリ秒</strong>しかかからないこともあります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-状態管理">2) 状態管理<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#2-%E7%8A%B6%E6%85%8B%E7%AE%A1%E7%90%86" class="hash-link" aria-label="2) 状態管理 への直接リンク" title="2) 状態管理 への直接リンク" translate="no">​</a></h3>
<p>ストリーム処理において非常に重要な問題が<strong>状態(State)</strong> です。たとえばユーザーのクリック回数の集計:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">用户A 点击 1次</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">用户A 点击 2次</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">用户A 点击 3次</span><br></div></code></pre></div></div>
<p>システムはそれまでのデータを覚えておく必要があります。Flink には状態管理機構(Keyed State、Operator State)が組み込まれており、ローカル状態と RocksDB による状態ストレージをサポートしているため、<strong>超大規模な状態データ</strong>を扱えます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-耐障害性">3) 耐障害性<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#3-%E8%80%90%E9%9A%9C%E5%AE%B3%E6%80%A7" class="hash-link" aria-label="3) 耐障害性 への直接リンク" title="3) 耐障害性 への直接リンク" translate="no">​</a></h3>
<p>分散システムでは、ノードのダウンは日常茶飯事です。Flink は <strong>Checkpoint 機構</strong>によってデータの安全を保証します。システムは定期的に計算状態を保存し、ノードがダウンしても Checkpoint から計算を復旧できます。この機構が <strong>Exactly Once(厳密に一度だけの処理)</strong> ——各データがちょうど一度だけ処理されること——を保証します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-イベントタイム">4) イベントタイム<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#4-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%BF%E3%82%A4%E3%83%A0" class="hash-link" aria-label="4) イベントタイム への直接リンク" title="4) イベントタイム への直接リンク" translate="no">​</a></h3>
<p>ストリーム処理には、もう一つ非常に難しい問題があります。<strong>イベントタイム(Event Time)</strong> です。ログデータは遅れて到着することがあり、システム時刻だけで計算すると集計に誤りが生じます。</p>
<p>Flink は <strong>Watermark(ウォーターマーク)機構</strong>を導入しました。Watermark はどのデータが「おおむね出そろった」かをシステムが判断する助けとなり、遅延データ、ウィンドウ計算、時間ベースの集約を正しく処理できるようになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="flink-の核心的な設計思想">Flink の核心的な設計思想<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#flink-%E3%81%AE%E6%A0%B8%E5%BF%83%E7%9A%84%E3%81%AA%E8%A8%AD%E8%A8%88%E6%80%9D%E6%83%B3" class="hash-link" aria-label="Flink の核心的な設計思想 への直接リンク" title="Flink の核心的な設計思想 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-stream-first">1) Stream First<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#1-stream-first" class="hash-link" aria-label="1) Stream First への直接リンク" title="1) Stream First への直接リンク" translate="no">​</a></h3>
<p>Flink の核心的な理念は<strong>すべてはストリームである</strong>というものです。バッチ処理は単なる<strong>有限のデータストリーム</strong>にすぎません。したがって、Flink の Batch と Stream は同じ一つのエンジンを使います。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-state-driven">2) State Driven<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#2-state-driven" class="hash-link" aria-label="2) State Driven への直接リンク" title="2) State Driven への直接リンク" translate="no">​</a></h3>
<p>Flink は<strong>状態駆動の計算システム</strong>であり、すべての計算は状態を中心に展開されます。ユーザー行動の集計、リアルタイム集約、リアルタイム監視——これらはすべて状態管理に依存しています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-exactly-once">3) Exactly Once<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#3-exactly-once" class="hash-link" aria-label="3) Exactly Once への直接リンク" title="3) Exactly Once への直接リンク" translate="no">​</a></h3>
<p>Flink は<strong>データ処理の正しさ</strong>を非常に重視します。Checkpoint、分散スナップショット、2 フェーズコミットによって、Flink は <strong>Exactly Once セマンティクス</strong>を実現できます。これは多くのリアルタイムシステムにとって極めて重要な能力です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実際のシステムにおける-flink-の活用">実際のシステムにおける Flink の活用<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#%E5%AE%9F%E9%9A%9B%E3%81%AE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B-flink-%E3%81%AE%E6%B4%BB%E7%94%A8" class="hash-link" aria-label="実際のシステムにおける Flink の活用 への直接リンク" title="実際のシステムにおける Flink の活用 への直接リンク" translate="no">​</a></h2>
<p>Flink は多くのインターネット企業で幅広く活用されており、典型的なシーンには、リアルタイムレコメンドシステム、ユーザー行動分析、広告クリック集計、リアルタイム不正検知システム、ログのリアルタイム分析などがあります。よくあるデータパイプラインは次のような形です。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">用户行为 → Kafka → Flink → ClickHouse</span><br></div></code></pre></div></div>
<p>Flink はその中間で、データクレンジング、リアルタイム計算、リアルタイム集約を担当します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/14/flink-ji-qun-da-jian#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Flink の登場は、本質的には「本物のストリーム処理」を実現するためのものでした。同じ一つのエンジンでバッチとストリームを統一し、組み込みの状態管理で大規模なステートフル計算を支え、Checkpoint 機構と引き換えに Exactly Once の正しさを手に入れ、さらに Watermark で順序の乱れや遅延データに対処する。これらのポイントを理解すれば、Flink と初期のリアルタイム計算システムとの根本的な違いも理解できます。この後で具体的な API やクラスタのデプロイを見ていくときも、ずっとスムーズになるはずです。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[Netty を深く学ぶ]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[BIO の限界から出発し、Java NIO の Channel・Buffer・Selector という 3 大コンポーネントと Reactor スレッドモデルを整理し、Netty の高性能ネットワーク通信を支える設計思想を理解します。]]></description>
            <content:encoded><![CDATA[<p>分散システムやマイクロサービスアーキテクチャにおいて、ネットワーク通信は最も基礎的で、かつ最も重要な部分です。<br>
多くの高性能フレームワーク（Dubbo、gRPC、RocketMQ、Elasticsearch など）は、その基盤のネットワーク通信を <strong>Netty</strong> に依存しています。</p>
<!-- -->
<p>私が最初に Netty に触れたとき、いきなり API とサンプルコードを読み始めたのですが、読めば読むほど混乱しました。なぜ EventLoop が必要なのか？なぜ接続の受け入れと読み書きを 2 つのスレッドグループに分けるのか？後になって気づいたのですが、これらの設計は何もないところから生まれたのではなく、従来のネットワークプログラミングが抱える根本的な問題を解決するためのものでした。この進化の歴史を飛ばして Netty を学ぶのは、問題文を見ずに答えを丸暗記するようなものです。</p>
<p>そこでこのノートでは、コードを書くのを急がず、まず Netty の背後にある <strong>NIO ネットワークモデルの設計思想</strong> を整理します。この本筋を理解すれば、Netty の各コンポーネントがすべて腑に落ちるはずです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="従来のネットワークプログラミングの問題">従来のネットワークプログラミングの問題<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#%E5%BE%93%E6%9D%A5%E3%81%AE%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%AE%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="従来のネットワークプログラミングの問題 への直接リンク" title="従来のネットワークプログラミングの問題 への直接リンク" translate="no">​</a></h2>
<p>初期の Java ネットワークプログラミングでは、ほとんどのプログラムが <strong>BIO（Blocking IO）</strong> モデルを使っていました。</p>
<p>BIO の「ブロッキング」は 2 か所に現れます。<code>accept()</code> が新しい接続を待つときにブロックし、<code>read()</code> がデータの到着を待つときにもブロックします。スレッドがある接続の読み書きでブロックされると、他のことは何もできなくなります。</p>
<p>例えば：</p>
<p>サーバーはクライアント接続を 1 つ受け入れるたびに、スレッドを 1 つ作成します。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">一个连接 = 一个线程</span><br></div></code></pre></div></div>
<p>このモデルの利点は、プログラミングがシンプルで直感的なことです。各スレッドは自分の担当する 1 つの接続だけを面倒見て、最初から最後まで読み続ければよいのです。接続数が少ないうちはこれで十分です。</p>
<p>しかし接続が非常に多くなると、例えば：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">1万连接 = 1万个线程</span><br></div></code></pre></div></div>
<p>これはいくつかの深刻な問題を引き起こします：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドリソースの消費が膨大">スレッドリソースの消費が膨大<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E6%B6%88%E8%B2%BB%E3%81%8C%E8%86%A8%E5%A4%A7" class="hash-link" aria-label="スレッドリソースの消費が膨大 への直接リンク" title="スレッドリソースの消費が膨大 への直接リンク" translate="no">​</a></h3>
<p>スレッド自体にメモリとスケジューリングのコストがかかります。各 Java スレッドは独立したスタックメモリを占有するため、接続数が 1 万を超えると、スレッドスタックだけで大量のメモリを食いつぶします。OS がスレッド構造を維持するオーバーヘッドはまだ含めていません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドのコンテキストスイッチのオーバーヘッドが大きい">スレッドのコンテキストスイッチのオーバーヘッドが大きい<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%82%B9%E3%82%A4%E3%83%83%E3%83%81%E3%81%AE%E3%82%AA%E3%83%BC%E3%83%90%E3%83%BC%E3%83%98%E3%83%83%E3%83%89%E3%81%8C%E5%A4%A7%E3%81%8D%E3%81%84" class="hash-link" aria-label="スレッドのコンテキストスイッチのオーバーヘッドが大きい への直接リンク" title="スレッドのコンテキストスイッチのオーバーヘッドが大きい への直接リンク" translate="no">​</a></h3>
<p>CPU は異なるスレッド間で頻繁に切り替えを行う必要があります。切り替えのたびにレジスタの保存と復元、キャッシュのフラッシュが発生し、スレッド数が CPU コア数を大幅に超えると、CPU 時間のかなりの部分が業務処理ではなく切り替え自体に費やされてしまいます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="システムのスケーラビリティが低い">システムのスケーラビリティが低い<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3%E3%81%8C%E4%BD%8E%E3%81%84" class="hash-link" aria-label="システムのスケーラビリティが低い への直接リンク" title="システムのスケーラビリティが低い への直接リンク" translate="no">​</a></h3>
<p>接続数が増えると、システムはクラッシュしやすくなります。さらに厄介なのは、大量の接続が実際にはアイドル状態である（例えば長時間接続の場面では、多くのクライアントは常にデータを送っているわけではない）にもかかわらず、それぞれがスレッドを 1 つ占有してただ待っていることです。リソースが「待機」に浪費されているのです。</p>
<p>したがって、従来の BIO は <strong>高並行なネットワークサービス</strong> には適していません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="nio-の核心となる設計思想">NIO の核心となる設計思想<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#nio-%E3%81%AE%E6%A0%B8%E5%BF%83%E3%81%A8%E3%81%AA%E3%82%8B%E8%A8%AD%E8%A8%88%E6%80%9D%E6%83%B3" class="hash-link" aria-label="NIO の核心となる設計思想 への直接リンク" title="NIO の核心となる設計思想 への直接リンク" translate="no">​</a></h2>
<p>BIO の問題を解決するために、Java は <strong>NIO（Non-Blocking IO）</strong> を打ち出しました。</p>
<p>NIO の核心となる考え方は非常にシンプルです：</p>
<blockquote>
<p><strong>少数のスレッドで、大量の接続を管理する</strong></p>
</blockquote>
<p>発想の転換はこうです。ほとんどの接続がほとんどの時間アイドル状態なのであれば、スレッドに接続を見張らせてただ待たせるのではなく、「どの接続にデータが来たか」の監視を OS に任せ、スレッドは本当にイベントが発生したときだけ処理に出動する。これが <strong>IO 多重化（IO マルチプレクシング）</strong> の考え方であり、Linux では select/poll/epoll といったシステムコールに対応します。</p>
<p>NIO は 3 つの中核コンポーネントに依存しています：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Channel</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Buffer</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Selector</span><br></div></code></pre></div></div>
<p>この 3 つが NIO のコアアーキテクチャを構成しています。順番に見ていきましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="channelチャネル">Channel（チャネル）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#channel%E3%83%81%E3%83%A3%E3%83%8D%E3%83%AB" class="hash-link" aria-label="Channel（チャネル） への直接リンク" title="Channel（チャネル） への直接リンク" translate="no">​</a></h2>
<p>Channel は次のように理解できます：</p>
<blockquote>
<p><strong>データ転送のパイプ</strong></p>
</blockquote>
<p>従来の IO と異なるのは：</p>
<p>従来の IO：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">输入流 / 输出流</span><br></div></code></pre></div></div>
<p>一方 Channel は：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">是双向的</span><br></div></code></pre></div></div>
<p>読み込みも書き込みもできます。Channel の本質は <strong>ネットワーク接続の抽象化</strong> です。</p>
<p>Java NIO でよく使われる実装には、<code>ServerSocketChannel</code>（ポートを監視して接続を受け入れる）と <code>SocketChannel</code>（具体的な 1 本の TCP 接続を表す）があります。Channel はノンブロッキングモードに設定できます。これが Selector によって一元管理されるための前提です。データが読めないときはスレッドを固まらせず、即座に戻ります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bufferバッファ">Buffer（バッファ）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#buffer%E3%83%90%E3%83%83%E3%83%95%E3%82%A1" class="hash-link" aria-label="Buffer（バッファ） への直接リンク" title="Buffer（バッファ） への直接リンク" translate="no">​</a></h2>
<p>NIO では、すべてのデータはまず <strong>Buffer</strong> に入らなければなりません。</p>
<p>次のように理解できます：</p>
<blockquote>
<p><strong>データの一時的な格納領域</strong></p>
</blockquote>
<p>データの流れ：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">网络 -&gt; Buffer -&gt; 程序</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">程序 -&gt; Buffer -&gt; 网络</span><br></div></code></pre></div></div>
<p>Buffer の本質は状態を持つメモリ領域で、内部では position、limit、capacity といったポインタで「どこまで書いたか、どこまで読めるか」を記録しています。読み書きモードの切り替えは <code>flip()</code> のようなメソッドで行う必要があり、これが NIO ネイティブ API で間違えやすいと悪名高いポイントの一つです。Netty が後に独自の ByteBuf を実装したのは、大部分はこの扱いにくい操作から解放されるためでした。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="selectorセレクタ">Selector（セレクタ）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#selector%E3%82%BB%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="Selector（セレクタ） への直接リンク" title="Selector（セレクタ） への直接リンク" translate="no">​</a></h2>
<p>Selector は <strong>NIO の最も中核となるコンポーネント</strong> です。</p>
<p>その役割は：</p>
<blockquote>
<p><strong>1 つのスレッドで複数の Channel を管理する</strong></p>
</blockquote>
<p>つまり：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">一个线程</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">监听多个连接</span><br></div></code></pre></div></div>
<p>動作方式は次のようなイメージです：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">事件轮询</span><br></div></code></pre></div></div>
<p>具体的には、各 Channel を Selector に登録するときに、自分が関心を持つイベントの種類を宣言します。新しい接続の到着（ACCEPT）、データ読み込み可能（READ）、書き込み可能（WRITE）などです。スレッドは <code>select()</code> を呼び出してブロックして待ち、いずれかの Channel が準備完了になると、準備完了の集合が返され、スレッドはそれを順番に処理すればよいのです。</p>
<p>フロー：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Selector</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">监听多个 Channel</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">哪个 Channel 有事件</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   |</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">处理哪个</span><br></div></code></pre></div></div>
<p>こうすることで、スレッドの時間はすべて「準備完了イベントの処理」に使われ、「特定の接続を待つこと」には使われません。これこそが NIO と BIO の根本的な違いです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reactor-スレッドモデル">Reactor スレッドモデル<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#reactor-%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%A2%E3%83%87%E3%83%AB" class="hash-link" aria-label="Reactor スレッドモデル への直接リンク" title="Reactor スレッドモデル への直接リンク" translate="no">​</a></h2>
<p>Reactor スレッドモデルは <strong>高並行ネットワークサーバーでよく使われるデザインパターン</strong> です。その核心となる考え方は、<strong>少数のスレッドで、イベント駆動の方式によって大量のネットワーク接続を処理する</strong> ことです。接続ごとにスレッドを作成するのではなく、ネットワークイベント（接続、読み込み、書き込みなど）を監視し、ある接続にデータが到着したときに初めてそれを処理します。これによって大量のスレッドがもたらすリソース消費とコンテキストスイッチの問題を回避し、サーバーの並行処理能力を大幅に向上させます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2026-03-6f2341a1-2edd-42d2-b66a-a174f49001c4-1d85f6faeca7214b64186dc69cd8562e.webp" width="1536" height="1024" class="img_ev3q"></p>
<p>Reactor モデルには通常、いくつかの重要な役割が含まれます：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1イベント監視reactor">1、イベント監視（Reactor）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#1%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E7%9B%A3%E8%A6%96reactor" class="hash-link" aria-label="1、イベント監視（Reactor） への直接リンク" title="1、イベント監視（Reactor） への直接リンク" translate="no">​</a></h3>
<p>ネットワークイベントの監視を担当します。例えば新しい接続の到着、データ読み込み可能、データ書き込み可能などです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2接続受け入れacceptor">2、接続受け入れ（Acceptor）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#2%E6%8E%A5%E7%B6%9A%E5%8F%97%E3%81%91%E5%85%A5%E3%82%8Cacceptor" class="hash-link" aria-label="2、接続受け入れ（Acceptor） への直接リンク" title="2、接続受け入れ（Acceptor） への直接リンク" translate="no">​</a></h3>
<p>新しいクライアント接続があったときに、接続を受け入れて後続の処理スレッドに登録する役割を担います。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3イベント分配dispatcher">3、イベント分配（Dispatcher）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#3%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E5%88%86%E9%85%8Ddispatcher" class="hash-link" aria-label="3、イベント分配（Dispatcher） への直接リンク" title="3、イベント分配（Dispatcher） への直接リンク" translate="no">​</a></h3>
<p>異なるネットワークイベントを対応する処理ロジックへ振り分けます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4業務処理handler">4、業務処理（Handler）<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#4%E6%A5%AD%E5%8B%99%E5%87%A6%E7%90%86handler" class="hash-link" aria-label="4、業務処理（Handler） への直接リンク" title="4、業務処理（Handler） への直接リンク" translate="no">​</a></h3>
<p>実際に業務ロジックを実行します。例えばプロトコルの解析、リクエストの処理、結果の返却などです。</p>
<p>Reactor モデル自体にもいくつかの進化形態があります。単一 Reactor 単一スレッド（1 つのスレッドが監視と処理をすべて担う）、単一 Reactor 複数スレッド（監視は単一スレッド、処理はスレッドプールに委譲）、マスター・スレーブ Reactor（接続の受け入れと読み書き処理をそれぞれ別の Reactor スレッドグループが担当）です。接続量が多くなるほど、受け入れと読み書きを分離して、互いに足を引っ張らないようにする必要があります。</p>
<p>Netty において Reactor モデルは通常 <strong>BossGroup + WorkerGroup のスレッド構造</strong> として現れ、これはまさにマスター・スレーブ Reactor 形態に対応します：</p>
<ul>
<li class=""><strong>Boss スレッド</strong>：クライアント接続の受け入れを担当</li>
<li class=""><strong>Worker スレッド</strong>：ネットワークの読み書きと業務ロジックの処理を担当</li>
</ul>
<p>この設計によって、Netty は <strong>少数のスレッドで数千数万の接続を処理</strong> できます。これこそが Netty が高性能なネットワーク通信を実現できる核心的な理由です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>このモデルを学び、使う際に、事前に知っておく価値のあるポイントがいくつかあります：</p>
<p>1）<strong>Worker スレッドの中で時間のかかる処理をしないこと</strong>。Reactor モデルの前提はイベント処理が十分に速いことです。1 つの Worker スレッドは通常複数の接続を担当するため、Handler の中で遅いクエリや同期的なリモート呼び出しを行うと、そのスレッド上の他の接続まで巻き添えで止まってしまいます。時間のかかる業務処理は独立した業務用スレッドプールに投げるべきです。</p>
<p>2）<strong>ネイティブの NIO API を直接使うと非常に間違えやすい</strong>。Buffer の読み書き切り替えや Selector のイベント処理の細部には落とし穴が多く、実際のプロジェクトのほとんどが生の NIO ではなく Netty を選ぶ理由もここにあります。Netty はこれらの複雑さをカプセル化して隠してくれるのです。</p>
<p>3）<strong>NIO が速いのは「スレッドを節約できる」からであり、単発の IO が速いからではない</strong>。接続数が少ない場面では BIO のほうが劣るとは限りません。NIO/Reactor の優位性は大量の接続が並存するときに発揮されます。技術選定は場面を見て判断すべきです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/14/netty-shen-ru-xue-xi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>この進化の本筋を振り返ってみましょう：</p>
<ul>
<li class="">BIO の問題は「1 接続 1 スレッド」で、スレッドがアイドル状態の接続に無駄に占有されること；</li>
<li class="">NIO は Channel + Buffer + Selector で IO 多重化を実現し、少数のスレッドが準備完了イベントだけを処理できるようにした；</li>
<li class="">Reactor モデルはその上に、イベントの監視・分配・処理という責務の分担を定義した；</li>
<li class="">Netty の BossGroup + WorkerGroup は、マスター・スレーブ Reactor のエンジニアリング的な実装である。</li>
</ul>
<p>この流れを理解してから Netty の EventLoop、Pipeline、ByteBuf といった具体的なコンポーネントを見れば、それらが何もないところから湧いて出た設計だとは感じなくなるはずです。Netty のコアコンポーネントと実践的な使い方については、また別の記事で展開していきます。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Redis 分散ロック]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[ローカルロックから分散ロックへ: Redis のアトミックなコマンドでロック取得と安全な解放を実装し、ロックの有効期限、WatchDog による延長、マスター/スレーブ切り替え時の一貫性リスク、そして etcd/ZooKeeper とのトレードオフを考えます。]]></description>
            <content:encoded><![CDATA[<p>分散システムに触れ始めたばかりの頃、多くの人がある問題に突き当たります。<br>
<strong>複数のサービスインスタンスが同時に同じ処理を行うとき、データの二重処理をどう防ぐか？</strong></p>
<!-- -->
<p>たとえば次のようなケースです。</p>
<ul>
<li class="">ユーザーによるクーポンの争奪</li>
<li class="">定期タスクの実行</li>
<li class="">在庫の引き当て</li>
<li class="">注文ステータスの更新</li>
</ul>
<p>システムにプロセスが 1 つしかなければ話は簡単で、<strong>ローカルロック（mutex）</strong> で解決できます。しかしマイクロサービスアーキテクチャやクラスタ構成になると、問題は変わります。システムには 10 個のサービスインスタンス、100 個の Worker、さらには複数のデータセンターが存在するかもしれません。こうなるとローカルロックは完全に無力です。異なるプロセス同士は、お互いのロック状態をまったく知り得ないからです。</p>
<p>そこで登場したのが <strong>分散ロック（Distributed Lock）</strong> という概念です。その目標はシンプルです。</p>
<blockquote>
<p>分散環境において、ある瞬間に 1 つのノードだけがあるロジックを実行できることを保証する。</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-redis-で分散ロックを実現できるのか">なぜ Redis で分散ロックを実現できるのか？<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#%E3%81%AA%E3%81%9C-redis-%E3%81%A7%E5%88%86%E6%95%A3%E3%83%AD%E3%83%83%E3%82%AF%E3%82%92%E5%AE%9F%E7%8F%BE%E3%81%A7%E3%81%8D%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ Redis で分散ロックを実現できるのか？ への直接リンク" title="なぜ Redis で分散ロックを実現できるのか？ への直接リンク" translate="no">​</a></h2>
<p>分散ロックを実装しようとするとき、多くの人が最初に思いつくのはデータベースです。たとえば:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- データベースの行ロックを利用して相互排他を実現する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">select</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">.</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">.</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">.</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">for</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">update</span><br></div></code></pre></div></div>
<p>しかしデータベースロックの問題は、性能が悪く、ロックの粒度が大きく、高並行時に負荷が大きいことです。そこで、ロックにより適したシステムが求められるようになり、Redis が非常に適任でした。Redis には生まれつき 3 つの強みがあるからです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1シングルスレッドモデル">1）シングルスレッドモデル<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#1%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%AB%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%A2%E3%83%87%E3%83%AB" class="hash-link" aria-label="1）シングルスレッドモデル への直接リンク" title="1）シングルスレッドモデル への直接リンク" translate="no">​</a></h3>
<p>Redis のコアコマンドはシングルスレッドで実行され、同時に実行されるコマンドは常に 1 つだけです。そのため、次のような操作は:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># NX: key が存在しないときだけ書き込む。天然の相互排他</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">SET key value NX</span><br></div></code></pre></div></div>
<p><strong>絶対的なアトミック操作</strong> であり、レースコンディションは発生しません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2メモリ操作で非常に高速">2）メモリ操作で、非常に高速<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#2%E3%83%A1%E3%83%A2%E3%83%AA%E6%93%8D%E4%BD%9C%E3%81%A7%E9%9D%9E%E5%B8%B8%E3%81%AB%E9%AB%98%E9%80%9F" class="hash-link" aria-label="2）メモリ操作で、非常に高速 への直接リンク" title="2）メモリ操作で、非常に高速 への直接リンク" translate="no">​</a></h3>
<p>Redis のデータはすべてメモリ上にあり、1 回のロック操作は通常 SET が 1 回、DEL が 1 回で済みます。遅延は通常 <strong>数十マイクロ秒のレベル</strong> で、データベースよりはるかに高速です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3自動的な有効期限をサポート">3）自動的な有効期限をサポート<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#3%E8%87%AA%E5%8B%95%E7%9A%84%E3%81%AA%E6%9C%89%E5%8A%B9%E6%9C%9F%E9%99%90%E3%82%92%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88" class="hash-link" aria-label="3）自動的な有効期限をサポート への直接リンク" title="3）自動的な有効期限をサポート への直接リンク" translate="no">​</a></h3>
<p>Redis の key には TTL を設定できます。たとえば:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># NX: 存在しないときだけ作成。EX 10: 10 秒で自動的に期限切れ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">SET lock:order </span><span class="token number" style="color:hsl(29, 54%, 61%)">123</span><span class="token plain"> NX EX </span><span class="token number" style="color:hsl(29, 54%, 61%)">10</span><br></div></code></pre></div></div>
<p>こうしておけば、たとえサービスがクラッシュしても、ロックが永遠に残り続けることはありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-ロックの解放">Redis ロックの解放<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#redis-%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E8%A7%A3%E6%94%BE" class="hash-link" aria-label="Redis ロックの解放 への直接リンク" title="Redis ロックの解放 への直接リンク" translate="no">​</a></h2>
<p>ロックを解放するとき、直接 DEL を実行してはいけません。次のような状況が起こり得るからです。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">スレッド A がロックを取得</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">スレッド A の実行がタイムアウト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ロックが期限切れ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">スレッド B がロックを取得</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">スレッド A が実行を終えてロックを削除</span><br></div></code></pre></div></div>
<p>このとき、スレッド A はスレッド B のロックを誤って削除してしまいます。正しいやり方は、<strong>自分が保持しているロックだけを削除する</strong> ことで、通常は Lua スクリプトで実現します。</p>
<div class="language-lua codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lua codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">-- まずロックの値（一意な識別子）を照合し、一致した場合のみ削除して、自分のロックだけを解放することを保証する</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">if redis.call("GET",KEYS[1]) == ARGV[1] then</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    return redis.call("DEL",KEYS[1])</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">else</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    return 0</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Lua スクリプトは Redis の中で全体としてアトミックに実行され、GET と DEL の間に他のコマンドが割り込むことはありません。これによって初めて安全な解放が保証されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-分散ロックの古典的な問題">Redis 分散ロックの古典的な問題<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#redis-%E5%88%86%E6%95%A3%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E5%8F%A4%E5%85%B8%E7%9A%84%E3%81%AA%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="Redis 分散ロックの古典的な問題 への直接リンク" title="Redis 分散ロックの古典的な問題 への直接リンク" translate="no">​</a></h2>
<p>Redis のロックはシンプルですが、実際のシステムでは多くの落とし穴に遭遇します。最もよくあるのは次の 2 つです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1ロックの期限切れ問題">1）ロックの期限切れ問題<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#1%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E6%9C%9F%E9%99%90%E5%88%87%E3%82%8C%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="1）ロックの期限切れ問題 への直接リンク" title="1）ロックの期限切れ問題 への直接リンク" translate="no">​</a></h3>
<p>ビジネスの実行時間が TTL を超えると、ロックは先に解放されてしまい、他のノードが同時に処理へ進んでしまう可能性があります。解決策は <strong>自動延長（WatchDog）</strong> です。たとえば:</p>
<ul>
<li class="">ロックの TTL を 10 秒に設定する</li>
<li class="">残り有効期間が最後の 30% に入ったら自動的に 1 回延長し、さらにランダムなジッターを加えて、大量の延長リクエストが同じ瞬間に Redis へ集中するのを避ける</li>
</ul>
<p>タスクが実行中である限り、ロックが期限切れになることはありません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2redis-の単一障害点問題">2）Redis の単一障害点問題<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#2redis-%E3%81%AE%E5%8D%98%E4%B8%80%E9%9A%9C%E5%AE%B3%E7%82%B9%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="2）Redis の単一障害点問題 への直接リンク" title="2）Redis の単一障害点問題 への直接リンク" translate="no">​</a></h3>
<p>Redis がダウンすると、すべてのロックが無効になります。よくある解決策は次のとおりです。</p>
<ul>
<li class="">Redis Sentinel</li>
<li class="">Redis Cluster</li>
<li class="">RedLock アルゴリズム</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-の-ap-設計と-etcdzookeeper-の-cp-アーキテクチャの違い">Redis の AP 設計と etcd・ZooKeeper の CP アーキテクチャの違い<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#redis-%E3%81%AE-ap-%E8%A8%AD%E8%A8%88%E3%81%A8-etcdzookeeper-%E3%81%AE-cp-%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E9%81%95%E3%81%84" class="hash-link" aria-label="Redis の AP 設計と etcd・ZooKeeper の CP アーキテクチャの違い への直接リンク" title="Redis の AP 設計と etcd・ZooKeeper の CP アーキテクチャの違い への直接リンク" translate="no">​</a></h2>
<p>Redis 自体は <strong>AP（可用性優先）</strong> 寄りのシステムです。ネットワーク分断やノード障害が起きたとき、Redis はデータの絶対的な一貫性よりもサービスの可用性を優先します。これが 1 つのリスクをもたらす可能性があります——極端なケースでは、<strong>2 つのクライアントが同時に「自分がロックを取った」と考える状況が起こり得る</strong> のです。</p>
<p>たとえば:</p>
<p>クライアント A がマスターノードでロックを取得した<br>
マスターノードがスレーブノードへ同期する前に<br>
マスターノードが突然ダウンした<br>
スレーブノードが新しいマスターノードに昇格した</p>
<p>このとき、新しいマスターノードは A がすでにロックを取得していたことを知らないため、クライアント B が再びロックを取得でき、2 つのクライアントが同時にロックを保持する状況が発生します。これが Redis 分散ロックの理論上の一貫性リスクです。</p>
<p>これに対して、<strong>etcd や ZooKeeper</strong> のようなシステムは <strong>CP モデル（強一貫性）</strong> に基づいて設計されており、内部で <strong>Raft や ZAB プロトコル</strong> によって過半数ノードの確認を経て初めてデータをコミットします。そのためロックの取得が成功すれば、クラスタ全体で合意が形成され、複数のクライアントが同時にロックを取得する状況は発生しません。</p>
<p>そこでエンジニアリングの実践では、一般に次のような経験則があります。<strong>ビジネスレベルのロック（在庫、タスク制御など）</strong> には Redis 分散ロックで通常十分ですが、<strong>強一貫性の要求が非常に高いシーン</strong>——金融取引、グローバルスケジューリングシステム、分散協調サービス——では、多くのシステムが etcd / ZooKeeper のような強一貫性のロック実装を選択します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/14/redisfen-bu-shi-tan-suo#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Redis 分散ロックの核心となる考え方は非常にシンプルです。<strong>Redis のアトミック操作を利用して相互排他を実現する</strong> こと。そこには 3 つの重要なポイントがあります。アトミックなロック取得、安全な解放、そして自動的な期限切れまたは延長です。実際のエンジニアリングでは、成熟した Redis ロックには通常さらにリトライ機構、WatchDog による自動延長、Lua による安全な解放、ロックの一意な識別子が加わります。同時に、Redis が AP 寄りであるという位置づけも理解しておくべきです——ビジネスレベルの相互排他には十分ですが、強一貫性が必要なシーンでは etcd / ZooKeeper を検討しましょう。これらの設計が組み合わさって初めて、本当に安定した信頼できる分散ロックシステムになるのです。</p>]]></content:encoded>
            <category>Redis</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[アルゴリズム：基礎知識]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi</guid>
            <pubDate>Sat, 14 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[「アルゴリズムとは何か」から始めて、アルゴリズムとデータ構造の関係、時間計算量の意味、そしてアルゴリズム学習では問題を解いた数ではなくモデリングの考え方に注目すべきであることを語ります。]]></description>
            <content:encoded><![CDATA[<p>コードを書き始めた頃、私はずっと、アルゴリズムは競技プログラミングの選手にだけ必要なもので、普段のビジネス開発では使わないと思っていました。しかし後になって、そうではないと気づきました。API が遅い、リストがカクつく、データ量が増えるとタイムアウトする——こうした日常の問題の背後には、往々にしてアルゴリズムの問題があるのです。この記事では、まず最も基礎的な概念をひととおり整理します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アルゴリズムとは何か">アルゴリズムとは何か？<a href="https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi#%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B" class="hash-link" aria-label="アルゴリズムとは何か？ への直接リンク" title="アルゴリズムとは何か？ への直接リンク" translate="no">​</a></h2>
<p>アルゴリズムとは、本質的には <strong>問題を解決するための一連の手順</strong> です。</p>
<p>次のようなものであれば：</p>
<p>一組のデータを入力し<br>
一定のルールに従って処理し<br>
結果を得る</p>
<p>この一連の処理プロセス全体が、アルゴリズムです。</p>
<p>必ずしも高度なものである必要はありません。アルファベット順に辞書を引くのもアルゴリズムですし、レシピもアルゴリズムです——どちらも明確で、繰り返し実行可能な一連の手順です。プログラムを書くときの違いはただひとつ。この手順をコンピュータに実行させるので、各ステップを十分に正確に書かなければならず、曖昧な部分があってはいけない、という点です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アルゴリズムの理解">アルゴリズムの理解<a href="https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi#%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%AE%E7%90%86%E8%A7%A3" class="hash-link" aria-label="アルゴリズムの理解 への直接リンク" title="アルゴリズムの理解 への直接リンク" translate="no">​</a></h2>
<p>なぜプログラマーはアルゴリズムを気にする必要があるのでしょうか？同じ問題でも、アルゴリズムが違えば効率が大きく変わりうるからです。あるやり方なら数秒で計算できるのに、別のやり方だと数分、あるいはもっと長くかかるかもしれません。データ量が大きくなると、この差はますます顕著になります。多くのシステムの性能の良し悪しは、実はアルゴリズム設計と大いに関係があるのです。</p>
<p>この差は通常「時間計算量」で記述されます。いわゆるビッグ O 記法です。これが問題にするのは、ある実行に何ミリ秒かかったかではなく、データ量が増えたときに演算量がどんな速度で増えるかです。線形スキャンは O(n) で、データが倍になれば作業量も倍になります。二分探索は O(log n) で、データが倍になっても探索は 1 回増えるだけです。一方、二重ネストのループの O(n²) は、データが倍になると作業量は 4 倍になります。データ量が小さいうちはどれも速いのですが、10 万、100 万のレベルになると、増加曲線の違いはそのまま「使える」と「使えない」の違いになります。</p>
<p>アルゴリズムは通常、単独では存在せず、データ構造と一緒に使われることがほとんどです。データ構造はデータをうまく組織することを担当します。配列、連結リスト、木、ハッシュテーブルなどです。一方、アルゴリズムはそれらのデータへの操作を担当します。簡単に言えば、片方がデータの保存を、もう片方がデータの処理を受け持ち、両者が連携して初めてプログラムは効率的に動作します。</p>
<p>この 2 つの選択は互いに影響し合います。同じ「ある要素が存在するか調べる」でも、配列に入っていれば先頭から末尾までスキャンする必要がありますが、ハッシュテーブルに入っていればほぼ一発で特定できます。同じ「中間への頻繁な挿入」でも、配列は要素を丸ごとずらす必要がありますが、連結リストはポインタを付け替えるだけです。ですから多くの場合、より適切なデータ構造に替えれば、アルゴリズムは自然と速くなります——問題をどうモデリングするかが、その後どんなアルゴリズムを使えるかを決めるのです。</p>
<p>現実のシステムでは、アルゴリズムは実のところ至るところにあります。例えば検索エンジンはアルゴリズムに基づいて Web ページをランキングし、ショート動画プラットフォームはアルゴリズムでコンテンツをレコメンドし、ナビゲーションソフトはアルゴリズムで最短ルートを計算し、EC プラットフォームもアルゴリズムで商品のレコメンドとランキングを行っています。私たちが毎日使っている多くのソフトウェアの背後では、実はさまざまなアルゴリズムが働いているのです。</p>
<p>アルゴリズムを学ぶのは、問題を何問解いたかのためではなく、問題解決の考え方を養うためです。ある問題に出会ったとき、素早くいくつかの解決方法を思いつき、その中からより効率の高いものを選べること。これこそがアルゴリズムの本当の価値です。経験豊富なエンジニアの多くは、実のところ問題解決の方法を絶えず最適化し続けている人たちなのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アルゴリズム学習では何に注目すべきか">アルゴリズム学習では何に注目すべきか？<a href="https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi#%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E5%AD%A6%E7%BF%92%E3%81%A7%E3%81%AF%E4%BD%95%E3%81%AB%E6%B3%A8%E7%9B%AE%E3%81%99%E3%81%B9%E3%81%8D%E3%81%8B" class="hash-link" aria-label="アルゴリズム学習では何に注目すべきか？ への直接リンク" title="アルゴリズム学習では何に注目すべきか？ への直接リンク" translate="no">​</a></h2>
<p>アルゴリズムを学ぶとき、多くの人は問題演習の量に陥りがちです。</p>
<p>しかし実際にはより重要なのは、次のことを理解することです：</p>
<ol>
<li class="">問題をどうモデリングするか</li>
</ol>
<p>曖昧な現実の要求を、明確な入力、出力、制約条件に翻訳すること。モデリングが正しくできれば、問題は往々にして半分解決したようなものです。モデリングを間違えれば、その後どれだけ速く書いても無駄になります。</p>
<ol start="2">
<li class="">データ構造をどう設計するか</li>
</ol>
<p>アクセスパターンに応じて構造を選びます。検索が多ければハッシュテーブル、順序付き走査が必要なら木を検討し、先入れ先出しならキューを使う。構造の選択が正しければ、コードは自然とシンプルになります。</p>
<ol start="3">
<li class="">時間計算量をどう最適化するか</li>
</ol>
<p>まず現在のやり方が目標のデータ量に耐えられるかを見積もり、それから最適化すべきかを考えます。すべてのコードが最適化に値するわけではありませんが、ボトルネックがどこにあるかは知っておくべきです。</p>
<ol start="4">
<li class="">アルゴリズムの考え方をどう発展させるか</li>
</ol>
<p>力任せの解法（ブルートフォース）から出発し、どこで重複計算をしているかを観察し、一歩ずつより良い解法へ改善していく。この導出のプロセスは、最適解を丸暗記するよりはるかに価値があります。</p>
<p><strong>本当のアルゴリズム能力とは、何問覚えたかではありません。問題を見て、素早く解決の道筋を思いつけることです。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">最初から最適解を追い求めないこと。まず動くブルートフォース解を書いて、問題を理解できたことを確認してから、最適化を語りましょう。実際のデータ量ではブルートフォース解で十分なことも多いのです。</li>
<li class="">計算量の分析はデータ量とセットで考えること。n が数百程度のシナリオでは、O(n²) と O(n log n) の違いは無視できます。技巧を見せびらかすためにシンプルなコードを複雑にしてはいけません。</li>
<li class="">問題演習で結論だけ覚えて過程を導出しないと、2 週間もすれば忘れます。解説を閉じて自分でもう一度導出し直して、初めて本当に身についたと言えます。</li>
<li class="">空間計算量を軽視しないこと。空間と時間のトレードオフはよく使う手段ですが、キャッシュもハッシュテーブルもメモリを食います。コストがどこにあるかを把握しておきましょう。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/14/suan-fa-ji-chu-zhi-shi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>アルゴリズムとは問題を解決する手順であり、データ構造はデータの組織を担当し、両者の連携がプログラムの効率を決めます。アルゴリズムの良し悪しを測る核心的なツールは計算量の分析で、それが記述するのはデータ量が増えたときの演算量の増加傾向です。アルゴリズム学習の重点は問題数ではなく、モデリング、構造の選択、考え方の導出という数点にあります——これらを鍛え上げれば、新しい問題に出会ったとき、どこから手をつければよいか自然とわかるようになります。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[AIの発展史]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi</guid>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[AI はここ数年で突然現れたものではなく、すでに 70 年以上の歴史を歩んできました。チューリングテストからディープラーニング、そして大規模モデルの時代まで、AI の 3 つの発展段階と現在の実用化状況、今後の方向性を整理します。]]></description>
            <content:encoded><![CDATA[<p>多くの人は AI がここ数年で突然現れたように感じていますが、実はそうではありません。AI の発展はすでに <strong>70 年以上</strong> の道のりを歩んでおり、大きく 3 つの段階に分けて理解できます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第一段階ai-という概念の誕生19501980">第一段階：AI という概念の誕生（1950—1980）<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#%E7%AC%AC%E4%B8%80%E6%AE%B5%E9%9A%8Eai-%E3%81%A8%E3%81%84%E3%81%86%E6%A6%82%E5%BF%B5%E3%81%AE%E8%AA%95%E7%94%9F19501980" class="hash-link" aria-label="第一段階：AI という概念の誕生（1950—1980） への直接リンク" title="第一段階：AI という概念の誕生（1950—1980） への直接リンク" translate="no">​</a></h2>
<p>1950 年、イギリスの科学者 <strong>チューリング（Alan Turing）</strong> はある問いを投げかけました。</p>
<blockquote>
<p>「機械は人間のように考えることができるのか？」</p>
</blockquote>
<p>そこで彼は有名なテスト（<strong>チューリングテスト</strong>）を提案しました。簡単に言えば、人間と機械がチャットをして、どちらが機械か区別できなければ、その機械は「知能を持っている」と見なせる、というものです。</p>
<p>1956 年、アメリカで開かれた会議において、初めて正式に次の言葉が提唱されました。</p>
<blockquote>
<p>Artificial Intelligence（人工知能）</p>
</blockquote>
<p>その時から、AI は正式な研究分野になりました。ただし当時のコンピュータは非力で、データも少なかったため、AI は主に理論研究の段階にとどまっていました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第二段階ai-が少し役に立ち始める19802010">第二段階：AI が少し役に立ち始める（1980—2010）<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#%E7%AC%AC%E4%BA%8C%E6%AE%B5%E9%9A%8Eai-%E3%81%8C%E5%B0%91%E3%81%97%E5%BD%B9%E3%81%AB%E7%AB%8B%E3%81%A1%E5%A7%8B%E3%82%81%E3%82%8B19802010" class="hash-link" aria-label="第二段階：AI が少し役に立ち始める（1980—2010） への直接リンク" title="第二段階：AI が少し役に立ち始める（1980—2010） への直接リンク" translate="no">​</a></h2>
<p>コンピュータの性能向上に伴い、いくつかの AI 技術が実際に実用化され始めました。例えば：</p>
<ul>
<li class="">音声認識</li>
<li class="">機械翻訳</li>
<li class="">レコメンドシステム</li>
</ul>
<p>多くのインターネットプロダクトは、実はずっと前から AI を使っています。例えば：</p>
<ul>
<li class="">タオバオの商品レコメンド</li>
<li class="">Google の検索ランキング</li>
<li class="">Netflix の映画レコメンド</li>
</ul>
<p>しかし当時の AI はまだ「賢い」とは言えず、基本的に <strong>単一タスク</strong> しかこなせませんでした。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="第三段階大規模モデルの時代2010現在">第三段階：大規模モデルの時代（2010—現在）<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#%E7%AC%AC%E4%B8%89%E6%AE%B5%E9%9A%8E%E5%A4%A7%E8%A6%8F%E6%A8%A1%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E6%99%82%E4%BB%A32010%E7%8F%BE%E5%9C%A8" class="hash-link" aria-label="第三段階：大規模モデルの時代（2010—現在） への直接リンク" title="第三段階：大規模モデルの時代（2010—現在） への直接リンク" translate="no">​</a></h2>
<p>AI を本当に爆発的に発展させたのは、次の 3 つの要因です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-計算能力の向上">1) 計算能力の向上<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#1-%E8%A8%88%E7%AE%97%E8%83%BD%E5%8A%9B%E3%81%AE%E5%90%91%E4%B8%8A" class="hash-link" aria-label="1) 計算能力の向上 への直接リンク" title="1) 計算能力の向上 への直接リンク" translate="no">​</a></h3>
<p>GPU の計算能力が急激に伸びました。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-データの増加">2) データの増加<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#2-%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E5%A2%97%E5%8A%A0" class="hash-link" aria-label="2) データの増加 への直接リンク" title="2) データの増加 への直接リンク" translate="no">​</a></h3>
<p>インターネットが膨大なデータを生み出しました。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-アルゴリズムのブレイクスルー">3) アルゴリズムのブレイクスルー<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#3-%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%AE%E3%83%96%E3%83%AC%E3%82%A4%E3%82%AF%E3%82%B9%E3%83%AB%E3%83%BC" class="hash-link" aria-label="3) アルゴリズムのブレイクスルー への直接リンク" title="3) アルゴリズムのブレイクスルー への直接リンク" translate="no">​</a></h3>
<p>特に <strong>ディープラーニング + Transformer アーキテクチャ</strong> です。</p>
<p>2017 年、Google が 1 本の論文を発表しました。</p>
<blockquote>
<p>Attention Is All You Need</p>
</blockquote>
<p>この論文が、今日の大規模モデルを直接生み出しました。そして一群の AI プロダクトが登場します。</p>
<ul>
<li class="">ChatGPT</li>
<li class="">Claude</li>
<li class="">Gemini</li>
<li class="">DeepSeek</li>
</ul>
<p>AI は突然「ツール」から、対話・執筆・プログラミング・絵描きができるインテリジェントなアシスタントへと変わったのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ai-は今どのレベルまで発展しているのか">AI は今どのレベルまで発展しているのか？<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#ai-%E3%81%AF%E4%BB%8A%E3%81%A9%E3%81%AE%E3%83%AC%E3%83%99%E3%83%AB%E3%81%BE%E3%81%A7%E7%99%BA%E5%B1%95%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="AI は今どのレベルまで発展しているのか？ への直接リンク" title="AI は今どのレベルまで発展しているのか？ への直接リンク" translate="no">​</a></h2>
<p>10 年前であれば、多くの人は AI を単なる実験室の技術、あるいはちょっと面白い小道具程度に考えていたかもしれません。しかし今や状況は完全に変わりました。今日の AI は「目新しいおもちゃ」から本物の <strong>生産性ツール</strong> へと変わり、ますます多くの業界で力を発揮しています。</p>
<p>最も顕著な変化は <strong>ソフトウェア開発分野</strong> で起きています。今では多くのプログラマーが日常業務で Cursor、Copilot、ChatGPT、Claude などのツールを使って AI にコーディングを補助してもらっています。AI は開発者の代わりに関数を書いたりスクリプトを生成したりできるだけでなく、バグの調査やコードロジックの説明も手伝い、さらには要件から完全なプログラムの骨組みを素早く生成することもできます。多くのチームでは、AI 導入後に開発効率が概ね <strong>30%〜50%</strong> 向上しています。</p>
<p><strong>コンテンツ制作分野</strong> でも AI の活用は非常に広がっています。今の AI はニュース記事、小説、広告コピー、サマリーレポートを書くことができ、多くのメディア運営者も記事執筆の補助、素材整理、アイデア出しに AI を使っています。最終的なコンテンツには人手による修正が必要なことが多いものの、AI はすでに基礎作業の大部分をこなせるようになり、制作効率を大きく高めています。</p>
<p><strong>エンタープライズサービス分野</strong> でも、AI はカスタマーサポートのような反復的な業務を大量に代替し始めています。多くの EC プラットフォーム、銀行、通信キャリアはすでに AI カスタマーサポートシステムを使ってよくある問い合わせに対応しています。従来の人力サポートと比べ、AI は <strong>24 時間オンラインで応答</strong> でき、大量のユーザーリクエストを同時に処理し、企業の人件費を大幅に削減します。</p>
<p>また、<strong>ビジュアルコンテンツ制作の面</strong> でも AI の能力は急速に進歩しています。Midjourney や DALL·E のようなツールはテキストから高品質な画像を生成でき、Sora や Runway などの動画生成技術も次第に成熟しつつあります。今ではショート動画や広告デザイン、さらには映像制作の現場でも、画面・キャラクター・アニメーションの生成補助に AI が使われ始めています。</p>
<p><strong>データ分析分野</strong> でも、AI はますます重要な役割を果たしています。多くの企業がすでに AI を使ってデータレポートを自動分析し、BI レポートを生成し、さらには業務トレンドの判断や意思決定の提案まで行わせています。いわゆる「AI データ分析アシスタント」を導入し、経営層がデータをより速く理解して意思決定できるよう支援している企業もあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ai-はすでにどの業界で実用化されているのか">AI はすでにどの業界で実用化されているのか？<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#ai-%E3%81%AF%E3%81%99%E3%81%A7%E3%81%AB%E3%81%A9%E3%81%AE%E6%A5%AD%E7%95%8C%E3%81%A7%E5%AE%9F%E7%94%A8%E5%8C%96%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="AI はすでにどの業界で実用化されているのか？ への直接リンク" title="AI はすでにどの業界で実用化されているのか？ への直接リンク" translate="no">​</a></h2>
<p>実は AI はすでに多くの業界にひっそりと入り込んでいます。TikTok（抖音）、タオバオ、bilibili などインターネットプラットフォームのコンテンツレコメンドは、本質的には AI があなたの見るコンテンツを決めています。金融業界では AI をリスク管理、不正検知、投資分析に使っており、多くの銀行の基幹システムでは AI モデルが稼働しています。医療分野では、AI はすでに医師の疾患診断や、X 線写真や CT などの医用画像の識別を補助でき、場面によっては人間よりも速く正確なこともあります。製造業では、AI は工場の自動化、品質検査、設備の予知保全に使われ、工場をますますスマートにしています。IT 業界でも AI は運用の領域に入り始めており、システムの自動監視、ログ分析、自動トラブルシューティング、オートスケーリングなどが挙げられます。将来的には、エンジニアが複雑なシステムを管理するのを助ける「AI 運用アシスタント」が登場するかもしれません。AI はすでに実験室の技術から、多くの業界を支える基盤ツールへと変わったと言えるでしょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ai-は人類にとって何を意味するのか">AI は人類にとって何を意味するのか？<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#ai-%E3%81%AF%E4%BA%BA%E9%A1%9E%E3%81%AB%E3%81%A8%E3%81%A3%E3%81%A6%E4%BD%95%E3%82%92%E6%84%8F%E5%91%B3%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="AI は人類にとって何を意味するのか？ への直接リンク" title="AI は人類にとって何を意味するのか？ への直接リンク" translate="no">​</a></h2>
<p>AI が人間に取って代わるのではないかと心配する人は多いですが、より現実的なのはこうです。<strong>AI は人間を置き換えるためではなく、人間の能力を増幅するために来たのです。</strong></p>
<p>技術の発展史を振り返ると、人類は常にツールで自らの能力を増幅してきました。蒸気機関は人類に自分自身をはるかに超える筋力を与え、コンピュータは強力な計算能力を与え、インターネットは情報伝達の速度から距離の制約をほぼ取り除きました。そして AI が増幅するのは、人類の <strong>知的労働の能力</strong> です。</p>
<p>かつてはチームでなければできなかった多くのことが、今では 1 人 + AI で実現できます。例えばコーディング、データ分析、コンテンツ制作、デザイン、運用施策の立案などです。AI はアイデアの素早い生成、情報の整理、大量の基礎作業を人間の代わりにこなし、人間は方向性の判断、意思決定、結果のコントロールを担います。</p>
<p>言い換えれば、将来の多くの仕事のスタイルはこうなるかもしれません。</p>
<p><strong>1 人 + AI は、かつての小さなチーム 1 つに相当する。</strong></p>
<p>したがって、より長期的な視点で見ると、AI が本当に変えるのは「人が失業するかどうか」ではなく、<strong>働き方そのものが変化する</strong> ということです。反復的で標準化された仕事はますます AI がこなすようになり、人間がより多く担うべきなのは創造、判断、意思決定、そしてイノベーションです。</p>
<p>AI は人類の敵ではなく、むしろ新しいツールのようなものだと言えます。かつてコンピュータとインターネットが世界を変えたように、AI は新しい基盤能力となりつつあり、あらゆる業界に少しずつ溶け込んでいます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="将来-ai-はどう発展していくのか">将来 AI はどう発展していくのか？<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#%E5%B0%86%E6%9D%A5-ai-%E3%81%AF%E3%81%A9%E3%81%86%E7%99%BA%E5%B1%95%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E3%81%AE%E3%81%8B" class="hash-link" aria-label="将来 AI はどう発展していくのか？ への直接リンク" title="将来 AI はどう発展していくのか？ への直接リンク" translate="no">​</a></h2>
<p>現在の発展トレンドから見ると、AI は今後いくつかの方向へ進んでいく可能性が高いでしょう。まず「チャットツール」から、<strong>自分でタスクを完遂できるインテリジェントエージェント（AI Agent）</strong> へと徐々に変わっていきます。例えばコードの自動作成、データの自動分析、システムの自動運用などで、人は目標を与えるだけで、AI がステップを踏んで物事を完成させます。次に、AI はますます現実世界に入り込み、ロボット、自動運転、スマートファクトリーなどと結びつき、機械が「考える」だけでなく「行動する」ようになります。さらに、AI は次第に一種の <strong>基盤能力</strong> となり、インターネットや電力のようにあらゆるソフトウェアやシステムに溶け込み、将来はほぼすべてのアプリケーションが AI 機能を備えるようになるでしょう。簡単に言えば、将来の AI は単なるツールではなく、人類の「インテリジェントアシスタント」であり「生産性の増幅器」として、個人や企業がより少ない人手でより多くのことを成し遂げるのを助ける存在になっていくはずです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ai-の現在の資金投入とコスト状況">AI の現在の資金投入とコスト状況<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#ai-%E3%81%AE%E7%8F%BE%E5%9C%A8%E3%81%AE%E8%B3%87%E9%87%91%E6%8A%95%E5%85%A5%E3%81%A8%E3%82%B3%E3%82%B9%E3%83%88%E7%8A%B6%E6%B3%81" class="hash-link" aria-label="AI の現在の資金投入とコスト状況 への直接リンク" title="AI の現在の資金投入とコスト状況 への直接リンク" translate="no">​</a></h2>
<p>グローバルに見ると、AI はすでに資本投入が最も大きいテクノロジー分野の一つとなっており、各国政府、テクノロジー企業、投資機関が継続的に投資を拡大しています。スタンフォード大学が発表した『AI Index 2025』レポートによると、<strong>2024 年の世界の企業による AI 分野への投資規模は約 2,523 億ドルに達し</strong>、過去最高を記録しました。</p>
<p>その中でもアメリカは依然として AI 投資が最も強い国であり、<strong>2024 年のアメリカの民間 AI 投資は 1,091 億ドルに達し</strong>、中国（約 93 億ドル）の約 12 倍、イギリス（45 億ドル）の約 24 倍です。</p>
<p>AI 投資の構成では、<strong>生成 AI（大規模モデルなど）が最も成長の速い分野</strong> であり、2024 年の世界の生成 AI への民間投資は <strong>339 億ドル</strong> に達し、前年比約 18.7% 増となりました。</p>
<p>同時に、企業における AI の利用率も急速に伸びています。データによると、<strong>2024 年には約 78% の企業がすでに業務で AI 技術を使用しており</strong>、前年の 55% から大きく上昇しました。これは AI が「技術的な試み」から企業の日常的なツールへと変わりつつあることを意味します。</p>
<p>市場規模の面でも、AI 産業は高成長の段階にあります。2025 年の世界の AI 市場規模は約 <strong>3,910 億ドル</strong> で、2030 年には <strong>1.8 兆ドル以上</strong> に成長すると予測されており、今後最も成長の速いテクノロジー産業の一つになる見込みです。</p>
<p>簡単に言えば、現在の AI の発展の背後には <strong>大量の資金、計算資源、そして企業の投入による共同の後押し</strong> があります。テックジャイアント、投資機関、各国政府が資源を投入し続け、将来の AI 産業競争で先頭に立とうとしているのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/13/aide-fa-zhan-shi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>AI は 70 年以上の歩みを経て、チューリングの一つの構想から、単一タスクしかできないツールへ、そして今日の対話・コーディング・画像生成ができる大規模モデルへと進化してきました。その背後にあるのは、計算能力・データ・アルゴリズムという 3 本の線の長期的な蓄積であり、ある年に突然爆発したわけではありません。今や AI はソフトウェア開発、コンテンツ制作、金融、医療など数多くの業界に浸透しており、その背後では資金投入も拡大し続けています。個人としては、取って代わられることを心配するより、自分の能力を増幅するツールとして捉え、早めに AI との協働を身につけるほうが賢明でしょう。</p>]]></content:encoded>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[go-lynxの設計思想]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu</guid>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[go-lynx は Kratos をベースにしたプラグイン化マイクロサービス基盤フレームワーク。プラグインのホットスワップ、設定駆動、組み込みのサービスガバナンスと TLS 証明書の自動ローテーションを備え、マイクロサービスアーキテクチャを「ブロック遊び」のように組み立てられることを目指しています。]]></description>
            <content:encoded><![CDATA[<p>github.com/go-lynx の設計目的は、企業がマイクロサービス体系を素早く構築できるようにするための基盤フレームワークを提供することです。リポジトリ全体(組織 go-lynx 配下の 29 個の repo を含む)を分割し、lynx アーキテクチャの基盤部分と、各種 lynx プラグインモジュールに分けています。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://avatars.githubusercontent.com/u/150900434?s=96&amp;v=4" alt="" class="img_ev3q"></p>
<p><a href="https://github.com/go-lynx" target="_blank" rel="noopener noreferrer" class="">https://github.com/go-lynx</a></p>
<p>最大の特徴は <strong>Plug-and-Play</strong>(本当の意味でのすぐ使える体験)で、複雑なマイクロサービスアーキテクチャを「ブロック遊び」のように組み立てられることです。</p>
<p>ゼロから車輪を再発明するのではなく、巨人の肩の上に立っています。</p>
<ul>
<li class="">コアランタイムは <strong>Kratos</strong>(bilibili のオープンソースフレームワーク)を利用</li>
<li class="">サービスディスカバリー/ガバナンスには <strong>Polaris</strong>(Tencent のクラウドネイティブサービスメッシュ)と <strong>Nacos(Alibaba のオープンソース)</strong> を採用</li>
<li class="">分散トランザクションには <strong>Seata、DTM など</strong>を利用</li>
<li class="">その上に、自作の<strong>プラグイン管理システム + イベントバス + コントロールプレーン</strong>を追加し、本当のホットスワップを実現しています。</li>
</ul>
<p><strong>Kratos との比較</strong>:Kratos は「基盤フレームワーク」であり、Lynx は「Kratos のプロダクション強化版 + プラグイン体系 + ゼロコンフィグ」です。Kratos は好きだけれど設定が面倒、プラグインが統一されていないと感じているなら、このフレームワークはまさにそのペインポイントのために生まれたものです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スキャフォールディング">スキャフォールディング<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#%E3%82%B9%E3%82%AD%E3%83%A3%E3%83%95%E3%82%A9%E3%83%BC%E3%83%AB%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0" class="hash-link" aria-label="スキャフォールディング への直接リンク" title="スキャフォールディング への直接リンク" translate="no">​</a></h2>
<p>ゼロコンフィグ + CLI スキャフォールディングにより、コマンド 1 つで完全なプロジェクトを素早く生成できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># lynx コマンドラインツールをインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">go </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> github.com/go-lynx/lynx/cmd/lynx@latest</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 完全なマイクロサービスプロジェクトの骨組みを生成</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">lynx</span><span class="token plain"> new my-service</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装済みのプラグイン体系">実装済みのプラグイン体系<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#%E5%AE%9F%E8%A3%85%E6%B8%88%E3%81%BF%E3%81%AE%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E4%BD%93%E7%B3%BB" class="hash-link" aria-label="実装済みのプラグイン体系 への直接リンク" title="実装済みのプラグイン体系 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">既存のプラグイン:gRPC、HTTP、Redis、PostgreSQL、Redis 分散ロック、Swagger、Tracer、Seata など。</li>
<li class="">プラグイン間は組み込みの <strong>Event Bus</strong> で通信し、プラグイン同士の連携と制御を実現。</li>
<li class="">サービス登録・ディスカバリー + ヘルスチェック + マルチバージョン + ロードバランシング(Polaris)</li>
<li class="">トラフィックガバナンス:レートリミット、サーキットブレーカー、ブルーグリーン/カナリア、fallback</li>
<li class="">セキュリティ:TLS 相互認証、JWT、OAuth2、RBAC/ABAC</li>
<li class="">分散トランザクション:Seata 完全自動化</li>
<li class="">オブザーバビリティ:Prometheus + OpenTelemetry + Zap(JSON ログ)</li>
<li class="">グレースフルシャットダウン、リトライ、デッドレターキュー、リカバリーマネージャー</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="設定駆動">設定駆動<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#%E8%A8%AD%E5%AE%9A%E9%A7%86%E5%8B%95" class="hash-link" aria-label="設定駆動 への直接リンク" title="設定駆動 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">各プラグインは固定の confPrefix(例:<code>lynx.grpc.service</code>、<code>lynx.grpc.client</code>)を持ち、設定は Lynx Runtime の <code>rt.GetConfig().Value(confPrefix).Scan(...)</code> から読み込まれます。</li>
<li class="">Configure(c) によるホットリロードをサポート:サーバー側では <code>confMu</code> で設定ポインタの差し替えを保護し、並行安全性を保証しつつ、再起動なしで一部の機能を更新できます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lynx-プロジェクトの設定例yaml-形式">lynx プロジェクトの設定例(YAML 形式)<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#lynx-%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%81%AE%E8%A8%AD%E5%AE%9A%E4%BE%8Byaml-%E5%BD%A2%E5%BC%8F" class="hash-link" aria-label="lynx プロジェクトの設定例(YAML 形式) への直接リンク" title="lynx プロジェクトの設定例(YAML 形式) への直接リンク" translate="no">​</a></h3>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token key atrule" style="color:hsl(29, 54%, 61%)">lynx</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">polaris</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> default</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># サービス登録・ディスカバリー</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">http</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">addr</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">":8080"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain">           </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># HTTP サービスのリッスンアドレス</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">grpc</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">addr</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">":9090"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain">           </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># gRPC サービスのリッスンアドレス</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># metrics/tracing/logging/tls/rate_limit もすべて既成のテンプレートあり</span><br></div></code></pre></div></div>
<p>全体的には Spring Boot に似ており、使いたいプラグインの分だけ設定を書けばよく、プラグインは完全にすぐ使える状態になっています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プラグイン化アーキテクチャの設計">プラグイン化アーキテクチャの設計<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E5%8C%96%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="プラグイン化アーキテクチャの設計 への直接リンク" title="プラグイン化アーキテクチャの設計 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">統一プラグインインターフェース:プラグインは Lynx の <code>plugins.Plugin</code> インターフェースを実装し、BasePlugin を埋め込むことで ID、名前、バージョン、設定プレフィックス、重みなどの共通機能を獲得します。</li>
<li class="">ファクトリー登録:クライアントプラグインは <code>init()</code> 内で <code>factory.GlobalTypedFactory().RegisterPlugin(clientPluginName, "lynx.grpc.client", ...)</code> によって登録され、フレームワークが設定プレフィックスに基づいて読み込み、ライフサイクルを管理します。</li>
<li class="">ライフサイクル:標準の 3 フェーズ——InitializeResources(設定の読み込み・検証)、StartupTasks(サービス起動・接続)、CleanupTasks(グレースフルシャットダウン・リソース解放)。</li>
<li class="">依存性注入:サーバー側プラグインは <code>SetDependencies</code> を通じてアプリケーション名、Logger、証明書、コントロールプレーンなどが注入され、グローバルシングルトンを避けることで、テストやマルチインスタンス化が容易になります。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="セキュリティモジュールの設計">セキュリティモジュールの設計<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="セキュリティモジュールの設計 への直接リンク" title="セキュリティモジュールの設計 への直接リンク" translate="no">​</a></h2>
<p>内部ネットワークのマイクロサービス間通信のセキュリティを確保するため、Lynx には TLS 証明書の自動管理とローテーション機能が組み込まれています。サービスの稼働中に証明書を更新しても再起動は不要で、新しく確立される接続は自動的に最新の証明書を使用するため、本当の意味でのゼロダウンタイム更新を実現しています。全体の設計としては、統一された証明書プロバイダー(CertificateProvider)で証明書を管理し、内部ネットワークの gRPC サーバーは TLS ハンドシェイク時に現在の証明書を動的に取得します。クライアント側も同じ仕組みで Root CA を取得し、クレデンシャルのリフレッシュをサポートするため、証明書の更新は自動的に反映されます。さらにこの仕組みはファイル監視、クラウド証明書サービス、鍵管理システムとの連携が可能で、証明書更新の自動化とエンタープライズレベルのセキュアな通信を実現し、システムのセキュリティを高めると同時に、運用コストも大幅に削減します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/13/go-lynxshe-ji-si-lu#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>go-lynx のコアとなる考え方は、車輪の再発明ではなく、Kratos の上にプロダクション環境で必要となるレイヤーを補完することです。統一されたプラグイン体系、設定駆動によるすぐ使える体験、そしてイベントバスとコントロールプレーンがもたらすホットスワップ機能。サービスガバナンス、分散トランザクション、オブザーバビリティ、TLS 自動ローテーションといった機能はすべてプラグインとして提供され、使いたいものだけ設定すればよい仕組みです。Kratos を使っていて設定やプラグインの不統一に悩まされたことがあるなら、ぜひ <a href="https://github.com/go-lynx" target="_blank" rel="noopener noreferrer" class="">go-lynx</a> を見て、この設計があなたのペインポイントを解決できるか確かめてみてください。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[OpenClaw 使用記録]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/13/openclaw</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/13/openclaw</guid>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[OpenClaw はオープンソースの AI Agent 自動実行フレームワークで、タスクの自動プランニング、ツール呼び出し、継続的なイテレーションが可能です。この記録では、そのコア能力、明らかな弱点、適したユースケースを整理します。]]></description>
            <content:encoded><![CDATA[<p>OpenClaw は <strong>オープンソースの AI Agent 自動実行フレームワーク</strong> です。
その中核的な目標は、AI を単なる「対話」にとどめず、<strong>タスクを自動でプランニングし、ツールを呼び出し、操作を実行し、継続的にイテレーションして複雑な目標を達成できる</strong> ようにすることです。最近しばらく使ってみて、いくつかの内容をまとめました。</p>
<!-- -->
<p>この記録を書いたきっかけは単純です。チャット型の AI を長く使っていると、その天井は「うまく答えられるか」ではなく「答えた後どうするか」にあると気づきます。コードの提案は出た、コマンドも列挙された、しかし実際に実行し、検証し、間違いを直すのは結局人間です。Agent フレームワークが解決しようとしているのはまさにこのラストワンマイル——「言う」を「やる」に変えることです。OpenClaw は私が最近実際にしばらく動かしてみたものの一つで、使用中の観察と問題を書き留めておきます。自分が後で見返すためでもあり、技術選定中の人への参考でもあります。</p>
<p>簡単に言えば、OpenClaw は次のように理解できます。</p>
<blockquote>
<p><strong>コードを自動で書き、コマンドを実行し、プロジェクトを分析し、タスクを継続的にイテレーションできる AI エンジニアリングアシスタント。</strong></p>
</blockquote>
<p>通常の ChatGPT や Claude と異なり、OpenClaw の設計目標は次のとおりです。</p>
<ul>
<li class="">AI に <strong>タスク実行能力を持たせる</strong></li>
<li class=""><strong>複雑な目標を分解</strong> できる</li>
<li class="">自動的に <strong>ツールを呼び出しコマンドを実行</strong> する</li>
<li class="">タスク完了まで <strong>イテレーションを継続</strong> する</li>
</ul>
<p>したがって、これはむしろ <strong>AI 自動化開発アシスタント（AI Software Engineer）</strong> に近い存在です。</p>
<p><a href="https://openclaw.ai/" target="_blank" rel="noopener noreferrer" class="">OpenClaw — Personal AI Assistant</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="openclaw-のコア能力">OpenClaw のコア能力<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#openclaw-%E3%81%AE%E3%82%B3%E3%82%A2%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="OpenClaw のコア能力 への直接リンク" title="OpenClaw のコア能力 への直接リンク" translate="no">​</a></h2>
<p>OpenClaw の能力は主に <strong>タスクプランニング + 自動実行 + ツール呼び出し</strong> の 3 つの面に現れます。</p>
<p>この 3 つは実は Agent システムの古典的なループに対応しています。モデルはまず目標に基づいて計画を生成し（プランニング）、次にツールを通じて実環境に働きかけ（実行）、そして実行結果——コマンド出力、エラーメッセージ、テスト結果——を観察としてモデルにフィードバックし（フィードバック）、それに基づいて次のアクションを修正します。このループをタスク完了とモデルが判断するまで繰り返します。この「プランニング—実行—観察」のクローズドループを理解すれば、OpenClaw の挙動は予測しやすくなります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-タスクプランニングplanning">1) タスクプランニング（Planning）<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#1-%E3%82%BF%E3%82%B9%E3%82%AF%E3%83%97%E3%83%A9%E3%83%B3%E3%83%8B%E3%83%B3%E3%82%B0planning" class="hash-link" aria-label="1) タスクプランニング（Planning） への直接リンク" title="1) タスクプランニング（Planning） への直接リンク" translate="no">​</a></h3>
<p>ユーザーが OpenClaw に目標を与えると、例えば：</p>
<blockquote>
<p>「このプロジェクトに Redis キャッシュを追加して」</p>
</blockquote>
<p>OpenClaw は自動的にタスクを分解します。例えば：</p>
<ol>
<li class="">プロジェクト構造を分析する</li>
<li class="">データベースアクセスのコードを見つける</li>
<li class="">キャッシュ戦略を設計する</li>
<li class="">コードを修正する</li>
<li class="">テストを追加する</li>
<li class="">検証を実行する</li>
</ol>
<p>このプロセスは <strong>AI が開発計画を自動生成する</strong> ようなものです。</p>
<p>強調しておきたいのは、この計画は一度生成されたらそのまま最後まで実行されるわけではないということです。1 ステップ完了するごとに、モデルは残りのステップを見直します——例えばプロジェクト構造を分析した結果、JPA ではなく MyBatis が使われていると分かれば、その後の変更方針もそれに合わせて調整されます。計画は生きているのです。これこそが Agent と「モデルに一度でスクリプトを出力させる」やり方との本質的な違いです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-タスクの自動実行">2) タスクの自動実行<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#2-%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AE%E8%87%AA%E5%8B%95%E5%AE%9F%E8%A1%8C" class="hash-link" aria-label="2) タスクの自動実行 への直接リンク" title="2) タスクの自動実行 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw は提案を出すだけでなく、次のことができます。</p>
<ul>
<li class="">コードの修正</li>
<li class="">ファイルの作成</li>
<li class="">Shell コマンドの実行</li>
<li class="">依存関係のインストール</li>
<li class="">スクリプトの実行</li>
</ul>
<p>例えば：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 対象プロジェクトをローカルに取得</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">git</span><span class="token plain"> clone project</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># プロジェクトの依存関係をインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">npm</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># テストを一通り実行し、変更が既存機能を壊していないか検証</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">npm</span><span class="token plain"> run </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">test</span><br></div></code></pre></div></div>
<p>AI はこれらの操作を直接実行できます。</p>
<p>実行の鍵は結果のフィードバックにあります。コマンドの stdout、stderr、終了コードはすべて次のラウンドのコンテキストに入ります。テストが落ちれば、モデルは具体的にどのテストケースがどんなエラーで失敗したかを見て、自分で修正しにいきます——これが「タスク完了までイテレーションを継続する」の具体的な姿です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-ツール呼び出し能力">3) ツール呼び出し能力<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#3-%E3%83%84%E3%83%BC%E3%83%AB%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%97%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="3) ツール呼び出し能力 への直接リンク" title="3) ツール呼び出し能力 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw は Tool を通じてさまざまな能力を呼び出せます。例えば：</p>
<ul>
<li class="">ファイルシステム</li>
<li class="">Shell</li>
<li class="">Git</li>
<li class="">HTTP API</li>
<li class="">コンパイルツール</li>
<li class="">テストフレームワーク</li>
</ul>
<p>ツールとは本質的に、モデルに公開された説明付きインターフェースの集合であり、モデルはタスクに応じてどれを呼び、どんなパラメータを渡すかを自分で決めます。この仕組みは拡張可能です。社内システムをインターフェースでラップして登録すれば、Agent はそれを操作できるようになります。能力の境界はフレームワーク自体ではなく、どれだけのツールをつなぐ気があるかで決まります——もちろん、つなげばつなぐほど、権限管理はより慎重にする必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="openclaw-の欠点">OpenClaw の欠点<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#openclaw-%E3%81%AE%E6%AC%A0%E7%82%B9" class="hash-link" aria-label="OpenClaw の欠点 への直接リンク" title="OpenClaw の欠点 への直接リンク" translate="no">​</a></h2>
<p>OpenClaw は非常に強力ですが、現時点ではいくつかの明らかな問題が残っています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-モデル能力への強い依存">1) モデル能力への強い依存<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#1-%E3%83%A2%E3%83%87%E3%83%AB%E8%83%BD%E5%8A%9B%E3%81%B8%E3%81%AE%E5%BC%B7%E3%81%84%E4%BE%9D%E5%AD%98" class="hash-link" aria-label="1) モデル能力への強い依存 への直接リンク" title="1) モデル能力への強い依存 への直接リンク" translate="no">​</a></h3>
<p>OpenClaw の能力は <strong>基盤となるモデルに大きく依存</strong> します。</p>
<p>モデルの能力が不足すると：</p>
<ul>
<li class="">タスクプランニング能力が低下する</li>
<li class="">コード品質が低下する</li>
<li class="">実行でエラーが起きやすくなる</li>
</ul>
<p>そのため多くの場合：</p>
<p>Agent の能力 ≈ モデルの能力</p>
<p>モデルが弱ければ、OpenClaw も「かなり間抜け」になります。</p>
<p>この問題は Agent のシナリオでは増幅されます。通常の対話ならモデルが一言間違えても、人が一目見て訂正できます。しかし複数ステップの実行ループでは、最初のステップの誤ったプランニングが後続のすべてのステップに継承され、エラーは蓄積していきます。フレームワーク側のプロンプトエンジニアリングでカバーできるのは一部だけで、モデル自体の推論力の弱さまではカバーしきれません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-token-コストの問題">2) Token コストの問題<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#2-token-%E3%82%B3%E3%82%B9%E3%83%88%E3%81%AE%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="2) Token コストの問題 への直接リンク" title="2) Token コストの問題 への直接リンク" translate="no">​</a></h3>
<p>Agent システムには通常、次のものが必要です。</p>
<ul>
<li class="">複数ラウンドの思考</li>
<li class="">複数回のモデル呼び出し</li>
<li class="">継続的なコンテキスト</li>
</ul>
<p>Token の消費が非常に大きく、毎回大量のコンテキストを携えてモデルを呼び出します。チューニングしても一部のコストを削減できるだけで、あるいは安いモデルに置き換える手もありますが、安いモデルに置き換えると上記の問題が発生し、OpenClaw は完全に信頼できず、制御不能になってしまいます。</p>
<p>原因は難しくありません。毎ラウンドの呼び出しには、タスク目標、過去の操作、ファイル内容、コマンド出力を含める必要があり、コンテキストはイテレーション回数にほぼ比例して増大し、しかも毎ラウンドが完全なモデル呼び出しです。複数ステップのタスクを一つこなすと、消費は単発の対話の数十倍になることもあります。コストと能力はここで避けられないトレードオフになります——お金を節約して弱いモデルにすれば、最初の問題がすぐに襲ってきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>前述の 2 つの欠点を踏まえて、実際の使用ではいくつかの提案があります。</p>
<ul>
<li class=""><strong>本番環境で直接動かさないこと。</strong> Agent は本当にコマンドを実行します。ファイル削除も設定変更も、本当に削除され本当に変更されます。コンテナや隔離された作業ディレクトリの中で動かし、権限は最小限にしましょう。</li>
<li class=""><strong>タスクは小さく分割すること。</strong> 目標が大きいほどプランニングは脱線しやすく、コンテキストの膨張も速くなります。「キャッシュを 1 つ追加する」は「データ層全体をリファクタリングする」よりずっと確実です。</li>
<li class=""><strong>実行プロセスを見張ること。</strong> 前述の実行プロセスが不透明という問題について、監視プラットフォームが整っていない状況では、少なくとも完全な操作ログを残し、事後にそれが何をしたのか追跡できるようにしておくべきです。</li>
<li class=""><strong>コストに上限を設けること。</strong> 複数ラウンドのイテレーションによる Token 消費は簡単に制御不能になります。特にモデルがある間違いに対して繰り返しリトライしているときは要注意です。ラウンド数の上限や予算上限を設定し、朝起きたら請求書が爆発していた、という事態を避けましょう。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="総評">総評<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#%E7%B7%8F%E8%A9%95" class="hash-link" aria-label="総評 への直接リンク" title="総評 への直接リンク" translate="no">​</a></h2>
<p>OpenClaw は非常にポテンシャルのある <strong>AI Agent フレームワーク</strong> です。</p>
<p>メリット：</p>
<ul>
<li class="">自動タスクプランニング</li>
<li class="">自動実行能力</li>
<li class="">拡張可能な Agent アーキテクチャ</li>
</ul>
<p>デメリット：</p>
<ul>
<li class="">モデルへの強い依存</li>
<li class="">Token コストが高い（モデル呼び出しが非常に多く、複雑なタスクの能力は完全にモデル次第）</li>
<li class="">実行プロセスが不透明（非常に深刻。実行プロセスは完全にブラックボックスで、コントロールプレーンと組み合わせて監視しないと使えない）</li>
</ul>
<p>したがって、現時点では次の用途に向いています。</p>
<ul>
<li class="">AI 自動化開発</li>
<li class="">DevOps 自動化</li>
<li class="">AI Agent の実験</li>
</ul>
<p>しかし、次のものと組み合わせれば：</p>
<ul>
<li class="">強力なモデル</li>
<li class="">可視化システム</li>
<li class="">Agent 管理プラットフォーム</li>
</ul>
<p>将来はこうなるかもしれません。</p>
<blockquote>
<p><strong>AI 自動化チームの中核インフラストラクチャ。</strong></p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/13/openclaw#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Agent フレームワークの価値は、モデルの出力を実環境につないでクローズドループで実行することにあり、OpenClaw はこの道において十分に完成度が高いです。プランニング、実行、ツール呼び出しの 3 つがすべて揃っています。しかしその上限はモデルの能力に縛られ、下限はコストと透明性に足を引っ張られています。現段階での私の使い方は、小さいタスク、隔離環境、ログを見張りながら動かす、というものです。周辺の監視・管理エコシステムがもう少し成熟してから、より重要なフローへの投入を検討するつもりです。</p>]]></content:encoded>
            <category>技術</category>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[運用（Ops）の歴史的変遷と K8S 以降のトレンド]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/13/ops-evolution</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/13/ops-evolution</guid>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[手作業でのサーバー管理から IaC、クラウドコンピューティング、Kubernetes への進化の流れ、そして GitOps、可観測性、Serverless、AI 運用といったその後のトレンドを整理し、運用という職業の行方についても語ります。]]></description>
            <content:encoded><![CDATA[<p>過去 30 数年間、サーバー運用は実のところずっと 1 つのことをやり続けてきました。<strong>人が「手作業でサーバーを管理する」ことを、どんどん減らしていく。</strong> この記事ではこの進化の流れを整理し、Kubernetes 以降の運用がどこへ向かうのかについても語ります。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>初期の運用は非常に原始的でした。サーバーを購入したら自分でラックに載せ、OS をインストールし、ネットワークを設定し、設定ファイルを書き換える。マシンに問題が起きれば SSH でログインして調査する。多くの場合、頼りになるのは経験と記憶でした。当時の運用は、本質的に「人がマシンを見張る」ことだったのです。</p>
<p>しかしサーバーの台数がどんどん増えると、この方式はすぐに立ち行かなくなりました。問題は主に 2 つ、<strong>反復労働</strong> と <strong>設定の混乱</strong> です。</p>
<p>反復労働はわかりやすい話です。例えば同じサービスを数十台のマシンにデプロイする場合、毎回手作業で大量のコマンドを実行しなければなりません。規模が大きくなると、運用担当者は毎日こうした繰り返し作業に追われます。SRE の体系では、この種の作業には専門の名前があり、<strong>toil</strong>（自動化可能で、反復的だが、長期的な価値の低い作業）と呼ばれます。</p>
<p>もうひとつの問題は <strong>設定ドリフト（configuration drift）</strong> です。今日あるマシンで一時的にパラメータを変更したのに、半年後には誰も覚えていない。マシンごとの設定が少しずつ食い違っていき、システムはどんどん保守しにくくなります。</p>
<p>これらの問題を解決するため、運用は自動化へと歩み始めました。最初は各種の <strong>Shell スクリプト</strong> で、その後 <strong>構成管理ツール</strong>（Puppet、Chef、Ansible など）へと発展しました。さらにその先で、より重要な理念が登場します。<strong>Infrastructure as Code（IaC）</strong> です。</p>
<p>簡単に言えば、サーバー設定をコードとして書き、Git で管理するということです。こうすればすべての変更が記録に残り、ロールバックもでき、繰り返し実行もできます。運用はもはや「マシンに入って何かを変更する」ことではなく、「コードを変更する」ことになったのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="仮想化とクラウドコンピューティング">仮想化とクラウドコンピューティング<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#%E4%BB%AE%E6%83%B3%E5%8C%96%E3%81%A8%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0" class="hash-link" aria-label="仮想化とクラウドコンピューティング への直接リンク" title="仮想化とクラウドコンピューティング への直接リンク" translate="no">​</a></h2>
<p>さらにその後、非常に重要な変化が起こりました。<strong>仮想化とクラウドコンピューティング</strong> です。</p>
<p>以前はサーバーの購入はとても重い作業で、調達、ラッキング、配線が必要でした。仮想化の登場後、1 台の物理サーバーで多数の仮想マシンを動かせるようになり、サーバーはもはやハードウェアではなく、一種の「リソース」になりました。クラウド時代に至っては、自分でサーバーを買う必要すらなくなり、コンソールで数回クリックするだけでリソースを申請できます。運用の役割も、「マシンを管理する人」から「リソースをスケジューリングする人」へと変わり始めました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナ化技術">コンテナ化技術<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E5%8C%96%E6%8A%80%E8%A1%93" class="hash-link" aria-label="コンテナ化技術 への直接リンク" title="コンテナ化技術 への直接リンク" translate="no">​</a></h2>
<p>ソフトウェアのデリバリー方式を本当に変えたのは、<strong>Docker と Kubernetes</strong> です。</p>
<p>Docker が解決したのは古くからの問題、すなわち <strong>ソフトウェアが環境によって挙動が一致しない</strong> ことです。開発マシンでは動くのにテストマシンでは動かない、本番環境ではまた違う。Docker はアプリケーションと実行環境を丸ごと 1 つのイメージにパッケージングします。お弁当を詰めるようなもので、どこへ持って行っても動きます。</p>
<p>しかし Docker だけではまだ足りません。コンテナの数が数十、数百、数千になると、これらのコンテナを管理するシステムが必要になります。そこで Kubernetes が登場しました。</p>
<p>Kubernetes の核心的な考え方は実にシンプルです。<strong>「システムがどうあってほしいか」を伝えるだけで、システムが現実をその目標状態に絶えず引き戻してくれる。</strong></p>
<p>例えば「サービスインスタンスを 3 つ欲しい」と宣言すれば、1 つが落ちても Kubernetes が自動でもう 1 つ立ち上げます。マシンが壊れれば、コンテナは自動的に別のマシンへスケジューリングされます。このメカニズムは <strong>制御ループ（Control Loop）</strong> と呼ばれ、Kubernetes が大規模クラスタを自動管理できる鍵でもあります。</p>
<p>というわけで、よりマクロな視点から見ると、運用の発展は実は 3 つの大きな飛躍でした：</p>
<ul>
<li class=""><strong>構成管理 / IaC</strong>：サーバー設定をバージョン管理できるようにした</li>
<li class=""><strong>仮想化 / クラウドコンピューティング</strong>：サーバーをリソースに変えた</li>
<li class=""><strong>Docker + Kubernetes</strong>：クラスタ全体を自動で動かせるようにした</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="kubernetes-以降どう発展していくか">Kubernetes 以降、どう発展していくか<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#kubernetes-%E4%BB%A5%E9%99%8D%E3%81%A9%E3%81%86%E7%99%BA%E5%B1%95%E3%81%97%E3%81%A6%E3%81%84%E3%81%8F%E3%81%8B" class="hash-link" aria-label="Kubernetes 以降、どう発展していくか への直接リンク" title="Kubernetes 以降、どう発展していくか への直接リンク" translate="no">​</a></h2>
<p>Kubernetes は終着点ではなく、「クラスタ管理」という問題を比較的うまく解決したにすぎません。Kubernetes 以降の運用の発展は、主に 3 つの方向に集中しています。<strong>より自動的に、より可観測に、サーバーへの心理的負担をより少なく。</strong></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-gitops">1) GitOps<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#1-gitops" class="hash-link" aria-label="1) GitOps への直接リンク" title="1) GitOps への直接リンク" translate="no">​</a></h3>
<p>GitOps の考え方はシンプルです。<strong>Git こそがシステムの唯一の信頼できる情報源（Single Source of Truth）である。</strong></p>
<p>すべての設定を Git に書き、システムが本番環境を Git の状態に自動的に一致させます。設定を変更するときは、サーバー上で操作するのではなく、PR を出します。コードがマージされれば、システムが自動的にデプロイを完了します。</p>
<p>このモデルの最大の利点は、変更が監査可能で、ロールバックが簡単で、プロセスがより明確になることです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-可観測性とサービスガバナンス">2) 可観測性とサービスガバナンス<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#2-%E5%8F%AF%E8%A6%B3%E6%B8%AC%E6%80%A7%E3%81%A8%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%AC%E3%83%90%E3%83%8A%E3%83%B3%E3%82%B9" class="hash-link" aria-label="2) 可観測性とサービスガバナンス への直接リンク" title="2) 可観測性とサービスガバナンス への直接リンク" translate="no">​</a></h3>
<p>システムがマイクロサービスになると、本当に複雑なのは個々のサービスではなく、サービス間の呼び出し関係であることが多くなります。1 つのリクエストが十数個のサービスを経由することもあり、どこか 1 か所が遅くなれば、システム全体が影響を受けます。</p>
<p>そこで <strong>Service Mesh、Prometheus、OpenTelemetry</strong> といった技術が登場しました。これらの核心的な目標は、<strong>システムの稼働状態をより透明にし、問題をより発見しやすくする</strong> ことです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-serverless-とエッジコンピューティング">3) Serverless とエッジコンピューティング<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#3-serverless-%E3%81%A8%E3%82%A8%E3%83%83%E3%82%B8%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B0" class="hash-link" aria-label="3) Serverless とエッジコンピューティング への直接リンク" title="3) Serverless とエッジコンピューティング への直接リンク" translate="no">​</a></h3>
<p>Serverless の理念はこうです。開発者はコードを書くだけでよく、サーバーはプラットフォームが自動管理する。例えばある関数はリクエストがあるときだけ実行され、リクエストがなければリソースを占有しません。多くのシナリオにとって、このモデルは運用コストを大幅に削減できます。</p>
<p>同時に、一部のコンピューティングは「エッジ」へと移動し始めています。店舗のデバイス、工場の設備、車載システムなどです。こうしたシナリオに適応するため、軽量化 Kubernetes（K3s など）やクラウド・エッジ連携技術も数多く登場しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ai-は運用をどう変えるか">AI は運用をどう変えるか<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#ai-%E3%81%AF%E9%81%8B%E7%94%A8%E3%82%92%E3%81%A9%E3%81%86%E5%A4%89%E3%81%88%E3%82%8B%E3%81%8B" class="hash-link" aria-label="AI は運用をどう変えるか への直接リンク" title="AI は運用をどう変えるか への直接リンク" translate="no">​</a></h2>
<p>ここ数年、AI も徐々に運用の領域に入り始めています。</p>
<p>従来の監視システムの多くは <strong>固定しきい値</strong> ベースです。例えば CPU が 80% を超えたらアラート、というものです。しかし複雑なシステムでは、多くの異常はしきい値では表現しにくいのが実情です。そこで、ますます多くのプラットフォームが <strong>機械学習による異常検知</strong> を使い始めています。</p>
<p>例えば：</p>
<ul>
<li class="">AWS の DevOps Guru はメトリクスデータを分析し、異常を自動発見して提案を提示します</li>
<li class="">Azure のスマート検知は、パフォーマンス低下やエラー率上昇を自動的に識別します</li>
<li class="">Elastic の異常検知は、まずシステムの正常な振る舞いを学習し、それから異常パターンを識別します</li>
</ul>
<p>さらに、ディープラーニングでログを分析し、ログのシーケンスから異常パターンを発見する研究やプロダクトもあります。これらの技術の目標は実にシンプルです。<strong>アラートのノイズを減らし、運用担当者が本当の問題をより早く見つけられるようにする。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="運用という職業はどこへ向かうのか">運用という職業は、どこへ向かうのか<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#%E9%81%8B%E7%94%A8%E3%81%A8%E3%81%84%E3%81%86%E8%81%B7%E6%A5%AD%E3%81%AF%E3%81%A9%E3%81%93%E3%81%B8%E5%90%91%E3%81%8B%E3%81%86%E3%81%AE%E3%81%8B" class="hash-link" aria-label="運用という職業は、どこへ向かうのか への直接リンク" title="運用という職業は、どこへ向かうのか への直接リンク" translate="no">​</a></h2>
<p>自動化と AI を目にして、運用は消えてしまうのではと心配する人は少なくありません。しかし現実はむしろこうです。<strong>運用の仕事のやり方が変わりつつある。</strong></p>
<p>これまで運用の価値は、多くの場合「手作業で問題に対処できるか」に表れていました。マシンにログインし、設定を変更し、サービスを再起動し、障害を調査する、といったことです。しかし今後より価値を持つ能力は、実はこちらです：</p>
<ul>
<li class="">経験を <strong>自動化</strong> できること</li>
<li class="">プロセスを <strong>プラットフォーム化</strong> できること</li>
<li class="">ビジネスメトリクスからシステムの問題を理解できること</li>
<li class=""><strong>可観測性と安定性の体系</strong> を設計できること</li>
</ul>
<p>私の考えでは、<strong>未来の運用とは、マシンを直しに行く人ではなく、システムが自分で自分を直し、自分を観測し、人間の言葉による指示に従って構築を自動的に完了できるようにする、その人のことです。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/13/ops-evolution#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>運用のこの 30 数年の変遷を貫く核心の糸はただ 1 本、人を反復労働から解放することです。構成管理と IaC は変更をバージョン管理可能にし、仮想化とクラウドコンピューティングはサーバーをリソースに変え、Docker と Kubernetes はクラスタを自動で動かせるようにしました。この先を見ると、GitOps、可観測性、Serverless は確度の高い方向であり、AI は異常検知と障害の特定を徐々に引き受けつつあります。運用は消えませんが、「手作業で問題に対処する」ことの価値は下がり続け、自動化の体系を設計し構築できる人の価値はますます高まっていくでしょう。</p>]]></content:encoded>
            <category>Linux</category>
            <category>AI</category>
        </item>
        <item>
            <title><![CDATA[Redis のスレッドモデル]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing</guid>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Redis はよく「シングルスレッド」と言われますが、この言い方は半分しか正しくありません。イベントループ、IO 多重化から Redis 6 の IO マルチスレッドまで、Redis の高性能の源泉とシングルスレッドモデルにおける使用上の注意点を整理します。]]></description>
            <content:encoded><![CDATA[<p>Redis は高性能で知られていますが、その中核的な理由の一つが独特な <strong>スレッドモデルの設計</strong> です。多くの人は Redis が「シングルスレッド」だと聞いたことがあるでしょうが、実際には Redis のスレッドモデルはバージョンを重ねる中で進化してきました。Redis のスレッドモデルを理解することは、その高性能の原理を理解するうえでも、高並行シーンでの使い方を理解するうえでも非常に重要です。</p>
<!-- -->
<p>このテーマを独立した一本の記事として書こうと思ったのは、「Redis はシングルスレッドである」という言葉が面接や日常の議論であまりに頻繁に登場する一方で、大半の人の理解が字面のレベルにとどまっているからです。Redis プロセス全体にスレッドが 1 本しかないと思い込んでいるか、あるいは Redis 6 以降はコマンド実行もマルチスレッドになったと思い込んでいるか——どちらの理解も正確ではありません。そしてこのモデルは、私たちの Redis の使い方に直接影響します。どのコマンドを気軽に実行してはいけないのか、大きな Key がなぜ危険なのか、Pipeline がなぜ効果的なのか——その根源はすべてスレッドモデルにあります。</p>
<p>本記事では、<strong>Redis のシングルスレッド設計、イベント駆動モデル、IO 多重化、そして Redis 6 以降のマルチスレッド改善</strong> という観点から紹介していきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-はなぜシングルスレッドを選んだのか">Redis はなぜシングルスレッドを選んだのか<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#redis-%E3%81%AF%E3%81%AA%E3%81%9C%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%AB%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%82%92%E9%81%B8%E3%82%93%E3%81%A0%E3%81%AE%E3%81%8B" class="hash-link" aria-label="Redis はなぜシングルスレッドを選んだのか への直接リンク" title="Redis はなぜシングルスレッドを選んだのか への直接リンク" translate="no">​</a></h2>
<p>Redis の初期バージョン（Redis 6 より前）における中核的な実行モデルは、<strong>シングルスレッドでのコマンド処理</strong> です。</p>
<p>つまり：</p>
<ul>
<li class="">すべてのクライアントリクエスト</li>
<li class="">すべてのコマンド実行</li>
<li class="">データの読み書き</li>
</ul>
<p>これらすべてが <strong>1 本のメインスレッドで完結します</strong>。</p>
<p>この設計は、非常に重要な性質をもたらします。<strong>任意の時点で実行されているコマンドは 1 つだけ</strong> ということです。そのため Redis の単一コマンドは本質的にアトミックであり、ロックを取る必要がなく、2 つのコマンドが交錯して同じ Key を変更するという問題も存在しません。<code>INCR</code> や <code>SETNX</code> といったコマンドをそのまま並行処理のプリミティブとして使えるのは、このモデルの副産物なのです。</p>
<p>ただし注意が必要です。</p>
<p>Redis のシングルスレッドとは、<strong>コマンド実行がシングルスレッドであることだけを指し</strong>、Redis プロセス全体にスレッドが 1 本しかないという意味ではありません。たとえば：</p>
<ul>
<li class="">RDB 永続化</li>
<li class="">AOF rewrite</li>
<li class="">非同期削除</li>
<li class="">BIO スレッド</li>
</ul>
<p>これらは実際にはバックグラウンドスレッドです。</p>
<p>非同期削除を例にとると、<code>UNLINK</code> コマンドはメインスレッド上では 1 つのことしかしません——Key を辞書から取り外すことです。実際にメモリを解放する作業はバックグラウンドスレッドに委ねられ、ゆっくり処理されます。こうすることで、非常に大きなオブジェクトを削除する場合でもメインスレッドがブロックされません。RDB 永続化はさらに一歩進んでいて、直接子プロセスを fork してスナップショットを書き込み、オペレーティングシステムのコピーオンライト（copy-on-write）を利用してメインスレッドのブロックを回避します。</p>
<p>Redis のコアスレッドが担当するのは以下だけです。</p>
<ul>
<li class="">ネットワーク IO</li>
<li class="">コマンドのパース</li>
<li class="">コマンドの実行</li>
<li class="">結果の返却</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="シングルスレッドなのになぜ速いのか">シングルスレッドなのになぜ速いのか<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%AB%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AA%E3%81%AE%E3%81%AB%E3%81%AA%E3%81%9C%E9%80%9F%E3%81%84%E3%81%AE%E3%81%8B" class="hash-link" aria-label="シングルスレッドなのになぜ速いのか への直接リンク" title="シングルスレッドなのになぜ速いのか への直接リンク" translate="no">​</a></h2>
<p>多くの人が疑問に思うでしょう。シングルスレッドでどうして数十万 QPS を支えられるのか？</p>
<p>Redis の高性能は、主に以下の理由によるものです。</p>
<p>インメモリデータベース：Redis のすべてのデータはメモリ上にあり、ディスク IO を回避しています。メモリの速度とディスクの速度は桁が違います。通常の <code>GET</code> コマンド 1 つをとっても、本当の実行部分はハッシュテーブルの検索 1 回だけで、所要時間はマイクロ秒あるいはナノ秒レベル——ボトルネックはそもそも CPU の計算にはないのです。</p>
<p>スレッド切り替えのオーバーヘッドの回避：シングルスレッドでコマンドを実行することで、ロック、競合、複雑な並行制御を回避しています。マルチスレッドモデルでは、スレッドのコンテキストスイッチ、ロックの奪い合い、キャッシュの無効化といったオーバーヘッドは決して安くありません。各リクエスト自体の処理時間が極めて短い場合、これらの並行制御のコストはかえって利益を上回る可能性があります。Redis がシングルスレッドを選んだのは、本質的には「<strong>コマンド実行はボトルネックではなく、ネットワーク IO こそがボトルネックだ</strong>」という判断です。ならば並行性の問題は IO 層で解決し、実行層は最もシンプルな形に保とう、というわけです。</p>
<p>さらに、Redis 内部のデータ構造もこのモデルのために設計されています。SDS 文字列、スキップリスト、圧縮リストなどはいずれもメモリアクセスに最適化されており、単一操作のパスは非常に短くなっています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="io-多重化">IO 多重化<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#io-%E5%A4%9A%E9%87%8D%E5%8C%96" class="hash-link" aria-label="IO 多重化 への直接リンク" title="IO 多重化 への直接リンク" translate="no">​</a></h2>
<p>Redis は一度に 1 つの接続だけを処理しているわけではなく、<strong>IO 多重化モデル</strong> を使用しています。</p>
<p>IO 多重化とは、1 本のスレッドで複数の socket を同時に監視し、「どの接続に今読み書き可能なデータがあるか」をオペレーティングシステムに教えてもらい、スレッドは準備ができた部分だけを処理する仕組みです。「1 接続 1 スレッド」の従来モデルと比べて、接続ごとにスレッドのメモリとスケジューリングのコストを払う必要がなく、シングルスレッドで膨大な数の接続を支えられます。</p>
<p>Redis が使用する IO モデルには以下があります。</p>
<ul>
<li class="">epoll（Linux）</li>
<li class="">kqueue（MacOS / BSD）</li>
<li class="">select</li>
<li class="">evport</li>
</ul>
<p>Redis はコンパイル時にプラットフォームに応じて最適な実装を自動選択します。Linux では epoll です。この抽象化層はソースコード内で ae（A simple Event driven programming library）と呼ばれ、上位層に統一されたイベントインターフェースを公開しています。</p>
<p>Redis はイベントループを通じてすべてのクライアント接続を監視します。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">             ┌───────────────┐</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client 1 ───▶│               │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client 2 ───▶│               │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client 3 ───▶│  IO Multiplex │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client N ───▶│               │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">             └───────┬───────┘</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                     │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                     ▼</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">               Redis EventLoop</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                     │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                     ▼</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">               Command Execute</span><br></div></code></pre></div></div>
<p>処理フロー：</p>
<ol>
<li class="">複数のクライアント socket を監視する</li>
<li class="">どの socket にデータの準備ができたかを検知する</li>
<li class="">リクエストを読み取る</li>
<li class="">コマンドを実行する</li>
<li class="">結果を返す</li>
</ol>
<p>イベントループは毎周、多重化インターフェースから準備完了イベントを取り出し、1 つずつ処理します。読み取りイベントならコマンドをパースして実行し、書き込みイベントなら結果をクライアントへ送り返します。各イベントの処理が非常に速く、ループが十分な速さで回転するため、クライアントは自分が「順番待ち」していることに気づかないのです。</p>
<p>したがって Redis は <strong>シングルスレッドで大量の接続を同時に処理</strong> できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-6-のマルチスレッド改善">Redis 6 のマルチスレッド改善<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#redis-6-%E3%81%AE%E3%83%9E%E3%83%AB%E3%83%81%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E6%94%B9%E5%96%84" class="hash-link" aria-label="Redis 6 のマルチスレッド改善 への直接リンク" title="Redis 6 のマルチスレッド改善 への直接リンク" translate="no">​</a></h2>
<p>ハードウェアの発展に伴い CPU のコア数はますます増え、Redis のシングルスレッドモデルは特定のシーンでボトルネックに直面するようになりました。ボトルネックは主に <strong>ネットワーク IO</strong> にあります。</p>
<p>たとえば：</p>
<ul>
<li class="">大量のクライアント接続</li>
<li class="">ネットワークデータの送受信量が膨大な場合</li>
</ul>
<p>コマンド実行自体は純粋なメモリ操作で非常に高速です。しかし、リクエストをカーネルバッファから読み込んだり、レスポンスを書き戻したりする read/write システムコールには実際のオーバーヘッドがあります。トラフィックが一定レベルに達すると、メインスレッドの時間の大半がデータの送受信に費やされ、実際にコマンドを実行する時間の割合はかえって小さくなります——これがシングルスレッドモデルの天井です。</p>
<p>そこで Redis 6 は <strong>IO マルチスレッド</strong> を導入しました。</p>
<p>実行フローは次のように変わります。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Client Request</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">IO Threads (read)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Main Thread Execute Command</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       │</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       ▼</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">IO Threads (write)</span><br></div></code></pre></div></div>
<p>考え方は明快です。読み取り・パースとレスポンスの書き戻しという 2 つの「運搬役」の仕事を複数の IO スレッドに分担させて並列に行い、コマンド実行は引き続きメインスレッドに集約してシリアルに処理します。こうすることでマルチコアを活用してネットワークスループットを向上させつつ、シングルスレッド実行のアトミック性のセマンティクスを完全に維持しています——上位のビジネスコードは何も変更する必要がありません。</p>
<p>したがって：</p>
<ul>
<li class=""><strong>コマンド実行は依然としてシングルスレッド</strong></li>
<li class=""><strong>IO 操作は並列化可能</strong></li>
</ul>
<p>補足すると、IO マルチスレッドはデフォルトで無効になっており、設定で有効化します。</p>
<div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># redis.conf</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># IO スレッド数。CPU コア数より少なくすることを推奨</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">io-threads</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">4</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># デフォルトでは書き戻しのみマルチスレッド。読み取り・パースも並列化したい場合はこちらも有効にする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">io-threads-do-reads</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">yes</span><br></div></code></pre></div></div>
<p>ネットワークトラフィックが本当にボトルネックになっているインスタンスで有効化してこそ意味があります。トラフィックの少ないシーンで有効にすると、かえってスレッド間の調整というオーバーヘッドが一層増えるだけです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-シングルスレッドの使用上のアドバイス">Redis シングルスレッドの使用上のアドバイス<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#redis-%E3%82%B7%E3%83%B3%E3%82%B0%E3%83%AB%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AE%E4%BD%BF%E7%94%A8%E4%B8%8A%E3%81%AE%E3%82%A2%E3%83%89%E3%83%90%E3%82%A4%E3%82%B9" class="hash-link" aria-label="Redis シングルスレッドの使用上のアドバイス への直接リンク" title="Redis シングルスレッドの使用上のアドバイス への直接リンク" translate="no">​</a></h2>
<p>Redis のコマンド実行はシングルスレッドであるため、以下の点に注意が必要です。1 つの遅いコマンドが <strong>すべての</strong> クライアントのリクエストをブロックします。これはシングルスレッドモデルにおいて最も畏敬の念をもって扱うべきポイントです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="遅いコマンドを避ける">遅いコマンドを避ける<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#%E9%81%85%E3%81%84%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E9%81%BF%E3%81%91%E3%82%8B" class="hash-link" aria-label="遅いコマンドを避ける への直接リンク" title="遅いコマンドを避ける への直接リンク" translate="no">​</a></h3>
<p>例：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># キー空間全体を走査する O(N) 操作。その間すべてのリクエストがブロックされる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">KEYS *</span><br></div></code></pre></div></div>
<p>このようなコマンドは Redis をブロックします。</p>
<p>代わりにこちらを使うべきです。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># カーソルベースでバッチごとにイテレートし、毎回少量ずつ処理するため長時間ブロックしない</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">SCAN</span><br></div></code></pre></div></div>
<p>同様に、大きなコレクションに対して <code>SMEMBERS</code>、<code>HGETALL</code>、<code>LRANGE 0 -1</code> などの全量読み取りコマンドを実行するのも、本質的には O(N) の仕事をシングルスレッドに押し込むことになります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="大きな-key-を抑制する">大きな Key を抑制する<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#%E5%A4%A7%E3%81%8D%E3%81%AA-key-%E3%82%92%E6%8A%91%E5%88%B6%E3%81%99%E3%82%8B" class="hash-link" aria-label="大きな Key を抑制する への直接リンク" title="大きな Key を抑制する への直接リンク" translate="no">​</a></h3>
<p>大きな Key は以下を引き起こします。</p>
<ul>
<li class="">ネットワークのブロック</li>
<li class="">CPU 実行時間の増大</li>
</ul>
<p>さらに、大きな Key の削除と期限切れ処理も同様にメインスレッド上で行われます（<code>UNLINK</code> を使うか、遅延削除を有効にしない限り）。数百 MB の Key が期限切れになると、目に見えるリクエストのスパイクを引き起こす可能性があります。</p>
<p>推奨：</p>
<ul>
<li class="">データを分割する</li>
<li class="">hash / set を使う</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="pipeline-を使う">Pipeline を使う<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#pipeline-%E3%82%92%E4%BD%BF%E3%81%86" class="hash-link" aria-label="Pipeline を使う への直接リンク" title="Pipeline を使う への直接リンク" translate="no">​</a></h3>
<p>Pipeline はネットワークの往復を減らせます。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">client -&gt; redis -&gt; client</span><br></div></code></pre></div></div>
<p>Pipeline を使わない場合、各コマンドは前のコマンドのレスポンスが返ってくるのを待ってから次を送信するため、スループットはネットワークの RTT に縛られてしまいます。Pipeline はコマンドをまとめて一度に送信し、すべてのレスポンスを一度に受け取ることで、N 回の往復を 1 回に圧縮します。</p>
<p>全体のスループットが向上します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>スレッドモデルを踏まえると、実践で見落としがちなポイントがいくつかあります。</p>
<ol>
<li class="">
<p><strong>Lua スクリプトも同様にメインスレッドをブロックします</strong>。スクリプト実行中、Redis は他のいかなるコマンドも処理しません。スクリプト内でループを書いて大量のデータを処理すると、その効果は 1 本の超遅いコマンドと同じです。</p>
</li>
<li class="">
<p><strong>Redis 6 の IO マルチスレッドを「コマンドの並行実行」と理解しないでください</strong>。トランザクション、Lua、<code>INCR</code> のアトミック性のセマンティクスはまったく変わっておらず、これを理由にロックを追加する必要はありません。同様に、IO スレッドを有効にすれば遅いコマンドの問題が解決すると期待するのも誤りです——実行層で遅いものは、マルチスレッド IO では助けになりません。</p>
</li>
<li class="">
<p><strong>ブロックの調査ではまずスローログを見ましょう</strong>。<code>SLOWLOG GET</code> が記録するのはコマンドの実行所要時間（ネットワークを含まない）で、「Redis がたまに一瞬固まる」現象を突き止める最初の入り口です。<code>latency</code> 関連のコマンドと組み合わせれば、コマンドが遅いのか、fork が遅いのか、ディスクが遅いのかをさらに切り分けられます。</p>
</li>
<li class="">
<p><strong>単一インスタンスがマルチコアを使い切れないのは正常です</strong>。コマンド実行は 1 コアしか使いません。マルチコアマシン全体を活用したいなら、一般的なやり方は 1 台に複数インスタンスを立てるか Redis Cluster を導入することであって、Redis がマルチスレッド実行になるのを待つことではありません。</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/13/redis-nei-cun-mo-xing#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Redis は <strong>メモリ操作、IO 多重化、イベント駆動（Event Loop）、そしてシングルスレッドでのコマンド実行</strong> というアーキテクチャ設計により、ロック競合とスレッド切り替えのオーバーヘッドを回避しながら、極めて高い並行処理能力を実現しています。Redis 6 の IO マルチスレッドはネットワーク送受信を並列化しただけで、コマンド実行は依然としてシリアルです——この核心は変わらないため、それを取り巻く使用原則（遅いコマンドを避ける、大きな Key を抑制する、Pipeline を活用する）も常に成り立ちます。この点を理解すれば、Redis の多くのベストプラクティスは暗記すべき項目ではなく、モデルから導き出される必然的な結論になるのです。</p>]]></content:encoded>
            <category>技術</category>
            <category>Redis</category>
        </item>
        <item>
            <title><![CDATA[データ構造：二分木]]></title>
            <link>https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao</link>
            <guid>https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao</guid>
            <pubDate>Fri, 13 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[二分木の基本概念とよくある形態を整理：全二分木、完全二分木、二分探索木、さらに BST が連結リストに退化する問題と走査方式まで。木構造を学ぶための入門ノートです。]]></description>
            <content:encoded><![CDATA[<p>連結リストと配列が解決するのは「線形」データの格納問題ですが、階層関係や高速な検索が絡んでくると、線形構造では力不足になります。木構造はまさにそのために生まれたもので、二分木はその中で最も基礎的であり、最もよく問われるものでもあります。ヒープ、BST、赤黒木はすべて二分木の上に成り立っています。</p>
<!-- -->
<p><strong>二分木（Binary Tree）</strong> はよく使われるデータ構造で、複数のノードから構成され、各ノードは最大でも <strong>2 つの子ノード</strong> しか持ちません：</p>
<ul>
<li class="">左の子ノード（Left Child）</li>
<li class="">右の子ノード（Right Child）</li>
</ul>
<p>そのため <strong>二分木</strong> と呼ばれます。</p>
<p>「最大 2 つ」という制約は一見単純ですが、重要な性質をもたらします。木の形態を再帰的に定義できるのです。すべての二分木は、根ノード、左部分木、右部分木の 3 つの部分から構成され、左右の部分木自身もまた二分木です。この後に出てくる二分木に関するほぼすべてのアルゴリズム（走査、検索、挿入）は、この再帰構造を軸に展開されます。</p>
<p>各ノードは通常 3 つの部分を含みます：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Node {</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    value   // ノードが格納するデータ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    left    // 左の子ノードへの参照。なければ空</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    right   // 右の子ノードへの参照。なければ空</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">}</span><br></div></code></pre></div></div>
<p>つまり、二分木はメモリ上で連続して格納される必要はなく、ノード同士は参照（ポインタ）で繋がっています。この点は連結リストと似ていますが、各ノードが「1 つの後続」から「最大 2 つの子」に変わっただけです。</p>
<p>シンプルな構造の例：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        A</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       / \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      B   C</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">     / \   \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    D   E   F</span><br></div></code></pre></div></div>
<p>このうち：</p>
<ul>
<li class="">A は <strong>根ノード（Root）</strong></li>
<li class="">B、C は A の <strong>子ノード</strong></li>
<li class="">D、E は B の <strong>子ノード</strong></li>
</ul>
<p>子ノードを持たないノード（D、E、F など）は <strong>葉ノード（Leaf）</strong> と呼ばれます。根から葉までに通る層の数を木の <strong>高さ</strong> と呼び、これが木に対するほとんどの操作の所要時間を直接決定します。</p>
<p>形態の違いによって、二分木にはいくつかのよくある特殊なカテゴリがあります。順番に見ていきましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="全二分木full-binary-tree">全二分木（Full Binary Tree）<a href="https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao#%E5%85%A8%E4%BA%8C%E5%88%86%E6%9C%A8full-binary-tree" class="hash-link" aria-label="全二分木（Full Binary Tree） への直接リンク" title="全二分木（Full Binary Tree） への直接リンク" translate="no">​</a></h2>
<p>ある木の <strong>すべてのノードが、2 つの子ノードを持つか、子ノードをまったく持たないか</strong> のいずれかである場合、全二分木と呼びます。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        A</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       / \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      B   C</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">     / \ / \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    D  E F  G</span><br></div></code></pre></div></div>
<p>特徴：</p>
<ul>
<li class="">各ノードは <strong>子ノード 0 個</strong> か</li>
<li class=""><strong>子ノード 2 個</strong> のいずれか</li>
</ul>
<p>言い換えれば、全二分木には「子が 1 つだけ」のノードは存在しません。この形態は最も「詰まって」おり、同じ層数の場合に格納できるノード数が最大になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="完全二分木complete-binary-tree">完全二分木（Complete Binary Tree）<a href="https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao#%E5%AE%8C%E5%85%A8%E4%BA%8C%E5%88%86%E6%9C%A8complete-binary-tree" class="hash-link" aria-label="完全二分木（Complete Binary Tree） への直接リンク" title="完全二分木（Complete Binary Tree） への直接リンク" translate="no">​</a></h2>
<p>完全二分木の要件は：</p>
<ul>
<li class="">最後の層を除き、他の層はすべて埋まっている</li>
<li class="">最後の層のノードは <strong>左から右へ連続して並んでいる</strong></li>
</ul>
<p>例：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       / \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      2   3</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">     / \  /</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    4  5 6</span><br></div></code></pre></div></div>
<p>この構造は <strong>配列での格納</strong> に非常に適しているため、<strong>ヒープ（Heap）</strong> は完全二分木なのです。</p>
<p>なぜ完全二分木は配列格納に向いているのでしょうか。ノードに「上から下へ、左から右へ」番号を振ると、途中に穴が空かないからです。根を添字 0 に置くと、添字 i のノードの左の子は <code>2i + 1</code>、右の子は <code>2i + 2</code>、親ノードは <code>(i - 1) / 2</code>（切り捨て）になります。ポインタを一切格納せず、添字の計算だけで親子間を行き来できるので、メモリを節約できるうえキャッシュにも優しい。ヒープソートと優先度付きキューはまさにこの性質を利用しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="二分探索木bst">二分探索木（BST）<a href="https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao#%E4%BA%8C%E5%88%86%E6%8E%A2%E7%B4%A2%E6%9C%A8bst" class="hash-link" aria-label="二分探索木（BST） への直接リンク" title="二分探索木（BST） への直接リンク" translate="no">​</a></h2>
<p>前の 2 つの分類が注目するのは「形」ですが、二分探索木が注目するのは「ノードの値の並び方の規則」です。二分探索木（Binary Search Tree）は次を満たします：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">左子树 &lt; 根节点 &lt; 右子树</span><br></div></code></pre></div></div>
<p>この制約は再帰的に効くことに注意してください。左右の子だけでなく、左部分木全体のすべての値が根より小さく、右部分木全体のすべての値が根より大きいのです。</p>
<p>例：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        8</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">       / \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      3   10</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">     / \    \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    1   6    14</span><br></div></code></pre></div></div>
<p>特徴：</p>
<ul>
<li class="">検索効率が高い</li>
<li class="">平均時間計算量：<strong>O(log n)</strong></li>
</ul>
<p>検索の過程は二分探索とそっくりです。根から始めて、目標値が現在のノードより小さければ左へ、大きければ右へ進む。1 層進むごとに、候補ノードのおよそ半分を除外できます。挿入も同様で、検索の経路に沿って空きの位置まで進み、新しいノードをそこにぶら下げるだけです。</p>
<p>しかし木が連結リストに退化すると：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"> \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  2</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   \</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    3</span><br></div></code></pre></div></div>
<p>時間計算量は次のように退化します：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">O(n)</span><br></div></code></pre></div></div>
<p>退化の最も典型的な引き金は、<strong>ソート済みの列</strong> を順に挿入することです。新しいノードがどれも以前のものより大きいと、ひたすら右側にぶら下がるしかなく、木は一本の鎖のように「歪んで」成長します。検索時に各層で 1 ノードしか除外できず、BST の利点は完全に失われます。この問題を解決するために、AVL 木や赤黒木といった <strong>自己平衡二分探索木</strong> が生まれました。これらは挿入・削除時に回転によって構造を調整し、木の高さを常に O(log n) レベルに保ちます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="走査方式">走査方式<a href="https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao#%E8%B5%B0%E6%9F%BB%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="走査方式 への直接リンク" title="走査方式 への直接リンク" translate="no">​</a></h2>
<p>二分木のすべてのノードを訪問する方法を走査と呼び、よく使われるのは 4 種類です：</p>
<ol>
<li class=""><strong>前順走査（先行順）</strong>：根 → 左部分木 → 右部分木。木のコピーやシリアライズによく使われる</li>
<li class=""><strong>中順走査（通りがけ順）</strong>：左部分木 → 根 → 右部分木。BST に対して中順走査を行うと、得られるのはまさに <strong>昇順の列</strong></li>
<li class=""><strong>後順走査（帰りがけ順）</strong>：左部分木 → 右部分木 → 根。「先に子を処理してから自分を処理する」場面、例えば木全体の解放に適している</li>
<li class=""><strong>レベル順走査</strong>：層ごとに左から右へ訪問する。キューを使って実装する、木の上での幅優先探索</li>
</ol>
<p>前の 3 つは再帰で書くと非常に自然です。これは前述の内容の裏付けでもあります。二分木自体が再帰的に定義された構造なのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>上記の内容を踏まえて、実際の使用や問題演習でつまずきやすいポイントがいくつかあります：</p>
<ul>
<li class=""><strong>BST が平衡だとデフォルトで思い込まない</strong>。計算量を分析するときは「平均 O(log n)」と「最悪 O(n)」を区別すること。面接で突っ込まれるのはたいてい退化の場面です。</li>
<li class=""><strong>中順走査で BST を検証する</strong>。ある木が正しい BST かどうかの判定は、ノードとその直接の子を比較するだけでは不十分で、部分木全体が大小関係を満たすことを保証しなければなりません。中順走査で列が厳密に単調増加かをチェックするのが最も間違いにくい書き方です。</li>
<li class=""><strong>再帰の深さ</strong>。木が連結リストに退化すると、再帰走査のコールスタックの深さも O(n) になります。データ量が大きいとスタックオーバーフローの可能性があるので、必要なら明示的なスタックを使う反復の書き方に切り替えましょう。</li>
<li class=""><strong>「全」と「完全」を区別する</strong>。この 2 つの定義は混同しやすいものです。全二分木は子が 1 つだけのノードを許さず、完全二分木は最後の層が左詰めであることを要求します。正誤問題ではこの 2 つの概念でお互いに引っかけてくることがよくあります。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2026/03/13/shu-ju-jie-gou-shuang-xiang-lian-biao#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>二分木は最も基礎的かつ最も重要なデータ構造の一つで、その核心的な特徴は</p>
<ul>
<li class="">各ノードは最大 <strong>2 つの子ノード</strong></li>
<li class=""><strong>効率的な検索</strong> をサポート</li>
<li class="">さまざまな高度なデータ構造へ拡張できる</li>
</ul>
<p>よくある派生には次のものがあります：</p>
<ul>
<li class="">二分探索木（BST）</li>
<li class="">AVL 木</li>
<li class="">赤黒木</li>
<li class="">ヒープ（Heap）</li>
</ul>
<p>二分木の理解は <strong>アルゴリズムとデータ構造</strong> を学ぶうえでの重要な基礎です。その再帰的な定義、いくつかの形態の違い、そして走査の定石を押さえてから平衡木やヒープを見れば、それらの「高度な」構造が、二分木に追加の制約を重ねただけのものに過ぎないと気づくはずです。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Istio-レートリミット設定]]></title>
            <link>https://tanzhuo.xyz/ja/2025/11/04/istio</link>
            <guid>https://tanzhuo.xyz/ja/2025/11/04/istio</guid>
            <pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Istio IngressGateway 上で Envoy RateLimit + Redis によるグローバルレートリミットを実現する：設定の解説、リクエスト判定のフロー、そして domain・descriptor のマッチングでハマりやすい点。]]></description>
            <content:encoded><![CDATA[<p>本番の API が一度攻撃で叩かれたのを機に、レートリミットをアプリケーション層からゲートウェイ層へ移しました。この記事では、Istio IngressGateway 上で Envoy RateLimit + Redis によるグローバルレートリミットを実現する完全な設定と、ハマりやすいいくつかのマッチングの細部を整理します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景と課題">背景と課題<a href="https://tanzhuo.xyz/ja/2025/11/04/istio#%E8%83%8C%E6%99%AF%E3%81%A8%E8%AA%B2%E9%A1%8C" class="hash-link" aria-label="背景と課題 への直接リンク" title="背景と課題 への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャでは、システムは通常 <strong>Istio IngressGateway</strong> を通じて統一的な入口を外部に提供します。トラフィックが急増したとき（イベント時のトラフィック、クローラー、API が叩かれる）、レートリミットの仕組みがないと、バックエンドで次のような問題が起こりえます。</p>
<ul>
<li class="">API が高頻度で呼び出され、サービスの CPU やデータベースの負荷が過大になる</li>
<li class="">突発的なトラフィックがシステムの雪崩や連鎖障害を引き起こす</li>
<li class="">コアな API が悪意を持って叩かれ、正常なユーザーのアクセスを圧迫する</li>
</ul>
<p>各サービスの中でそれぞれレートリミットを実装するのももちろん可能ですが、ルールが分散し、基準がバラバラになりますし、しかもリクエストはすでに業務プロセスまで到達しているため、リソースはやはり消費されてしまいます。より合理的なやり方は、<strong>ゲートウェイ層で統一的にレートリミットをかける</strong>ことです。リクエストがバックエンドに入る前にトラフィックガバナンスを完了させます。</p>
<p>Istio のデータプレーンはまさに Envoy Proxy であり、生来この能力を備えています。<strong>Envoy RateLimit フィルター + 独立した ratelimit サービス + Redis</strong> を通じて、ゲートウェイのレプリカをまたぐグローバルなレートリミットを実現でき、特定パスに対してアクセス頻度の制御を行えます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="原理の概説">原理の概説<a href="https://tanzhuo.xyz/ja/2025/11/04/istio#%E5%8E%9F%E7%90%86%E3%81%AE%E6%A6%82%E8%AA%AC" class="hash-link" aria-label="原理の概説 への直接リンク" title="原理の概説 への直接リンク" translate="no">​</a></h2>
<p>このパイプライン全体には三つの役割があります。それらの関係を理解すれば、後の YAML は難しくありません。</p>
<p>1）<strong>Envoy RateLimit フィルター</strong>。IngressGateway の HTTP フィルターチェーンに掛かっており、各リクエストが通過する際、ルート上に設定された <code>actions</code> に従って一組の descriptor（ディスクリプタ）を生成し、<code>domain</code> とともに gRPC で ratelimit サービスに問い合わせます——このリクエストを通すか否か、を。</p>
<p>2）<strong>ratelimit サービス</strong>（envoyproxy/ratelimit）。独立してデプロイされた gRPC サービスで、ConfigMap 内のクォータルールを読み込み、ディスクリプタを受け取るとルールを照会してクォータを消費し、OK または OVER_LIMIT を返します。</p>
<p>3）<strong>Redis</strong>。クォータのカウンターのストレージです。カウントが Redis にあるからこそ、複数のゲートウェイレプリカが同一のカウントを共有でき、これでこそ「グローバル」なレートリミットと呼べます——ローカルレートリミット（local rate limit）では各 Envoy インスタンスがそれぞれ別々に計算することになります。</p>
<p>マッチングのカギは次の点にあります。ルート上の <code>actions</code> が生成するディスクリプタの組み合わせは、ConfigMap 内の <code>descriptors</code> の key/value の階層と<strong>完全に対応</strong>していなければならず、なおかつ両者の <code>domain</code> が一致していて初めて、クォータがヒットします。どこか一箇所でも噛み合わないと、レートリミットは静かに機能しなくなり、リクエストはすべて通ってしまいます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="どんなときに使うか">どんなときに使うか<a href="https://tanzhuo.xyz/ja/2025/11/04/istio#%E3%81%A9%E3%82%93%E3%81%AA%E3%81%A8%E3%81%8D%E3%81%AB%E4%BD%BF%E3%81%86%E3%81%8B" class="hash-link" aria-label="どんなときに使うか への直接リンク" title="どんなときに使うか への直接リンク" translate="no">​</a></h2>
<p>典型的なのは、低頻度だが機微で、叩かれたときの代償が大きいこの種の API です。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">/api/login</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">/api/send-code</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">/api/payment</span><br></div></code></pre></div></div>
<p>ログイン API が叩かれると大量のパスワード検証とリスクコントロール計算が走ります。認証コード送信 API が叩かれると直接 SMS 費用が燃えます。決済 API は下流のリソースに関わります。これらの API はそもそも QPS が高くないので、ごく小さなクォータを与えるだけで悪意あるトラフィックの大部分を防げますし、誤って正常なユーザーを巻き込む範囲も小さくて済みます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="完全な設定">完全な設定<a href="https://tanzhuo.xyz/ja/2025/11/04/istio#%E5%AE%8C%E5%85%A8%E3%81%AA%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="完全な設定 への直接リンク" title="完全な設定 への直接リンク" translate="no">​</a></h2>
<p>以下はそのまま流用できる一式の設定で、四つの部分に分かれています。Secret で Redis のパスワードを保存、ConfigMap でクォータを定義、ratelimit サービス本体、そして二つの EnvoyFilter がそれぞれフィルターの取り付けとディスクリプタの付与を担当します。</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 0) Redis のパスワードは Secret に置く（より安全）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    Secret で機微な情報（Redis のパスワードなど）を保存し、平文がイメージや Pod 環境変数の履歴に入るのを避ける</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Secret</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">redis</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">secret          </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Secret 名。後続の Deployment が secretKeyRef で参照する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system               </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ratelimit サービスと同じ名前空間に置き、参照と管理を容易にする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">type</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Opaque</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">stringData</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">REDIS_AUTH</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"password"</span><span class="token plain">       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 平文は K8s が base64 エンコードして保存してくれる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 1) Ratelimit クォータ設定（Envoy Ratelimit Server のランタイム設定）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - この ConfigMap はコンテナに読み取り専用で /data/ratelimit/config にマウントされる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - domain は Envoy の HTTP フィルター内の domain と完全に一致していなければヒットしない</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - descriptors はレートリミットの「次元の組み合わせ」を記述する（actions を組み合わせて生成されるディスクリプタ）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ConfigMap</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">config</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">data</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">config.yaml</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token scalar string" style="color:hsl(95, 38%, 62%)"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">    domain: ingress-ratelimit          # RateLimit フィルターの domain と一致していること</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">    descriptors:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">      - key: header_match              # 最上位 key：HTTP_ROUTE 内の header_value_match に対応（actions 1/2）</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">        value: path-api         # 最上位 value：ルート上の actions の descriptor_value から来る</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">        descriptors:                   # 二段目のディスクリプタ：さらに細分化する（組み合わさって一意のレートリミットキーを形成）</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">          - key: generic_key           # HTTP_ROUTE 内の generic_key に対応（actions 2/2）</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">            value: istio-limit-v1      # ルート内で設定した descriptor_value に対応</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">            rate_limit:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">              unit: second             # 時間単位：second / minute / hour / day</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">              requests_per_unit: 5     # クォータ：単位時間内に許可するリクエスト数（ここでは 1 秒 1 回）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 2) Ratelimit Service（既存の Redis 単体に接続する）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - envoyproxy/ratelimit でグローバルなスライディングウィンドウ／トークンバケットのレートリミットを実現する設定サービス</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - replicas=1：テスト環境は単一レプリカ。本番では少なくとも 2〜3 レプリカ + Redis の高可用性を推奨</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - readiness/liveness：6070 ポートでヘルスチェックを行う</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - 重要な環境変数：REDIS_URL / REDIS_AUTH / RUNTIME_ROOT / RUNTIME_SUBDIRECTORY</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Service</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit                         </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Envoy がクラスタ名で解決するためのもの (outbound|8081||ratelimit.istio-system.svc.cluster.local)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">selector</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">app</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain">            </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Deployment の labels と一致させる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">ports</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> grpc</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">port</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">8081</span><span class="token plain">                          </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># gRPC サービスのポート（Envoy RateLimit フィルターがこのポートに接続する）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">targetPort</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">8081</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> apps/v1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Deployment</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">replicas</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token plain">                              </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># テストでは 1 に設定。本番では ≥3 とし、HPA/PodDisruptionBudget も整えることを推奨</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">selector</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">matchLabels</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">app</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">template</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">labels</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">app</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">containers</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">image</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoyproxy/ratelimit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">875d418c   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># commit/tag を指定し、バージョンの再現性を保証する。公式の新しめの tag へのアップグレードも検討可</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">imagePullPolicy</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> IfNotPresent</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">command</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token string" style="color:hsl(95, 38%, 62%)">"/bin/ratelimit"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">            </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 起動エントリ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">ports</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">containerPort</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">8080</span><span class="token plain">                </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># HTTP Admin（任意：metrics/デバッグ）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">containerPort</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">8081</span><span class="token plain">                </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># gRPC のメインサービスポート</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">containerPort</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">6070</span><span class="token plain">                </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ヘルスチェックポート（/healthcheck）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">env</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> REDIS_SOCKET_TYPE</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> tcp                         </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># TCP で Redis に接続する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> REDIS_URL</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"redis://1.1.1.1:6379"</span><span class="token comment" style="color:hsl(220, 10%, 40%)"># あなたの Redis アドレス（本番では内網ドメイン + Sentinel またはクラスタを推奨）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> REDIS_POOL_SIZE</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"20"</span><span class="token plain">                        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># コネクションプールのサイズ。並行度／レプリカ数に応じてチューニング</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> REDIS_AUTH</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">valueFrom</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">secretKeyRef</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">redis</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">secret   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Secret からパスワードを注入し、平文を避ける</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">key</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> REDIS_AUTH</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> USE_STATSD</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"false"</span><span class="token plain">                     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># statsd/Prometheus のサイドカーがあれば有効化できる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> RUNTIME_ROOT</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> /data                       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># マウントポイントのルートディレクトリに対応</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> RUNTIME_SUBDIRECTORY</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit                   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># サブディレクトリ。最終的な設定パスは /data/ratelimit/config</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> RUNTIME_IGNOREDOTFILES</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"true"</span><span class="token plain">                      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 隠しファイルを無視する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">volumeMounts</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> config</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">mountPath</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> /data/ratelimit/config  </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 上の runtime パスと一致させ、config サブディレクトリに確実にマウントする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">readinessProbe</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">httpGet</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">path</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> /healthcheck</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">port</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">6070</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">initialDelaySeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">2</span><span class="token plain">               </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 初期遅延。コールドスタートの誤判定を避ける</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">periodSeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">5</span><span class="token plain">                     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 探査頻度</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">livenessProbe</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">httpGet</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">path</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> /healthcheck</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">port</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">6070</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">initialDelaySeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">10</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">periodSeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">10</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">volumes</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> config</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">configMap</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">config               </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 上の ConfigMap をマウントする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 3) IngressGateway にグローバルな RateLimit フィルターを注入 + ratelimit を指す CLUSTER</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - EnvoyFilter を使って HTTP Router の前にグローバルな ratelimit HTTP フィルターを挿入する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - workloadSelector：ゲートウェイのワークロードにのみ作用するよう限定する（label: istio=ingressgateway）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - rate_limit_service：上で定義した ratelimit gRPC に接続する（クラスタ名/authority 経由）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - Lua レスポンスフィルターを追加：バックエンドが 429 を返したとき、統一 JSON に書き換える</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> networking.istio.io/v1alpha3</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> EnvoyFilter</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> gateway</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">global</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">limit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">filter</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">workloadSelector</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">labels</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ingressgateway                 </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># IngressGateway のインスタンスにのみ作用する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">configPatches</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 3.1 router の前に ratelimit HTTP フィルターを挿入する（グローバルに有効）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">applyTo</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> HTTP_FILTER</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">match</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">context</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> GATEWAY</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">listener</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">filterChain</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">filter</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoy.filters.network.http_connection_manager    </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># HTTP コネクションマネージャ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">subFilter</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoy.filters.http.router                      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Router の前に挿入する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">patch</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">operation</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> INSERT_BEFORE</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoy.filters.http.ratelimit</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">typed_config</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">"@type"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> type.googleapis.com/envoy.extensions.filters.http.ratelimit.v3.RateLimit</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">domain</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ingress</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">ratelimit          </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ConfigMap 内の domain と一致していること</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">failure_mode_deny</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:hsl(220, 14%, 71%);font-weight:bold">false</span><span class="token plain">           </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># バックエンドの ratelimit サービスが異常なときにリクエストを拒否するか。false=通す（推奨）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">timeout</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> 10s                       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ratelimit サービスへのアクセスのタイムアウト</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">rate_limit_service</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">grpc_service</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">envoy_grpc</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">cluster_name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> outbound</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token plain">8081</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token plain">ratelimit.istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system.svc.cluster.local  </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Istio が自動生成するクラスタ名</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">authority</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ratelimit.istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system.svc.cluster.local                     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># HTTP/2 Host (SNI)。省略可</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">transport_api_version</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> V3         </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># v3 API を使う（推奨）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 3.2 ポート 1035 の Listener に Lua レスポンスフィルターをさらに挿入する（任意：この待受ポートにのみ有効）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">applyTo</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> HTTP_FILTER</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">match</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">context</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> GATEWAY</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">listener</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">portNumber</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">1035</span><span class="token plain">                      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 1035 ポートの Listener にのみ有効（あなたのゲートウェイがこのポートを公開している）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">filterChain</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">filter</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoy.filters.network.http_connection_manager</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">subFilter</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoy.filters.http.router</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">patch</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">operation</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> INSERT_BEFORE</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> envoy.filters.http.lua</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">typed_config</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">"@type"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">inlineCode</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token scalar string" style="color:hsl(95, 38%, 62%)"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">              function envoy_on_response(handle)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                local s = tonumber(handle:headers():get(":status") or "0")</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                if s == 429 then</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                  local body = '{"code":429,"message":"Too many requests. Please try again later."}'</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                  handle:headers():replace("content-type", "application/json")</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                  handle:headers():replace("content-length", tostring(#body))</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                  handle:body(true):setBytes(body)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">                end</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token scalar string" style="color:hsl(95, 38%, 62%)">              end</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 注意：</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># - ゲートウェイが 1035 を待ち受けていない（または複数ポートの）場合、このフィルターを全ポートに拡大すべきか確認すること（portNumber 条件を外す）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># - ローカルレートリミットでローカル拒否時に自機で 429 を生成し、この Lua で統一的に書き換えることもできる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">---</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 4) VirtualHost に per-route のレートリミットアクションを注入する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - 指定した Route（または vhost 全体）に actions を設定し、ConfigMap とマッチするディスクリプタを生成する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - ここでは二段の action を使う：</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#        a) header_value_match：:path が /api プレフィックスにヒットしたとき、descriptor_value=path-api を書き込む</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#        b) generic_key：さらに descriptor_value=istio-limit-v1 を追加する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - 二段の組み合わせ =&gt; (key=header_match,value=path-api) + (key=generic_key,value=istio-limit-v1)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#      これがちょうど ConfigMap と対応し、「1 秒 1 回」のレートリミットが有効になる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --------------------------------------------</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> networking.istio.io/v1alpha3</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> EnvoyFilter</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> gateway</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">limit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">descriptor</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">svc</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">system</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">workloadSelector</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">labels</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">istio</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ingressgateway                       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 作用範囲をゲートウェイに限定する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">configPatches</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">applyTo</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> HTTP_ROUTE</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">match</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">context</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> GATEWAY</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">routeConfiguration</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">vhost</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> host</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">port       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># この vhost 名に注意：通常は "&lt;host&gt;:&lt;port&gt;"</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">patch</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">operation</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> MERGE</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">route</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">rate_limits</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">              </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">actions</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">header_value_match</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">headers</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                      </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">":path"</span><span class="token plain">             </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 疑似ヘッダー :path をマッチ。プレフィックスで判定</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">prefix_match</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"/api"</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">expect_match</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:hsl(220, 14%, 71%);font-weight:bold">true</span><span class="token plain">            </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># マッチしたときのみこのディスクリプタを追加する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">descriptor_value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"path-api"</span><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ConfigMap の最上位 key/value に対応</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">generic_key</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">descriptor_value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"istio-limit-v1"</span><span class="token plain">    </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ConfigMap の二段目 key/value に対応</span><br></div></code></pre></div></div>
<p>いくつかの設定ポイントを掘り下げておきます。</p>
<p>1）<strong>二つの EnvoyFilter はどちらも欠かせません</strong>。一つ目は ratelimit フィルターをフィルターチェーンに取り付け、ratelimit サービスをどこに探しに行くかを伝えるだけです。「どのリクエストがどんなディスクリプタを持ってレートリミットに問い合わせるか」を実際に決めるのは二つ目です——ルートに <code>rate_limits.actions</code> がなければ、フィルターはリクエストに対して何もしません。</p>
<p>2）<strong><code>failure_mode_deny: false</code></strong>。ratelimit サービスや Redis が落ちたとき、拒否ではなく通す選択をします。レートリミットは保護手段であり、新たな単一障害点になるべきではありません。API が「利用不可でもいいから叩かれてはならない」ほど機微でない限り、false のままにすることを推奨します。</p>
<p>3）<strong>Lua フィルターは体験の最適化です</strong>。Envoy はレートリミットにヒットしたとき、デフォルトでは素の 429 を返すので、フロントエンドが統一的に処理しづらいです。この Lua はレスポンス段階で 429 の body を統一された JSON 構造に書き換え、クライアントにとってより親切にします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2025/11/04/istio#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>domain が一致しないと、レートリミットは静かに機能しなくなります</strong>。フィルター内の <code>domain</code> と ConfigMap 内の <code>domain</code> は一字一句同じでなければなりません。噛み合わないときエラーは出ず、ratelimit サービスがルールを見つけられないだけで、すべて OK を返します。</li>
<li class=""><strong>ディスクリプタの階層は完全に対応していなければなりません</strong>。ConfigMap で <code>header_match/path-api</code> が <code>generic_key/istio-limit-v1</code> をネストする二層構造は、ルート上の二つの action の前後の順序に対応します。action が一つ足りない、value を書き間違える、階層が逆——どれもヒットしません。</li>
<li class=""><strong>vhost 名は適当に書けません</strong>。<code>routeConfiguration.vhost.name</code> は通常 <code>"&lt;host&gt;:&lt;port&gt;"</code> の形式で、Envoy が実際に生成するルート設定内の vhost 名と一致させる必要があります。ゲートウェイの config_dump から確認できます。書き間違えると MERGE が効きません。</li>
<li class=""><strong>EnvoyFilter がマッチするポートに注意しましょう</strong>。上の Lua フィルターは <code>portNumber: 1035</code> に限定しています。ゲートウェイが別のポートや複数のポートを待ち受けている場合は、それに応じて調整するかこの条件を外してください。さもないと 429 の書き換えが一部のトラフィックにしか効きません。</li>
<li class=""><strong>ratelimit サービス自体の可用性</strong>。テスト環境では単一レプリカで問題ありませんが、本番では複数レプリカを推奨し、Redis も高可用性を考慮する必要があります。<code>REDIS_POOL_SIZE</code> は並行量とレプリカ数に応じて調整してください。</li>
<li class=""><strong>EnvoyFilter は比較的低レベルの拡張メカニズム</strong>で、Envoy の設定を直接パッチするため、Envoy 内部の API とバージョン的に結合します。Istio のメジャーバージョンをアップグレードする前に、まずテストクラスタでこの二つの filter が正常に効くかを検証しましょう。</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>レートリミットが効かないときは、パイプラインに沿って順に確認します。ratelimit サービスのログにリクエストが届いているか → domain が一致しているか → ディスクリプタの組み合わせが ConfigMap と揃っているか → vhost 名が正しいか。大半の問題は後ろの三つの「噛み合っていない」に起因します。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2025/11/04/istio#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ゲートウェイ層のグローバルレートリミットの核心は三つのことに尽きます。ルート上の <code>actions</code> がリクエストにディスクリプタを付与し、ratelimit サービスが ConfigMap 内のルールに従ってクォータを照会し、Redis がすべてのゲートウェイレプリカにカウントを共有させる。設定自体は複雑ではなく、難しさは domain と descriptor のマッチングに集中しています——これらの箇所は間違えてもエラーが出ず、ただ黙って効かなくなるだけです。マッチングのパイプラインを整理して理解できれば、あとは業務の必要に応じてクォータを調整するだけです。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[Alibaba Cloud リアルタイムデータウェアハウス]]></title>
            <link>https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang</link>
            <guid>https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang</guid>
            <pubDate>Wed, 10 Jan 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Alibaba Cloud の Flink、DataHub、Hologres でフルマネージドなリアルタイムデータウェアハウスを構築する方式：コンポーネント選定、処理フロー、そして ODS/DWD/DWS/ADS のレイヤー設計。]]></description>
            <content:encoded><![CDATA[<p>自前でビッグデータクラスタを構築すると、複雑でメンテナンスが難しく、デプロイコストも高くなりがちです。Alibaba Cloud の既存プロダクトエコシステムを活用すれば、これらの問題を回避できます。ここでは Alibaba Cloud のマネージドプロダクトでリアルタイムデータウェアハウスを構築する方式を記録します。必要に応じて対応するプロダクトを有効化するだけで、ビジネス要件を満たせます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="直面していた課題">直面していた課題<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E7%9B%B4%E9%9D%A2%E3%81%97%E3%81%A6%E3%81%84%E3%81%9F%E8%AA%B2%E9%A1%8C" class="hash-link" aria-label="直面していた課題 への直接リンク" title="直面していた課題 への直接リンク" translate="no">​</a></h2>
<p>1）基盤のデータベースが大量データを支えきれない。企業の成長にともない、10TB、100TB、さらには PB・EB 級のデータ量は保存しきれず、高速なクエリ応答、データ分析、データマイニングといった作業も支えられなくなります。</p>
<p>2）リアルタイム計算の性能が不足している。ミリ秒級のリアルタイム計算を行える信頼性の高い計算エンジンが必要で、なおかつデータ品質は信頼でき、制御でき、観測可能でなければなりません。</p>
<p>3）データモデルの変更効率が十分に速くない。データモデルの構造を柔軟に調整できず、ビジネスシーンのレポート要件に素早く応えることが困難です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="応用シーン">応用シーン<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E5%BF%9C%E7%94%A8%E3%82%B7%E3%83%BC%E3%83%B3" class="hash-link" aria-label="応用シーン への直接リンク" title="応用シーン への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><a href="https://yq.aliyun.com/articles/719246?spm=a2c6h.12873639.article-detail.10.35a62292oUX3aq&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class=""><strong>Flink とルールエンジンによるリアルタイム風険管理（リスクコントロール）ソリューション</strong></a></li>
<li class=""><a href="https://yq.aliyun.com/articles/692345?spm=a2c6h.12873639.article-detail.12.35a62292oUX3aq&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class=""><strong>リアルタイム計算（Flink）とガウスモデルによるリアルタイム異常検知システムの構築</strong></a></li>
<li class=""><a href="https://yq.aliyun.com/articles/691497?spm=a2c6h.12873639.article-detail.19.35a62292oUX3aq&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">リアルタイム計算（Flink）でシンプルなリアルタイム推薦システムを作る</a></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リアルタイムデータウェアハウス">リアルタイムデータウェアハウス<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%8F%E3%82%A6%E3%82%B9" class="hash-link" aria-label="リアルタイムデータウェアハウス への直接リンク" title="リアルタイムデータウェアハウス への直接リンク" translate="no">​</a></h2>
<p>全体のデータ開発フロー：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2024-03-p301715-260628d991918b4359af348e3a5f2781.webp" width="1162" height="547" class="img_ev3q"></p>
<p>パイプラインを要約すると、データ取得 → データバッファリング → リアルタイム計算 → シンクして格納、という流れです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2024-03-p399558-45f9e637bf9658f8bc0a4901192767fb.png" width="798" height="448" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンポーネント選定">コンポーネント選定<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E9%81%B8%E5%AE%9A" class="hash-link" aria-label="コンポーネント選定 への直接リンク" title="コンポーネント選定 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="flink">Flink<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#flink" class="hash-link" aria-label="Flink への直接リンク" title="Flink への直接リンク" translate="no">​</a></h3>
<p><a href="https://www.aliyun.com/product/bigdata/sc?spm=a2c4g.11186623.J_4VYgf18xNlTAyFFbOuOQe.115.7cf75c67wmW96D&amp;scm=20140722.S_product%40%40%E4%BA%91%E4%BA%A7%E5%93%81%40%40247054._.ID_product%40%40%E4%BA%91%E4%BA%A7%E5%93%81%40%40247054-RL_flink-LOC_menu%7EUND%7Eproduct-OR_ser-V_3-P0_0&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">Alibaba Cloud リアルタイムコンピューティング Flink 版</a></p>
<p>Alibaba Cloud のフルマネージド Flink リアルタイム計算エンジンで、企業が自前で Yarn や Flink のノードを構築したり、煩雑な設定作業を行ったりする必要がなくなります。主な役割は DataHub メッセージバス内のビジネスデータを処理し、データのビジネスシーンに応じて計算結果を後続のパイプラインへ適切に流すことです。Alibaba Cloud のエコシステムに支えられ、メタデータ管理もいっそう便利になります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="datahub">DataHub<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#datahub" class="hash-link" aria-label="DataHub への直接リンク" title="DataHub への直接リンク" translate="no">​</a></h3>
<p><a href="https://www.aliyun.com/product/bigdata/datahub?spm=5176.28508143.J_4VYgf18xNlTAyFFbOuOQe.48.6ce5154ajrJlWg&amp;scm=20140722.S_product%40%40%E4%BA%91%E4%BA%A7%E5%93%81%40%40813456._.ID_product%40%40%E4%BA%91%E4%BA%A7%E5%93%81%40%40813456-RL_datahub-LOC_menu%7EUND%7Eproduct-OR_ser-V_3-P0_0&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">データバス DataHub</a></p>
<p>DataHub の役割はデータバッファリング層に相当し、リアルタイムデータストリームに高性能なストレージとストリーミング転送を提供します。Flink の計算途中で生成される一部の DWD・DWS 層の一時データや下流データは、すべて DataHub を経由してタスクの転送と保存が行われ、メッセージバスの役割を果たします。topic は 1 日あたり TB 級のデータ量を読み書きでき、データの流れは高効率かつ安定しています。このコンポーネントも同様にフルマネージドで、ノード数を気にする必要はありません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="hologres">Hologres<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#hologres" class="hash-link" aria-label="Hologres への直接リンク" title="Hologres への直接リンク" translate="no">​</a></h3>
<p><a href="https://www.aliyun.com/product/bigdata/hologram?spm=5176.28348429.J_4VYgf18xNlTAyFFbOuOQe.86.59056284OdWWsx&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">リアルタイムデータウェアハウス Hologres - ビッグデータインタラクティブ分析 – 分析とサービングの一体化</a></p>
<p>Flink の計算が完了すると、対応するレイヤーのデータは Hologres データウェアハウスへ出力・シンクされます。Hologres は行ストア、カラムストア、そしてフィールドに応じたパーティションストレージをサポートしており、大量データの保存問題を解決するとともに、PB 級データのサブセカンド分析を実現しています。Hologres のストレージには Alibaba 自社開発の Pangu 分散ファイルシステム（HDFS に類似）が使われており、アーキテクチャ自体がビッグデータウェアハウスの設計体系に属し、全体として Storage Disaggregation（ストレージとコンピューティングの分離）アーキテクチャを採用しています。計算ノードは水平スケールでき、並列計算能力を高めて、大量データクエリの即時性の問題を解決します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2024-03-p431174-3bb38ec6264307fcee5ef97365184050.png" width="1011" height="595" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="処理フロー">処理フロー<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E5%87%A6%E7%90%86%E3%83%95%E3%83%AD%E3%83%BC" class="hash-link" aria-label="処理フロー への直接リンク" title="処理フロー への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2024-01-3-ad3171d5d2d0142e9db892126744680c.webp" width="1530" height="1140" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データウェアハウスのレイヤー設計">データウェアハウスのレイヤー設計<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%8F%E3%82%A6%E3%82%B9%E3%81%AE%E3%83%AC%E3%82%A4%E3%83%A4%E3%83%BC%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="データウェアハウスのレイヤー設計 への直接リンク" title="データウェアハウスのレイヤー設計 への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2024-03-v2-27465cd3ad33b8afaa35e914c7f308cb_r-1130bc39ed4a403069f137fd10db67df.png" width="695" height="466" class="img_ev3q"></p>
<p>ビッグデータの分野では、ODS、DWD、DWS、ADS はデータの組織化と管理に用いられる一般的なレイヤリングの概念で、データの処理と利用においてそれぞれ役割を分担しています。</p>
<ol>
<li class="">ODS（Operational Data Store、オペレーショナルデータストア層）：運用系業務に向けたデータストレージ層で、通常はソースシステムの生データを保存し、データの完全性と詳細性を保持します。ODS 層の主な目的は、オンライントランザクション処理やリアルタイムクエリなど、リアルタイムまたは準リアルタイムの運用ニーズを支えることです。この層のデータは通常、大規模な加工や計算を行わず、運用系業務にそのまま使われます。</li>
<li class="">DWD（Data Warehouse Detail、データウェアハウス明細層）：データウェアハウスに向けた明細データ層で、クレンジング・統合・変換を経たデータを保存します。通常はサブジェクト指向でクエリ可能なデータです。DWD 層のデータは ETL（抽出・変換・ロード）処理を経ており、データ分析、レポーティング、意思決定支援などのタスクを支えます。品質と一貫性が高く、通常はファクトテーブルとディメンションテーブルの形式で組織されます。</li>
<li class="">DWS（Data Warehouse Summary、データウェアハウス集計層）：集約・サマリー・事前計算を経たデータを保存し、より高いレベルのデータサマリーと分析性能を提供します。DWS 層のデータは通常、集計テーブルや事前計算された指標が中心で、複雑なデータ分析、データマイニング、ビジネスインテリジェンスのニーズを支えます。</li>
<li class="">ADS（Application Data Service、アプリケーションデータサービス層）：アプリケーションに向けたデータストレージ層で、さらなる加工・計算・最適化を経たデータを保存します。ADS 層のデータは通常、特定アプリケーション向けのデータビューやデータセットで、適合化と最適化を経て、より効率的なデータアクセスと処理能力を提供します。</li>
</ol>
<p>まとめると、ODS 層は運用系業務にサービスを提供し、生データの完全性を保持します。DWD 層はクレンジング・統合・変換後のクエリ可能なデータを提供します。DWS 層は集計と事前計算を行い、より高いクエリ性能と引き換えにします。ADS 層は特定のアプリケーションに向けて、加工・最適化済みのデータを提供します。</p>
<p>このレイヤー設計にはいくつかのメリットがあります。</p>
<ol>
<li class="">データ処理と管理の分離：データを異なるレイヤーに分けることで、処理と管理の責任も分かれます。DWD 層は生データの抽出・クレンジング・統合を、DWS 層は集約とサマリーを、ADS 層はアプリケーション向けデータサービスを担当し、データ処理はモジュール化されてより柔軟になります。</li>
<li class="">データ品質と一貫性：DWD 層は生データの完全性と詳細を保持し、データ品質分析やデータリネージ（トレーサビリティ）に利用できます。DWS 層は集約・サマリー済みのデータを提供し、クエリ性能に優れます。ADS 層は異なるアプリケーションシーンにおけるデータの一貫性と正確性を保証します。</li>
<li class="">クエリ性能とアプリケーション開発効率：サマリーと事前計算により、DWS 層はデータ分析と意思決定のプロセスを加速できます。ADS 層はアプリケーション向けのデータサービスとインターフェースを提供し、アプリケーション開発を簡素化して開発効率を高めます。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2024/01/10/a-li-yun-shi-shi-shu-cang#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>この方式の核心的な考え方は、クラスタ運用をクラウドベンダーに任せることです。DataHub がデータのバッファリングと流通を、Flink がリアルタイム計算を、Hologres がストレージと分析を担い、全パイプラインがマネージドなので自分でノードを維持する必要がありません。ODS/DWD/DWS/ADS のレイヤー設計と組み合わせることで、各層のデータの責務が明確になり、クエリ性能とレポート開発の効率がともに保証されます。ビッグデータクラスタの自前構築に人手を割きたくないチームにとって、これは現実的な道筋です。</p>]]></content:encoded>
            <category>Linux</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[gRPC TLS 自己署名証明書]]></title>
            <link>https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s</link>
            <guid>https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s</guid>
            <pubDate>Wed, 08 Nov 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[OpenSSL で gRPC の内部ネットワーク通信用に自己署名証明書を生成する一連の流れの記録:ルート証明書、サービス証明書、SAN 拡張フィールド、証明書内容の検証。]]></description>
            <content:encoded><![CDATA[<p>マイクロサービス間の通信では、内部ネットワーク通信の安全を確保するためにデータの暗号化が必要です。そこで、自己署名証明書の生成手順を記録しておきます。</p>
<!-- -->
<p>まず背景から説明します。gRPC はデフォルトで平文の HTTP/2 を使うため、サービス間のリクエストボディは内部ネットワーク上でパケットキャプチャすればそのまま読めてしまいます。本番環境では、たとえ内部ネットワークであってもネットワークが絶対に信頼できるとは想定できません。一台のマシンが侵害されれば、横方向にトラフィックを盗聴するコストは非常に低いのです。gRPC に TLS を加えることが、最も直接的な防御手段です。そして内部サービスのためにパブリック CA へ証明書を申請する必要はなく(そもそも申請できないことも多く)、自前でルート証明書を構築して各サービスに証明書を発行するのが、より一般的なやり方です。この一連の流れは OpenSSL だけで完結しますが、細かい点が多く、特に Go 側の証明書フィールド検証ルールについては、一通り記録しておく価値があります。</p>
<p>以下は Linux または MacOS で OpenSSL コマンドラインツールを使って自己署名証明書を生成する手順です。この過程では、まず新しいルート証明書(CA)を作成し、次にそのルート証明書で新しいサーバー証明書に署名します。</p>
<p>ここでの証明書の体系は 2 層構造です。ルート証明書は自分自身に署名し、信頼のアンカーとして機能します。サービス証明書はルート証明書によって発行され、クライアントはルート証明書をあらかじめ信頼しておくだけで、それが発行したどのサービス証明書も検証できます。クライアント側に <code>ca.crt</code> だけを配布すればよく、各サービスの証明書を個別に配布する必要がないのはこのためです。</p>
<p>まず全体の流れを簡潔に示します。</p>
<p>ルート証明書:</p>
<p>1、ルート秘密鍵を生成する</p>
<p>2、ルート秘密鍵からルート証明書を生成する</p>
<p>サービス証明書</p>
<p>1、サービス秘密鍵を生成する</p>
<p>2、サービス秘密鍵からサービスの <strong>CSR</strong> を生成する</p>
<p>3、サービス .csr ファイル + ルート証明書 + ルート証明書の key でサービス証明書 .crt ファイルに署名する</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ルート証明書の秘密鍵を生成する"><strong>ルート証明書の秘密鍵を生成する</strong><a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%83%AB%E3%83%BC%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E7%A7%98%E5%AF%86%E9%8D%B5%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B" class="hash-link" aria-label="ルート証明書の秘密鍵を生成する への直接リンク" title="ルート証明書の秘密鍵を生成する への直接リンク" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 2048 ビットの RSA 秘密鍵を生成し、ルート CA の秘密鍵とする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl genrsa </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> ca.key </span><span class="token number" style="color:hsl(29, 54%, 61%)">2048</span><br></div></code></pre></div></div>
<p>このコマンドは、長さ 2048 ビットの新しい RSA 秘密鍵を生成します。この秘密鍵は ca<code>.key</code> という名前のファイルに保存されます。</p>
<p>この秘密鍵は信頼チェーン全体の根であり、手に入れた者は誰でも、すべてのクライアントから信頼される証明書を発行できてしまいます。厳重に保管し、コードリポジトリにはコミットしないでください。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ルート証明書を生成する"><strong>ルート証明書を生成する</strong><a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%83%AB%E3%83%BC%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B" class="hash-link" aria-label="ルート証明書を生成する への直接リンク" title="ルート証明書を生成する への直接リンク" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ルート秘密鍵で X.509 ルート証明書を自己署名する。有効期間は 365 日</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl req </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-new</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-x509</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-days</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">365</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-key</span><span class="token plain"> ca.key </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> ca.crt</span><br></div></code></pre></div></div>
<p>このコマンドは、SHA-256 アルゴリズムで署名された新しい X.509 ルート証明書を生成します。有効期間は 365 日です(日数は自由に調整できます)。この証明書は ca<code>.crt</code> という名前のファイルに保存されます。</p>
<p><code>-x509</code> オプションは、署名リクエストではなく自己署名証明書を直接出力することを意味します。これこそが「ルート証明書が自分自身に署名する」ということです。</p>
<p>このコマンドを実行すると、OpenSSL はいくつかの情報の入力を求めてきます。これらの情報は証明書に含まれます。ほとんどの場合、そのまま Enter キーを押してデフォルト値を受け入れて構いません。</p>
<p>ただし <strong>Subject Name または Common Name</strong> にはマイクロサービス側の名前を記入する必要があります。</p>
<p>ルート証明書の準備ができたら、次は個々のサービス用の証明書を生成します。各サービスごとに以下の 3 ステップを繰り返すだけです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サーバー証明書の秘密鍵を生成する"><strong>サーバー証明書の秘密鍵を生成する</strong><a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E7%A7%98%E5%AF%86%E9%8D%B5%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B" class="hash-link" aria-label="サーバー証明書の秘密鍵を生成する への直接リンク" title="サーバー証明書の秘密鍵を生成する への直接リンク" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># サービス用に 2048 ビットの RSA 秘密鍵を生成する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl genrsa </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.key </span><span class="token number" style="color:hsl(29, 54%, 61%)">2048</span><br></div></code></pre></div></div>
<p>このコマンドは最初のコマンドと同様で、生成された秘密鍵は [service-name]<code>.key</code> という名前のファイルに保存されます。[service-name] はあなた自身のサービス名です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サーバー証明書の証明書署名リクエストcsrを生成する"><strong>サーバー証明書の証明書署名リクエスト(CSR)を生成する</strong><a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E8%A8%BC%E6%98%8E%E6%9B%B8%E7%BD%B2%E5%90%8D%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88csr%E3%82%92%E7%94%9F%E6%88%90%E3%81%99%E3%82%8B" class="hash-link" aria-label="サーバー証明書の証明書署名リクエストcsrを生成する への直接リンク" title="サーバー証明書の証明書署名リクエストcsrを生成する への直接リンク" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># サービス秘密鍵をもとに証明書署名リクエスト(CSR)を生成する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl req </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-new</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-key</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.key </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.csr</span><br></div></code></pre></div></div>
<p>このコマンドは、新しい証明書署名リクエスト(CSR)を生成します。このリクエストにはサーバー証明書の公開鍵といくつかの付加情報が含まれます。このリクエストは <code>server.csr</code> という名前のファイルに保存されます。</p>
<p>CSR 自体は証明書ではありません。「私は誰で、私の公開鍵は何か」を記した申請書類にすぎず、CA に署名してもらって初めて使える証明書になります。</p>
<p>同様に、このコマンドを実行すると OpenSSL はいくつかの情報の入力を求めてきます。これらの情報は CSR に含まれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ルート証明書でサーバー証明書に署名する"><strong>ルート証明書でサーバー証明書に署名する</strong><a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%83%AB%E3%83%BC%E3%83%88%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%A7%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AB%E7%BD%B2%E5%90%8D%E3%81%99%E3%82%8B" class="hash-link" aria-label="ルート証明書でサーバー証明書に署名する への直接リンク" title="ルート証明書でサーバー証明書に署名する への直接リンク" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ルート証明書 + ルート秘密鍵で CSR に署名し、サービス証明書を得る</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-req</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-in</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.csr </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-CA</span><span class="token plain"> ca.crt </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-CAkey</span><span class="token plain"> ca.key </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-CAcreateserial</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.crt </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-days</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">365</span><br></div></code></pre></div></div>
<p>このコマンドはルート証明書でサーバー証明書に署名し、生成された証明書は <code>server.crt</code> という名前のファイルに保存されます。</p>
<p><code>-CAcreateserial</code> は <code>.srl</code> というシリアル番号ファイルを生成します。これはこの CA が発行した証明書の連番を記録するもので、以降の発行時には自動的にインクリメントされるため、手動で扱う必要はありません。</p>
<p>以上の手順で生成した <code>rootCA.crt</code>、<code>server.crt</code>、<code>server.key</code> を対応するサーバー側とクライアント側に設定すれば、gRPC の暗号化通信に使えます。サーバー側は自身の証明書と秘密鍵をロードし、クライアント側はサーバーの身元検証のためにルート証明書をロードします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="一部のカスタム項目">一部のカスタム項目<a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E4%B8%80%E9%83%A8%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E9%A0%85%E7%9B%AE" class="hash-link" aria-label="一部のカスタム項目 への直接リンク" title="一部のカスタム項目 への直接リンク" translate="no">​</a></h2>
<p>Go のより新しいバージョンでは、証明書内の一部フィールドの値の検証と取得に変更がありました。(Go 1.15 以降は証明書の alt_names フィールドの情報を取得してサービス名の検証を行います。)そのため、証明書生成時に一部のカスタムフィールド情報を指定する必要があります。</p>
<p>つまり、Common Name にサービス名を書くだけではもう不十分だということです。Go の TLS クライアントはハンドシェイク時に、接続先の名前を証明書の SAN(Subject Alternative Name)フィールドと照合し、一致しなければハンドシェイクは即座に失敗します。したがって証明書には SAN 拡張を明示的に含める必要があります。こうした拡張フィールドはコマンドラインの対話入力では設定しにくいので、設定ファイル方式に切り替えます。</p>
<p>san.cnf というファイルを作成し、内容は以下のとおりにします。</p>
<div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">[req]</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">default_bits</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">2048</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">prompt</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">no</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">default_md</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">sha256</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">distinguished_name</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">dn</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 証明書のサブジェクト情報。対話式入力の代わり</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">[dn]</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">C</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">CN</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">ST</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">Beijing</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">L</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">Beijing</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">O</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">'lynx'</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">OU</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">'lynx'</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">emailAddress</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">'lynx'</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">CN</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">[service-name]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># CSR 段階で使う拡張:SAN を宣言する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">[req_ext]</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">subjectAltName</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">@alt_names</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># SAN のリスト。Go クライアントが検証するのはここの DNS 名</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">[alt_names]</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">DNS.1</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">[service-name]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 証明書発行段階で使う拡張</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">[v3_ext]</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">authorityKeyIdentifier</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">keyid,issuer:always</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">basicConstraints</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">CA:FALSE</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">keyUsage</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">keyEncipherment,dataEncipherment,digitalSignature</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">extendedKeyUsage</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">serverAuth,clientAuth</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">subjectAltName</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">@alt_names</span><br></div></code></pre></div></div>
<p>設定内の <code>prompt = no</code> により、OpenSSL は対話式の質問をやめて <code>[dn]</code> セクションの内容を直接読み込みます。<code>extendedKeyUsage</code> には <code>serverAuth</code> と <code>clientAuth</code> の両方が宣言されているので、この証明書はサーバー側で使えるだけでなく、相互 TLS(mTLS)のシーンではクライアント証明書としても使えます。</p>
<p>その上で、前述の証明書生成の手順に以下を加えます。</p>
<p>サービスの csr ファイル生成時に -extensions req_ext -config san.cnf を追加します。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># CSR 生成時に設定ファイルを参照し、req_ext セクションで宣言した SAN を含める</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl req </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-new</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-key</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.key </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.csr </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-extensions</span><span class="token plain"> req_ext </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-config</span><span class="token plain"> san.cnf</span><br></div></code></pre></div></div>
<p>サービス証明書の生成時にも、-extensions v3_ext -extfile san.cnf を追加する必要があります。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 発行時にも v3_ext 拡張を付けること。付けないと SAN が最終的な証明書に書き込まれない</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-req</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-in</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.csr </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-CA</span><span class="token plain"> ca.crt </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-CAkey</span><span class="token plain"> ca.key </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-CAcreateserial</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-out</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.crt </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-days</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">365</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-extensions</span><span class="token plain"> v3_ext </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-extfile</span><span class="token plain"> san.cnf</span><br></div></code></pre></div></div>
<p>こうして生成された証明書ファイルには、指定したフィールドの内容と、いくつかの証明書記述情報が含まれます。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>発行ステップの <code>-extensions v3_ext -extfile san.cnf</code> は見落としやすいポイントです。<code>openssl x509 -req</code> はデフォルトでは CSR 内の拡張フィールドを最終的な証明書に持ち込みません。CSR 段階でしか SAN を宣言していないと、発行された証明書には SAN が含まれないままになります。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="証明書内容の検証">証明書内容の検証<a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E8%A8%BC%E6%98%8E%E6%9B%B8%E5%86%85%E5%AE%B9%E3%81%AE%E6%A4%9C%E8%A8%BC" class="hash-link" aria-label="証明書内容の検証 への直接リンク" title="証明書内容の検証 への直接リンク" translate="no">​</a></h2>
<p>コマンドで、生成された証明書に本当にフィールド情報が含まれているか確認できます。</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 証明書の内容をテキスト形式で出力し、SAN などの拡張フィールドを確認する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">openssl x509 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-in</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">service-name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.crt </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-text</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-noout</span><br></div></code></pre></div></div>
<p>出力では、<code>X509v3 Subject Alternative Name</code> のセクションに期待どおりの DNS 名が含まれているか、そして有効期間と発行者が正しいかを重点的に確認します。このステップは定型フローに組み込むことをおすすめします。サービスをリリースしてからハンドシェイクエラーで調査するより、はるかに手間が省けます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1)クライアントが接続時に使う宛先の名前は、証明書の SAN 内の DNS 名と一致していなければなりません。IP 直結で接続する場合は、SAN に <code>IP.1 = ...</code> の形式で IP エントリを宣言する必要があります。DNS エントリでは一致しません。</p>
<p>2)証明書の有効期限が切れると gRPC のハンドシェイクは即座に失敗しますが、この種の障害は期限当日になって初めて表面化しがちです。内部の自己署名証明書は誰も更新をリマインドしてくれないので、有効期限を記録して前もって差し替えるのが得策です。</p>
<p>3)<code>ca.key</code> と各サービスの <code>.key</code> ファイルはいずれも機密情報です。ファイル権限に注意し、イメージやリポジトリと一緒に配布しないこと。クライアントに必要なのは <code>ca.crt</code> だけです。</p>
<p>4)san.cnf を変更したら、CSR と発行の 2 ステップをやり直す必要があります。CSR を再生成せずに証明書だけ再署名すると、古いサブジェクト情報を引きずる可能性があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/11/08/grpcs-t-l-s#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>全体の流れは一本の信頼チェーンに凝縮できます。ルート秘密鍵からルート証明書を生成し、サービス秘密鍵から CSR を生成し、ルート証明書が CSR に署名してサービス証明書が得られる。Go の gRPC のシーンでのポイントは、san.cnf で SAN フィールドを証明書に書き込むこと、CSR と発行の両方の段階で拡張を明示的に指定すること、そして最後に <code>openssl x509 -text</code> で検証することです。これらのステップをスクリプトに固めてしまえば、新しいサービスへの証明書発行は 1 分で済む作業になります。</p>]]></content:encoded>
            <category>Go</category>
        </item>
        <item>
            <title><![CDATA[Dinky]]></title>
            <link>https://tanzhuo.xyz/ja/2023/09/08/dinky</link>
            <guid>https://tanzhuo.xyz/ja/2023/09/08/dinky</guid>
            <pubDate>Fri, 08 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Dinky はオープンソースの Flink ジョブ管理・開発プラットフォームで、Flink SQL のオンライン作成・デバッグ、データベース丸ごと同期、データリネージ分析をサポートします。私が理解した内容と使用シナリオを記録します。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>リアルタイム処理をやっている方の多くは、似たような実感を持っているのではないでしょうか。Flink 自体は十分に強力なのに、それを取り巻く開発・運用の体験はあまり親切ではありません。書き上げた SQL はパッケージングして提出しなければならず、デバッグはログをめくる必要があり、複数のクラスタはそれぞれバラバラに管理され、タスクが増えると混乱してしまいます。最近リアルタイムデータウェアハウスのツールチェーンを調査する中で Dinky に出会ったので、これがどんな問題を解決してくれるのかをここに記録しておきます。</p>
<!-- -->
<p>Dinky はオープンソースの <strong>Flink ジョブ管理・開発プラットフォーム</strong> で、全体の設計が軽量で使いやすく、複数の Flink クラスタを統一的に管理できます。<br>
開発者は Web コンソールから直接オンラインで Flink SQL を作成・デバッグでき、開発完了後はワンクリックで指定した Flink クラスタにジョブを提出して実行できます。これによりリアルタイム処理タスクの開発・運用コストが大幅に下がります。</p>
<p>言い換えれば、「SQL を書く、デバッグする、提出する、監視する」という一連のチェーン全体を、1 つのブラウザページに収めてくれるのです。ネイティブな Flink の使い方——ローカルでコードを書き、jar をビルドし、コマンドラインで提出し、Flink Web UI で状態を確認する——と比較すると、このワンストップの体験は、純粋な SQL ベースのリアルタイムタスク開発にとって非常に手間を省いてくれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="公式サイト">公式サイト<a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E5%85%AC%E5%BC%8F%E3%82%B5%E3%82%A4%E3%83%88" class="hash-link" aria-label="公式サイト への直接リンク" title="公式サイト への直接リンク" translate="no">​</a></h2>
<p><a href="https://github.com/DataLinkDC/dinky" target="_blank" rel="noopener noreferrer" class="">https://github.com/DataLinkDC/dinky</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2023-09-image-45b4a814277ea0241a0bcacb441651e3.webp" width="1600" height="832" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="機能の説明">機能の説明<a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E6%A9%9F%E8%83%BD%E3%81%AE%E8%AA%AC%E6%98%8E" class="hash-link" aria-label="機能の説明 への直接リンク" title="機能の説明 への直接リンク" translate="no">​</a></h2>
<p>Dinky には <strong>データベース丸ごと同期（整庫同期）機能</strong> が組み込まれており、マイクロサービスシステム内のデータベーステーブルのデータをリアルタイムデータウェアハウスに同期できます。これにより、マイクロサービスアーキテクチャにおけるデータサイロの問題を効果的に解決できます。</p>
<p>この点は少し掘り下げる価値があります。マイクロサービスアーキテクチャでは各サービスが自分のデータベースを持っており、データ分析の際には数十から数百のテーブルを 1 か所に集約する必要がしばしばあります。ネイティブな Flink CDC を使う場合、通常はテーブル 1 枚につき source 定義を 1 つ書く必要があり、テーブルが多いと煩雑なうえに大量のデータベース接続を占有します。丸ごと同期の考え方はこうです。1 つのジョブでデータベース全体の変更ログ（MySQL の binlog など）を読み取り、ジョブ内部でテーブルごとに分流して下流に書き込む。これなら設定量もリソース占有もはるかに小さくなります。</p>
<p>同時に、Dinky は <strong>マルチバージョンの Flink SQL の開発と管理</strong> をサポートし、<strong>データリネージ（血縁）分析機能</strong> も提供しています。これにより開発者はデータの流れと依存関係をより明確に把握でき、問題の調査やシステムの保守がしやすくなります。</p>
<p>リネージ分析の価値は、タスクの規模が大きくなってから初めてはっきりします。ある結果テーブルのデータに異常が出たとき、リネージグラフをたどって、それが依存する中間テーブルやソーステーブルへ遡って調査できます。人間の記憶やコードの読み返しに頼る必要はありません。逆に、あるテーブルを廃止・変更したいときも、どの下流タスクが影響を受けるかを先に見通せます。</p>
<p>全体として見ると、Dinky はリアルタイムデータ開発、タスク管理、データガバナンスの面で機能がかなり充実しており、非常に実用的な Flink データ開発プラットフォームです。リアルタイムデータウェアハウスのプロジェクトでの使用と普及に値します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リアルタイムデータウェアハウスの構築"><strong>リアルタイムデータウェアハウスの構築</strong><a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%8F%E3%82%A6%E3%82%B9%E3%81%AE%E6%A7%8B%E7%AF%89" class="hash-link" aria-label="リアルタイムデータウェアハウスの構築 への直接リンク" title="リアルタイムデータウェアハウスの構築 への直接リンク" translate="no">​</a></h2>
<p>Flink SQL + Dinky によって、リアルタイムデータウェアハウスのタスクを素早く構築できます。例えば：</p>
<ul>
<li class="">ユーザー行動のリアルタイム分析</li>
<li class="">取引データのリアルタイム集計</li>
<li class="">リアルタイムのリスク管理システム</li>
</ul>
<p>これらのシナリオに共通するのは、データが絶え間なく流れ込み、ビジネス側が分単位、さらには秒単位で結果を見たいと要求することです。従来のオフラインデータウェアハウスは日次や時間単位のバッチ実行で、この即時性の要求を満たせません。一方、Flink SQL でロジックをストリーミングタスクとして書き、Dinky にライフサイクルの管理を任せれば、開発のリズムはオフラインの SQL を書くのとかなり近いものになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データ同期と-cdc"><strong>データ同期と CDC</strong><a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E3%83%87%E3%83%BC%E3%82%BF%E5%90%8C%E6%9C%9F%E3%81%A8-cdc" class="hash-link" aria-label="データ同期と-cdc への直接リンク" title="データ同期と-cdc への直接リンク" translate="no">​</a></h2>
<p>Dinky のデータベース丸ごと同期機能を利用すれば、データベースの変更データキャプチャ（CDC）を実現し、ビジネスデータベースのデータをデータウェアハウスやメッセージキューへリアルタイムに同期できます。</p>
<p>CDC の原理は、簡単に言えばデータベースのトランザクションログを購読することです。INSERT / UPDATE / DELETE の 1 件 1 件がログに記録を残し、CDC ツールがそれらの記録を変更イベントのストリームとして解析します。定期的にテーブル全体をスキャンする同期方式に比べて、ソースデータベースへの侵入が小さく、レイテンシが低く、削除操作までキャプチャできます。典型的な Flink SQL の CDC ソーステーブル定義は、だいたい次のような形です：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- CDC ソーステーブルを宣言し、MySQL の orders テーブルの変更をリアルタイムにキャプチャする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">TABLE</span><span class="token plain"> orders_source </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  order_id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">BIGINT</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  user_id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">BIGINT</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  amount </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">DECIMAL</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token number" style="color:hsl(29, 54%, 61%)">10</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">2</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">KEY</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token plain">order_id</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">NOT</span><span class="token plain"> ENFORCED</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">WITH</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token string" style="color:hsl(95, 38%, 62%)">'connector'</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'mysql-cdc'</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- MySQL CDC コネクタを使って binlog を読み取る</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token string" style="color:hsl(95, 38%, 62%)">'hostname'</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'...'</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token string" style="color:hsl(95, 38%, 62%)">'database-name'</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'...'</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token string" style="color:hsl(95, 38%, 62%)">'table-name'</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'orders'</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><br></div></code></pre></div></div>
<p>Dinky ではこの SQL をそのままオンラインでデバッグして結果をプレビューでき、問題がないことを確認してからクラスタに提出できます。ローカルで何度もパッケージングして検証するプロセスを省けるのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リアルタイムデータ開発プラットフォーム"><strong>リアルタイムデータ開発プラットフォーム</strong><a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E3%83%AA%E3%82%A2%E3%83%AB%E3%82%BF%E3%82%A4%E3%83%A0%E3%83%87%E3%83%BC%E3%82%BF%E9%96%8B%E7%99%BA%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0" class="hash-link" aria-label="リアルタイムデータ開発プラットフォーム への直接リンク" title="リアルタイムデータ開発プラットフォーム への直接リンク" translate="no">​</a></h2>
<p>Dinky は企業内部の統一されたリアルタイム処理開発プラットフォームとしても使え、データ開発エンジニアに標準化された開発環境を提供します。</p>
<p>統一プラットフォームの意義は手間の削減だけではありません。SQL スクリプトが集中管理されてバージョン履歴が残り、タスクの提出と運用の方式が統一されるので、新しいメンバーがプロジェクトを引き継ぐときに、各人バラバラの提出スクリプトや環境の違いを先に把握する必要がなくなります。チームコラボレーションに対するこの価値は、個々の機能ポイントよりも大きいことが少なくありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">Dinky 自体は計算タスクを実行しません。あくまで提出と管理の入り口であり、Flink クラスタは別途デプロイ・保守する必要があります。両者のバージョン互換性は事前に確認しましょう。</li>
<li class="">CDC によるデータベース丸ごと同期を使う前に、ソースデータベースで変更ログ（MySQL の binlog など）が有効になっていることを確認し、同期用アカウントに相応の権限を付与する必要があります。そうしないとジョブが起動しません。</li>
<li class="">オンラインデバッグは便利ですが、デバッグ環境のリソースとデータ量は本番クラスタとは異なります。ロジックが複雑なタスクは、リリース前に本番と同スペックの環境で一度検証することをおすすめします。</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>選定の際は、テスト環境を直接立ち上げて、自分のビジネスで最も複雑なチェーン 1 本（例えば大きなテーブルの丸ごと同期 + 複数テーブル join の SQL）で全フローを通してみることをおすすめします。機能リストを眺めるよりずっと多くのことがわかります。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/09/08/dinky#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Dinky が補っているのは、Flink エコシステムにおける「開発体験」という弱点です。オンラインでの SQL 作成、ワンクリック提出、データベース丸ごと同期、リネージ分析と、リアルタイムデータウェアハウス開発の主な日常業務をカバーしています。チームのリアルタイムタスクが Flink SQL 中心で、統一された開発・管理の入り口がなくて困っているなら、選定候補に加える価値があります。</p>]]></content:encoded>
            <category>ビッグデータ</category>
        </item>
        <item>
            <title><![CDATA[SonarQube]]></title>
            <link>https://tanzhuo.xyz/ja/2023/09/08/sonarqube</link>
            <guid>https://tanzhuo.xyz/ja/2023/09/08/sonarqube</guid>
            <pubDate>Fri, 08 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[コード品質管理プラットフォーム SonarQube 入門:静的解析の仕組み、よく使われる品質指標、Maven プロジェクトへの導入方法、そして CI/CD パイプラインで Quality Gate として組み合わせる使い方を紹介します。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>コードが書き終わって動いたとしても、それが良いコードだとは限りません。この記事では、SonarQube について自分なりに整理した内容を記録しておきます。どんな問題を検出できるのか、裏側でどう動いているのか、そして CI/CD の中でどう使うのか、という観点です。</p>
<!-- -->
<p>なぜこのテーマを 1 本の記事として書く価値があるのでしょうか。コード品質の問題には、「書いているときには見えず、問題が起きたときには高くつく」という特徴があるからです。人手による Code Review である程度は食い止められますが、レビュアーの労力と経験に依存しますし、多くの繰り返し作業(null チェック、リソースのクローズ忘れ、ハードコードされたパスワードの確認など)は完全にツールに任せられます。チームの規模が大きくなると、統一された自動化済みの品質基準がなければ、コードベースはあっという間にバラバラの書き方になってしまいます。</p>
<p>SonarQube は<strong>コード品質管理</strong>のためのオープンソースプラットフォームで、開発プロセスの中でコードに潜む問題を継続的に検出する手助けをしてくれます。<br>
静的コード解析、コードカバレッジの集計、多角的な品質指標を通じて、SonarQube はコードが本番環境に入る前に欠陥、セキュリティ脆弱性、パフォーマンス上の懸念を発見し、システム全体の安定性を高め、後々のメンテナンスコストを下げることができます。</p>
<p>チーム開発や継続的インテグレーション環境において、SonarQube は通常**コード品質のゲートキーパー(Quality Gate)**として使われ、コード品質チェックの自動化と標準化を実現します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="動作の仕組み">動作の仕組み<a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E5%8B%95%E4%BD%9C%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="動作の仕組み への直接リンク" title="動作の仕組み への直接リンク" translate="no">​</a></h2>
<p>まずは SonarQube がどう動くのかを簡単に説明します。仕組みを理解すれば、後の設定は難しくありません。</p>
<p>SonarQube の解析は<strong>静的解析</strong>に分類されます。コードを実行せず、ソースコードを構文木に解析し、ルールエンジンで既知の問題パターンとマッチングします。全体のアーキテクチャは 2 つの役割に分かれています。</p>
<ol>
<li class=""><strong>Scanner(スキャナー側)</strong>:ビルドマシン上で動作し、ソースコードの解析、ルールの実行、カバレッジレポートの収集を担当し、解析結果をサーバー側に送信します。Maven、Gradle、コマンドラインそれぞれに対応する Scanner があります。</li>
<li class=""><strong>Server(サーバー側)</strong>:履歴データの保存、Web レポートのレンダリング、Quality Gate の判定を担当します。各スキャン結果は過去のバージョンと比較されるため、品質のトレンドを追うことができます。</li>
</ol>
<p>Quality Gate は本質的にしきい値アサーションの集合です。たとえば「新規コードの Bug 数が 0」「新規コードのカバレッジが一定割合以上」などです。スキャン完了後、サーバー側が 1 条件ずつ判定し、1 つでも満たされなければ全体のステータスは失敗になります。CI パイプラインはこのステータスをもとにリリースを中断するかどうかを判断できます。これこそが「実行後にレポートを眺めるだけ」のツールとの決定的な違いです。結果がプロセスをブロックできるのです。</p>
<p>もうひとつ触れておきたいのが「新規コード(New Code)」という概念です。既存の古いプロジェクトでは、すべての過去の問題を一度に修正するのは現実的ではありません。SonarQube のデフォルト戦略は、新規追加・変更されたコードにのみ厳しく要求し、既存の問題は徐々に消化していくというものです。これによって古いプロジェクトでもスムーズに導入できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コード品質の向上"><strong>コード品質の向上</strong><a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E3%82%B3%E3%83%BC%E3%83%89%E5%93%81%E8%B3%AA%E3%81%AE%E5%90%91%E4%B8%8A" class="hash-link" aria-label="コード品質の向上 への直接リンク" title="コード品質の向上 への直接リンク" translate="no">​</a></h2>
<p>SonarQube はコード内の潜在的な問題を自動検出できます。たとえば:</p>
<ul>
<li class="">コード欠陥(Bug)</li>
<li class="">セキュリティ脆弱性(Security Vulnerabilities)</li>
<li class="">コードスメル(Code Smell)</li>
<li class="">潜在的なパフォーマンス問題</li>
</ul>
<p>これらの問題の深刻度は順に下がっていきます。Bug は高い確率でエラーを起こすロジックで、たとえば条件が常に真になる、null ポインタ参照の可能性があるといったものです。セキュリティ脆弱性はインジェクションやハードコードされた認証情報のようなリスクに対応します。コードスメルは正しさには影響しないものの、コードをどんどん変更しづらくしていくもので、たとえば長すぎるメソッドや深すぎるネストなどです。SonarQube は各問題に深刻度レベルと修正の見積もり時間を付与するので、優先順位付けがしやすくなります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コード保守性の向上"><strong>コード保守性の向上</strong><a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E3%82%B3%E3%83%BC%E3%83%89%E4%BF%9D%E5%AE%88%E6%80%A7%E3%81%AE%E5%90%91%E4%B8%8A" class="hash-link" aria-label="コード保守性の向上 への直接リンク" title="コード保守性の向上 への直接リンク" translate="no">​</a></h2>
<p>SonarQube はコードを多角的に分析します。たとえば:</p>
<ul>
<li class="">複雑度</li>
<li class="">重複コード</li>
<li class="">テストカバレッジ</li>
<li class="">技術的負債(Technical Debt)</li>
</ul>
<p>これらの指標を通じて、開発者はシステムの健康状態をより直感的に把握し、的を絞ったリファクタリングや最適化を行えます。</p>
<p>ここでいう技術的負債はとても直感的な指標です。修正待ちのすべての問題の見積もり工数を合計すると、「借金を完済する」のに必要な時間が得られます。必ずしも正確ではありませんが、トレンドには意味があります。技術的負債が増え続けているなら、チームは未来の開発効率を前借りしているということです。</p>
<p>開発者はコードのリリース前にこれらの問題を発見・修正でき、本番環境での障害を減らすことができます。</p>
<p>検出能力そのもの以外にも、SonarQube にはエンジニアリング実践上いくつかの利点があります。</p>
<ol>
<li class="">豊富なプラグインエコシステム:SonarQube は多くのプログラミング言語とフレームワークをサポートし、豊富なプラグインエコシステムを提供しています。プロジェクトの要件に応じて適切なプラグインを選び、機能を拡張できます。</li>
<li class="">継続的インテグレーション・継続的デプロイ:SonarQube は継続的インテグレーション(CI)や継続的デプロイ(CD)ツールと統合でき、コードチェックと品質測定を自動化することで、すべてのビルドが良好なコード品質を持つことを保証します。</li>
<li class="">チームコラボレーション効率の向上:SonarQube は集中管理型のプラットフォームを提供し、チームメンバーが統一された場所でコード品質と各種指標を確認できるため、チームコラボレーションの効率向上に役立ちます。</li>
<li class="">より良いコードメンテナンス:コード品質指標の分析を通じて、開発者はコードの健康状態をより深く理解でき、より的を絞ったメンテナンスと最適化を行えます。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="導入例">導入例<a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E5%B0%8E%E5%85%A5%E4%BE%8B" class="hash-link" aria-label="導入例 への直接リンク" title="導入例 への直接リンク" translate="no">​</a></h2>
<p>サンプルコード:<br>
Java プロジェクトがあると仮定します。まずプロジェクトに SonarQube の Maven プラグインを導入し、次のコマンドを実行して静的コード解析を行います。</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">&lt;!-- Maven プラグイン設定 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">build</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">plugins</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">plugin</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token comment" style="color:hsl(220, 10%, 40%)">&lt;!-- SonarQube 公式提供の Maven Scanner プラグイン --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">groupId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">org.sonarsource.scanner.maven</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">groupId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">artifactId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">sonar-maven-plugin</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">artifactId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">version</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">3.9.1.2184</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">version</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">executions</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">execution</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token comment" style="color:hsl(220, 10%, 40%)">&lt;!-- verify フェーズにバインド:ユニットテスト完了後、インストール・デプロイ前にスキャンを実行 --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">phase</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">verify</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">phase</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goals</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                        </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goal</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">sonar-check</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goal</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goals</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">execution</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">executions</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">plugin</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">plugins</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">build</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><br></div></code></pre></div></div>
<p>プラグインそのもの以外に、Scanner は結果の送信先も知る必要があります。サーバーのアドレス(<code>sonar.host.url</code>)と認証トークン(<code>sonar.login</code> または token)は、通常 Maven の <code>settings.xml</code> や CI の環境変数に設定します。<code>pom.xml</code> にハードコードしてリポジトリにコミットするのはおすすめしません。</p>
<p><code>mvn clean verify</code> コマンドを実行すると、SonarQube は自動的にプロジェクトのソースコードを解析し、SonarQube サーバー上に対応するレポートを生成します。開発者は SonarQube サーバーの Web インターフェースにアクセスしてレポートを確認し、その情報をもとにコードを最適化できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="devops-における組み合わせ方">DevOps における組み合わせ方<a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#devops-%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B%E6%96%B9" class="hash-link" aria-label="DevOps における組み合わせ方 への直接リンク" title="DevOps における組み合わせ方 への直接リンク" translate="no">​</a></h2>
<p>単体でスキャンを回すのは出発点にすぎず、SonarQube が本当に価値を発揮するのはパイプラインの中です。実際の DevOps フローでは、SonarQube は通常 <strong>CI/CD ツールチェーン</strong>と連携して使われます。たとえば:</p>
<ol>
<li class="">開発者がコードを Git リポジトリにコミットする</li>
<li class="">CI システム(Jenkins など)がビルドタスクをトリガーする</li>
<li class="">ビルド過程で SonarQube のコードスキャンを実行する</li>
<li class="">Quality Gate に基づいてリリースを続行するかどうかを判断する</li>
<li class="">スキャン結果を DingTalk / WeChat Work / メールでチームに通知する</li>
</ol>
<p>ステップ 4 がこのチェーン全体の核心です。Quality Gate を通過しなければパイプラインを中断し、品質問題をマージやリリースの前で食い止めます。事後にレポートを見返すのではありません。プラットフォームの Pull Request 分析機能と組み合わせれば、問題をレビュー画面に直接アノテーションすることもでき、Code Review は設計レベルに集中し、機械的なチェックはツールに任せられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>実際のデプロイと運用では、いくつかハマりやすいポイントがあります。</p>
<ol>
<li class=""><strong>サーバー側のリソース要件は低くない</strong>。SonarQube はインデックス用に Elasticsearch を内蔵しており、メモリに一定の要件があります。Linux にデプロイする場合、通常は <code>vm.max_map_count</code> などのカーネルパラメータを引き上げる必要があり、そうしないとサービスが起動しません。小規模チームならまず Docker で単一ノード運用してもよいですが、リソースがギリギリのマシンには置かないようにしましょう。</li>
<li class=""><strong>カバレッジは SonarQube が計算しているのではない</strong>。SonarQube はカバレッジレポートを読み取るだけです(Java エコシステムでは JaCoCo が生成するものが一般的)。ビルドの中で先にレポートを生成していなければ、画面上のカバレッジはずっと 0 のままです。これは導入初期に最もよくある疑問です。</li>
<li class=""><strong>ルールはチームの実情に合わせて調整する</strong>。デフォルトのルールセットはかなり網羅的で、そのまま全部有効にすると大量の警告が出て、チームはすぐに結果に「免疫」ができてしまいます。デフォルトの Quality Profile から始めて、明らかに適用外のルールを無効化し、すべての警告が対処に値する状態にすることをおすすめします。</li>
<li class=""><strong>古いプロジェクトは「新規コード」から手をつける</strong>。既存の問題を一気にゼロにしようとせず、Quality Gate の制約は新規コードにかけ、既存の負債は長期計画に組み込んで少しずつ返済していきましょう。</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>スキャンをパイプラインのどこに置くかにもコツがあります。早すぎる(push のたびにフルスキャン)とフィードバックが遅くなり、遅すぎる(リリース前だけスキャン)と問題が積み上がって修正しづらくなります。よくあるやり方は、マージリクエスト時とメインブランチのビルド時にそれぞれ 1 回ずつスキャンすることです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/09/08/sonarqube#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>SonarQube が解決する核心的な問題は、コード品質を「人が見張る」から「プロセスで保証する」に変えることです。静的解析で Bug、脆弱性、コードスメルを見つけ、多角的な指標で保守性を定量化し、Quality Gate でこれらの基準を CI/CD に組み込み、基準未達なら通しません。導入コストは高くありません。Maven プラグイン 1 つとコマンド 1 行で動き始めます。難しいのはその後、ルールとしきい値をチームに合ったレベルに調整し、無視される赤い警告ではなく、本当に信頼されるゲートキーパーに育てていくことです。</p>]]></content:encoded>
            <category>CI/CD</category>
            <category>技術</category>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Canal コンポーネント]]></title>
            <link>https://tanzhuo.xyz/ja/2023/09/06/canal</link>
            <guid>https://tanzhuo.xyz/ja/2023/09/06/canal</guid>
            <pubDate>Wed, 06 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Canal は Alibaba がオープンソースで公開している、MySQL Binlog ベースの増分購読・消費コンポーネントです。本記事では、その動作原理、よくある CDC コンポーネントとの比較、そしてクラスターによる高可用デプロイでの注意点を整理します。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2023/09/06/canal#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>業務システムを作っていると、こんな要件によく出会います。データベースのある行が変更されたら、下流のキャッシュ、検索エンジン、データウェアハウスも追従して変わってほしい。業務コードでのダブルライトは漏れやすく、定期タスクでの全量突き合わせは重すぎる。この種の問題には、よりエレガントな解法があります——データベースの変更ログを直接購読することです。これが、この Canal ノートを整理した理由でもあります。</p>
<!-- -->
<p>Canal は Alibaba がオープンソースで公開している <strong>MySQL Binlog ベースのデータリアルタイム購読・消費コンポーネント</strong>で、データベースの変更データキャプチャ(CDC、Change Data Capture)の実現によく使われます。</p>
<p>実際の業務シナリオでは、データベース内のデータ変更は業務の読み書きだけに使われるとは限らず、他のシステムへの同期が必要になることもあります。例えば:</p>
<ul>
<li class="">リアルタイムデータ同期(データベース → データウェアハウス)</li>
<li class="">データ変更の MQ へのプッシュ(Kafka / RocketMQ)</li>
<li class="">リアルタイムデータ分析・監視システムの構築</li>
<li class="">検索エンジンへのデータ同期(Elasticsearch など)</li>
</ul>
<p>これらのシナリオに共通するのは、下流システムが関心を持つのは「データベースが今どうなっているか」ではなく、「データにどんな変化が起きたか」だという点です。業務コードが書き込みのたびに手動で下流に通知する方式は、侵入性が高いだけでなく、データベーストランザクションとの整合性を保証するのが困難です——書き込みは成功したのに通知が失敗すれば、データはいつの間にか不整合になってしまいます。一方 Binlog は MySQL 自身が書き込みを保証するログであり、これをデータソースにすればダブルライト問題を自然に回避できます。</p>
<p>これらの機能を実現するために、Canal は <strong>MySQL Slave をシミュレートする方式で Binlog ログを購読</strong>し、データベースの追加・削除・更新操作を解析して、これらの変更データを下流の消費システムにリアルタイムでプッシュすることで、データの準リアルタイム同期を実現しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="動作原理">動作原理<a href="https://tanzhuo.xyz/ja/2023/09/06/canal#%E5%8B%95%E4%BD%9C%E5%8E%9F%E7%90%86" class="hash-link" aria-label="動作原理 への直接リンク" title="動作原理 への直接リンク" translate="no">​</a></h2>
<p>Canal の実装方式は、MySQL のレプリケーション(主従複製)の仕組みを借用したものです。通常のレプリケーションのフローは次のとおりです。Slave が Master に dump リクエストを送り、Master が Binlog イベントを継続的に Slave にプッシュし、Slave がそれらのイベントをリプレイしてデータ同期を完了します。</p>
<p>Canal がやっているのは、自分自身を Slave に「偽装」することです:</p>
<ol>
<li class="">MySQL に dump プロトコルのリクエストを送信し、本物のレプリカと同じやり取りの方式を使う。</li>
<li class="">MySQL が Binlog イベントを Canal にプッシュし、Canal はバイナリログを解析して、各行データの変更内容を復元する。</li>
<li class="">解析後の構造化イベントを下流の消費に渡す——クライアントが TCP で能動的にプルすることも、Kafka / RocketMQ などのメッセージキューに直接投入することもできます。</li>
</ol>
<p>MySQL から見れば、Canal はごく普通のレプリカにすぎず、データベース側にプラグインを一切インストールする必要はありません。この方式の侵入性が低いのはそのためです。</p>
<p>Canal が正常に購読できるようにするには、MySQL 側でいくつかの前提条件を満たす必要があります:</p>
<div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># my.cnf の重要な設定</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">log-bin</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">mysql-bin        # Binlog を有効化</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">binlog-format</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">ROW        # ROW モード必須。各行の変更前後の完全なデータを取得するため</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">server_id</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">1              # レプリケーション体系内の各ノードの一意な識別子。Canal 側に設定する slaveId と衝突しないこと</span><br></div></code></pre></div></div>
<p>このうち Binlog のフォーマットは必ず ROW モードでなければなりません。STATEMENT モードで記録されるのは SQL 文そのものであり、各行データの具体的な変化を復元できません。ROW モードだけが行レベルの前後イメージを記録するため、CDC のシナリオで意味を持ちます。また、Canal が MySQL への接続に使うアカウントには <code>REPLICATION SLAVE</code> と <code>REPLICATION CLIENT</code> の権限が必要で、これは本物のレプリカの要件と同じです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="よくある-cdc-コンポーネントの比較">よくある CDC コンポーネントの比較<a href="https://tanzhuo.xyz/ja/2023/09/06/canal#%E3%82%88%E3%81%8F%E3%81%82%E3%82%8B-cdc-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E6%AF%94%E8%BC%83" class="hash-link" aria-label="よくある CDC コンポーネントの比較 への直接リンク" title="よくある CDC コンポーネントの比較 への直接リンク" translate="no">​</a></h2>
<p>現在、市場でよく見かける CDC コンポーネントには主に次のものがあります:</p>
<ul>
<li class="">Canal</li>
<li class="">Debezium</li>
<li class="">Flink CDC</li>
</ul>
<p>Canal が現在サポートしているのは MySQL データベースのみです。5.x、8.x バージョンに対応しています。</p>
<p><a href="https://github.com/alibaba/canal" target="_blank" rel="noopener noreferrer" class="">GitHub - alibaba/canal: 阿里巴巴 MySQL binlog 增量订阅&amp;消费组件</a></p>
<p>これらのコンポーネントのコアとなる動作メカニズムは基本的に同じで、<strong>データベースの Binlog ログを解析することで、データの変更内容と具体的な操作タイプ(INSERT / UPDATE / DELETE)を取得</strong>し、これらの変更データを構造化イベントに変換して下流システムに消費させます。</p>
<p>3 者の違いは主にエコシステム上の位置付けにあります。Debezium は Kafka Connect の上に構築されており、MySQL、PostgreSQL など複数のデータベースをサポートするため、すでに Kafka 体系を持つチームに向いています。Flink CDC は変更キャプチャを Flink のストリーム処理に直接組み込み、キャプチャと加工を同一ジョブ内で完結させます。Canal はより軽量で、MySQL という 1 つの方向に特化しています。</p>
<p>注意すべきは、<strong>Canal が現在主にサポートしているのは MySQL データベース(5.x と 8.x バージョン)</strong> だという点で、そのため MySQL エコシステムの中でリアルタイムデータ同期・データ配信システムの構築に広く使われています。技術スタックが純粋な MySQL で、Kafka Connect や Flink のような重い依存を持ち込みたくないなら、Canal は現実的な選択肢と言えます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="高可用性について">高可用性について<a href="https://tanzhuo.xyz/ja/2023/09/06/canal#%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6" class="hash-link" aria-label="高可用性について への直接リンク" title="高可用性について への直接リンク" translate="no">​</a></h2>
<p>高可用性の面では、Canal は<strong>クラスターデプロイモード</strong>を提供しています。クラスターアーキテクチャでは、各 Service ノードが複数の同期タスクインスタンス(Instance)を管理し、タスク分配メカニズムによって負荷分散を実現します。</p>
<p>クラスターモードは調整役として ZooKeeper に依存します。複数の Server ノードが同一 Instance の実行権を奪い合い、同時刻に実際にその Instance を実行しているのは 1 ノードだけで、残りのノードは standby 状態にあります。実行中のノードに障害が発生すると、standby ノードが ZooKeeper を通じてそれを検知してタスクを引き継ぎます。同時に、消費ポジション(つまり Binlog の消費進捗)も ZooKeeper に記録されているため、引き継いだ側は前回のポジションから消費を続行でき、データの損失や大量の重複を回避できます。</p>
<p>ただし実際の運用では、ネットワークの揺らぎ、データベース接続の異常、リソース制限などが原因で、同期タスクのインスタンスが停止してしまうことが時折あります。そのため本番環境では、Canal は通常<strong>自動再起動メカニズムや運用監視システム</strong>と組み合わせて、異常時にタスクが自動的に復旧できるようにします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/09/06/canal#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>原理を踏まえて、導入前に考えておいた方がよいポイントがいくつかあります。</p>
<ol>
<li class="">
<p><strong>Binlog のフォーマットと保持期間</strong>。上流のデータベースは必ず ROW モードにすること。同時に Binlog の保持期間を十分長くしておくこと。そうしないと、Canal の停止時間が長引いた場合にポジションに対応するログがすでに削除されており、タスクを続行できず、再初期化するしかなくなります。</p>
</li>
<li class="">
<p><strong>消費のセマンティクスは at-least-once</strong>。フェイルオーバーや再起動の後、変更イベントの一部区間が重複して配信される可能性があります。下流の消費側は主キーで冪等処理を行う必要があり、各変更が一度しか届かないと仮定してはいけません。</p>
</li>
<li class="">
<p><strong>順序性はパーティション戦略に依存する</strong>。MQ に投入する際、複数のテーブルや複数の主キーの変更が異なるパーティションに分散されると、消費順序が Binlog の順序と一致しなくなる可能性があります。順序に敏感なシナリオでは、通常テーブル名や主キーでパーティションルーティングを行います。</p>
</li>
<li class="">
<p><strong>Instance の疑似停止(ハングアップ)問題</strong>。タスクインスタンスは必ずしもきれいに終了するとは限らず、あるポジションで止まったまま進まなくなることがあります。監視ではプロセスの生存だけを見るのではなく、ポジションが継続的に前進しているか、データベースの現在の Binlog 位置との遅延がどれくらいかも見る必要があります。</p>
</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>リリース前に一度「切断訓練」をしておくとよいでしょう。実行中の Server ノードを手動で kill し、standby が期待どおりに引き継ぐか、ポジションが正しく続行されるかを観察します。障害が起きてから検証するより、はるかに余裕を持って対応できます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/09/06/canal#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Canal の考え方は複雑ではありません。MySQL のレプリカに偽装し、標準のレプリケーションプロトコルで Binlog を購読し、行レベルの変更を構造化イベントに復元して下流に消費させる。データベースへの侵入はゼロで、デプロイも軽量なため、純粋な MySQL 技術スタックでのリアルタイム同期・データ配信に適しています。エンジニアリング面で重点的にケアすべきなのは、ROW フォーマットと Binlog の保持ポリシー、下流の冪等な消費、そしてポジションの進行状況に基づく監視です——これらをしっかり作り込んでこそ、パイプラインの安定性が保証されます。</p>]]></content:encoded>
            <category>Java</category>
            <category>Linux</category>
            <category>DB</category>
            <category>ビッグデータ</category>
        </item>
        <item>
            <title><![CDATA[Polaris Mesh]]></title>
            <link>https://tanzhuo.xyz/ja/2023/09/01/polaris</link>
            <guid>https://tanzhuo.xyz/ja/2023/09/01/polaris</guid>
            <pubDate>Fri, 01 Sep 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[PolarisMesh は Tencent がオープンソース化したワンストップのクラウドネイティブサービスガバナンスプラットフォームで、サービス登録・ディスカバリ、トラフィックガバナンス、フォールトトレランス、設定管理を中心に完全な機能を提供します。本記事ではその中核機能を整理し、Nacos との位置づけを比較します。]]></description>
            <content:encoded><![CDATA[<p>PolarisMesh は完全なマイクロサービスガバナンス機能を提供する、Tencent がオープンソース化したワンストップのクラウドネイティブサービスガバナンスプラットフォームです。プラットフォームは <strong>サービス登録とディスカバリ、トラフィックガバナンス、フォールトトレランス、設定管理</strong> といった中核機能を軸に、マイクロサービスアーキテクチャへ安定した信頼性の高い実行基盤を提供します。PolarisMesh によって、サービスインスタンスは登録とディスカバリを自動的に完了でき、さらにインテリジェントなロードバランシング戦略と組み合わせることで、リクエストを複数インスタンス間に適切に分配し、システム全体の安定性とスループットを高められます。</p>
<!-- -->
<p>公式サイト：<a href="http://polarismesh.cn/" target="_blank" rel="noopener noreferrer" class="">PolarisMesh</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスガバナンス">マイクロサービスガバナンス<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%AC%E3%83%90%E3%83%8A%E3%83%B3%E3%82%B9" class="hash-link" aria-label="マイクロサービスガバナンス への直接リンク" title="マイクロサービスガバナンス への直接リンク" translate="no">​</a></h2>
<p>トラフィックガバナンスの面では、PolarisMesh は <strong>サービスの流量制限（レートリミット）、サーキットブレーカーとデグレード、リトライ機構、カナリアリリース</strong> などの機能をサポートしています。あるサービスに異常が発生したり応答時間が長くなりすぎたりした場合、システムは自動的にサーキットブレーカーによる保護を発動し、障害のさらなる拡散を防ぎます。同時に、柔軟なトラフィック制御戦略によって、比率別・ラベル別・バージョン別のトラフィック制御が可能になり、バージョンアップや新機能リリースの際にもシステムをより安全にコントロールできます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="高性能プロキシ">高性能プロキシ<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E9%AB%98%E6%80%A7%E8%83%BD%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7" class="hash-link" aria-label="高性能プロキシ への直接リンク" title="高性能プロキシ への直接リンク" translate="no">​</a></h2>
<p>PolarisMesh には高性能なプロキシコンポーネント Polaris Sidecar が組み込まれており、サービスメッシュのデータプレーンコンポーネントとして機能します。このプロキシは高性能なネットワークモデルに基づいて設計され、効率的なリクエスト転送とレスポンス処理を実現し、サービスガバナンス機能を保証しながらネットワークオーバーヘッドを可能な限り抑えます。プロキシモードによって、アプリケーションはビジネスコードに手を入れることなく完全なサービスガバナンス機能を得られ、導入コストが大幅に下がります。</p>
<p>さらに、PolarisMesh は <strong>Proxyless モード</strong> もサポートしており、Sidecar プロキシなしでもサービスガバナンス機能を利用できます。開発者は SDK または Java Agent の方式で直接 PolarisMesh に接続でき、異なるアーキテクチャのシナリオに応じて最適な接続方式を柔軟に選択できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マルチプロトコル対応">マルチプロトコル対応<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E3%83%9E%E3%83%AB%E3%83%81%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB%E5%AF%BE%E5%BF%9C" class="hash-link" aria-label="マルチプロトコル対応 への直接リンク" title="マルチプロトコル対応 への直接リンク" translate="no">​</a></h2>
<p>PolarisMesh は <strong>HTTP、gRPC、TCP など</strong> の主要な通信プロトコルをサポートし、さまざまなタイプのマイクロサービス間の通信ニーズに応えられます。同時に、プラットフォームは <strong>Java、Go、Python、C++ などの主要な開発言語</strong> の SDK を提供し、Spring Cloud、Dubbo といった一般的なマイクロサービスフレームワークにも対応しています。開発者はビジネスコードの大規模な改修なしに、既存システムを素早く PolarisMesh に接続できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サービス設定管理">サービス設定管理<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E8%A8%AD%E5%AE%9A%E7%AE%A1%E7%90%86" class="hash-link" aria-label="サービス設定管理 への直接リンク" title="サービス設定管理 への直接リンク" translate="no">​</a></h2>
<p>サービスガバナンス機能に加えて、PolarisMesh は設定センター（コンフィグセンター）機能も提供し、マイクロサービスの実行に必要な各種設定を集中管理できます。開発者は設定センターを通じてサービスパラメータを統一管理でき、動的な設定更新もサポートされているため、アプリケーションは再起動なしで新しい設定を読み込めます。運用効率と柔軟性がともに向上します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="可観測性オブザーバビリティ">可観測性(オブザーバビリティ)<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E5%8F%AF%E8%A6%B3%E6%B8%AC%E6%80%A7%E3%82%AA%E3%83%96%E3%82%B6%E3%83%BC%E3%83%90%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3" class="hash-link" aria-label="可観測性(オブザーバビリティ) への直接リンク" title="可観測性(オブザーバビリティ) への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャにおいて、可観測性はシステムの安定稼働を保障する重要な能力です。PolarisMesh は充実した監視・ログ機能を提供し、サービスの呼び出しチェーン、リクエスト成功率、レイテンシ指標などの重要データをリアルタイムに収集できます。これらの指標により、運用担当者はシステムのボトルネックや異常のあるサービスを素早く特定でき、障害調査の効率が上がります。</p>
<p>同時に、PolarisMesh は <strong>イベントセンターと操作監査の機能</strong> も提供し、システム内で発生した重要なイベントとユーザーの操作記録を記録できます。これはシステム運用とセキュリティ監査の根拠となります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="拡張性と柔軟性">拡張性と柔軟性<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E6%8B%A1%E5%BC%B5%E6%80%A7%E3%81%A8%E6%9F%94%E8%BB%9F%E6%80%A7" class="hash-link" aria-label="拡張性と柔軟性 への直接リンク" title="拡張性と柔軟性 への直接リンク" translate="no">​</a></h2>
<p>PolarisMesh はクラウドネイティブなアーキテクチャ設計を採用しており、<strong>Kubernetes、コンテナ環境、ハイブリッドクラウドデプロイ</strong> をネイティブにサポートします。プラットフォームのコンポーネント間は疎結合な方式で通信するため、ユーザーはビジネスニーズに応じて各モジュールを柔軟に拡張できます。さらに、PolarisMesh は Service Mesh アーキテクチャとの組み合わせもサポートしており、マイクロサービスシステムは高性能を保ったまま、より強力なガバナンス能力を備えられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="polarismesh-と-nacos-の比較">PolarisMesh と Nacos の比較<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#polarismesh-%E3%81%A8-nacos-%E3%81%AE%E6%AF%94%E8%BC%83" class="hash-link" aria-label="PolarisMesh と Nacos の比較 への直接リンク" title="PolarisMesh と Nacos の比較 への直接リンク" translate="no">​</a></h2>
<p>マイクロサービスアーキテクチャにおいて、PolarisMesh と Nacos はどちらもよく使われるサービスガバナンスコンポーネントですが、両者の設計上の位置づけと機能の重点は異なります。</p>
<p>位置づけから見ると、<strong>Nacos はサービスレジストリと設定センター寄り</strong> で、主にサービスの登録・ディスカバリと設定管理の問題を解決します。一方 <strong>PolarisMesh は完全なマイクロサービスガバナンスプラットフォームとして位置づけられ</strong>、サービス登録と設定管理だけでなく、より豊富なトラフィックガバナンスとサービスガバナンスの機能を提供します。</p>
<p>アーキテクチャ能力から見ると、PolarisMesh はトラフィックガバナンス、サービスガバナンス、可観測性の面で、ルーティング戦略、流量制限、サーキットブレーカー、カナリアリリースといった機能を含む、より完全な一体型の能力を提供しています。一方 Nacos はサービス登録と設定管理そのものにより重点を置いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/09/01/polaris#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>PolarisMesh は登録・ディスカバリ、トラフィックガバナンス、フォールトトレランス、設定管理、可観測性を一つのプラットフォームに集約し、Sidecar と Proxyless という 2 つの接続方式によって、異なるアーキテクチャのチームに選択の余地を与えています。レジストリと設定センターだけが必要なら Nacos は十分に軽量です。しかし流量制限、サーキットブレーカー、カナリアリリースといったガバナンスのニーズが浮上してきたら、一体型の PolarisMesh は複数コンポーネントを組み合わせるコストを省いてくれます。選定の前に、まず自分たちのガバナンス要件を整理してから、どのレイヤーの能力を導入するかを決めるとよいでしょう。</p>]]></content:encoded>
            <category>技術</category>
            <category>Go</category>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[アリババ ASK クラスタ]]></title>
            <link>https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun</link>
            <guid>https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun</guid>
            <pubDate>Sat, 29 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[アリババクラウド ASK（Serverless Kubernetes）を調査した記録：その位置づけと動作の仕組み、自前構築の K8S と比べたメリット・デメリット、選定時に注意すべきコストとベンダーロックインの問題について。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>最近チームのコンテナ化方針を検討する中で、避けて通れない問いがありました。K8S クラスタは自前で構築するのか、マネージドを買うのか、それともいっそノードすら持たないのか。ASK のような Serverless 形態のクラスタはちょうど最後の選択肢に当たるため、個別に記録しておく価値があります。</p>
<!-- -->
<p>小さなチームにとって、自前 K8S の隠れたコストはしばしば過小評価されます。コントロールプレーンの高可用化、etcd のバックアップ、バージョンアップグレード、ノードへのパッチ適用。これらの作業はビジネス価値を生まないのに、どれも省くことができません。クラウドベンダーのマネージドサービスはまさにここを狙ったもので、ASK はさらに一歩進んで、ノードというレイヤーまでユーザーの視界から取り除いてしまいました。</p>
<p>アリババクラウド ASK（Alibaba Cloud ACK）クラスタは、アリババクラウドが提供するマネージド Kubernetes サービスです。オープンソースの Kubernetes プロジェクトをベースに、使いやすく、弾力的にスケールし、高可用で安全かつ信頼性の高いコンテナデプロイ・管理プラットフォームをユーザーに提供する、いわばサーバーレス Kubernetes コンテナサービスです。ノードを購入することなくコンテナアプリケーションを直接デプロイでき、クラスタのノードメンテナンスやキャパシティプランニングも不要で、アプリケーションに設定した CPU とメモリのリソース量に応じた従量課金となります。ASK クラスタは充実した Kubernetes 互換性を提供しつつ、Kubernetes の利用ハードルを下げ、下層のインフラ管理ではなくアプリケーションそのものに集中できるようにしてくれます。</p>
<p>ASK の SK は Serverless Kubernetes を指し、公式ドキュメントでは現在 ACK Serverless とも呼ばれています。通常のマネージド版 ACK との核心的な違いは次のとおりです。マネージド版はコントロールプレーンを管理してくれますが、Worker ノードは依然として自分の ECS です。一方 ASK には Worker ノードという概念自体がなく、向き合う最小リソース単位が直接 Pod になります。</p>
<p><a href="https://help.aliyun.com/zh/ack/serverless-kubernetes/product-overview/ask-overview?spm=a2c4g.750001.0.i1&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">什么是容器服务 Serverless 版ACK Serverless_容器服务 Kubernetes 版 ACK-阿里云帮助中心</a></p>
<p>ASK クラスタ内の Pod は、アリババクラウドの Elastic Container Instance（ECI）をベースに、セキュリティ隔離されたコンテナ実行環境で動作します。各 Pod コンテナインスタンスは、下層で軽量仮想化のセキュアサンドボックス技術により完全に強く隔離され、コンテナインスタンス同士は互いに影響しません。</p>
<p>この仕組みをもう少し掘り下げてみます。従来の K8S では、複数の Pod が同一ホストマシンのカーネルを共有し、隔離は namespace と cgroup に頼るソフトな隔離でした。コンテナエスケープ系の脆弱性が出れば、同じノード上の他の Pod も影響を受ける可能性があります。ECI のやり方は、各 Pod に軽量仮想マシンのサンドボックスを一枚かぶせるというもので、Pod 間でカーネルを共有せず、隔離レベルは仮想マシンに近い一方、起動速度とリソースオーバーヘッドは従来の仮想マシンよりずっと小さくて済みます。ユーザーにとってこのサンドボックス層は透過的です。提出するのは標準的な Deployment や Service といった K8S リソースのままで、スケジューリング層が Pod を ECI 上に載せて実行してくれます。</p>
<p>そして、Pod が宣言した CPU とメモリで課金されるからこそ、リソースの requests/limits の設定は ASK においてもはや単なるスケジューリングパラメータではなく、請求額の数字を直接決めるものになります。この点は自前クラスタの考え方とは大きく異なります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ask-クラスタのメリット">ASK クラスタのメリット<a href="https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun#ask-%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88" class="hash-link" aria-label="ASK クラスタのメリット への直接リンク" title="ASK クラスタのメリット への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">使いやすさ：ASK クラスタは直感的で分かりやすい GUI と CLI ツールを提供し、Kubernetes クラスタの作成・デプロイ・管理を簡単に行えます。</li>
<li class="">弾力的なスケーリング：ASK クラスタは実際の需要に応じた自動的な拡張・縮小をサポートし、アプリケーションが常に良好なパフォーマンスを保ち、トラフィックの変動に柔軟に対応できるようにします。</li>
<li class="">高可用性：ASK クラスタはマルチアベイラビリティゾーン構成を採用し、自動フォールトトレランスと自動復旧の機能を提供することで、障害発生時もアプリケーションの可用性を維持し、業務中断時間を最小限に抑えます。</li>
<li class="">安全性と信頼性：ASK クラスタはネットワーク隔離、アクセス制御、データ暗号化など多層のセキュリティメカニズムを提供し、アプリケーションとデータの安全を守ります。</li>
<li class="">エコシステムとの統合：ASK クラスタはコンテナイメージサービス、ログサービス、クラウドモニタリングなど、アリババクラウドの他の製品・サービスとシームレスに統合され、包括的なソリューションを提供します。</li>
</ol>
<p>この中で私が最も実用的だと感じるのは弾力的なスケーリングです。自前クラスタでオートスケーリングを実現するには、Pod を増やす前にまずスケジュール可能なノードがあることを保証しなければならず、ノードのスケールアウト自体に数分はかかるうえ、cluster-autoscaler のパラメータ調整も必要です。ASK はノードのレイヤーを抽象化して取り除いたことで、スケールアウトは「Pod を立ち上げる」という一点だけになり、スケーリングの経路がずっと短くなりました。トラフィックにはっきりしたピークとオフピークがある業務では、この差が応答速度とコストに直接反映されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="いくつかのデメリット">いくつかのデメリット<a href="https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun#%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E3%83%87%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88" class="hash-link" aria-label="いくつかのデメリット への直接リンク" title="いくつかのデメリット への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">価格が比較的高い：ASK クラスタの利用には一定のサービス料金がかかり、予算が限られたユーザーにとっては検討要素になり得ます。年額・月額課金の ACK クラスタと比べると、従量課金はリソース配分が不均一な場合に追加費用が発生します。</li>
<li class="">学習曲線がやや急：Kubernetes を使ったことのないユーザーにとっては、ASK クラスタの概念や操作方法の学習と適応に時間がかかります。</li>
<li class="">クラウドベンダーへの依存：ASK クラスタを使うことはアプリケーションをアリババクラウドのプラットフォームにデプロイすることを意味し、特定のクラウドベンダーに関わる制約や依存に直面する可能性があります。</li>
</ol>
<p>価格についてもう一言。従量課金の単価は通常、年額・月額の ECS より高いため、ASK が割に合う前提は、ワークロードに本当に弾力性があることです。常駐して 7x24 フル稼働するサービスは、予約リソースに載せたほうがむしろ安くなります。選定時には、自分のワークロードの「常駐部分」と「弾力部分」がそれぞれどれくらいを占めるか、先に見積もっておく価値があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）リソース仕様は真面目に設定すること。ASK は Pod が宣言したリソースで課金されるため、requests を適当に大きく書けば請求額もそれに従って増え、小さすぎれば OOM になる可能性があります。リリース前に負荷試験の結果に基づいて、実際の使用量に近い設定を出しておくのがベストです。</p>
<p>2）標準 K8S との挙動の違いに注意すること。実ノードが存在しないということは、DaemonSet、hostPath、hostNetwork といったノードに依存する機能の使い方が通常のクラスタと異なるということです。既存アプリケーションを移行する前に項目ごとに確認し、具体的なサポート状況は公式ドキュメントを正としてください。</p>
<p>3）コールドスタートはゼロコストではないこと。ECI で Pod を立ち上げるのは、既存ノード上でスケジュールするよりインスタンス作成の時間が余分にかかります。レイテンシに敏感なバーストトラフィックのシナリオでは、事前のウォームアップや一定数の常駐レプリカの確保を検討するとよいでしょう。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>Serverless K8S の使い勝手を体験してみたいだけなら、まず最小スペックの Pod でテストアプリケーションを動かし、しばらく請求額を観察してからコア業務を移行するかどうか決めるとよいでしょう。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/07/29/a-li-ask-ji-qun#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>K8S クラスタを自前で構築して維持することに比べると、投入時間が多く、メンテナンスコストが大きく、長期の保守に時間がかかり、イテレーションやアップグレードが煩雑で、パッチや脆弱性のセキュリティも自分で保証しなければならず、設定も煩雑で、チームの運用能力に一定の要求があります。ASK クラスタを直接使えばこうした悩みから解放され、より多くのエネルギーをプロダクト開発に投入できます。さらにその後トラフィックが伸びたときも、Pod のレプリカをスケールアウトするだけでよく、下層の Node リソースの物理的制約を管理する必要がありません。</p>
<p>私の見解はこうです。チームの規模が大きくなく、専任の運用担当もいない状況では、ASK のような Serverless クラスタは現実的な出発点です。業務規模が大きくなり、コストへの感度が高まってから、マネージド版や自前構築への移行を再評価しても、決して遅くはありません。</p>]]></content:encoded>
            <category>K8S</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[アーキテクチャ設計の原則]]></title>
            <link>https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze</link>
            <guid>https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze</guid>
            <pubDate>Sun, 18 Jun 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[システムアーキテクチャ設計では、拡張性・信頼性・性能・セキュリティ・保守性など複数の観点の間でトレードオフを行う必要があります。本記事ではこれらのよくある設計原則と、アーキテクトが持つべき思考法を整理します。]]></description>
            <content:encoded><![CDATA[<p>システムアーキテクチャを設計する際には、通常、複数の観点からシステムの能力を総合的に検討する必要があります。優れたシステムアーキテクチャは、現在のビジネス要件を満たすだけでなく、将来のビジネス成長や技術の進化にも適応できなければなりません。重点的に注目すべき設計原則には、以下のようなものがあります。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="拡張性">拡張性<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E6%8B%A1%E5%BC%B5%E6%80%A7" class="hash-link" aria-label="拡張性 への直接リンク" title="拡張性 への直接リンク" translate="no">​</a></h2>
<p>システム設計は良好な拡張能力を備え、将来のビジネス成長や要件の変化をサポートできる必要があります。アーキテクチャの設計では通常、モジュール化やレイヤードアーキテクチャなどの手法を採用し、システム全体の安定性に影響を与えることなく、新機能の追加、データ規模の拡大、より多くのユーザーへの対応を容易にします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="信頼性">信頼性<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E4%BF%A1%E9%A0%BC%E6%80%A7" class="hash-link" aria-label="信頼性 への直接リンク" title="信頼性 への直接リンク" translate="no">​</a></h2>
<p>システム設計には高い信頼性が求められ、長期運用におけるシステムの安定性と可用性を保証できなければなりません。アーキテクチャ設計では通常、障害復旧機構、フォールトトレランス機構、データバックアップ戦略を考慮し、システムに異常が発生した際に迅速に業務を復旧できるようにします。</p>
<p>高信頼なシステムは通常、以下の能力を備えている必要があります：</p>
<ul>
<li class="">障害の自動検知</li>
<li class="">問題の自動修復</li>
<li class="">自動切り替え（Failover）</li>
</ul>
<p>これらの仕組みによって、システム障害がビジネスに与える影響を最小限に抑えられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="極限の性能">極限の性能<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E6%A5%B5%E9%99%90%E3%81%AE%E6%80%A7%E8%83%BD" class="hash-link" aria-label="極限の性能 への直接リンク" title="極限の性能 への直接リンク" translate="no">​</a></h2>
<p>システムアーキテクチャでは性能設計にも注目し、システムがユーザーのリクエストに素早く応答し、大量の並行処理を捌けるようにする必要があります。よく使われる性能最適化の手段には次のものがあります：</p>
<ul>
<li class="">適切なデータ構造とアルゴリズムの設計</li>
<li class="">キャッシュ機構</li>
<li class="">ロードバランシング</li>
<li class="">並行制御</li>
</ul>
<p>実際のシステムでは、性能は通常 <strong>TPS（Transactions Per Second、毎秒トランザクション数）</strong> で測定されます。システム性能と同時接続ユーザー数は単純な正の相関関係にはなく、一般的には：</p>
<ul>
<li class="">システムの最大 TPS は一定の範囲内で固定されている</li>
<li class="">同時接続ユーザー数はキューイングや流量制限などの方法で調整できる</li>
</ul>
<p>性能テストでは通常、最悪のケースを想定してサーバーに負荷テストを行う必要があります。例えば：</p>
<ul>
<li class="">大規模システム：10000 ～ 50000 同時接続ユーザー</li>
<li class="">中小規模システム：約 5000 同時接続ユーザー</li>
</ul>
<p>システムスループットの計算式は次のとおりです：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">スループット (TPS) = 同時接続数 / 平均応答時間</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="セキュリティ">セキュリティ<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3" class="hash-link" aria-label="セキュリティ への直接リンク" title="セキュリティ への直接リンク" translate="no">​</a></h2>
<p>システム設計は、ユーザーデータとシステムリソースを保護するために、優れたセキュリティを備えていなければなりません。よくあるセキュリティ設計には次のものがあります：</p>
<ul>
<li class="">認証（Authentication）</li>
<li class="">アクセス制御（Authorization）</li>
<li class="">データ暗号化</li>
<li class="">セキュリティ監査</li>
</ul>
<p>多層のセキュリティ機構によって、データ漏洩、不正アクセス、潜在的な攻撃を効果的に防げます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="保守性">保守性<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E4%BF%9D%E5%AE%88%E6%80%A7" class="hash-link" aria-label="保守性 への直接リンク" title="保守性 への直接リンク" translate="no">​</a></h2>
<p>良いシステムアーキテクチャは高い保守性を備え、後の段階でシステムの修正・拡張・メンテナンスを容易に行えるようにするべきです。通常は次のことが求められます：</p>
<ul>
<li class="">明快なコード構造</li>
<li class="">完備されたドキュメント</li>
<li class="">規範に沿ったコードコメント</li>
<li class="">デバッグとテストのしやすさ</li>
</ul>
<p>これらにより、開発者はシステムを素早く理解し、反復開発を進められます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="伸縮性スケーラビリティ">伸縮性(スケーラビリティ)<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E4%BC%B8%E7%B8%AE%E6%80%A7%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%93%E3%83%AA%E3%83%86%E3%82%A3" class="hash-link" aria-label="伸縮性(スケーラビリティ) への直接リンク" title="伸縮性(スケーラビリティ) への直接リンク" translate="no">​</a></h2>
<p>システムには優れた伸縮能力も必要で、ビジネス規模に応じてリソースを動的に拡張・縮小できなければなりません。よくある拡張方式は次のとおりです：</p>
<ul>
<li class=""><strong>垂直スケーリング（Scale Up）</strong>：単一マシンのリソースを増強する。例えば CPU やメモリの追加</li>
<li class=""><strong>水平スケーリング（Scale Out）</strong>：サーバーノードを追加し、分散アーキテクチャで負荷を分担する</li>
</ul>
<p>モダンなシステムアーキテクチャでは通常、大規模なビジネス成長を支えるために水平スケーリングを優先的に選択します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="高可用性">高可用性<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E9%AB%98%E5%8F%AF%E7%94%A8%E6%80%A7" class="hash-link" aria-label="高可用性 への直接リンク" title="高可用性 への直接リンク" translate="no">​</a></h2>
<p>高可用性は大規模システムの重要な目標の一つです。障害が発生してもシステムはサービスを提供し続けられなければなりません。よくある実現方式には次のものがあります：</p>
<ul>
<li class="">複数ノードでのデプロイ</li>
<li class="">ロードバランシング</li>
<li class="">サービスの自動切り替え</li>
<li class="">データの多重レプリカ機構</li>
</ul>
<p>これらの仕組みによって、システムダウンがユーザーに与える影響を最小限に抑えられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="テスト容易性">テスト容易性<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E3%83%86%E3%82%B9%E3%83%88%E5%AE%B9%E6%98%93%E6%80%A7" class="hash-link" aria-label="テスト容易性 への直接リンク" title="テスト容易性 への直接リンク" translate="no">​</a></h2>
<p>システム設計は、自動テストと継続的インテグレーションをサポートするために、優れたテスト容易性を備える必要があります。通常はモジュール化と疎結合の設計を通じて、システムが以下を手軽に行えるようにします：</p>
<ul>
<li class="">単体テスト</li>
<li class="">結合テスト</li>
<li class="">システムテスト</li>
</ul>
<p>良好なテスト容易性は、ソフトウェアの品質と開発効率を大きく向上させます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="移行容易性">移行容易性<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E7%A7%BB%E8%A1%8C%E5%AE%B9%E6%98%93%E6%80%A7" class="hash-link" aria-label="移行容易性 への直接リンク" title="移行容易性 への直接リンク" translate="no">​</a></h2>
<p>システムアーキテクチャには優れた移行能力も必要で、異なる環境でも容易にデプロイ・実行できるようにするべきです。例えば：</p>
<ul>
<li class="">異なる OS やクラウドプラットフォームのサポート</li>
<li class="">標準化されたインターフェースの使用</li>
<li class="">特定プラットフォームへの依存の削減</li>
</ul>
<p>これにより、将来のシステム移行やアップグレードのコストを下げられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コスト指標">コスト指標<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E3%82%B3%E3%82%B9%E3%83%88%E6%8C%87%E6%A8%99" class="hash-link" aria-label="コスト指標 への直接リンク" title="コスト指標 への直接リンク" translate="no">​</a></h2>
<p>小規模なプロジェクトでは、コストは通常主要な関心事ではありません。しかしシステムの規模が徐々に拡大すると、コストはアーキテクチャ設計における重要な指標になります。</p>
<p>注意すべきは次の点です：</p>
<p><strong>低コスト・高性能・高可用性の 3 つは、しばしば互いに衝突します。</strong></p>
<p>そのためアーキテクチャ設計において低コストは通常、最優先の目標ではなく、性能と可用性を満たすことを前提に総合的にバランスを取るべきものです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アーキテクトが持つべき思考法">アーキテクトが持つべき思考法<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88%E3%81%8C%E6%8C%81%E3%81%A4%E3%81%B9%E3%81%8D%E6%80%9D%E8%80%83%E6%B3%95" class="hash-link" aria-label="アーキテクトが持つべき思考法 への直接リンク" title="アーキテクトが持つべき思考法 への直接リンク" translate="no">​</a></h2>
<p>実際のエンジニアリングにおいて、アーキテクトは単にシステム構造を設計するだけではなく、より重要なのは全体を見渡す思考力を持つことです。成熟したアーキテクトは通常、一つの技術ポイントだけに注目するのではなく、複数の観点からシステム設計のトレードオフを行う必要があります。</p>
<p>1）<strong>グローバルな視点</strong>。アーキテクトはシステム全体の観点から問題を考える必要があります。ビジネスの発展、技術選定、チームの能力、将来の拡張余地までを含めて考えるのであって、目の前の技術的な問題を解決するだけではありません。</p>
<p>2）<strong>トレードオフの能力</strong>。現実のシステム設計に「完璧な方案」はほとんど存在しません。性能、コスト、複雑さ、保守性の間ではしばしば取捨選択が必要です。アーキテクトの中核能力の一つは、これらの要素の間で最も合理的なバランスポイントを見つけることです。</p>
<p>3）<strong>長期的な思考</strong>。アーキテクチャ設計は現在の問題を解決するだけでなく、3 年、5 年、あるいはそれ以上のシステムの発展を考慮しなければなりません。良いアーキテクチャは、一定期間ごとにゼロから作り直すのではなく、ビジネスの継続的な進化を支え続けられるものです。</p>
<p>4）<strong>複雑な問題を分解する能力</strong>。大規模システムはしばしば非常に複雑です。アーキテクトは複雑なシステムを複数の明快なモジュールに分解し、チームが分業・協働しながら継続的にイテレーションできるようにする必要があります。</p>
<p>5）<strong>技術的な判断力</strong>。絶えず変化する技術エコシステムに向き合い、どの技術が本当にビジネスに適しているのか、どれが短期的な流行にすぎないのかを見極め、安定して信頼できる技術的決断を下せなければなりません。</p>
<p>簡単に言えば、アーキテクトは技術の設計者であるだけでなく、システムの長期的な発展の設計者でもあるのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/06/18/xi-tong-she-zhi-yuan-ze#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>アーキテクチャ設計に銀の弾丸はなく、これらの原則の間にはそもそも緊張関係があります。極限の性能は低コストと衝突し、高可用性はアーキテクチャのシンプルさと衝突します。実際の設計では、まずビジネスの現在のフェーズにおける本当の核心的な要求を明確にし、それを軸にトレードオフを行うべきであって、すべての観点で最高を目指そうとするべきではありません。アーキテクトにとって、個別の技術を習得すること以上に重要なのは、これらの観点の間で継続的にバランスを取り続ける能力なのです。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Ent-ORM]]></title>
            <link>https://tanzhuo.xyz/ja/2023/06/13/go</link>
            <guid>https://tanzhuo.xyz/ja/2023/06/13/go</guid>
            <pubDate>Tue, 13 Jun 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Ent は Meta がオープンソースで公開している Go の ORM フレームワークで、Schema First とコード生成という設計を採用しています。本記事ではその設計思想、メリット・デメリット、発展の歴史を整理し、GORM、XORM、SQLBoiler と比較します。]]></description>
            <content:encoded><![CDATA[<p>Ent は Facebook（現 Meta）が開発した <strong>Go 言語ベースの ORM フレームワーク</strong> で、核となる理念は Schema First + Code Generation です。この記事では、その成り立ち、メリット・デメリット、そして他の Go ORM フレームワークとの比較を整理します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2023/06/13/go#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>Ent は当初、Facebook の社内プロジェクトとして設計・実装され、大規模システムにおけるデータモデルの複雑さ、多数のリレーション、そしてデータアクセス層の保守の難しさといった問題を解決するために生まれました。Facebook 社内のシステム規模が拡大し続ける中で、従来の ORM は <strong>型安全性、クエリの保守性、複雑なリレーションのモデリング</strong> の面で次第に限界を露呈するようになり、そこで Facebook はまったく新しい ORM ソリューション——Ent を設計しました。</p>
<p>Ent の核となる理念は <strong>Schema First + Code Generation</strong> です。開発者はまず Go コードでデータモデルの Schema を定義し、次にコード生成ツールを使って、エンティティ構造体、クエリビルダー、リレーション操作、CRUD メソッドを含む完全なデータアクセス層のコードを生成します。ロジックの大部分が <strong>コンパイル段階で生成・検証される</strong> ため、Ent は開発効率を保ちながら、より高い型安全性とより良い実行時パフォーマンスを提供できます。</p>
<p>オープンソース化後、Ent はすぐに Go コミュニティで広く注目を集め、<strong>Go エコシステムで最も特色のある ORM フレームワークの一つ</strong> へと成長していきました。特に、SNS プラットフォーム、EC システム、マイクロサービスアーキテクチャにおける中核データサービスなど、データのリレーションが複雑な大規模システムに適しています。</p>
<p><a href="https://github.com/ent/ent" target="_blank" rel="noopener noreferrer" class="">GitHub - ent/ent: An entity framework for Go</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="フレームワークのメリット">フレームワークのメリット<a href="https://tanzhuo.xyz/ja/2023/06/13/go#%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88" class="hash-link" aria-label="フレームワークのメリット への直接リンク" title="フレームワークのメリット への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>高いパフォーマンス</strong>：Ent は <strong>コード生成（Code Generation）</strong> の方式でデータアクセス層を構築します。従来の ORM が実行時にリフレクションで構造体を解析して SQL を生成するのに対し、Ent はコンパイル段階で完全なデータ操作コードを生成するため、実行時のオーバーヘッドが小さく、パフォーマンスは手書き SQL に近づきます。この設計により、Ent は高並行のシナリオでも良好なパフォーマンスを維持できます。</li>
<li class=""><strong>使いやすさ</strong>：Ent のクエリビルダーは <strong>強い型付けの DSL（Domain Specific Language）</strong> です。すべてのクエリフィールド、リレーション、操作はコードジェネレータによって自動生成されるため、コンパイル段階でエラーを発見できます。</li>
<li class=""><strong>拡張性</strong>：Ent は豊富な拡張メカニズムを提供しており、開発者は実際のニーズに応じてカスタマイズできます。これらの拡張能力を通じて、Ent を既存システムと深く統合することが容易になります。</li>
<li class=""><strong>型安全性</strong>：Ent は Go 言語の型システムを活用してコードの正しさを保証し、実行時エラーのリスクを減らします。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="フレームワークの不足点">フレームワークの不足点<a href="https://tanzhuo.xyz/ja/2023/06/13/go#%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E4%B8%8D%E8%B6%B3%E7%82%B9" class="hash-link" aria-label="フレームワークの不足点 への直接リンク" title="フレームワークの不足点 への直接リンク" translate="no">​</a></h2>
<p>他の ORM フレームワークと比べると、Ent のコミュニティ規模は小さく、ドキュメントや資料が相対的に少ないです。一部の特定シナリオでは Ent のパフォーマンスが制限を受ける可能性があり、対象を絞った最適化が必要になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ent-の発展史">Ent の発展史<a href="https://tanzhuo.xyz/ja/2023/06/13/go#ent-%E3%81%AE%E7%99%BA%E5%B1%95%E5%8F%B2" class="hash-link" aria-label="Ent の発展史 への直接リンク" title="Ent の発展史 への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>2018 年初め</strong>：Ent が Facebook 社内でプロジェクトとして立ち上がり、開発が始まりました。</li>
<li class=""><strong>2019 年初め</strong>：Facebook が Ent フレームワークをオープンソース化しました。オープンソース化後、Ent は広く注目され利用されるようになり、コミュニティ規模も徐々に拡大しました。</li>
<li class=""><strong>2019 年中頃</strong>：Ent はパフォーマンス最適化、バグ修正、新機能追加を含む一連のバージョンアップを行い、フレームワークはより安定して効率的になりました。</li>
<li class=""><strong>2020 年初め</strong>：Ent が 1.0 バージョンをリリースし、マルチデータベース対応やカスタム型などの新機能が追加されました。1.0 バージョンのリリースは、Ent が成熟・安定し、本番環境で使えるようになったことを示すものでした。</li>
<li class=""><strong>2020 年中頃から現在まで</strong>：フレームワークの発展と改善が続く中で、ますます多くの開発者が Ent を使い始め、コードやドキュメントで貢献しています。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ent-と他の-go-orm-フレームワークの比較">Ent と他の Go ORM フレームワークの比較<a href="https://tanzhuo.xyz/ja/2023/06/13/go#ent-%E3%81%A8%E4%BB%96%E3%81%AE-go-orm-%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E6%AF%94%E8%BC%83" class="hash-link" aria-label="Ent と他の Go ORM フレームワークの比較 への直接リンク" title="Ent と他の Go ORM フレームワークの比較 への直接リンク" translate="no">​</a></h2>
<p>Go エコシステムでよく見かける ORM フレームワークには <strong>GORM、XORM、SQLBoiler、Ent</strong> があります。各フレームワークの設計理念には明らかな違いがあります。</p>






























<table><thead><tr><th>フレームワーク</th><th>タイプ</th><th>主な特徴</th></tr></thead><tbody><tr><td>GORM</td><td>実行時 ORM</td><td>Go で最も人気のある ORM。使い方が簡単でエコシステムが成熟</td></tr><tr><td>XORM</td><td>実行時 ORM</td><td>軽量な ORM。シンプルな業務に適する</td></tr><tr><td>SQLBoiler</td><td>コード生成 ORM</td><td>データベースの Schema からコードを生成。パフォーマンスが高い</td></tr><tr><td>Ent</td><td>Schema-first + Code Generation</td><td>強い型付けのクエリ構築。複雑なリレーションを持つシステムに適する</td></tr></tbody></table>
<p>設計パターンの観点から見ると：</p>
<ul>
<li class=""><strong>GORM / XORM</strong>：実行時 ORM。リフレクションで構造体を解析して SQL を生成し、習得が容易です。</li>
<li class=""><strong>SQLBoiler</strong>：データベースの Schema をもとにコードを生成し、型安全性とパフォーマンスに優れます。</li>
<li class=""><strong>Ent</strong>：Go の Schema でモデルを定義し、データアクセスコードを自動生成します。<strong>型安全性と複雑なリレーションのモデリング</strong> を重視しています。</li>
</ul>
<p>簡単に言えば：</p>
<ul>
<li class="">小規模プロジェクトや素早い開発なら：<strong>GORM が向いている</strong></li>
<li class="">データのリレーションが複雑、あるいは大規模システムなら：<strong>Ent に分がある</strong></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/06/13/go#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Ent の核となる競争力は、Schema First とコード生成がもたらす強い型の保証にあります。クエリのエラーは本番の実行時になって初めて見つかるのではなく、コンパイル段階で顕在化します。その代償として、コミュニティ規模や資料の豊富さは GORM に及ばず、習得にはまずコード生成のワークフローを受け入れる必要があります。プロジェクトのデータリレーションが複雑で、型安全性への要求が高いなら、Ent は真剣に検討する価値があります。小規模プロジェクトや素早いプロトタイピングだけなら、GORM のような実行時 ORM のほうが手軽でしょう。</p>]]></content:encoded>
            <category>Go</category>
        </item>
        <item>
            <title><![CDATA[Kratos]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/17/k-2</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/17/k-2</guid>
            <pubDate>Wed, 17 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Kratos は Bilibili がオープンソース化した Go のマイクロサービスフレームワークです。本記事ではその設計上の利点とコア機能を紹介し、Spring Cloud、Dubbo、Go-Micro などの主要フレームワークと比較します。]]></description>
            <content:encoded><![CDATA[<p>Kratos は Go 言語ベースのオープンソースマイクロサービスフレームワークで、Bilibili がオープンソース化し、メンテナンスしています。マイクロサービスに必要なサービスディスカバリ、登録、HTTP、gRPC、レート制限、設定センターなどの一般的な機能を提供します。フレームワークの設計は合理的で、軽量で、習得しやすいのが特徴です。</p>
<!-- -->
<p>公式ドキュメント: <a href="https://go-kratos.dev/" target="_blank" rel="noopener noreferrer" class="">Kratos</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="フレームワークの利点">フレームワークの利点<a href="https://tanzhuo.xyz/ja/2023/05/17/k-2#%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E5%88%A9%E7%82%B9" class="hash-link" aria-label="フレームワークの利点 への直接リンク" title="フレームワークの利点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class=""><strong>高性能</strong>: Kratos は Go のゴルーチンと効率的な並行モデルを活用しており、高並行なシーンを支えることができ、リクエストとレスポンスの処理オーバーヘッドが小さくなっています。</li>
<li class=""><strong>シンプルで使いやすい</strong>: フレームワークは豊富なドキュメントとサンプルを提供しており、設計が簡潔で、開発者が多くの概念や技術を習得する必要がなく、入門のハードルが低いです。</li>
<li class=""><strong>モジュール化設計</strong>: 設定、ログ、データベースなど一連のモジュールを内蔵しており、マイクロサービスを素早く構築するのに便利です。同時にプラグイン形式の開発をサポートし、必要に応じてフレームワークの機能を拡張できます。</li>
<li class=""><strong>高度なカスタマイズ性</strong>: 豊富な設定オプションを提供し、要件に合わせてカスタマイズできます。カスタムミドルウェアやインターセプターなどもサポートしており、開発者が自分で拡張できます。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="具体的な機能">具体的な機能<a href="https://tanzhuo.xyz/ja/2023/05/17/k-2#%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AA%E6%A9%9F%E8%83%BD" class="hash-link" aria-label="具体的な機能 への直接リンク" title="具体的な機能 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">マイクロサービスの登録とディスカバリ: etcd、consul など複数のレジストリをサポート。</li>
<li class="">設定管理: ローカルファイル、etcd など複数の設定ソースをサポート。</li>
<li class="">ログ管理: ファイル、コンソール、Kafka など複数のログ出力方式をサポート。</li>
<li class="">データベースアクセス: MySQL、PostgreSQL、Redis など複数のストレージをサポート。</li>
<li class="">HTTP サービス: HTTP、HTTPS サービスをサポート。</li>
<li class="">gRPC サービス: gRPC サービスをネイティブサポート。</li>
<li class="">セキュリティ: セキュリティミドルウェアを提供し、JWT 認証、TLS などをサポート。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスフレームワーク比較">マイクロサービスフレームワーク比較<a href="https://tanzhuo.xyz/ja/2023/05/17/k-2#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E6%AF%94%E8%BC%83" class="hash-link" aria-label="マイクロサービスフレームワーク比較 への直接リンク" title="マイクロサービスフレームワーク比較 への直接リンク" translate="no">​</a></h2>
<ol>
<li class=""><strong>Spring Cloud との比較</strong>: Spring Cloud は Java エコシステムのマイクロサービスフレームワークで、Kratos に比べて学習曲線が急であり、習得すべき概念や技術が多く、全体としても重量級で、デプロイと運用のコストが高くなります。</li>
<li class=""><strong>Dubbo との比較</strong>: Dubbo は Alibaba がオープンソース化した Java のマイクロサービスフレームワークで、Go 言語をサポートしていません。それに比べて Kratos のドキュメントとサンプルは Go 開発者により寄り添っており、学習と利用のコストが低くなります。</li>
<li class=""><strong>gRPC との比較</strong>: gRPC は Google がオープンソース化したクロス言語の RPC フレームワークで、性能は優れていますが、通信層の問題しか解決せず、サービスガバナンス関連のミドルウェアやインターセプターなどの周辺機能が欠けており、学習曲線も急です。</li>
<li class=""><strong>Go-Micro との比較</strong>: Go-Micro は同じく Go 言語のマイクロサービスフレームワークで、設計がより柔軟であり、複数のプロトコルとレジストリをサポートし、多言語にも対応していますが、学習曲線が急で、習得すべき概念や技術が多くなります。</li>
<li class=""><strong>Gin との比較</strong>: Gin は Go 言語の Web フレームワークで、より軽量であり、シンプルな Web サービスを素早く構築するのに向いていますが、サービスの登録とディスカバリ、RPC 呼び出しといったマイクロサービスフレームワークの能力が欠けています。</li>
<li class=""><strong>Micro との比較</strong>: Micro も Go 言語のマイクロサービスフレームワークで、設計が柔軟で複数のプロトコルとレジストリをサポートしていますが、概念が多く、習得の難易度は Kratos より高くなります。</li>
<li class=""><strong>Beego との比較</strong>: Beego は Go 言語の Web フレームワークで、位置づけは Gin に近く、Web サービスを素早く構築するのに向いていますが、同様にサービスの登録とディスカバリ、RPC 呼び出しなどのマイクロサービス能力が欠けています。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/17/k-2#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Go 言語のマイクロサービスフレームワークの中で、Kratos は性能、使いやすさ、機能の完成度の間で良いバランスを取っています。サービスディスカバリ、設定管理、ログ、gRPC/HTTP のデュアルプロトコルといったマイクロサービスの基盤をすべて内蔵しながら、軽量さとカスタマイズ性を保っています。Gin や Beego のような純粋な Web フレームワークと比べればサービスガバナンス能力を補完しており、Go-Micro や Micro と比べれば概念が少なく、より早く習得できます。さらに Bilibili による継続的なメンテナンスと豊富なドキュメント・サンプルもあり、Go でマイクロサービスを実現したいチームにとって、Kratos は優先的に検討する価値のある選択肢です。</p>]]></content:encoded>
            <category>Go</category>
        </item>
        <item>
            <title><![CDATA[Neo4j グラフデータベース]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database</guid>
            <pubDate>Tue, 16 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Neo4j はグラフデータモデルを採用した高性能なグラフデータベースで、リレーション密度の高いシナリオの処理を得意とします。本記事ではその料金モデル、データ構造、主要機能を整理し、選定時の注意点を補足します。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>ビジネスに多段の関連クエリが出てくると、リレーショナルデータベースの Join は途端に重たくなります。このノートでは、グラフデータベースである Neo4j について整理します。それが何であるか、料金体系はどうなっているか、データはどう組織されているか、そしてどんなシナリオで導入する価値があるか、です。</p>
<!-- -->
<p>Neo4j は高性能なグラフデータベースで、<strong>グラフデータモデル（Graph Model）</strong> を採用してデータを保存・処理します。従来のリレーショナルデータベースと比べ、Neo4j は複雑なリレーションを持つデータの処理を得意とし、特に <strong>リレーション密度の高いデータシナリオ</strong> に適しています。</p>
<p>多くの業務システムでは、データ間に大量の関連関係が存在します。例えば：</p>
<ul>
<li class="">ソーシャルネットワークにおける友人関係</li>
<li class="">レコメンドシステムにおけるユーザーと商品の関係</li>
<li class="">ナレッジグラフにおけるエンティティ間の関係</li>
<li class="">ネットワークセキュリティにおける攻撃経路の分析</li>
</ul>
<p>これらのシナリオで従来のリレーショナルデータベースを使ってクエリすると、大量の <strong>Join 操作</strong> が必要になり、クエリの複雑さも性能も急速に悪化します。一方グラフデータベースはリレーションを直接たどって走査できるため、複雑なリレーションを扱う際の効率がより高くなります。</p>
<p>なぜ Join がボトルネックになるのでしょうか。リレーショナルデータベースにおいて「リレーション」は第一級市民ではなく、外部キーと中間テーブルによって表現されます。Join を 1 段行うたびに、データベースはインデックス内で検索を行い、2 つのテーブルの行をマッチングしなければなりません。関連の段数が深くなり、データ量が増えるほど、このマッチングコストは急激に増加します。典型的な例が「友人の友人の友人」といったクエリです。MySQL では同じリレーションテーブルを 3 回自己結合する必要がありますが、グラフデータベースではポインタに沿って 2 歩余分に進むだけです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="料金モデル">料金モデル<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#%E6%96%99%E9%87%91%E3%83%A2%E3%83%87%E3%83%AB" class="hash-link" aria-label="料金モデル への直接リンク" title="料金モデル への直接リンク" translate="no">​</a></h2>
<p>公式サイト：<a href="https://neo4j.com/" target="_blank" rel="noopener noreferrer" class="">Neo4j Graph Database &amp; Analytics – The Leader in Graph Databases</a></p>
<p>Neo4j はオープンソースと商用ライセンスの 2 本立てモデルを採用しており、選定の前にまず 2 つのエディションの境界を把握しておくべきです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="community-editionコミュニティ版">Community Edition（コミュニティ版）<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#community-edition%E3%82%B3%E3%83%9F%E3%83%A5%E3%83%8B%E3%83%86%E3%82%A3%E7%89%88" class="hash-link" aria-label="Community Edition（コミュニティ版） への直接リンク" title="Community Edition（コミュニティ版） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">オープンソースで無料</li>
<li class="">個人プロジェクトや中小規模のアプリケーションに適する</li>
<li class="">単一インスタンスのデプロイのみで、クラスタやオンラインホットスタンバイの機能は提供されない</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="enterprise-editionエンタープライズ版">Enterprise Edition（エンタープライズ版）<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#enterprise-edition%E3%82%A8%E3%83%B3%E3%82%BF%E3%83%BC%E3%83%97%E3%83%A9%E3%82%A4%E3%82%BA%E7%89%88" class="hash-link" aria-label="Enterprise Edition（エンタープライズ版） への直接リンク" title="Enterprise Edition（エンタープライズ版） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">商用ライセンス</li>
<li class="">クラスタ、高可用性、セキュリティ認証などの高度な機能を提供</li>
<li class="">可用性と権限管理に厳格な要件のある本番環境向け</li>
</ul>
<p>簡単に言えば、機能検証、社内ツール、データ量がコントロール可能なシナリオならコミュニティ版で十分です。高可用性や細粒度の権限管理が要求されるようになった時点でエンタープライズ版は避けて通れず、このライセンスコストは事前に計画へ織り込んでおく必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="グラフデータベースのデータ構造">グラフデータベースのデータ構造<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#%E3%82%B0%E3%83%A9%E3%83%95%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0" class="hash-link" aria-label="グラフデータベースのデータ構造 への直接リンク" title="グラフデータベースのデータ構造 への直接リンク" translate="no">​</a></h2>
<p>グラフデータベースのデータ構造は次のとおりです</p>
<p>Node (ノード)<br>
Relationship (リレーションシップ)<br>
Property (プロパティ)</p>
<p>3 者の分担は明快です。ノードはエンティティ（人、商品、デバイス）を表し、リレーションシップはエンティティ間の接続を表し、<strong>必ず方向とタイプを持ちます</strong>。プロパティはノードまたはリレーションシップに付与されるキーバリューペアです。リレーショナルデータベースと対比して理解すると、ノードはテーブルの 1 行に、リレーションシップは外部キーに近いものです。ただしリレーションシップ自体もプロパティを持てる点（例えば「友人関係」に成立日時を保存できる）は、外部キーにはできないことです。</p>
<p>例：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">(A)-[FRIEND]-&gt;(B)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">(B)-[FRIEND]-&gt;(C)</span><br></div></code></pre></div></div>
<p>リレーションシップは <strong>ノード間に直接保存されるポインタ</strong> です。データベースは <strong>リレーションシップのポインタに沿って走査する</strong> だけでよく、テーブル結合を行う必要がありません。そのため関連関係のクエリではリレーショナルデータベースをはるかに上回ります。この設計には専門の呼び名があり、<strong>インデックスフリー隣接（index-free adjacency）</strong> といいます。あるノードの隣接ノードを見つけるのにグローバルインデックスを経由せず、ノード自身のポインタから直接出発するため、1 ステップの走査コストはそのノードが持つリレーションシップの数だけに依存し、データベース全体のデータ量には依存しません。</p>
<p>クエリには Cypher 言語を使います。書き方は上のグラフ表記とほぼ一致しています：</p>
<div class="language-cypher codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cypher codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">// A の友人の友人を検索する（2 段のリレーション走査）</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">MATCH (a:Person {name: 'A'})-[:FRIEND]-&gt;()-[:FRIEND]-&gt;(fof)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">RETURN fof.name</span><br></div></code></pre></div></div>
<p>パターンマッチをそのまま絵に描いたものがクエリ文になる。これもグラフデータベースのモデリングが直感的である理由の一つです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="主要機能">主要機能<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#%E4%B8%BB%E8%A6%81%E6%A9%9F%E8%83%BD" class="hash-link" aria-label="主要機能 への直接リンク" title="主要機能 への直接リンク" translate="no">​</a></h2>
<p>グラフデータの保存とクエリ：Neo4j はグラフデータモデルを使ってデータを保存し、複雑なリレーションデータを容易に処理できます。</p>
<p>効率的なクエリ性能：グラフデータモデルの採用により、Neo4j は深いクエリや複雑なリレーションクエリを容易に処理でき、より高いクエリ性能を持ちます。クエリの所要時間は主に走査で通過するサブグラフの大きさに依存し、データベース全体の規模には依存しません。</p>
<p>拡張性：Neo4j は数百億のノードとリレーションシップまで容易にスケールでき、優れた拡張性を持ちます。</p>
<p>ACID トランザクションのサポート：Neo4j は ACID トランザクションをサポートし、データの整合性と信頼性を保証します。この点が、オフライン分析専用の多くのグラフ計算フレームワークとの違いです。Neo4j は分析ツールではなく、オンラインの書き込みを受け止められるデータベースなのです。</p>
<p>Neo4j はグラフデータモデルによってデータのリレーション表現をより直感的にし、複雑なリレーションクエリのシナリオで明確な性能優位を持ちます。従来の業務システムでは、リレーショナルデータベースが依然として中核的なデータストレージです。しかし <strong>リレーション密度の高いシナリオ</strong>（ソーシャルグラフ、レコメンドシステム、ナレッジグラフなど）では、グラフデータベースをリレーション分析エンジンとして、従来のデータベースと補完的に使うことができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）グラフデータベースをリレーショナルデータベースの代替にしないこと。集計統計や一括レポートのような列スキャン型のシナリオでは、グラフモデルに優位性はありません。合理的なアーキテクチャは通常、RDB がマスタデータを持ち、Neo4j がリレーションのサブセットを持つ形です。</p>
<p>2）モデリングの発想を切り替えること。グラフモデリングではまず「ノードは何か、リレーションシップは何と呼ぶか、方向はどちらか」を考え抜くのであって、先にテーブル構造を設計するのではありません。リレーションシップの方向やタイプを間違えて定義すると、後のクエリが非常に書きづらくなります。</p>
<p>3）走査の深さを制御すること。Cypher は可変長パスマッチングをサポートしているため、クエリで深さを限定しなかったりノードラベルを付けなかったりすると、簡単に全グラフスキャンへと発展し、インスタンス全体を巻き込んでしまいます。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>導入の前に、実際のリレーションクエリで検証してください。ビジネス上のクエリの大半が 1、2 段の Join で済むなら、リレーショナルデータベースにインデックスをきちんと張れば十分なことが多く、もう一つストレージを運用する必要はありません。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/16/neo4j-graph-database#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>グラフデータベースが解決する核心的な問題はただ一つ、「リレーション」を第一級市民にし、Join をポインタ走査に置き換えることです。Neo4j のコミュニティ版はアイデアの検証に十分であり、本番の高可用性にはエンタープライズ版のライセンスを検討する必要があります。それはリレーショナルデータベースの代替品ではなく、リレーション密度の高いシナリオにおける補完エンジンです。クエリパターンが本当に深い関連走査であることを確認してから、導入を決めましょう。</p>]]></content:encoded>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[データベースの分類]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku</guid>
            <pubDate>Tue, 16 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[データモデル、処理方式、デプロイ方式、記憶媒体、アクセス方式といった軸からデータベースの分類を整理し、各種データベースの典型的なユースケースを紹介します。]]></description>
            <content:encoded><![CDATA[<p>データベースは複数の軸から分類でき、それぞれの分類方法は <strong>データモデル、処理能力、デプロイ方式、記憶媒体、応用シーン</strong> などにおけるデータベースの違いを反映しています。これらの分類を理解しておくことは、実際のシステムアーキテクチャ設計で適切なデータベースソリューションを選択するのに役立ちます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データモデルによる分類">データモデルによる分類<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E" class="hash-link" aria-label="データモデルによる分類 への直接リンク" title="データモデルによる分類 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="リレーショナルデータベースrdbms">リレーショナルデータベース（RDBMS）<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9rdbms" class="hash-link" aria-label="リレーショナルデータベース（RDBMS） への直接リンク" title="リレーショナルデータベース（RDBMS） への直接リンク" translate="no">​</a></h3>
<p>リレーショナルデータベースは <strong>テーブル（Table）</strong> を中核のデータ構造とし、<strong>行と列</strong> でデータを組織化し、<strong>SQL（Structured Query Language）</strong> を使ってデータのクエリと管理を行います。異なるテーブル間は <strong>主キー（Primary Key）と外部キー（Foreign Key）</strong> で関係を構築し、複雑なデータの関連クエリを実現します。</p>
<p>その特徴は：</p>
<ul>
<li class="">データ構造が明確で、強い制約を持つ</li>
<li class="">複雑なクエリ（Join、集計など）をサポートする</li>
<li class="">トランザクションと ACID 特性をサポートする</li>
<li class="">構造化データの保存に適している</li>
</ul>
<p>代表的なデータベース：MySQL、PostgreSQL、Oracle、SQL Server など。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="非リレーショナルデータベースnosql">非リレーショナルデータベース（NoSQL）<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E9%9D%9E%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9nosql" class="hash-link" aria-label="非リレーショナルデータベース（NoSQL） への直接リンク" title="非リレーショナルデータベース（NoSQL） への直接リンク" translate="no">​</a></h3>
<p>NoSQL（Not Only SQL）データベースは従来のリレーショナルなテーブル構造を使わず、より柔軟なデータモデルを採用することで、大規模データと高並行アクセスのシーンに対応します。</p>
<p>その特徴は：</p>
<ul>
<li class="">データ構造が柔軟</li>
<li class="">スケーラビリティが高い</li>
<li class="">高並行時の性能が良い</li>
<li class="">通常はトランザクションが弱いか、トランザクションを持たない</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="処理方式による分類">処理方式による分類<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E5%87%A6%E7%90%86%E6%96%B9%E5%BC%8F%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E" class="hash-link" aria-label="処理方式による分類 への直接リンク" title="処理方式による分類 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">トランザクション型データベース：トランザクション処理と ACID 特性（原子性、一貫性、独立性、永続性）をサポートします。</li>
<li class="">非トランザクション型データベース：トランザクション処理と ACID 特性をサポートしませんが、より高い性能とスケーラビリティを備えています。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイ方式による分類">デプロイ方式による分類<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E6%96%B9%E5%BC%8F%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E" class="hash-link" aria-label="デプロイ方式による分類 への直接リンク" title="デプロイ方式による分類 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">単一マシンデータベース：1 台のサーバーにデプロイされ、すべてのデータと計算が同一ノードで完結します。</li>
<li class="">分散データベース：データを複数のサーバーに分散させ、ネットワークを通じて協調動作することで、より高いスケーラビリティと可用性を実現します。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="記憶方式による分類">記憶方式による分類<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E8%A8%98%E6%86%B6%E6%96%B9%E5%BC%8F%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E" class="hash-link" aria-label="記憶方式による分類 への直接リンク" title="記憶方式による分類 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">インメモリデータベース：データを <strong>RAM（メモリ）</strong> に保存するため、読み書きが非常に高速です。Redis、Memcached など。</li>
<li class="">ディスクデータベース：データを <strong>ディスクや SSD</strong> 上に永続化して保存します。MySQL、PostgreSQL、Oracle など。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アクセス方式による分類">アクセス方式による分類<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E6%96%B9%E5%BC%8F%E3%81%AB%E3%82%88%E3%82%8B%E5%88%86%E9%A1%9E" class="hash-link" aria-label="アクセス方式による分類 への直接リンク" title="アクセス方式による分類 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">OLTP（Online Transaction Processing）データベースは、主に <strong>オンライントランザクション処理システム</strong> に使われます。</li>
<li class="">OLAP（Online Analytical Processing）データベースは、主に <strong>データ分析と意思決定支援</strong> に使われます。ClickHouse、Doris、StarRocks、Snowflake など。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ユースケースの紹介">ユースケースの紹介<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9%E3%81%AE%E7%B4%B9%E4%BB%8B" class="hash-link" aria-label="ユースケースの紹介 への直接リンク" title="ユースケースの紹介 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="リレーショナルデータベースrdbms-1">リレーショナルデータベース（RDBMS）<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9rdbms-1" class="hash-link" aria-label="リレーショナルデータベース（RDBMS） への直接リンク" title="リレーショナルデータベース（RDBMS） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">大量の構造化データを処理する必要のあるシーンに適しています。金融、EC、物流など。</li>
<li class="">トランザクション処理と ACID 特性のサポートが必要なシーンに適しています。銀行、通信、航空など。</li>
<li class="">複雑なクエリと分析が必要なシーンに適しています。データウェアハウス、ビジネスインテリジェンスなど。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="非リレーショナルデータベースnosql-1">非リレーショナルデータベース（NoSQL）<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E9%9D%9E%E3%83%AA%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%8A%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9nosql-1" class="hash-link" aria-label="非リレーショナルデータベース（NoSQL） への直接リンク" title="非リレーショナルデータベース（NoSQL） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">ドキュメント型データベース：半構造化データを処理するシーンに適しています。ブログ、ニュース、コメントなど。</li>
<li class="">キーバリュー型データベース：シンプルなキーバリューデータを処理するシーンに適しています。キャッシュ、セッション管理など。</li>
<li class="">カラム指向データベース：大量の構造化データを処理するシーンに適しています。ログ、イベント、時系列データなど。</li>
<li class="">グラフデータベース：複雑な関係性を持つデータを処理するシーンに適しています。ソーシャルネットワーク、レコメンドシステム、バイオインフォマティクス、ネットワークセキュリティなど。</li>
<li class="">オブジェクトデータベース：オブジェクト指向プログラミングのサポートが必要なシーンに適しています。ゲーム開発、マルチメディアアプリケーションなど。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="インメモリデータベース">インメモリデータベース<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E3%82%A4%E3%83%B3%E3%83%A1%E3%83%A2%E3%83%AA%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9" class="hash-link" aria-label="インメモリデータベース への直接リンク" title="インメモリデータベース への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">高速な応答が必要なシーンに適しています。高並行の取引処理、リアルタイムデータ分析など。</li>
<li class="">高速な読み書きが必要なシーンに適しています。キャッシュ、セッション管理など。</li>
<li class="">リアルタイムの計算と分析が必要なシーンに適しています。リアルタイムのリスク管理、リアルタイムレコメンドなど。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="分散データベース">分散データベース<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E5%88%86%E6%95%A3%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9" class="hash-link" aria-label="分散データベース への直接リンク" title="分散データベース への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">高可用性と耐障害性が必要なシーンに適しています。分散コンピューティング、分散ストレージなど。</li>
<li class="">大規模なデータ処理と分析のサポートが必要なシーンに適しています。ビッグデータ、機械学習など。</li>
<li class="">複数リージョンへのデプロイとデータ共有のサポートが必要なシーンに適しています。クラウドコンピューティング、IoT など。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2023/05/16/ren-shi-shu-ju-ku#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>データベースの分類軸は数多くあり、同じ製品が複数のカテゴリに同時に属することも珍しくありません。たとえば MySQL はリレーショナルデータベースであると同時に、ディスク保存のトランザクション型データベースでもあります。選定時には分類そのものにこだわる必要はなく、データ構造、一貫性の要件、並行規模、クエリパターンから出発して、ビジネスにマッチするカテゴリを見つけるべきです。実際のシステムでは複数のデータベースを組み合わせて使うこともよくあります。たとえば Redis をキャッシュに、MySQL をビジネスデータの保存に、ClickHouse を分析クエリに使う、といった具合です。各カテゴリのデータベースの位置づけを理解してこそ、アーキテクチャ設計で合理的なトレードオフができるのです。</p>]]></content:encoded>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[二重送信の防止]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao</guid>
            <pubDate>Tue, 09 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[サーバー側でワンタイム Token を生成して Redis に保存し、フォームとともにクライアントへ配布、送信時に検証が通れば即座に削除する。この仕組みによる二重送信・CSRF 対策の原理、実装手順、セキュリティ上の注意点を記録します。]]></description>
            <content:encoded><![CDATA[<p>送信ボタンのダブルクリック、ネットワークタイムアウト後の自動リトライ、ブラウザバックからの再送信——いずれも同じフォームがサーバーで二度処理される原因になりえます。Token（トークン）機構を使えば、CSRF 攻撃と二重送信を効果的に防げます。フォーム送信の際、サーバーが Token を生成して Redis に保存し、フォームの隠しフィールドや URL パラメータとしてクライアントに渡し、送信時に検証して使い終わったら即座に削除する、という流れです。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜこの問題に取り組む価値があるのか">なぜこの問題に取り組む価値があるのか<a href="https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao#%E3%81%AA%E3%81%9C%E3%81%93%E3%81%AE%E5%95%8F%E9%A1%8C%E3%81%AB%E5%8F%96%E3%82%8A%E7%B5%84%E3%82%80%E4%BE%A1%E5%80%A4%E3%81%8C%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜこの問題に取り組む価値があるのか への直接リンク" title="なぜこの問題に取り組む価値があるのか への直接リンク" translate="no">​</a></h2>
<p>二重送信のビジネス上の影響は、見た目以上に深刻なことが多いものです。注文 API が二度処理されれば注文が二件になり、決済リクエストが二度リトライされれば二重課金になります。フロントエンドで送信ボタンをグレーアウトしても、うっかりクリックは防げますが、ネットワーク層の自動リトライは防げませんし、悪意を持って組み立てられたリクエストならなおさらです。だからこそ、この防御は最終的にサーバー側で行う必要があります。</p>
<p>一方 CSRF 攻撃は、攻撃者がユーザーのブラウザを誘導して標的サイトへリクエストを送らせるものです。これが成立してしまうのは、ブラウザが Cookie を自動的に付与するからです。Token は Cookie に置かないため、第三者のサイトからはこの値を取得できません。したがって、一つの Token 機構でこの二種類の問題を同時に防ぐことができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="基本原理">基本原理<a href="https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao#%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86" class="hash-link" aria-label="基本原理 への直接リンク" title="基本原理 への直接リンク" translate="no">​</a></h2>
<p>Token 機構は、Web アプリケーションで二重送信と CSRF 攻撃を防ぐためによく使われる手法です。基本的な考え方は、フォーム送信のたびにサーバーが Token を生成して Redis に保存し、その Token をフォームの隠しフィールドまたは URL パラメータとしてクライアントに渡す、というものです。クライアントはフォーム送信時にこの Token を一緒にサーバーへ送ります。サーバーはフォームを処理する際にこの Token が正しいかを確認し、処理が終わったらこの Token を削除します。</p>
<p>二つの防御目標には、仕組みの上でそれぞれ要点があります。二重送信の防止は「検証が通ったら即座に削除する」ことに支えられています。同じ Token が二度目に到達したときにはすでに存在しないため、リクエストは拒否されます。CSRF の防止は「Token が正規のページにしか配布されない」ことに支えられています。攻撃者が組み立てたリクエストにはこの値が含まれません。Redis はここで二つの役割を担います。一つは集中管理——マルチインスタンス構成でもどのマシンからでも検証を完結できること。もう一つは有効期限の管理——TTL を利用して、消費されなかった Token を自動的に片付けることです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実装手順">実装手順<a href="https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao#%E5%AE%9F%E8%A3%85%E6%89%8B%E9%A0%86" class="hash-link" aria-label="実装手順 への直接リンク" title="実装手順 への直接リンク" translate="no">​</a></h2>
<p>1）サーバー側で Token を生成し、Redis に保存します。Token は乱数や UUID などで生成でき、十分にランダムかつ一意であることを保証する必要があります。書き込み時にあわせて有効期限も設定します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># NX で既存の Token を上書きしないことを保証し、EX で有効期限（秒）を設定して、無効な Token の長期滞留を防ぐ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">SET submit:token:</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">tokenValue</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token plain"> EX </span><span class="token number" style="color:hsl(29, 54%, 61%)">600</span><span class="token plain"> NX</span><br></div></code></pre></div></div>
<p>2）この Token をクライアントに渡します。フォーム送信であれば隠しフィールドとして、AJAX リクエストであれば HTTP ヘッダーとして渡せます。</p>
<p>3）クライアントはフォーム送信時に、この Token を一緒にサーバーへ送信します。フォーム送信であれば、フォームの submit イベントに JavaScript コードを追加して Token を隠しフィールドとして付与できます。AJAX リクエストであれば、リクエスト送信前に Token を HTTP ヘッダーとして付与できます。</p>
<p>4）サーバーはフォームを処理する際、この Token が正しいかを検証します。Token が正しければ有効なリクエストとして処理を続行し、そうでなければ二重送信または CSRF 攻撃とみなして処理を拒否すべきです。</p>
<p>5）フォームの処理が完了したら、サーバーは Redis からこの Token を削除し、再利用を防ぎます。</p>
<p>手順 4 と 5 の「検証」と「削除」は、必ずひとつのアトミックな操作として行わなければなりません。GET してから DEL する二段階の実装だと、並行する二つのリクエストが同時に検証を通過してしまい、二重送信防止が機能しなくなります。Lua スクリプトを使えば、Redis 上で検証と削除をアトミックに完結できます。</p>
<div class="language-lua codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-lua codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">-- 検証して削除：Token が存在すれば削除して 1 を返し、存在しなければ 0 を返す</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">-- スクリプト全体が Redis 内でアトミックに実行され、並行リクエストのうち成功するのは一つだけ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">if redis.call('GET', KEYS[1]) then</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  return redis.call('DEL', KEYS[1])</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">else</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  return 0</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">end</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="セキュリティ上の注意点">セキュリティ上の注意点<a href="https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E4%B8%8A%E3%81%AE%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="セキュリティ上の注意点 への直接リンク" title="セキュリティ上の注意点 への直接リンク" translate="no">​</a></h2>
<p>Token 機構は二重送信と CSRF 攻撃を効果的に防げますが、Token 自体の安全性を守ることに注意しないと、攻撃者に悪用される恐れがあります。具体的には、次の点に注意が必要です。</p>
<p>Token は十分にランダムかつ一意でなければなりません。そうでないと、攻撃者に推測されたり再利用されたりする恐れがあります。</p>
<p>Token は転送経路の安全を守る必要があります。そうでないと、攻撃者に傍受・改ざんされる恐れがあります。転送は必ず HTTPS を使い、ログに記録される URL に Token を含めないようにしましょう。</p>
<p>Token には有効期限を設定する必要があります。そうでないと、攻撃者に再利用される恐れがあります。</p>
<p>Token は Redis 上での保存の安全も守る必要があります。そうでないと、攻撃者に窃取・改ざんされる恐れがあります。Redis インスタンスをパブリックネットワークに公開せず、アクセス認証を有効にしましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>検証と削除を二段階に分けて書くのが、最もよくある誤った実装です。負荷試験中やユーザーの素早いダブルクリック時に、二つのリクエストがどちらも GET の検証を通過し、それぞれビジネス処理を一度ずつ実行してしまいます。必ず Lua スクリプトか同等の手段でアトミック性を保証してください。</p></div></div>
<p>有効期限は、ユーザーが実際にフォームを埋めるのにかかる時間を踏まえて決めましょう。短すぎると、長いフォームを入力している間に Token が期限切れになり、送信が必ず失敗します。長すぎると、無効な Token が Redis に溜まっていきます。</p>
<p>検証に失敗したときは、フロントエンドで明確なフィードバックを返し、ページを再読み込みして Token を取得し直してから再送信するようユーザーを誘導すべきです。リクエストを黙って破棄してしまうと、ユーザーは「押したのに反応がない」と感じて連打を続けるだけです。</p>
<p>また、削除のタイミングもよく考える必要があります。ビジネス処理が失敗したときに Token を返却してユーザーの即時リトライを許すかどうかは、そのビジネスが冪等性に何を求めるか次第で、統一的な答えはありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/09/fang-zhong-fu-ti-jiao#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Token 機構の核心は「ワンタイムの証憑」です。サーバーが発行し、Redis に保存し、送信時に検証し、使い終わったら即座に削除する——この一連の流れで、二重送信と CSRF という二つの問題を同時に解決します。実装時に重点的に押さえるべきは四つ——Token のランダム性、転送と保存の安全、適切な有効期限、そして検証・削除のアトミック性です。これらを押さえれば、低コストで効果の明確な汎用的防御手段になります。</p>]]></content:encoded>
            <category>Java</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[Docker の仕組み]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/06/docker</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/06/docker</guid>
            <pubDate>Sat, 06 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Docker 学習ノート:Daemon と Client のやり取りの流れ、Namespace/Cgroups/ユニオンファイルシステムによるコンテナ実装の仕組み、そしてイメージのレイヤー構造とデータボリュームの低レベルストレージの仕組み。]]></description>
            <content:encoded><![CDATA[<p>Docker の内部アーキテクチャ、コンテナ実装の仕組み、低レベルストレージの仕組みについてのノートです。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="内部アーキテクチャのコンポーネント">内部アーキテクチャのコンポーネント<a href="https://tanzhuo.xyz/ja/2023/05/06/docker#%E5%86%85%E9%83%A8%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88" class="hash-link" aria-label="内部アーキテクチャのコンポーネント への直接リンク" title="内部アーキテクチャのコンポーネント への直接リンク" translate="no">​</a></h2>





























<table><thead><tr><th>コンポーネント</th><th>説明</th></tr></thead><tbody><tr><td>Docker Daemon</td><td>Docker の中核コンポーネント。デーモンプロセスとして動作し、コンテナの作成・起動・停止・削除などの操作を管理します。</td></tr><tr><td>Docker Client</td><td>Docker Daemon とやり取りするコマンドラインツール。ユーザーはこのツールでコマンドを送信し、Docker コンテナを操作します。</td></tr><tr><td>Docker Registry</td><td>Docker イメージを保管する中央リポジトリ。Docker Client を通じて必要なイメージをここから取得できます。</td></tr><tr><td>Docker Image</td><td>コンテナの土台となる読み取り専用のファイルシステムで、アプリケーション、依存ライブラリ、設定ファイルなどを含みます。Dockerfile から作成できます。</td></tr><tr><td>Docker Container</td><td>Docker イメージのインスタンス。完全なファイルシステム、ランタイム環境、システムツールを含みます。</td></tr></tbody></table>
<p>Docker Daemon と Docker Client の間の通信には RESTful API が使われます。Docker Client がコマンドを Docker Daemon に送信し、Docker Daemon がそれを受け取って処理し、最終的に結果を Docker Client に返します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2023-05-image-3-8e8f77d570d13db756ced57e591ce0d5.webp" width="816" height="742" class="img_ev3q"></p>
<p>Docker Daemon がコンテナ作成のコマンドを受け取ると、まずローカルに必要な Docker イメージが存在するかを確認します。ローカルにイメージがなければ、Docker Daemon は Docker Registry から必要なイメージを取得します。ローカルにすでにイメージがあれば、そのイメージからコンテナを作成し、独立した名前空間の中で実行します。</p>
<p>コンテナの実行中、Docker Daemon はコンテナの出力とエラー情報を stdout と stderr に出力し、ユーザーは Docker Client でコンテナの出力を確認できます。コンテナが不要になったら、Docker Client からコマンドを送って停止できます。Docker Daemon はコンテナ内のプロセスに SIGTERM シグナルを送り、グレースフルに終了させます。一定時間内にプロセスが終了しない場合は SIGKILL シグナルを送り、プロセスを強制終了します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナ実装の仕組み">コンテナ実装の仕組み<a href="https://tanzhuo.xyz/ja/2023/05/06/docker#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E5%AE%9F%E8%A3%85%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="コンテナ実装の仕組み への直接リンク" title="コンテナ実装の仕組み への直接リンク" translate="no">​</a></h2>
<p>Docker コンテナの実装の仕組みは、以下のいくつかの側面に分けられます。</p>
<ol>
<li class="">名前空間(Namespace):Docker は名前空間を使って、コンテナごとのプロセス、ネットワーク、ファイルシステム、ユーザーなどのリソースを分離します。各コンテナは自分専用の名前空間を持ち、コンテナ同士は互いに独立し、影響を与え合いません。</li>
<li class="">コントロールグループ(Control Groups、略して Cgroups):Docker は Cgroups を使って、コンテナが使用する CPU、メモリ、ネットワーク帯域などのリソースを制限します。各コンテナには一定のリソース割り当てが与えられ、コンテナ同士がホストのリソースを公平に共有できます。</li>
<li class="">ユニオンファイルシステム(Union File System):Docker はユニオンファイルシステムを使ってコンテナのファイルシステム分離を実現します。各コンテナは自分のファイルシステムを持ちながら、ホストのファイルシステムの一部を共有できます。この方式によりコンテナのファイルシステムは非常に軽量になり、素早く作成・破棄できます。</li>
<li class="">コンテナイメージ(Container Image):Docker コンテナはコンテナイメージから作成されます。コンテナイメージは読み取り専用のファイルシステムで、アプリケーション、依存ライブラリ、設定ファイルなどを含みます。Docker はユニオンファイルシステムでイメージのレイヤー保存を実現しており、イメージの共有と再利用が可能になり、ストレージ容量とダウンロード時間を大幅に削減できます。</li>
<li class="">コンテナランタイム(Container Runtime):Docker はコンテナランタイムを使ってコンテナを起動・管理します。コンテナランタイムはコンテナの名前空間、Cgroups、ユニオンファイルシステムを作成し、その後コンテナ内のプロセスを起動します。さらに、コンテナの状態やリソース使用状況を監視し、必要に応じてリソース割り当てを調整する役割も担います。</li>
</ol>
<p>Docker の動作原理とは、つまり Docker Daemon と Docker Client のやり取りを通じて、Docker コンテナを作成・起動・停止・削除することです。Docker イメージは Docker コンテナの土台であり、Dockerfile から作成できます。Docker コンテナは独立した名前空間の中で動作し、分離性と安全性を提供します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="低レベルストレージの仕組み">低レベルストレージの仕組み<a href="https://tanzhuo.xyz/ja/2023/05/06/docker#%E4%BD%8E%E3%83%AC%E3%83%99%E3%83%AB%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="低レベルストレージの仕組み への直接リンク" title="低レベルストレージの仕組み への直接リンク" translate="no">​</a></h2>
<p>Docker の低レベルストレージの仕組みは、主にコンテナイメージとデータボリュームの二つの側面に関わります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナイメージの保存">コンテナイメージの保存<a href="https://tanzhuo.xyz/ja/2023/05/06/docker#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E4%BF%9D%E5%AD%98" class="hash-link" aria-label="コンテナイメージの保存 への直接リンク" title="コンテナイメージの保存 への直接リンク" translate="no">​</a></h3>
<p>Docker はユニオンファイルシステム(Union File System)を使って、コンテナイメージのレイヤー保存を実現しています。コンテナイメージは複数の読み取り専用レイヤーで構成され、各レイヤーは共有・再利用できます。コンテナ起動時、Docker はこれらの読み取り専用レイヤーを結合し、その上に書き込み可能なレイヤーを重ねてコンテナのファイルシステムを構成します。この方式によりコンテナイメージは非常に軽量になり、素早く作成・破棄できます。</p>
<p>Docker はデフォルトで AUFS(Advanced Multi-Layered Unification File System)をユニオンファイルシステムとして使用しており、多層の結合、素早い作成と破棄、読み取り専用レイヤーの共有などの特性をサポートします。また、OverlayFS、DeviceMapper、Btrfs など他のユニオンファイルシステムもサポートしています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="データボリュームの保存">データボリュームの保存<a href="https://tanzhuo.xyz/ja/2023/05/06/docker#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%9C%E3%83%AA%E3%83%A5%E3%83%BC%E3%83%A0%E3%81%AE%E4%BF%9D%E5%AD%98" class="hash-link" aria-label="データボリュームの保存 への直接リンク" title="データボリュームの保存 への直接リンク" translate="no">​</a></h3>
<p>Docker コンテナのデータボリュームは、コンテナが読み書きできるディレクトリまたはファイルで、コンテナ間のデータ共有や永続化ストレージなどに使えます。Docker はホストマウントボリューム、名前付きボリューム、匿名ボリュームなど、複数の種類のデータボリュームをサポートしています。</p>
<p>ホストマウントボリュームは、ホスト上のディレクトリやファイルをコンテナにマウントするもので、コンテナはそのディレクトリやファイルを読み書きできます。名前付きボリュームは Docker が管理するデータボリュームで、名前を付けられるため管理と共有がしやすくなります。匿名ボリュームは Docker が自動生成するデータボリュームで、コンテナ内の一時データの保存に使われます。</p>
<p>Docker コンテナのデータボリューム保存は、主に Linux のファイルシステムマウント機構によって実現されています。コンテナ起動時、Docker はデータボリュームをコンテナ内の指定ディレクトリにマウントし、コンテナがそのディレクトリに書き込んだデータはボリュームに保存されます。コンテナ停止時、Docker はデータボリュームをアンマウントしますが、データはボリュームに残るため、永続化ストレージが実現されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/06/docker#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Docker のアーキテクチャは複雑ではありません。Client が RESTful API で Daemon に指示を送り、Daemon がイメージの取得とコンテナのライフサイクル全体の管理を担います。コンテナ自体は Linux の Namespace によるリソース分離と Cgroups によるリソース制限に依存し、ユニオンファイルシステムがイメージのレイヤー保存とコンテナの書き込み可能レイヤーを支えています。データの永続化はデータボリュームに任せ、ファイルシステムのマウント機構によってデータをコンテナのライフサイクルから独立させています。これらの仕組みを理解すれば、コンテナの「軽量・ポータブル・再利用可能」という特性も自然に説明がつくでしょう。</p>]]></content:encoded>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[Flink CDC コンポーネント]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/05/flink</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/05/flink</guid>
            <pubDate>Fri, 05 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[Flink CDC の基本概念と組み込みコンポーネントを紹介します。変更ログキャプチャの原理から始めて、Source、Debezium Connector、Sink などの中核モジュール、および利用時のよくある注意点を整理します。]]></description>
            <content:encoded><![CDATA[<p>最近リアルタイムデータパイプラインを整理する中で、Flink CDC に関する知識をあらためてまとめ直しました。「データベースの変更をリアルタイムに外へ運び出す」というニーズはほぼすべてのデータチームが直面するものなので、独立した記事として記録しておく価値があると思います。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2023/05/05/flink#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>データ同期をやろうとしたとき、最も素朴な方法は定時バッチです。毎日深夜に業務 DB からフルダンプを取り、データウェアハウスに書き込む。実装はシンプルですが、遅延は時間単位、下手をすると日単位になり、データ量が増えるとソース DB への負荷も無視できません。ビジネス側から「レポートはリアルタイムで見たい」という要望が出た瞬間、バッチ方式は破綻します。CDC はまさにこの問題を解決するために登場した技術路線であり、Flink CDC は CDC の能力とストリーム処理エンジンをかなり徹底的に統合した実装の一つです。</p>
<p>Flink CDC（Change Data Capture）は Apache Flink エコシステムの重要なコンポーネントで、データベース内のデータ変更をリアルタイムにキャプチャし、それらの変更をデータストリームに変換して処理するために使われます。モダンなデータアーキテクチャにおいて、CDC 技術はリアルタイムデータ同期、リアルタイムデータウェアハウス、イベント駆動システムの構築に広く利用されています。</p>
<p>従来のデータ同期方式（定時のフル同期など）と比べ、CDC はデータベースの <strong>Binlog / WAL などの変更ログ</strong> を読み取ることでデータ変化をキャプチャし、低遅延・低侵襲のデータ同期を実現できます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2023-05-flink-header-logo-5b83b4e45940e5c5870da4d58bde8b88.svg" width="427" height="220" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cdc-とは">CDC とは<a href="https://tanzhuo.xyz/ja/2023/05/05/flink#cdc-%E3%81%A8%E3%81%AF" class="hash-link" aria-label="CDC とは への直接リンク" title="CDC とは への直接リンク" translate="no">​</a></h2>
<p>CDC（Change Data Capture）とは <strong>変更データキャプチャ</strong> のことで、その核心となる考え方は次のとおりです。<br>
データベース内のデータに <strong>INSERT / UPDATE / DELETE</strong> 操作が発生したとき、それらの変化を記録し、リアルタイムに下流システムへ伝達する。</p>
<p>ここで実装メカニズムを少し掘り下げてみます。CDC は大きく 2 種類に分かれます。1 つはクエリベースのもので、タイムスタンプや自動採番の主キー列をポーリングして新しいデータを発見します。実装は簡単ですが DELETE を捕捉できず、2 回のポーリングの間に複数回変更されたレコードを取りこぼしやすいという欠点があります。もう 1 つはログベースのもので、データベースがマスター・スレーブレプリケーションのために保持している変更ログを直接消費します。MySQL の Binlog、PostgreSQL の WAL がこれに該当します。ログには各行の変更前後の完全な状態が自然に記録されているため、ログベースの CDC は挿入・更新・削除のすべてのイベントを取得でき、しかもソース DB への追加のクエリ負荷はほとんどありません。Flink CDC が採っているのはこのログの路線です。</p>
<p>実際のデータアーキテクチャにおいて、CDC は次のような用途でよく使われます：</p>
<ul>
<li class="">データベース → データウェアハウス（リアルタイム DWH）</li>
<li class="">データベース → Kafka（イベントストリーム）</li>
<li class="">データベース → 検索エンジン（Elasticsearch など）</li>
<li class="">データベース → キャッシュシステム（Redis など）</li>
</ul>
<p>このように、CDC はモダンな <strong>リアルタイムデータプラットフォーム（Real-time Data Platform）</strong> の重要な基盤能力の一つです。</p>
<p>Flink CDC コンポーネントの動作原理は、ソースデータシステムの変更ログをキャプチャし、それをデータストリームに変換してリアルタイムに処理するというものです。CDC コンポーネントはソースシステムのログを読み取るだけで、ソースシステムへの書き込みは行わないため、このプロセスはソースシステムに影響を与えずに実行できます。データベースの視点から見ると、CDC クライアントは通常のスレーブと本質的な違いはありません。レプリケーションクライアントに擬態してマスターに変更ログを購読し、ログイベントを構造化された変更レコードに解析して Flink に渡します。</p>
<p>Flink CDC コンポーネントの利点は、リレーショナルデータベース（MySQL、Oracle など）と NoSQL データベース（MongoDB、Cassandra など）を含む多様なデータソースをサポートできることです。さらに、JSON、CSV など複数のデータフォーマットにも対応しています。</p>
<p>もう一つ過小評価されがちな利点は、Flink 本体との統合の深さです。変更ストリームが Flink に入ってしまえば、それは普通のデータストリームであり、Flink のウィンドウ計算、複数ストリームの Join、状態管理、checkpoint による耐障害機構をそのまま適用できます。つまり「変更のキャプチャ」と「変更の処理」が同一エンジン内で完結し、中継のために Kafka Connect クラスタを別途運用する必要がなく、パイプラインは短く、運用対象も少なくなります。</p>
<p>Flink CDC コンポーネントを使えば、リアルタイムのデータ同期とデータストリーム処理が実現できます。例えば、CDC コンポーネントで MySQL データベースのデータを Kafka に同期し、その後 Flink でリアルタイムのデータ処理を行うといった使い方ができます。こうしてデータのリアルタイム同期と処理が実現でき、データの効率と正確性が向上します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="flink-cdc-コンポーネント">Flink CDC コンポーネント<a href="https://tanzhuo.xyz/ja/2023/05/05/flink#flink-cdc-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88" class="hash-link" aria-label="Flink CDC コンポーネント への直接リンク" title="Flink CDC コンポーネント への直接リンク" translate="no">​</a></h2>
<p>全体像として、Flink CDC パイプラインの分担はおおむね次のとおりです。Source が「入口」を、Sink が「出口」を担当し、その間を Flink のオペレーターと状態機構が計算で埋めます。Flink CDC には以下のコンポーネントが組み込まれています：</p>
<ol>
<li class="">Source：データソースからデータ変更ログを読み取り、Flink のデータストリームに変換するコンポーネントです。通常、CDC Source はまず既存データのスナップショット読み取りを行い、その後シームレスに増分ログの消費へ移行します。これにより下流は「フル + 増分」の完全なデータを受け取れます。</li>
<li class="">Debezium Connector：オープンソースの CDC ツールで、複数のデータソース（MySQL、PostgreSQL、MongoDB など）に接続してデータ変更ログをキャプチャできます。Flink CDC は下層で Debezium のログ解析能力を再利用し、それを組み込み（embedded）方式で Flink ジョブ内に走らせることで、Debezium サービスを独立してデプロイするコストを省いています。</li>
<li class="">Sink：Flink のデータストリームをターゲットデータソース、例えば Kafka、HDFS、Elasticsearch などに書き込むコンポーネントです。Sink は Flink の checkpoint 機構と連携し、ジョブが失敗して再起動した際のデータロスを防げます。</li>
<li class="">State：ステートフルなデータストリームを処理するためのコンポーネントです。例えば 2 つのデータストリームをマージする場合、中間状態を保存するために State コンポーネントが必要になります。CDC のシナリオでは State がとりわけ重要です。例えばディメンションテーブルとの結合、主キーによる重複排除、UPDATE イベントから最新スナップショットの復元などは、いずれも状態ストレージに依存します。</li>
<li class="">Table API：SQL ライクな API を提供するコンポーネントで、データストリームの処理とクエリを手軽に行えます。大半の同期系の要件は、Flink SQL で CDC ソーステーブルとターゲットテーブルを 1 つずつ宣言し、INSERT INTO を 1 文書くだけでパイプライン全体を動かせます。Java コードを書く必要はありません。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/05/flink#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>実際の利用にあたって、事前に押さえておきたいポイントがいくつかあります：</p>
<ol>
<li class="">
<p>ソース DB のログ設定を先に確認すること。MySQL を例にとると、Binlog が有効かつフォーマットが ROW である必要があります。そうでないと行レベルの変更前後のイメージが取得できません。接続アカウントにも Binlog 読み取りに関わるレプリケーション権限が必要です。</p>
</li>
<li class="">
<p>ログの保持期間を短くしすぎないこと。ジョブをしばらく停止してから再開したとき、その間の Binlog がすでにデータベースによって削除されていると、ジョブは再開位置を見つけられず、フルスナップショットをやり直すしかなくなります。</p>
</li>
<li class="">
<p>下流が UPDATE と DELETE を処理できること。CDC ストリームはリトラクト（回撤）のセマンティクスを含む変更ストリームです。Sink 側（例えば追記書き込みしかサポートしないストレージ）が削除や更新のイベントを消化できない場合、パイプライン設計で追加の対処が必要になります。</p>
</li>
</ol>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>CDC ジョブは本質的に長時間走り続けるストリームジョブなので、checkpoint は必ずきちんと設定してください。checkpoint のない CDC ジョブは、一度失敗すると再開位置を失い、データを失う可能性もあります。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/05/flink#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>CDC が解決するのは「データベース内の変化を、いかに低遅延・低侵襲で外に流すか」という問題であり、ログベースの実装が現在の主流路線です。Flink CDC の価値は、ログキャプチャ（Debezium の活用）とストリーム計算（Flink エンジン自体）を一つのフレームワークにまとめ上げたことにあります。Source が変更を読み、Sink がターゲットに書き、State がステートフルな計算を支え、Table API が利用の敷居を下げる。リアルタイム DWH を構築したい、あるいはヘテロジニアスなデータ同期をやりたいチームにとって、パイプラインが短く、コンポーネントが少なく、優先的に検討する価値のある選択肢です。</p>]]></content:encoded>
            <category>ビッグデータ</category>
        </item>
        <item>
            <title><![CDATA[MySQL の時刻の四捨五入]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru</guid>
            <pubDate>Fri, 05 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[MySQL の DATETIME はデフォルトで秒までの精度しかなく、Java から渡されたミリ秒が四捨五入されて、時刻が 1 秒増えてしまうことがあります。その原理と、DATETIME(fsp) および Java 側での切り捨てという 2 つの解決策を記録します。]]></description>
            <content:encoded><![CDATA[<p>MySQL の DATETIME はデフォルトで秒までの精度ですが、Java の LocalDateTime はナノ秒まで扱えます。ミリ秒付きの時刻を MySQL に書き込むと、カラムの精度を超えた部分は四捨五入され、保存された時刻がどこからともなく 1 秒増えることがあります。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="問題の現象">問題の現象<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#%E5%95%8F%E9%A1%8C%E3%81%AE%E7%8F%BE%E8%B1%A1" class="hash-link" aria-label="問題の現象 への直接リンク" title="問題の現象 への直接リンク" translate="no">​</a></h2>
<p>例えば、Java 側の時刻が <code>2022-01-01 12:34:56.789</code> のとき、これを通常の DATETIME カラムに書き込んで読み出すと <code>2022-01-01 12:34:57</code> になっています。ミリ秒部分が四捨五入されて繰り上がったのです。</p>
<p>このズレは普段は目立ちませんが、いくつかの場面で表面化します：</p>
<ol>
<li class="">
<p>単体テストでエンティティを保存して読み出し、<code>assertEquals</code> で時刻フィールドを比較すると、たまに失敗する。ミリ秒が 500 以上のときだけ繰り上がるため、問題が出たり出なかったりします。</p>
</li>
<li class="">
<p>作成日時をカーソルページネーションや範囲クエリに使うと、境界のレコードが 1 件重複したり漏れたりする。</p>
</li>
<li class="">
<p>ログの時刻と DB 内の時刻が 1 秒ずれ、トレースを追うときに突き合わせが合わない。</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="原理の概要">原理の概要<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#%E5%8E%9F%E7%90%86%E3%81%AE%E6%A6%82%E8%A6%81" class="hash-link" aria-label="原理の概要 への直接リンク" title="原理の概要 への直接リンク" translate="no">​</a></h2>
<p>MySQL は 5.6.4 以降、DATETIME、TIMESTAMP、TIME のいずれも小数秒をサポートしています。精度は型定義の fsp（fractional seconds precision）で決まり、値は 0 から 6、デフォルトは 0、つまり秒までです。</p>
<p>鍵となる挙動はこうです。挿入値の精度がカラムの精度より高い場合、MySQL は SQL 標準に従って切り捨てではなく四捨五入を行い、しかもデフォルトでは何の警告も出しません。そのため <code>.789</code> は秒の位を繰り上げ、<code>.400</code> は黙って捨てられます。どちらの結果も Java のメモリ上の値とは一致しません。</p>
<p>Java 側では、<code>LocalDateTime</code> は内部的にナノ秒で時刻を保持しており、JDBC ドライバは書き込み時に小数秒も一緒にサーバーへ送ります。つまり丸めは MySQL 側で発生します。<code>java.sql.Timestamp</code> も同じくナノ秒精度なので、Java の型をこれに替えるだけではデータベース側の丸めは避けられません。問題の根はカラムの精度定義にあるのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="解決策">解決策<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#%E8%A7%A3%E6%B1%BA%E7%AD%96" class="hash-link" aria-label="解決策 への直接リンク" title="解決策 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-カラムの精度を上げる">1) カラムの精度を上げる<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#1-%E3%82%AB%E3%83%A9%E3%83%A0%E3%81%AE%E7%B2%BE%E5%BA%A6%E3%82%92%E4%B8%8A%E3%81%92%E3%82%8B" class="hash-link" aria-label="1) カラムの精度を上げる への直接リンク" title="1) カラムの精度を上げる への直接リンク" translate="no">​</a></h3>
<p>ビジネス上ミリ秒精度が必要なら（作成・更新時刻を記録する大半のケースはこれで十分）、カラムを <code>DATETIME(3)</code> として定義します：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token keyword" style="color:hsl(286, 60%, 67%)">ALTER</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">TABLE</span><span class="token plain"> t_order</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">MODIFY</span><span class="token plain"> create_time </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">DATETIME</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token number" style="color:hsl(29, 54%, 61%)">3</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">NOT</span><span class="token plain"> </span><span class="token boolean" style="color:hsl(29, 54%, 61%)">NULL</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><br></div></code></pre></div></div>
<p>マイクロ秒が必要なら <code>DATETIME(6)</code> を使います。対になるデフォルト値の関数にも精度を付ける必要がある点に注意してください。<code>NOW()</code> は秒までなので、<code>NOW(3)</code> または <code>CURRENT_TIMESTAMP(3)</code> と書かなければ、デフォルト値自体に小数秒が付きません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-java-側で事前に切り捨てる">2) Java 側で事前に切り捨てる<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#2-java-%E5%81%B4%E3%81%A7%E4%BA%8B%E5%89%8D%E3%81%AB%E5%88%87%E3%82%8A%E6%8D%A8%E3%81%A6%E3%82%8B" class="hash-link" aria-label="2) Java 側で事前に切り捨てる への直接リンク" title="2) Java 側で事前に切り捨てる への直接リンク" translate="no">​</a></h3>
<p>テーブル構造を変えたくない場合は、書き込み前に精度を秒に揃えます：</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">// 秒未満の部分を捨てて、DATETIME(0) の精度に揃える</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token class-name" style="color:hsl(29, 54%, 61%)">LocalDateTime</span><span class="token plain"> now </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token class-name" style="color:hsl(29, 54%, 61%)">LocalDateTime</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">.</span><span class="token function" style="color:hsl(207, 82%, 66%)">now</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">.</span><span class="token function" style="color:hsl(207, 82%, 66%)">truncatedTo</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token class-name" style="color:hsl(29, 54%, 61%)">ChronoUnit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">.</span><span class="token constant" style="color:hsl(29, 54%, 61%)">SECONDS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><br></div></code></pre></div></div>
<p>こうすればメモリ上の値と DB に保存される値が完全に一致し、比較やアサーションでズレが出ることはなくなります。共通のエンティティ基底クラスや監査フィールドの設定処理で一度だけ統一的に処理すれば十分です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">丸めは「繰り上げ」であって「ゼロ埋め切り捨て」ではありません。余分な精度は切り捨てられると無意識に思い込んでいる人が多いのですが、実際は四捨五入されます。これこそが「時刻が 1 秒増える」という奇妙な問題の出どころです。MySQL 8.0 には <code>TIME_TRUNCATE_FRACTIONAL</code> という sql_mode があり、挙動を切り捨てに変えられますが、これはグローバルな挙動に影響するため、変更前に既存業務への影響を評価する必要があります。</li>
<li class=""><code>DATETIME(3)</code> に変更した後、既存データの小数秒はすべて <code>.000</code> です。新旧データを混ぜてソートする際はこの点に注意してください。</li>
<li class="">fsp が高いほどストレージ消費も増えます。必要に応じて選べばよく、一律に <code>DATETIME(6)</code> を使う必要はありません。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/05/mysql-shi-jian-si-she-wu-ru#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>時刻が「1 秒増える」ことの本質は精度の不一致です。Java はナノ秒を渡すのに、MySQL のカラムは秒までしか保存できず、はみ出した部分が四捨五入されます。解決の道は 2 つ。カラムを <code>DATETIME(3)</code>/<code>DATETIME(6)</code> に変えてデータベース側で保存できるようにするか、Java 側で <code>truncatedTo(ChronoUnit.SECONDS)</code> を使って事前に揃えるかです。原則はただ一つ、メモリ上の精度とカラム定義の精度を一致させること。そうすれば、書き込んだものがそのまま読み出せます。</p>]]></content:encoded>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[データレイク Iceberg]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu</guid>
            <pubDate>Fri, 05 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[データレイクとデータウェアハウスの違いを整理し、オープンソースのテーブルフォーマット Apache Iceberg の位置づけとコア機能（バージョン管理、メタデータ管理、パーティション管理、スナップショット）を紹介し、選定時の注意点も記録します。]]></description>
            <content:encoded><![CDATA[<p>現在人気のオープンソースデータレイク、Iceberg について。</p>
<!-- -->
<p>最近ビッグデータのストレージ方式を整理していると、「データレイク」という言葉を避けて通れませんでした。ビジネスデータはもはやリレーショナルデータベースのテーブルだけではありません。トラッキングログ、JSON メッセージ、画像・音声・動画など、さまざまな形態のデータをどこかに格納する必要があります。従来のデータウェアハウス（DWH）はこうしたシナリオでますます苦しくなっており、データレイクを取り巻く一連のオープンソースプロジェクトがちょうどこのギャップを埋めています。この記事では、データレイクと DWH の違い、そしてその中でも代表的な Iceberg について簡単に整理し、自分の学習ノートとして残しておきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データレイクとデータウェアハウス">データレイクとデータウェアハウス<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%AC%E3%82%A4%E3%82%AF%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A6%E3%82%A7%E3%82%A2%E3%83%8F%E3%82%A6%E3%82%B9" class="hash-link" aria-label="データレイクとデータウェアハウス への直接リンク" title="データレイクとデータウェアハウス への直接リンク" translate="no">​</a></h2>
<p>データレイクとは、構造化データと非構造化データを保存するための集中型データリポジトリであり、従来のリレーショナルデータ、半構造化データ、非構造化データなど、あらゆる種類のデータを保存できます。データレイクの主な特徴は高い柔軟性と拡張性を備えていることで、データの保存と処理を手軽に行えます。</p>
<p>データレイクの核心的な考え方のひとつが「先に保存し、後で管理する」（schema-on-read）です。データは生の形式のまま安価な分散ストレージ（HDFS やオブジェクトストレージなど）に書き込まれ、実際に分析する段階になって初めて構造を解析します。これは DWH の「先にモデリングしてから格納する」（schema-on-write）という考え方とは正反対で、あらゆるデータ形態に対応できる理由でもあります。</p>
<p>これに対し、従来のデータウェアハウスは主に構造化データの保存に使われ、通常はリレーショナルデータベースで保存・管理されます。データウェアハウスの主な特徴は高度な正規化と構造化であり、データの正確性と一貫性を保証できます。しかしデータウェアハウスの欠点は、柔軟性に欠けること、非構造化・半構造化データを保存・処理できないこと、そして拡張やアップグレードが難しいことにあります。</p>
<p>データレイクがデータウェアハウスに比べて優れている点は、主に以下のとおりです：</p>
<ol>
<li class="">ストレージに制限がない：データレイクは、従来の構造化データ、半構造化データ、非構造化データなど、あらゆる種類のデータを保存できます。これによりデータレイクはより柔軟になり、さまざまな種類・規模のデータストレージのニーズに適応します。</li>
<li class="">処理速度が速い：データレイクは通常、分散ストレージとコンピューティング技術を採用しており、高速なデータ処理と分析を実現できます。一方、従来のデータウェアハウスは何度もデータ変換と計算を行う必要があり、処理速度が遅くなりがちです。</li>
<li class="">コストが低い：データレイクは通常、Hadoop や Spark などのオープンソース技術を採用しており、コストが比較的低く抑えられます。一方、従来のデータウェアハウスは商用データベースソフトウェアを採用する必要があり、コストが高くなります。</li>
<li class="">より柔軟：データレイクはより高い柔軟性を持ち、データの保存と処理を手軽に行えます。一方、従来のデータウェアハウスは何度もデータ変換と計算を行う必要があり、データ処理のプロセスが複雑で困難になりがちです。</li>
</ol>
<p>もちろん、柔軟性には代償があります。素のデータレイクはただのファイルディレクトリの山にすぎず、トランザクションもスキーマ制約も欠けているため、書き込み側と読み取り側が少し油断するとお互いを踏み荒らしてしまい、時間が経つと簡単に「データスワンプ（沼）」に変わってしまいます。テーブルフォーマット（table format）という抽象化レイヤーはまさにこの問題を解決するためのもので、ファイルの上に「テーブル」のセマンティクスを定義します。Iceberg はまさにこの種のプロジェクトです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="apache-iceberg">Apache Iceberg<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu#apache-iceberg" class="hash-link" aria-label="Apache Iceberg への直接リンク" title="Apache Iceberg への直接リンク" translate="no">​</a></h2>
<p><a href="http://iceberg.incubator.apache.org/" target="_blank" rel="noopener noreferrer" class="">Apache Iceberg</a></p>
<p>Iceberg はオープンソースのデータテーブルフォーマットおよび処理ライブラリで、データレイクにおけるデータテーブルの管理と処理の問題を解決することを目的としています。Iceberg は Netflix によって開発され、現在では Apache ソフトウェア財団のトップレベルプロジェクトのひとつになっています。</p>
<p>Iceberg は、データテーブルのバージョン管理、メタデータ管理、パーティション管理、スナップショット管理など、従来のデータレイクにおけるいくつかの問題を解決しました。Iceberg は Hadoop、Spark、Presto、Flink など、複数のデータストレージ・処理システムをサポートしています。</p>
<p>ここで、その動作メカニズムについて少し掘り下げてみましょう。Iceberg 自体はデータを保存しません。データは相変わらず Parquet や ORC といった列指向ファイルであり、Iceberg が管理するのはデータの上にあるメタデータレイヤーです。メタデータは大きく 3 つのレベルに分かれます：</p>
<ol>
<li class="">スナップショット（snapshot）：ある時点でのテーブルの完全な状態です。書き込みがコミットされるたびに新しいスナップショットが生成され、古いスナップショットも引き続き読み取り可能です。</li>
<li class="">マニフェストリストとマニフェストファイル（manifest list / manifest）：スナップショットにどのデータファイルが含まれるか、また各ファイルのパーティション範囲、行数、列レベルの統計などの情報を記録します。</li>
<li class="">データファイル：実際の列指向ファイルです。メタデータはこれらを参照するだけで、変更はしません。</li>
</ol>
<p>書き込み時には新しいメタデータが先にディスクに書き込まれ、最後にテーブルポインタへの 1 回のアトミックな置き換えによってコミットが完了します。これにより、ファイルシステムの上にデータベースに近い ACID セマンティクスがもたらされます。クエリ時にはエンジンがまずメタデータを読み、その中の統計情報を利用して無関係なファイルをスキップします。Hive のようにディレクトリ全体を List する必要はありません。オブジェクトストレージ上では、この点がパフォーマンスに大きく影響します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iceberg-の主な特徴">Iceberg の主な特徴<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu#iceberg-%E3%81%AE%E4%B8%BB%E3%81%AA%E7%89%B9%E5%BE%B4" class="hash-link" aria-label="Iceberg の主な特徴 への直接リンク" title="Iceberg の主な特徴 への直接リンク" translate="no">​</a></h2>
<p>Iceberg の主な特徴は以下のとおりです：</p>
<ol>
<li class="">データテーブルのバージョン管理：Iceberg はデータテーブルのバージョン管理をサポートしており、テーブルのバージョンを管理・制御できます。ユーザーは過去のバージョンへ簡単にロールバックしたり、異なるバージョンのデータテーブルをマージしたりできます。</li>
<li class="">データテーブルのメタデータ管理：Iceberg はメタデータ管理メカニズムを提供しており、データテーブルの管理とクエリを手軽に行えます。ユーザーはメタデータを通じて、テーブルの構造、パーティション、データ統計などの情報を把握できます。</li>
<li class="">データパーティション管理：Iceberg はハッシュパーティション、レンジパーティション、ハッシュ・レンジ混合パーティションなど、複数のパーティション方式をサポートしています。ユーザーは自分のニーズに応じて異なるパーティション方式を選択できます。</li>
<li class="">データテーブルのスナップショット管理：Iceberg はデータテーブルのスナップショット管理をサポートしており、データのバックアップとリストアを簡単に行えます。</li>
</ol>
<p>パーティションについてはもうひとつ細かい点があります。Iceberg のパーティションは「隠しパーティション（hidden partitioning）」です。パーティション値は列の値から変換関数（例えばタイムスタンプの日単位切り捨て）によって自動的に導出され、クエリ時には元の列でフィルタ条件を書くだけで、エンジンが自動的にパーティションプルーニングを活用してくれます。クエリを書く人はテーブルがどうパーティションされているかを知る必要がなく、Hive でパーティション列を手動管理する方式よりずっと親切です。パーティションルール自体も進化させることができ、ルールを変更しても過去のデータを書き直す必要はありません。</p>
<p>スキーマ進化（schema evolution）も同様に得意分野です。列の追加、削除、リネームはメタデータの変更だけで済み、列は名前や位置ではなく一意の ID で追跡されるため、古いテーブルフォーマットにあった「列名を変えただけでデータを読み間違えるかもしれない」という問題を回避しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">小さなファイルの問題は依然として存在します。ストリーミング書き込みの頻度が高いと、大量の小さなデータファイルとメタデータファイルが生成されるため、定期的にコンパクション（compaction）を実行する必要があります。さもないとクエリ性能は劣化し続けます。</li>
<li class="">スナップショットは自動では掃除されません。コミットのたびにスナップショットが 1 つ残り、過去のスナップショットが参照するファイルは削除されないため、長期間スナップショットの期限切れ処理をしないと、ストレージがどんどん積み上がります。</li>
<li class="">Catalog の選定は先にしっかり考えましょう。Iceberg のテーブルポインタは catalog による管理が必要で（Hive Metastore、JDBC などいずれも可）、異なるエンジンから同じテーブルにアクセスするには同じ catalog を共用しなければならず、移行はかなり面倒です。</li>
<li class="">同種のプロジェクトには Delta Lake と Apache Hudi もあります。3 つは位置づけが近いもののエコシステムの重点が異なるため、選定前には自分が主に使うコンピューティングエンジンと合わせて比較検討するのがよく、流行りに流される必要はありません。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-hu#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>データレイクが解決するのは「どんなデータでも保存できる」という問題ですが、ストレージだけあって管理がなければ、レイクは簡単にスワンプ（沼）に変わってしまいます。Iceberg のようなテーブルフォーマットは、ファイルの上にトランザクション、バージョン、パーティション、スキーマ管理を補完し、データレイクに DWH に近いテーブルセマンティクスを持たせると同時に、オープンフォーマットと低コストという利点を保持しています。私にとって、これを理解する鍵はあの 3 層のメタデータ構造にありました。スナップショットとマニフェストファイルがどう組織されているかを理解できれば、ロールバック、タイムトラベル、パーティションプルーニングといった機能も自然と腑に落ちます。今後機会があれば、Flink / Spark 上で実際に Iceberg テーブルを読み書きするプロセスを別途記録するつもりです。</p>]]></content:encoded>
            <category>ビッグデータ</category>
        </item>
        <item>
            <title><![CDATA[データ構造：スタック]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou</guid>
            <pubDate>Fri, 05 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[スタックは先入れ後出し（LIFO）のデータ構造で、スタックトップでのみ挿入と削除が可能であり、配列または連結リストで実装できます。本記事ではスタックの基本操作、計算量、そして関数呼び出し、式の評価、コンパイラ、OS における応用を整理します。]]></description>
            <content:encoded><![CDATA[<p>スタック（Stack）は先入れ後出し（LIFO）のデータ構造で、スタックトップでのみ挿入と削除の操作を許可します。スタックは配列または連結リストで実装できます。</p>
<!-- -->
<p>スタックについて単独で 1 本書こうと思ったのは、これがほぼ最もシンプルなデータ構造でありながら、どこにでも存在するからです。関数呼び出し、例外スタックトレース、ブラウザの「戻る」ボタン、エディタの取り消し（Undo）操作、その背後にあるのはすべて同じモデルです。一見複雑に見える多くの問題（例えば括弧のマッチングや式の評価）も、スタックを使うことを思いつけば、途端に道筋がはっきりします。スタックを理解することは、再帰やコールスタックのオーバーフローといった問題を理解する前提でもあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スタックの基本概念">スタックの基本概念<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou#%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%81%AE%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5" class="hash-link" aria-label="スタックの基本概念 への直接リンク" title="スタックの基本概念 への直接リンク" translate="no">​</a></h2>
<p>スタックにおいて、挿入と削除の操作は通常プッシュ（push）とポップ（pop）と呼ばれます。要素を挿入するときはスタックトップに置かれ、要素を削除するときはスタックトップから取り除かれます。スタックトップはスタックの中で最も新しく追加された要素、スタックボトムは最も古く追加された要素です。</p>
<p>スタックはお皿の山にたとえられます。新しいお皿は一番上にしか置けず、取るときも一番上からしか取れません。この「口を 1 つしか開けない」という制約こそがスタックの価値です。「誰が最後に入ったか」を自然に記録してくれるので、「来た道を戻る」必要のあるシナリオの処理にとりわけ適しています。</p>
<p>スタックの応用は非常に幅広く、例えばコンピュータにおける関数呼び出しと再帰呼び出しは、いずれもスタックによって実現されています。ある関数が呼び出されると、その引数、リターンアドレス、ローカル変数などの情報がスタックにプッシュされ、関数が戻るときにこれらの情報がスタックからポップされます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="基本操作">基本操作<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou#%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C" class="hash-link" aria-label="基本操作 への直接リンク" title="基本操作 への直接リンク" translate="no">​</a></h2>
<p>以下がスタックの基本操作です：</p>
<p>push(element)：要素をスタックトップにプッシュします。</p>
<p>pop()：スタックトップから要素を 1 つポップします。</p>
<p>top()：スタックトップの要素を返しますが、スタック自体は変更しません。</p>
<p>isEmpty()：スタックが空かどうかを判定します。</p>
<p>size()：スタック内の要素数を返します。</p>
<p>pop と top の違いに注意してください。pop はスタックトップの要素を取り除いてから返しますが、top（実装によっては peek と呼ばれます）は「一目見る」だけで、スタック自体は変わりません。コードを書くときにこの 2 つの操作を混同するのは、よくあるバグの原因です。</p>
<p>配列によるスタックの実装は非常に素直で、スタックトップを指すインデックスを 1 つ維持するだけです：</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">// 配列ベースのシンプルなスタック実装、コアロジックのみのデモ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">class</span><span class="token plain"> </span><span class="token class-name" style="color:hsl(29, 54%, 61%)">ArrayStack</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">private</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> data</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">private</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> top </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">-</span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// スタックトップのインデックス、-1 は空スタックを表す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token class-name" style="color:hsl(29, 54%, 61%)">ArrayStack</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> capacity</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        data </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">new</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">capacity</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">void</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">push</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> element</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 実際に使う場合はここで拡張するかスタック満杯の例外をスローすべき</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        data</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token operator" style="color:hsl(207, 82%, 66%)">++</span><span class="token plain">top</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> element</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 先にスタックトップポインタを進めてから書き込む</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">pop</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 空スタックの場合は例外をスローすべきだが、ここではチェックを省略</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">return</span><span class="token plain"> data</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">top</span><span class="token operator" style="color:hsl(207, 82%, 66%)">--</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// スタックトップの要素を返しつつポインタを戻す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">top</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">return</span><span class="token plain"> data</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">top</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 読み取るだけで、スタックは変更しない</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">boolean</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">isEmpty</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">return</span><span class="token plain"> top </span><span class="token operator" style="color:hsl(207, 82%, 66%)">==</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">-</span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">public</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">size</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">return</span><span class="token plain"> top </span><span class="token operator" style="color:hsl(207, 82%, 66%)">+</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><br></div></code></pre></div></div>
<p>連結リストによる実装では、リストの先頭をスタックトップとみなし、push は先頭への挿入、pop は先頭ノードの削除になります。利点は容量を事前に確保する必要がないことです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="計算量の分析">計算量の分析<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou#%E8%A8%88%E7%AE%97%E9%87%8F%E3%81%AE%E5%88%86%E6%9E%90" class="hash-link" aria-label="計算量の分析 への直接リンク" title="計算量の分析 への直接リンク" translate="no">​</a></h2>
<p>スタックの時間計算量は O(1) です。すべての操作がスタックトップで行われるためです。しかし、スタックの空間計算量は O(n) です。すべての要素を保存する必要があるためです。</p>
<p>別の見方をすれば、スタックの高効率はまさにその制約から来ています。中間位置での挿入や削除を許さないため、すべての操作はスタックトップへの 1 回の読み書きに帰着し、他の要素を移動する必要も走査する必要もありません。配列実装における唯一の例外は拡張時の移し替えですが、償却すればプッシュは依然として定数時間です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スタックの典型的な応用">スタックの典型的な応用<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou#%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E3%81%AE%E5%85%B8%E5%9E%8B%E7%9A%84%E3%81%AA%E5%BF%9C%E7%94%A8" class="hash-link" aria-label="スタックの典型的な応用 への直接リンク" title="スタックの典型的な応用 への直接リンク" translate="no">​</a></h2>
<p>コンピュータにおいて、スタック（Stack）は関数呼び出し、式の評価、コンパイラ、オペレーティングシステムなどの分野で広く応用されています。</p>
<ol>
<li class="">関数呼び出し：ある関数が呼び出されると、その引数、リターンアドレス、ローカル変数などの情報がスタックにプッシュされ、関数が戻るときにこれらの情報がスタックからポップされます。このプロセスは関数コールスタックと呼ばれ、関数呼び出しを実現する基盤です。</li>
<li class="">式の評価：コンピュータが式を評価するとき、通常はスタックを使って実現します。例えば、中置記法の式を後置記法の式に変換するとき、式の値を正しく計算できるように、スタックを使って演算子を保存する必要があります。</li>
<li class="">コンパイラ：コンパイラがソースコードをターゲットコードに変換する過程で、構文解析やコード生成などの機能をスタックで実現しています。例えばコンパイラでは、変数、関数、文などの情報をスタックで保存します。</li>
<li class="">オペレーティングシステム：OS におけるプロセススケジューリングや割り込み処理などの機能もスタックを使って実現されています。例えばあるプロセスが割り込まれたとき、OS は現在のプロセスのコンテキスト情報（レジスタの値、プログラムカウンタの値など）をスタックにプッシュしてから、割り込みハンドラを実行します。割り込みハンドラの実行が終わると、OS はスタックからコンテキスト情報をポップし、現在のプロセスの実行を復元します。</li>
</ol>
<p>この 4 つのシナリオには共通点があります。いずれも「入る — 処理する — 逆順に出る」というネスト構造が存在することです。関数のネスト呼び出し、括弧のネスト、割り込みのネストは、本質的にすべて同じ種類の問題であり、だからこそすべてスタックというモデルに落ち着くのです。普段の競技プログラミングで出会う括弧のマッチング、単調スタック、深さ優先探索の非再帰版なども、すべてこの考え方の延長線上にあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">
<p>空スタックの操作：空スタックに pop や top を実行するのは未定義動作か、そのまま例外がスローされます。呼び出す前に isEmpty で判定するか、明示的に例外処理に依存するかのどちらかで、思い込みで扱ってはいけません。</p>
</li>
<li class="">
<p>スタックオーバーフロー：再帰は本質的にコールスタックを消費するので、再帰の階層が深すぎるとスタックオーバーフローを引き起こします（Java では StackOverflowError）。深さがコントロールできない再帰に出会ったら、明示的なスタックとループによる反復版に書き換え、データをヒープ上に移すとよいでしょう。</p>
</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>Java では java.util.Stack の使用はもうおすすめしません。Vector を継承しているため、メソッドに同期のオーバーヘッドがあり、設計も古くなっています。公式ドキュメントは ArrayDeque をスタックとして使うことを推奨しています。</p></div></div>
<ol start="3">
<li class="">pop と top の混同：スタックトップを読みたいだけなのに pop を呼んでしまうと、こっそり要素を失います。この種のバグはループの中では特に見つけにくいものです。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/05/shu-ju-jie-gou#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>スタックは「制約と引き換えに効率を得る」典型例です。スタックトップという 1 つの操作口だけを開放することで、すべての操作を O(1) のコストで実現し、同時にあらゆるネスト・バックトラック系の問題に自然にフィットします。push/pop/top というこれらの操作と LIFO のメンタルモデルを身につければ、関数コールスタックや式の評価といったメカニズムも、同じモデルが異なるレイヤーで繰り返し応用されているだけだとわかるはずです。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[主要な Ingress の比較]]></title>
            <link>https://tanzhuo.xyz/ja/2023/05/03/ingress</link>
            <guid>https://tanzhuo.xyz/ja/2023/05/03/ingress</guid>
            <pubDate>Wed, 03 May 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[k8s クラスタでよく使われる Ingress コントローラーを比較し、Ingress の仕組みと、TCP/UDP の L4 サービスを公開する方法を整理します。]]></description>
            <content:encoded><![CDATA[<p>今回は k8s クラスタでよく使われる ingress コンポーネントを比較してみます。ingress はクラスタにおける HTTP と HTTPS トラフィックのエントリーポイントの役割を担っています。Ingress はトラフィックを Kubernetes クラスタ内のさまざまな Service にルーティングでき、ロードバランシングとトラフィック制御の機能を実現します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-ingress-に注目するのか">なぜ Ingress に注目するのか<a href="https://tanzhuo.xyz/ja/2023/05/03/ingress#%E3%81%AA%E3%81%9C-ingress-%E3%81%AB%E6%B3%A8%E7%9B%AE%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ Ingress に注目するのか への直接リンク" title="なぜ Ingress に注目するのか への直接リンク" translate="no">​</a></h2>
<p>クラスタ内のサービスは、デフォルトではクラスタ内部からしかアクセスできません。サービスを外部に公開する最も手軽な方法は NodePort または LoadBalancer タイプの Service ですが、どちらにも制約があります。NodePort はポート範囲が限られ、ドメインやパスの管理がしづらい。LoadBalancer はサービスを 1 つ公開するごとにロードバランサーを 1 台占有し、クラウド上では実費がかかることを意味します。</p>
<p>Ingress が解決するのはまさにこの問題です。統一されたエントリーポイントですべての外部 HTTP/HTTPS トラフィックを受け止め、ドメインとパスに応じてクラスタ内の各 Service へ振り分けます。どの Ingress コントローラーを選ぶかは、その後のルーティング設定、TLS 管理、L4 公開といった日常運用の体験を直接左右するため、選定段階で主要な選択肢を並べて比較する価値があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="動作の仕組み">動作の仕組み<a href="https://tanzhuo.xyz/ja/2023/05/03/ingress#%E5%8B%95%E4%BD%9C%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="動作の仕組み への直接リンク" title="動作の仕組み への直接リンク" translate="no">​</a></h2>
<p>Ingress の動作原理は、HTTP と HTTPS のリクエストを Service にマッピングすることで実現されています。Kubernetes において、Service はトラフィックを Pod にルーティングするためのリソースオブジェクトです。Ingress が外部からの HTTP または HTTPS リクエストを受け取ると、リクエスト内のホスト名とパス情報をもとに Ingress ルールとマッチングを行い、リクエストを対応する Service にルーティングします。</p>
<p>ここに混同しやすいポイントがあります。Ingress リソース自体は宣言的なルーティングルールにすぎず、書いただけでは自動的に有効にはなりません。実際に働くのは Ingress コントローラーです。コントローラーは Pod としてクラスタ内で動作し、API Server 上の Ingress、Service、Endpoint などのリソース変更を継続的に watch し、これらのルールを自身の下層プロキシ(Nginx、Envoy、Traefik 内蔵のプロキシなど)の設定に変換し、ホットリロードで反映します。つまり:</p>
<ol>
<li class="">Ingress リソース:「どのドメインのどのパスをどの Service に転送するか」を宣言する。</li>
<li class="">Ingress コントローラー:ルールの変更を監視し、プロキシ設定を生成してロードする。</li>
<li class="">下層プロキシ:実際に外部リクエストを受け取り、転送を行う。</li>
</ol>
<p>リクエストのマッチング時、コントローラーはまず Host でバーチャルホストをマッチングし、次に Path でルーティングルールをマッチングして、最後にリクエストを対応する Service の背後にある Pod へ転送します。TLS 証明書は通常 Secret の形で Ingress ルールにマウントされ、コントローラーがエントリーポイントで TLS 終端(オフロード)を行います。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="主要な-ingress-コントローラー">主要な Ingress コントローラー<a href="https://tanzhuo.xyz/ja/2023/05/03/ingress#%E4%B8%BB%E8%A6%81%E3%81%AA-ingress-%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%83%BC" class="hash-link" aria-label="主要な Ingress コントローラー への直接リンク" title="主要な Ingress コントローラー への直接リンク" translate="no">​</a></h2>
<p>Ingress を使うには、まず Ingress コントローラーをインストールする必要があります。Kubernetes はデフォルトの Ingress コントローラーを提供していませんが、サードパーティ製の Ingress コントローラーが数多く存在し、選択できます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2023-05-image-bc5d955ed94dca937cb7cdc2516b29d2.webp" width="1518" height="718" class="img_ev3q"></p>
<p>主要な選択肢のトレードオフを簡単に説明します。</p>
<ol>
<li class="">Nginx Ingress Controller:コミュニティで最もよく使われる選択肢。Nginx をリバースプロキシとして使い、ドキュメントと事例が豊富で、多くの設定を annotation で行います。デフォルトの選択肢として適しています。</li>
<li class="">Traefik:Dashboard が付属し、設定が動的に反映されます。Nginx のような reload が不要で、Let's Encrypt と連携した証明書の自動発行も扱いやすいです。</li>
<li class="">Istio Ingress Gateway:クラスタがすでに Istio サービスメッシュを導入しているなら、その Gateway をエントリーポイントにすることで、メッシュ内のトラフィック制御(カナリアリリース、サーキットブレーカー、テレメトリ)と連携できます。ただしエントリーポイントのためだけに Istio を導入するのは重すぎます。</li>
<li class="">その他、HAProxy や Kong などは、それぞれ性能や API ゲートウェイ機能に強みがあります。</li>
</ol>
<p>選定の考え方はシンプルでかまいません。特別な要件がなければ Nginx Ingress を使う。動的設定やよりモダンな運用体験が欲しければ Traefik を検討する。すでにサービスメッシュを使っているなら、その流れでメッシュ付属の Gateway を使う。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="l4-サービスの公開">L4 サービスの公開<a href="https://tanzhuo.xyz/ja/2023/05/03/ingress#l4-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E5%85%AC%E9%96%8B" class="hash-link" aria-label="L4 サービスの公開 への直接リンク" title="L4 サービスの公開 への直接リンク" translate="no">​</a></h2>
<p>ところで、HTTP の L7 プロトコルだけでなく、MySQL、Redis、MongoDB などの L4 プロトコルベースのサービスも、クラスタ外部に公開してアクセスできるようにしたい場面はよくあります。</p>
<p>しかし Kubernetes の Ingress 仕様自体は HTTP と HTTPS プロトコルしかサポートしていないため、デフォルトの Ingress コントローラーもこの 2 つのプロトコルしかサポートしていません。ただし、一部のサードパーティ Ingress コントローラーは拡張によって TCP と UDP プロトコルの公開をサポートできます。</p>
<ol>
<li class="">Traefik:Traefik は TCP プロトコルの公開をサポートしており、Ingress ルールで <code>traefik.tcp.routers</code> と <code>traefik.tcp.services</code> を指定することで TCP サービスとルーティングを設定できます。</li>
<li class="">Istio:Istio はサービスメッシュフレームワークで、その Ingress Gateway コンポーネントを通じて TCP プロトコルの公開をサポートできます。Ingress Gateway 内で VirtualService を定義することで、TCP サービスとルーティングを設定できます。</li>
</ol>
<p>補足すると、Nginx Ingress Controller も TCP/UDP サービスを公開できます。やり方は、<code>tcp-services</code> と <code>udp-services</code> という 2 つの ConfigMap をメンテナンスし、「外部公開ポート → Namespace/Service:ポート」のマッピングを書き込み、さらにコントローラーの Service 側で対応するポートを開放するというものです。これは Ingress リソースを経由せず、コントローラー独自の拡張機構です。</p>
<p>L4 公開には L7 と本質的な違いがあります。TCP 層には Host や Path の概念がなく、HTTP のようにドメインでバックエンドを区別できないため、ポート番号でしかサービスを区別できません。そのため TCP サービスを 1 つ公開するごとにエントリーポイントのポートを 1 つ占有することになり、サービスが増えるとポート管理が煩雑になっていきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2023/05/03/ingress#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">Ingress リソースを適用したのに効かない場合、たいていはクラスタにそもそもコントローラーがインストールされていないか、Ingress の <code>ingressClassName</code> がコントローラーと一致していないかのどちらかです。クラスタ内で複数のコントローラーを動かしているときに特にハマりやすいポイントです。</li>
<li class="">データベース系サービス(MySQL、Redis など)を Ingress 経由でパブリックに公開するのは慎重に。L4 転送には認証の強化が一切付いてこないため、内部ネットワーク、VPN、踏み台サーバー経由にするのがより堅実な方法です。</li>
<li class="">コントローラーごとに annotation は互換性がありません。Nginx から Traefik に移行する際は、annotation で実現していたリライト、レート制限、タイムアウトの設定を 1 つずつ翻訳し直す必要があります。</li>
<li class="">ロングコネクション系の L4 サービスでは、プロキシのアイドルタイムアウト設定に注意しましょう。デフォルト値はデータベースクライアントのコネクションプールが想定するよりも短いことが多く、コネクションが途中で切断される現象が起きやすくなります。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2023/05/03/ingress#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Ingress はクラスタの南北(North-South)トラフィックの統一エントリーポイントです。ルールは Ingress リソースで宣言し、実際の転送はコントローラーが行います。L7 のシナリオでは主要なコントローラーのどれもが十分に役割を果たせ、違いは主に設定方法とエコシステムにあります。L4 公開は Ingress 仕様自体の範囲を超えており、Traefik、Istio Gateway、Nginx Ingress の ConfigMap といった各実装独自の拡張機構に頼る必要があります。選定の際は、機能リストの充実度を追い求めるよりも、チームの既存技術スタックと運用習慣に合わせて決めるほうが現実的です。</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Server-Mesh 入門]]></title>
            <link>https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh</link>
            <guid>https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh</guid>
            <pubDate>Wed, 14 Dec 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[マイクロサービスの限界から出発し、Service Mesh の Sidecar 実装アプローチと Istio などの主要ソリューションを整理し、さらにサービスメッシュの上に発展した Knative と Serverless までを解説します。]]></description>
            <content:encoded><![CDATA[<p><strong>今回の技術共有は、技術のマクロな考え方と認識レベルの共有にとどめます。具体的な設定の詳細やデプロイ手順は今回の議論の範囲外です。</strong></p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>今回の議論は次の 3 つのポイントを中心に進めます。</p>
<ol>
<li class="">マイクロサービスの限界</li>
<li class="">Server-Mesh の機能と実装方式の理解</li>
<li class="">現在 Server-Mesh をベースに実現されている Serverless</li>
</ol>
<p>私たちが最もよく知り、日常的に使っている Java、Spring-Cloud、Spring-Cloud-Alibaba などの技術スタックは、従来型マイクロサービスのアーキテクチャ設計に属します。この設計には SDK との強い結合という問題があり、下位レイヤーの SDK バージョンが更新されるたびに、ビジネスレイヤーのサービス安定性に影響を及ぼす可能性が高くなります。SDK とコード環境が強く結合していると多くの制約が生まれ、世の中のさまざまな言語とそのエコシステムのコンポーネントを、それぞれの得意分野で十分に活用することができません。たとえば、柔軟で高速な Go や Python とそれに対応するコルーチン、クローラー、ニューラルネットワークなどのコンポーネント、あるいは厳密でガベージコレクションのオーバーヘッドがない Rust は、エンタープライズ向けミドルウェアの高効率・低メモリ消費のシーンにより適しています。つまり、各言語にはそれぞれの強みとエコシステムがあり、より長期的な視点で見れば、大規模なリーディング企業がビジネスのデプロイと実装を異なる言語に落とし込んで反復していくことは一つのトレンドになるでしょう。</p>
<p>従来型マイクロサービス体系の図:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-12-9fdf013b22e8458233744cad658d63a2-18ebc7a7b51196340270f5c789e43fc9.webp" width="479" height="422" class="img_ev3q"></p>
<p>さらに、今日 JVM が誇りとしてきた OS を跨げるという優位性も、K8S、Docker、Containerd、CRI-O、Kata などのコンテナ技術とそのエコシステムに取って代わられつつあります。ガベージコレクション機構、動的プロキシ、Agent、ZGC などの高度な機能も他の言語に取り入れられています。現時点で Java の地位を支えているのは、長年蓄積されてきたエコシステム（Apache、Spring、Ali、Huawei などのオープンソースコミュニティが新しいミドルウェアを注ぎ込んでおり、たとえば有名な Kafka、Hadoop、Hbase、RocketMQ、Flink、Seata など、そして今後もリリースされ続けるミドルウェアが言語エコシステムの活力を維持しています）です。しかし時間の経過とともに、長期的に見れば（20〜30 年以上、主観的な予測であり参考程度にしてください）、これらの優位性は後発の新星に奪われる可能性があり、次世代のマイクロサービス体系は必然的に各言語の強みの上に反復・更新されていくでしょう。</p>
<p>個人的な考察: Java はすでに Oracle に買収されていますが、Oracle は技術的に先鋭的な企業ではないため、今後 Java の発展を制限する可能性が十分にあります。オープンソース版はあるものの、オープンソースの力は企業の集中的な投資には及びません。現在、IT 業界の巨人 Google の Go 言語は急速に台頭し、すでに Java の市場の一部を占めています。これが ByteDance、Bilibili、Tencent、Baidu、JD、Xiaomi などの企業が Go を選んだ理由の一つかもしれません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サービスメッシュ">サービスメッシュ<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%83%A1%E3%83%83%E3%82%B7%E3%83%A5" class="hash-link" aria-label="サービスメッシュ への直接リンク" title="サービスメッシュ への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="概要">概要<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E6%A6%82%E8%A6%81" class="hash-link" aria-label="概要 への直接リンク" title="概要 への直接リンク" translate="no">​</a></h3>
<p>私たちが注目している Server-Mesh（サービスメッシュ）に話を戻すと、現在の主な実装アプローチは Sidecar（サイドカーパターン）です。K8S では Pod を基本的なデプロイ単位として使いますが、Sidecar は 1 つの Pod の中に Proxy コンテナをデプロイする方式です。Proxy コンテナはビジネス用の Server コンテナと同じ Pod 内にデプロイされ（Pod へのコンテナ注入）、Proxy コンテナが Server コンテナのすべてのトラフィックをプロキシします。</p>
<p>Istio による Server-Mesh アーキテクチャの俯瞰図:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-12-image-50-29cab4357ba6d65501863969da0b1a2a.webp" width="1057" height="715" class="img_ev3q"></p>
<p><strong>citadel:</strong> 認証と証明書管理を担うコアセキュリティコンポーネント。</p>
<p><strong>galley:</strong> 設定を管理するコンポーネント。設定ファイルのフォーマットや内容の正しさを検証し、それらの設定情報を pilot と mixer に提供します。</p>
<p><strong>pilot:</strong> コントロールの中枢であり、サービスディスカバリとルールの変換・配布を含みます。</p>
<p><strong>proxy:</strong> C++ で開発された Envoy と Pilot-agent によって実装され、動的サービスディスカバリ、ロードバランシング、TLS、サーキットブレーカー、ヘルスチェック、トラフィック分割、カナリアリリースなどの機能を提供します。さらにテレメトリデータを生成し、マイクロサービスにオブザーバビリティを提供します。</p>
<p><strong>Ingressgateway:</strong> 入口にある gateway で、メッシュ外からメッシュ内のサービスへのアクセスはこの gateway を通して行われます。</p>
<p>Proxy コンテナによって、ビジネスコードと SDK の完全な分離が実現され、さらに多くの機能が手に入ります。<strong>トラフィックミラーリング、カナリアリリース、サービスの登録とディスカバリ、リモート呼び出し、トラフィックのサーキットブレーカー、デグレード、分散トレーシング、コントロールプレーン、内部ネットワークのデータ暗号化転送（分散トランザクションについては現時点で特別な解決策はありません。トランザクションは厳密に言えばサービスガバナンスのレイヤーには属さないためです）</strong>。しかもプログラマーにとっては完全に透過的で、開発者はデプロイの詳細を理解する必要がなく、ほぼ完全にスタンドアロン版の感覚で開発でき、言語の制約もなく、いかなるマイクロサービス SDK にも依存しません。企業は異なる言語の特性を活かして異なる機能モジュールを開発することもできます（コード管理コストやコミュニケーションコストが増える可能性はありますが、チームが扱える言語の幅は広がります）。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="公式-demo">公式 Demo<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E5%85%AC%E5%BC%8F-demo" class="hash-link" aria-label="公式 Demo への直接リンク" title="公式 Demo への直接リンク" translate="no">​</a></h3>
<p>公式 Demo の Book-Info マイクロサービスは、Helm で K8S 環境にワンクリックでデプロイでき、その機能を体験できます（Book-Info アーキテクチャ図）:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-12-noistio-6757dcc885fcdd61bb4011d88efc5dd7.svg" width="662" height="453" class="img_ev3q"></p>
<p>Bookinfo マイクロサービスは Istio によるサイドカーデプロイを採用し、4 種類の異なる言語のプログラムを使用しています。さらに Java で開発された Reviews サービスは 3 つの異なるバージョンのアプリケーションを提供しており、マルチバージョン管理、トラフィック管理、トラフィック移行、統合など多方面における Istio の能力をよく示しています。ぜひ自分でデプロイして体験してみてください。Service-Mesh の機能の強力さと設定の柔軟さ（すべて秒単位で反映される設定で、無停止・無感知のトラフィック移行が可能）を直感的に感じられるはずです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="市場で最も一般的な実装">市場で最も一般的な実装<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E5%B8%82%E5%A0%B4%E3%81%A7%E6%9C%80%E3%82%82%E4%B8%80%E8%88%AC%E7%9A%84%E3%81%AA%E5%AE%9F%E8%A3%85" class="hash-link" aria-label="市場で最も一般的な実装 への直接リンク" title="市場で最も一般的な実装 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="istio">Istio<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#istio" class="hash-link" aria-label="Istio への直接リンク" title="Istio への直接リンク" translate="no">​</a></h3>
<p>Istio は Google、IBM、Lyft が立ち上げたオープンソースの Service Mesh フレームワークです。プロジェクトは 2017 年に発表され、2018 年 7 月に 1.0 版がリリースされました。2022-12-06 時点の最新バージョンは 1.16.0 です。Istio は現在の Service Mesh 実装の代表格であり、Sidecar が Service Mesh 全体のデータプレーンだとすれば、Istio は主にコントロールプレーンでより多くの改善を行っています。Istio は Envoy を Sidecar として使い、コントロールプレーン関連はすべて Golang で書かれており、パフォーマンスが大きく向上しています。Istio はまずサービスメッシュですが、単なるサービスメッシュにとどまりません。Linkerd や Envoy のような典型的なサービスメッシュの上に、Istio は完全なソリューションを提供し、サービスメッシュ全体に行動の洞察と運用制御を提供して、マイクロサービスアプリケーションの多様なニーズに応えます。</p>
<p><a href="https://istio.io/" target="_blank" rel="noopener noreferrer" class="">Istio</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="linkerd2">Linkerd2<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#linkerd2" class="hash-link" aria-label="Linkerd2 への直接リンク" title="Linkerd2 への直接リンク" translate="no">​</a></h3>
<p>Linkerd は Buoyant 社が 2016 年にいち早くオープンソース化した高性能ネットワークプロキシで、業界初の Service Mesh フレームワークです。主に分散環境におけるサービス間通信が直面する問題、たとえばネットワークの信頼性の低さ、安全性の欠如、遅延やパケットロスなどを解決するために使われます。Linkerd は当初 Scala で書かれていましたが、Linkerd2 では Go と Rust で書き直されました。</p>
<p><a href="https://linkerd.io/" target="_blank" rel="noopener noreferrer" class="">The world’s lightest, fastest service mesh.</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conduit">Conduit<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#conduit" class="hash-link" aria-label="Conduit への直接リンク" title="Conduit への直接リンク" translate="no">​</a></h3>
<p>Conduit は 2017 年 12 月にリリースされた、Buoyant が Linkerd に続いて支援するもう一つのオープンソースプロジェクトで、Linkerd の Kubernetes 向け独立バージョンという位置づけです。Conduit は、Linkerd のようにさまざまなプラットフォームに向けて最適化するのではなく、Kubernetes 上でサービスメッシュを使う際の複雑さを徹底的に簡素化し、ユーザー体験を向上させることを目指しています。Conduit の主な目標は、軽量、高性能、安全であること、そして非常に理解しやすく使いやすいことです。Linkerd や Istio と同様、Conduit もデータプレーンとコントロールプレーンを含みます。データプレーンは Rust で開発されており、Conduit は非常に少ないメモリリソースで動作します。コントロールプレーンは Go で開発されています。</p>
<p><a href="https://buoyant.io/" target="_blank" rel="noopener noreferrer" class="">Buoyant. All of the service mesh. None of the service mess.</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="各アーキテクチャの比較図">各アーキテクチャの比較図<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E5%90%84%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E6%AF%94%E8%BC%83%E5%9B%B3" class="hash-link" aria-label="各アーキテクチャの比較図 への直接リンク" title="各アーキテクチャの比較図 への直接リンク" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-12-075097e8-ce36-4d97-bba0-5864fa1f31c3-66b23389ce67b4293afd3eb03341d981.webp" width="1542" height="1352" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h3>
<p>Kubernetes の登場により、運用におけるコンテナのデプロイ、高可用性、マルチレプリカ、コンテナ移行、弾力的なスケールアウト、ローリングアップデート、イメージのバージョン管理、サービスの死活監視、計算リソースの割り当て、計算ノードの監視など、大部分の問題はすでに解決され、優れた自動運用の仕組みを備えています。しかし K8S は、アプリケーションレイヤーの Service コンテナに対する監視やトラフィック管理、コンテナ間の相互呼び出し、監視、登録、設定、トラフィックガバナンスなどの機能を提供していません。この機能モジュールを補うために Server-Mesh 体系の技術が登場し、K8S のこの分野の能力を拡張しました。これによりマイクロサービスは具体的な設定や環境の詳細を気にすることなく、ビジネス機能の実装に集中でき、言語環境の制約からも解放されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="serverless">Serverless<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#serverless" class="hash-link" aria-label="Serverless への直接リンク" title="Serverless への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="概要-1">概要<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E6%A6%82%E8%A6%81-1" class="hash-link" aria-label="概要 への直接リンク" title="概要 への直接リンク" translate="no">​</a></h3>
<p>マイクロサービスの理念が浸透するにつれて、ますます多くの企業が自社のアプリケーションをモノリスからマイクロサービスアーキテクチャへと段階的に移行させており、Container 技術の登場がこの移行を加速させました。コンテナは多くのサービス実行環境の差異の問題を効果的に解決しましたが、サービス数が増えるにつれて、コンテナのオーケストレーションと管理が新たな課題になりました。Kubernetes の登場は、大規模マイクロサービスのコンテナオーケストレーションとデプロイがもたらす課題を解決し、業界全体に PaaS の実現が現実のものになり得ることを認識させました。そしてマイクロサービス体系のコンテナ数がますます増えると、サービスガバナンスとトラフィック管理が必然的に解決すべき問題となり、そこで Istio が登場しました。ネットワークプロキシと制御を分離する実装戦略に基づき、サービスの制御ポリシーを効果的かつ合理的に管理できるようにしています。</p>
<p>アーキテクチャの反復はここまで来ると、とても良い段階に到達したように見えます。</p>
<ul>
<li class=""><strong>マイクロサービス</strong>: アプリケーションの凝集と肥大化の問題を解決。</li>
<li class=""><strong>Container</strong>: サービス実行環境の差異とデプロイの問題を解決。</li>
<li class=""><strong>Kubernetes</strong>: 大量のマイクロコンテナのオーケストレーション管理と「集約」デプロイの問題を解決。</li>
<li class=""><strong>Istio</strong>: サービスリリース時に直面するトラフィック、リリース、ガバナンスなど一連のコンテナ関連の問題を解決。</li>
</ul>
<p>この段階を一見すると、コンテナクラウドの構築には完全な道筋と解決方法があるように見え、すべてが「完璧」になったかのようです。</p>
<p>しかし振り返って掘り下げてみると、マイクロサービス体系におけるサービス間のやり取りには、依然として問題が残っているのではないでしょうか。まず、HTTP であれ RPC であれ、本質的にはサービスとサービスのリモート呼び出しであり、アプリケーション開発においてサービス間の相互透過性を実現することはできません。これにより、マイクロサービスのビジネス分割がどれだけ「精緻」であっても、本質的にはビジネスユニット同士が独立して実行・進化することはできず、完全には疎結合になっていないという問題が生じます。同時に、異なる開発領域へと派生していくとき、最適な実装方式を選択することもできません。そこで私たちは、異なる「テンプレート」や「設定」に基づいて開発環境を標準化し、同時に「イベント」の仕組みを提供して、サービス間のやり取りの結合度を最小限に抑えたいと考えます。次に、サービスの本番運用における動的スケーリングの問題です。現在の Kubernetes 環境での弾力的なスケーリングは、利用者が監視データを収集し、自ら手動で実現する必要がありますが、私たちはサービスの運用がもっと自動化・インテリジェント化されることを望んでいます。</p>
<p>最後にサービスの標準化の問題です。サービス内部のモデルは標準的で、迅速に複製・構築できること。サービス通信は標準的であること（プロトコルの標準、フォーマットの標準）。実行環境は標準的であること(迅速なデプロイ、迅速な移行)。これらが求められます。</p>
<p>そこで Knative の登場が、リモート直接呼び出し、サービスのオンライン自動スケーリング、バージョンスナップショット、そして一連の標準化の問題をちょうど解決してくれました。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="knative">Knative<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#knative" class="hash-link" aria-label="Knative への直接リンク" title="Knative への直接リンク" translate="no">​</a></h3>
<p>Knative は 2018 年に Google が発表した Serverless の世界における強力なツールで、あらゆるパブリッククラウドやプライベートクラウド上でサーバーレスアーキテクチャを実現でき、ユーザーはサーバーレスプログラミング技術を利用できます。現在参加している企業は主に Google、Pivotal、IBM、Red Hat で、2018 年 7 月 24 日に対外的に公開され、現在も急速に発展している段階です。Kubernetes とは異なり、K8S はアプリケーションにサービスを提供するために常に少なくとも 1 つの Pod インスタンスを実行し続ける必要がありますが、Knative はゼロまでスケールインできます（<strong>コールド/ホットスタート技術</strong>）。クライアントからアプリケーションへのリクエストが来たときに初めて、Knative はアプリケーションの Pod を実際に起動します。これにより、アプリを一年中稼働させ続けるための費用を大幅に節約できます。たとえばアクセス頻度が低い、利用頻度の少ない機能モジュールは、コールドスタートでの運用を検討することで、メモリと計算リソースを節約できます（技術的な手段でコンテナの起動時間を最適化し、コールドスタートの遅延を減らすことができます【最速でミリ秒単位の起動】。現在、中国国内で最も優れているのはおそらく Alibaba Cloud でしょう）。</p>
<p>公式サイト:</p>
<p>公式ドキュメント <a href="https://knative.dev/docs/" target="_blank" rel="noopener noreferrer" class="">knative.dev/docs</a> と <a href="https://github.com/knative" target="_blank" rel="noopener noreferrer" class="">github.com/knative</a></p>
<p>Knative の目標は、Kubernetes の上で開発ライフサイクル全体を支援することです。その具体的な実現方法は、まず開発者として望む言語と方法でコードを書けるようにし、次にアプリケーションのビルドとパッケージングを支援し、最後にアプリケーションの実行とスケーリングを支援するというものです。Knative は主に Build、Serving、Eventing の 3 大コアコンポーネントで構成されており、この 3 つのコアコンポーネントこそが、Knative という Serverless の巨艦を前進させる原動力です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="buildビルドシステム">Build（ビルドシステム）<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#build%E3%83%93%E3%83%AB%E3%83%89%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" class="hash-link" aria-label="Build（ビルドシステム） への直接リンク" title="Build（ビルドシステム） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">内部ビルド: ビルドは Kubernetes の中で行われ、関数を素早くコンパイルしてリリースでき、Kubernetes エコシステム全体との結びつきがより緊密です。</li>
<li class="">標準化: 汎用的で標準化されたビルドコンポーネントを提供することを目指しており、他のより大きなシステムの一部として使うことができます。デプロイスクリプトが標準化・構造化されているため、サービスの移行デプロイをより円滑に行えます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="servingサービングシステム">Serving（サービングシステム）<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#serving%E3%82%B5%E3%83%BC%E3%83%93%E3%83%B3%E3%82%B0%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" class="hash-link" aria-label="Serving（サービングシステム） への直接リンク" title="Serving（サービングシステム） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">迅速なデプロイ: Serverless コンテナを素早くデプロイ。</li>
<li class="">オンデマンドスケーリング: 自動スケールアウト/インと 0 インスタンスへの縮退をサポート。</li>
<li class="">ルーティングポリシー: Istio コンポーネントをベースに、ルーティングとネットワークプログラミングを提供。</li>
<li class="">バージョンスナップショット: デプロイのスナップショットをサポート（本番環境のコンテナスナップショットを長期保存でき、任意のスナップショットバージョンへ復元可能）。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="eventingイベントシステム">Eventing（イベントシステム）<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#eventing%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" class="hash-link" aria-label="Eventing（イベントシステム） への直接リンク" title="Eventing（イベントシステム） への直接リンク" translate="no">​</a></h3>
<p><strong>Source(ソース)、Channel(チャネル)、Subscription(サブスクリプション)</strong></p>
<p>イベントシステムは、イベントの生成と消費を容易にします。イベントソースを抽象化し、運用者が自ら選んだメッセージングレイヤーを使えるようにします。Serverless で最も重要なのはイベントベースのトリガー機構、つまり何かが起きたときに特定の関数がトリガーされるという仕組みです。イベントという概念の登場により、関数と具体的な呼び出し側を疎結合にできます。関数はデプロイされても誰が呼び出すかを気にする必要がなく、イベントソースのトリガーも誰が処理するかを気にする必要がありません。簡単に言えば、コードの中で具体的な呼び出し先 Service を書く必要はなく、イベントの送信とイベントの処理にだけ注目すればよいのです（MQ キューに詳しい人ならすでにピンと来ているでしょう。ただし Eventing のイベントシステムはより完成度が高く、複数のイベント処理モードがあります）。私たちの Service はリモート RPC や HTTP 呼び出しの際、具体的なサービスインスタンスを気にする必要も、レジストリを購読する必要もなく、イベントソースを発行するだけでよく、イベントの戻りデータはすべて Eventing が処理します。これによりサービス間の透過性が真に実現され、サービスの結合問題が解決されます。<strong>【結合と疎結合の差は、往々にして中間層が一つあるかないかの違いにすぎない、というわけです】</strong></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-12-650cc9d17fb6fb116fb265ff782a307308c-1508a550eb4a75c7aff60d1624fa8156.webp" width="1017" height="722" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="全体的な優位性">全体的な優位性<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E5%85%A8%E4%BD%93%E7%9A%84%E3%81%AA%E5%84%AA%E4%BD%8D%E6%80%A7" class="hash-link" aria-label="全体的な優位性 への直接リンク" title="全体的な優位性 への直接リンク" translate="no">​</a></h3>
<p><strong>利便性</strong>: Knative は Kubernetes を基盤フレームワークとしているため、オンラインでもオフラインでも、クラウドの Kubernetes サービスでも自前で構築した Kubernetes クラスタでも、Knative プラグインをインストールするだけで Serverless プラットフォームを素早く構築できます。</p>
<p><strong>標準化</strong>: Knative は CNCF と連携してすべてのイベントを標準化し、CloudEvent に統一することで、イベントのクロスプラットフォーム能力を提供し、同時に関数と具体的な呼び出し側の疎結合を実現しています。</p>
<p><strong>サービス間の疎結合</strong>: Knative を使うことで、アプリケーションは下位の依存サービスと強く結びつかなくなり、クラウドを跨いだビジネス連携が可能になります。</p>
<p><strong>成熟したエコシステム</strong>: Knative は Kubernetes 体系の上に構築されており、Kubernetes エコシステムとの結びつきがより緊密です。</p>
<p><strong>自動スケーリング</strong>: アプリケーションへのリクエストを監視し、自動的にスケールアウト/インします。Istio（ambassador、gloo など）の力を借りて、ブルーグリーンデプロイやロールバック機能を最初からサポートしており、アプリケーションのリリースフローを簡便にします。</p>
<p><strong>アプリケーション監視</strong>: ログの収集・検索・分析をサポートし、metrics データの表示や呼び出し関係の tracing もサポートします。</p>
<p><strong>スナップショットデプロイ</strong>: リリースごとにサービスのスナップショット情報を記録し、長期保存が可能で、任意の時点で無停止・無感知のままあるスナップショットバージョンへ復元できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ-1">まとめ<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E3%81%BE%E3%81%A8%E3%82%81-1" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Serverless（サーバーレスアーキテクチャ）は現在の新しい技術ホットスポットとなっており、従来のコンテナ技術と Server-Mesh サービスメッシュの上に発展してきたものです。サーバーレスのクラウドファンクションにより、開発者はサーバーのデプロイや運用を気にすることなく、最もコアとなるビジネスロジックや関数を開発するだけでサービスインでき、自動的に分散災害対策能力を備え、負荷に応じて自動スケーリングします。パブリッククラウドでは実際の呼び出し回数、実行時間、計算リソース消費量に応じて課金されるため、計算リソースの無駄がなく、企業の支出をより効果的に節約できます。</p>
<p><strong>Serverless は大きく 2 つのタイプに分けられます。【BaaS は Backend as a Service、FaaS は Function as a Service】</strong></p>
<p><strong>BaaS:</strong> サーバーレスという言葉は、まずサーバーサイドのロジックと状態を管理するために、サードパーティのクラウドホスティングされたアプリケーションやサービスを大幅に、あるいは完全に取り込んだアプリケーションを表すために使われました。これらは通常「リッチクライアント」アプリケーション（シングルページ Web アプリケーションやモバイルアプリなど）であり、クラウドからアクセス可能な巨大なデータベースエコシステム（例: Parse、Firebase）、認証サービス（例: Auth0、AWS Cognito）などを利用します。この種のサービスは以前「Backend as a Service」と呼ばれていました。つまり私たちが後端でよく使う Spring-boot、Tomcat、Dubbo、Weblogic、Gin、Flask などのコンテナやサービスミドルウェアに相当します。</p>
<p><strong>FaaS:</strong> サーバーレスは、サーバーサイドのロジックを依然としてアプリケーション開発者が書くアプリケーションを指すこともあります。ただし従来のアーキテクチャとは異なり、それはステートレスな計算コンテナの中で実行されます。これらのコンテナはイベントトリガーで起動し、関数を 1 つ実装するだけでよく、他の環境を気にする必要がなく、短命であり（1 回の呼び出しだけで終わることもあります）、完全にサードパーティによって管理されます。これを理解する一つの方法が「Function as a Service」、つまり「FaaS」です。海外では AWS Lambda が現在最も人気のある Function as a Service プラットフォームの実装の一つで、中国国内で FaaS サービスを提供しているのは Alibaba Cloud の FC（Function Compute）サービスです。</p>
<p>要するに、サーバーレスアーキテクチャの登場は従来のアプリケーションを置き換えるためのものではなく、高度な柔軟性を持つ利用モデルとイベント駆動という特徴を出発点に、デプロイの削減、スケーラビリティの向上、コードの背後にあるインフラの保守負担の軽減を助け、企業により適したデプロイ方式を選ぶための、より多くの可能性を提供してくれるものなのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="今後の考察">今後の考察<a href="https://tanzhuo.xyz/ja/2022/12/14/chu-tan-service-mesh#%E4%BB%8A%E5%BE%8C%E3%81%AE%E8%80%83%E5%AF%9F" class="hash-link" aria-label="今後の考察 への直接リンク" title="今後の考察 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">
<p>将来のデプロイアーキテクチャはどのように発展していくのでしょうか。高可用性・高性能・高並行という 3H の特性は、どのような新しい技術体系を生み出すのでしょうか。企業がデプロイするプライベートクラウドの信頼性は、どうすれば 6〜12 個の 9 の可用性を実現できるのでしょうか【<strong>6 個の 9: (1-99.9999%)*365*24*60*60=31秒、年間の停止時間は 31 秒を超えられません。しかし 12 個の 9 となると相当な挑戦であり、企業のコストの観点からは必要性がなく、実現不可能とすら言えます。年間の停止時間は (1-99.9999999999%)*365*24*60*60=0.00003秒 を超えられないのです</strong>】。マルチリージョン・アクティブアクティブのアーキテクチャ体系と組み合わせると、さらにどれだけの新しいミドルウェア技術が派生するでしょうか。</p>
</li>
<li class="">
<p>このアーキテクチャには利点だけで欠点はないのでしょうか。そのオーバーヘッドと規模、制御可能性はすべての企業に適しているのでしょうか。ビジネスシーンを考慮せずにいきなり Serverless 体系を導入するのは科学的と言えるでしょうか。</p>
</li>
<li class="">
<p>ソフトウェアにおけるコンテナエコシステム技術と、ハードウェアのハイパーコンバージド技術には、異曲同工の妙があることに気づいたでしょうか。その目的はいずれも、弾力性、移行、コスト削減、柔軟性、俊敏性、そしてより高い信頼性です。</p>
</li>
</ol>
<p>不足があればぜひ補足してください。コメントでの交流をお待ちしています。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[IDEA デフォルトショートカットキー]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian</guid>
            <pubDate>Fri, 25 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[IntelliJ IDEA のデフォルトキーマップで最もよく使うショートカットキーを、検索、編集、リファクタリング、デバッグに分類して整理し、覚え方のコツと競合の調査方法も添えます。]]></description>
            <content:encoded><![CDATA[<p>IDEA のデフォルトショートカットキーを記録しておきます。Java を書いてきたこの数年、本当に効率を一段引き上げてくれたのはプラグインではなく、両手をマウスから離すことでした。この記事では、デフォルトのキーバインドを使用シーン別にひととおり見ていきます。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-20200928180550215-f90b66344ed049dc5d225ec73fc74153.webp" width="1600" height="702" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜデフォルトキーマップを覚える価値があるのか">なぜデフォルトキーマップを覚える価値があるのか<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E3%81%AA%E3%81%9C%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%82%AD%E3%83%BC%E3%83%9E%E3%83%83%E3%83%97%E3%82%92%E8%A6%9A%E3%81%88%E3%82%8B%E4%BE%A1%E5%80%A4%E3%81%8C%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜデフォルトキーマップを覚える価値があるのか への直接リンク" title="なぜデフォルトキーマップを覚える価値があるのか への直接リンク" translate="no">​</a></h2>
<p>IDEA はカスタム Keymap をサポートしており、Eclipse や VS Code スタイルへワンクリックで切り替えることもできます。それでも私はデフォルトのキーマップをそのまま使うことをおすすめします。理由はシンプルで、PC を替えたとき、同僚のマシンにつないだとき、公式ドキュメントや動画チュートリアルを見るとき、デフォルトのキーバインドならどこでも通用し、筋肉の記憶が無駄にならないからです。</p>
<p>以下のショートカットキーは Windows/Linux のデフォルト Keymap を基準にしています。macOS ではほとんどの場合 <code>Ctrl</code> を <code>Cmd</code> に、<code>Alt</code> を <code>Option</code> に置き換えれば OK です。個別の違いは都度注記します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="検索とジャンプ">検索とジャンプ<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E6%A4%9C%E7%B4%A2%E3%81%A8%E3%82%B8%E3%83%A3%E3%83%B3%E3%83%97" class="hash-link" aria-label="検索とジャンプ への直接リンク" title="検索とジャンプ への直接リンク" translate="no">​</a></h2>
<p>このカテゴリは使用頻度が最も高いので、優先的に習熟することをおすすめします。</p>
<ul>
<li class=""><strong>Shift を 2 回押し</strong>:Search Everywhere。クラス、ファイル、シンボル、設定項目をグローバル検索。何を使えばいいかわからないときはこれを使いましょう。</li>
<li class=""><strong>Ctrl + N</strong>:名前でクラスを検索。<strong>Ctrl + Shift + N</strong>:ファイルを検索。<strong>Ctrl + Alt + Shift + N</strong>:シンボル(メソッド、フィールド)を検索。</li>
<li class=""><strong>Ctrl + Shift + F</strong>:プロジェクト全体のテキスト検索。<strong>Ctrl + Shift + R</strong>:プロジェクト全体の置換。</li>
<li class=""><strong>Ctrl + B</strong>(または Ctrl + マウス左クリック):定義へジャンプ。<strong>Ctrl + Alt + B</strong>:実装クラスへジャンプ。インターフェースを見ているときは定義へのジャンプより役立ちます。</li>
<li class=""><strong>Alt + F7</strong>:Find Usages。メソッドやフィールドがどこから呼ばれているかを調べます。</li>
<li class=""><strong>Ctrl + E</strong>:最近開いたファイル。<strong>Ctrl + Shift + E</strong>:最近編集したファイル。</li>
<li class=""><strong>Ctrl + G</strong>:指定した行番号へジャンプ。ログのスタックトレースを調べるときによく使います。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="編集とコード生成">編集とコード生成<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E7%B7%A8%E9%9B%86%E3%81%A8%E3%82%B3%E3%83%BC%E3%83%89%E7%94%9F%E6%88%90" class="hash-link" aria-label="編集とコード生成 への直接リンク" title="編集とコード生成 への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>Alt + Enter</strong>:コンテキストに応じたクイックフィックス。インポートの追加、try-catch の補完、未定義メソッドの作成まで全部これ頼み。IDEA の魂のキーと言っていいでしょう。</li>
<li class=""><strong>Alt + Insert</strong>(macOS は Cmd + N):Generate メニュー。コンストラクタ、getter/setter、equals/hashCode、オーバーライドメソッドを生成します。</li>
<li class=""><strong>Ctrl + D</strong>:現在行を複製。<strong>Ctrl + Y</strong>:現在行を削除。Ctrl + Y は多くのエディタでは「やり直し(Redo)」ですが、IDEA では行削除なので、使い始めは誤爆しやすい点に注意。</li>
<li class=""><strong>Ctrl + W</strong> / <strong>Ctrl + Shift + W</strong>:構文構造に沿って選択範囲を拡大/縮小。式ひとつ、コードブロックひとつを選択するのにとても便利です。</li>
<li class=""><strong>Ctrl + Alt + L</strong>:コードのフォーマット。<strong>Ctrl + Alt + O</strong>:import の最適化。</li>
<li class=""><strong>Ctrl + /</strong>:行コメント。<strong>Ctrl + Shift + /</strong>:ブロックコメント。</li>
<li class=""><strong>Shift + Alt + 上/下</strong>:行単位で上下移動。<strong>Ctrl + Shift + 上/下</strong>:構文ブロック単位で移動。文がメソッド本体の外に出てしまうことはありません。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リファクタリング">リファクタリング<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E3%83%AA%E3%83%95%E3%82%A1%E3%82%AF%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%B0" class="hash-link" aria-label="リファクタリング への直接リンク" title="リファクタリング への直接リンク" translate="no">​</a></h2>
<p>リファクタリングのショートカットキーの価値は安全性にあります。IDEA がすべての参照を同期して変更してくれるので、手作業の検索置換よりはるかに信頼できます。</p>
<ul>
<li class=""><strong>Shift + F6</strong>:リネーム。クラス、メソッド、変数に共通で使えます。</li>
<li class=""><strong>Ctrl + Alt + M</strong>:メソッドの抽出(Extract Method)。<strong>Ctrl + Alt + V</strong>:変数の抽出。</li>
<li class=""><strong>Ctrl + Alt + C</strong>:定数の抽出。<strong>Ctrl + Alt + F</strong>:フィールドの抽出。</li>
<li class=""><strong>Ctrl + Alt + Shift + T</strong>:Refactor This。現在の位置で使えるすべてのリファクタリング項目がポップアップします。上のいくつかを思い出せないときの最後の砦です。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実行とデバッグ">実行とデバッグ<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E5%AE%9F%E8%A1%8C%E3%81%A8%E3%83%87%E3%83%90%E3%83%83%E3%82%B0" class="hash-link" aria-label="実行とデバッグ への直接リンク" title="実行とデバッグ への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>Shift + F10</strong>:現在の構成で実行。<strong>Shift + F9</strong>:デバッグ実行。</li>
<li class=""><strong>F8</strong>:ステップオーバー。<strong>F7</strong>:ステップイン。<strong>Shift + F8</strong>:現在のメソッドからステップアウト。</li>
<li class=""><strong>F9</strong>:実行を再開し、次のブレークポイントまで進む。</li>
<li class=""><strong>Ctrl + F8</strong>:現在行にブレークポイントを設定/解除。</li>
<li class=""><strong>Alt + F8</strong>:デバッグ中に Evaluate Expression を開き、式を一時的に実行して結果を確認します。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>ショートカットキーが効かなくなったときは、慌てて再インストールする前に。たいていは他のソフトに横取りされています。</p>
<p>1)Windows では IME、NetEase Cloud Music、QQ などの常駐ソフトがグローバルホットキーを登録します。<code>Ctrl + Alt + L</code> のような組み合わせは特に奪われやすいので、該当ソフト側でグローバルホットキーをオフにすれば解決します。</p>
<p>2)Linux のデスクトップ環境(GNOME/KDE)に付属するウィンドウ管理ショートカットも IDEA と競合します。システム設定でシステム側のバインドを変更しましょう。</p>
<p>3)IDEA 内で <strong>Ctrl + Shift + A</strong>(Find Action)を押して機能名を入力すると、任意のアクションに現在割り当てられているキーバインドを調べられます。競合の調査にも、マイナー機能の発見にも使える万能の入り口です。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>一度に全部覚えようとしないでください。高頻度キーを 3~5 個(Shift 2 回押し、Alt + Enter、Ctrl + W、Shift + F6)選んでまず 1 週間使い、筋肉の記憶ができてから次のバッチを追加するほうが、リストとにらめっこして丸暗記するよりずっと効果的です。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/25/idea-mo-ren-kuai-jie-jian#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ショートカットキーのリターンは複利です。1 回あたりの節約は 1~2 秒でも、検索、ジャンプ、リファクタリングは毎日何百回も行います。デフォルトのキーバインドはマシンをまたいで通用し、Ctrl + Shift + A でいつでも逆引きできるので、余計な暗記の負担はほとんどありません。このリストは私自身も使い続けるので、忘れたらまたここに戻って見返します。</p>]]></content:encoded>
            <category>Java</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[スレッドプールのコアスレッド数の計算方法]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi</guid>
            <pubDate>Mon, 21 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[計算集約型、IO 集約型、混合型という 3 つのワークロードから出発して、スレッドプールのコアスレッド数の見積もり公式と、なぜ最終的には負荷テストによる調整が必要なのかを論じます。]]></description>
            <content:encoded><![CDATA[<p>スレッドプールのコアスレッド数をいくつに設定すべきかは、プログラムのワークロードによって決まります。一般に、計算集約型、IO 集約型、混合型の 3 つのシナリオに分けられます。それぞれ見ていきましょう。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="計算集約型">計算集約型<a href="https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi#%E8%A8%88%E7%AE%97%E9%9B%86%E7%B4%84%E5%9E%8B" class="hash-link" aria-label="計算集約型 への直接リンク" title="計算集約型 への直接リンク" translate="no">​</a></h2>
<p>大量の計算を必要とし、CPU 使用率が高く、CPU Loading は 90〜100% になります。CPU が I/O（ディスク/メモリ）の読み書きを行うことはあっても、それらの I/O はごく短い時間で完了し、大部分は CPU が大量のデータ演算や数学的計算を行っています。例えばデータ分析やストリーム処理などで、この種のプログラムの実行中は CPU 使用率が一般に非常に高くなります。</p>
<p>仮にシングルコア CPU の場合、スレッドプールに 6 つのスレッドがあっても、シングルコアなので同時に実行できるスレッドは 1 つだけです。スレッド間のコンテキストスイッチの時間コストを考えると、単一スレッドで実行するほうがむしろ効率的です。つまり、シングルコア CPU で計算集約型プログラムを処理するなら、マルチスレッドは使わないほうがよいのです。</p>
<p>仮に 6 コアの CPU で 6 スレッドを設定すれば、理論上は実行速度が 6 倍になります（実際にはそこまで届きません。マルチスレッド間には並行処理や最適化すべき点があるためです）。各スレッドに実行用の CPU が割り当てられ、CPU タイムスライスの待ちが発生せず、スレッド切り替えのオーバーヘッドもありません。マルチコア CPU こそ計算集約型プログラムの処理に適しており、その間スレッドのコンテキストスイッチも発生しない可能性があります（<strong>1 コアに 1 スレッドを対応させ、通常は CPU 数 + 1 を超えない設定にします</strong>）。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="io-集約型">IO 集約型<a href="https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi#io-%E9%9B%86%E7%B4%84%E5%9E%8B" class="hash-link" aria-label="IO 集約型 への直接リンク" title="IO 集約型 への直接リンク" translate="no">​</a></h2>
<p>計算集約型とは逆に、ディスクやネットワーク上の IO の処理が中心で、CPU の占有率は高くありません。大部分の時間は CPU が IO の応答を待っており、待っている間スレッドはブロックされ、CPU はアイドル状態です。負荷はむしろディスクやネットワークの転送効率にかかっています。このシナリオで単純に CPU コア数 × 2 でスレッド数を設定するのは実は厳密さに欠けます。IO 集約型には当てはめられる公式があります。例えば Web アプリケーションのバックエンドでは、データベースやキャッシュへの CRUD が主な場面で、多くの API の所要時間はディスクやネットワークの IO に費やされています。そういう場合、スレッドプールのコアスレッド数を設定するときは、サーバーに割り当てられた CPU リソースに基づいて公式を当てはめればよいのです。</p>
<p>『Java Concurrency in Practice（Java 並行処理プログラミング）』にある計算公式：</p>
<p><strong>Nthreads = Ncpu × Ucpu × (1 + W/C)</strong></p>
<ul>
<li class="">Ncpu：CPU コア数</li>
<li class="">Ucpu：CPU 使用率</li>
<li class="">W/C：待ち時間 / 計算時間</li>
</ul>
<p>公式から期待されるスレッド数を導くことはできますが、実際のプログラムでは、<strong>正確な待ち時間と計算時間を得るのは一般に非常に困難です。プログラムは複雑で、「計算」だけではないからです</strong>。一つのコードの中には、メモリの読み書き、計算、I/O などの複合的な操作が数多く含まれており、この 2 つの指標を正確に取得するのは難しい。そのため公式だけでスレッド数を計算するのは理想論に過ぎます。とはいえ、これをベースにして、さらに負荷テストを通じてコアスレッド数を具体的に調整すれば、最高の効率という期待値に到達できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="混合型">混合型<a href="https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi#%E6%B7%B7%E5%90%88%E5%9E%8B" class="hash-link" aria-label="混合型 への直接リンク" title="混合型 への直接リンク" translate="no">​</a></h2>
<p>アプリケーションの中には、データの計算処理をしながら、同時にディスクやネットワークのデータ転送も必要とするものがあります。このようなプログラムでは、どうスレッドプールを構成すれば最高の性能が得られるでしょうか。一般的には、サーバーの CPU コア数に基づいて分割し、2 つのスレッドプールを作成します。一方は計算部分に、もう一方は IO 部分に対応させる構成が合理的です。ネット上には別の方式もあります：</p>
<p><strong>核心线程数 = (线程等待时间 / 线程 CPU 时间 + 1) × CPU 核心数</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実際のプログラムにおけるスレッド数">実際のプログラムにおけるスレッド数<a href="https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi#%E5%AE%9F%E9%9A%9B%E3%81%AE%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E6%95%B0" class="hash-link" aria-label="実際のプログラムにおけるスレッド数 への直接リンク" title="実際のプログラムにおけるスレッド数 への直接リンク" translate="no">​</a></h2>
<p>では実際のプログラム、つまり Java の業務システムでは、スレッド数（スレッドプールのサイズ）をどれくらいに計画するのが適切でしょうか。</p>
<p><strong>先に結論を言うと、決まった答えはありません。まず期待値を設定します。例えば期待する CPU 使用率、負荷、GC 頻度などの指標です。その後、公式でコアスレッド数を設定し、テストを通じて合理的なスレッド数へと調整し続けます。</strong></p>
<p>公式で出せるのはおおよその値だけで、実際には妨害要因が非常に多く、特にサーバーの計算リソースの奪い合いが問題になります。アプリケーションがサーバー上で直接動いていて他のプログラムの干渉がなければ、負荷テストと調整を繰り返すことで適切なスレッド数を見つけられます。しかし現在、多くのアプリケーションはすでにコンテナ化されており、K8S 環境ではコンテナが異なる worker ノードに分散しています。1 つの worker ノードで多くのコンテナが動いていることもあり、コンテナにリソース制限をかけないと、計算リソースの奪い合いが容易に発生します。そのため各コンテナの CPU、メモリ、ディスク、ネットワークなどのリソース制限をきちんと設定する必要があります。さもないと実行効率が期待を大きく下回る可能性があります（計算リソースが他のコンテナに奪われてしまうためです）。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/21/xian-cheng-chi-he-xin-xian-cheng-shu-ji-suan-fang-shi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>スレッドプールのコアスレッド数に万能の数値はありません。計算集約型は CPU コア数に沿って設定し、IO 集約型は Nthreads = Ncpu × Ucpu × (1 + W/C) で見積もり、混合型は計算と IO で 2 つのスレッドプールに分割することを検討します。公式が与えるのは出発点に過ぎず、待ち時間と計算時間は実際のプログラムでは正確に測定しにくく、さらにコンテナ化環境でのリソース奪い合いの問題も重なります。したがって合理的なやり方は、まず公式で初期値を決め、CPU 使用率や負荷などの期待指標を設定したうえで、負荷テストによって期待に合致するまで段階的に調整していくことです。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[InnoDB の BufferPool 徹底解説]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool</guid>
            <pubDate>Wed, 16 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[InnoDB BufferPool の階層構造とページ管理を詳しく解説：Page Hash、Free/LRU/Flush リスト、ホット・コールドデータの分離、そしてダーティページをディスクにフラッシュする 4 つのタイミング。]]></description>
            <content:encoded><![CDATA[<p>InnoDB ストレージエンジンでは、ディスク IO の回数を減らしてサービス性能を高めるために、すべての DML 操作はメモリ上の BufferPool 内で完結し、変更されたデータは非同期のフラッシュ戦略によってディスクへ書き込まれます。本記事では BufferPool のデータ構造、ページの管理方式、そして淘汰・フラッシュ戦略を詳しく整理していきます。</p>
<!-- -->
<p>BufferPool の公式ドキュメント：</p>
<p><a href="https://dev.mysql.com/doc/refman/8.0/en/innodb-buffer-pool.html" target="_blank" rel="noopener noreferrer" class="">MySQL :: MySQL 8.0 Reference Manual :: 15.5.1 Buffer Pool</a></p>
<p>なぜ BufferPool を設計する必要があるのでしょうか。コンピュータの基礎知識があれば、メモリとディスクの効率には天と地ほどの差があることをご存じでしょう。CPU から見ると、ディスクの転送速度や応答時間はあまりにも遅すぎます。そこで MySQL は、ディスク上の実データをマッピングする BufferPool を設計し、メモリ上でデータの管理と操作を完結させることで、DML 文の実行効率を飛躍的に向上させました。BufferPool 内のデータをクラッシュや電源断の場面で失わないための仕組み（物理的な破損はさすがにどうしようもありませんが）については、以前の記事でも解説しています：</p>
<p><a href="https://tanzhuo.xyz/mysql-zhixing/" target="_blank" rel="noopener noreferrer" class="">MySQL 実行フロー</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="buffer-pool-の概要">Buffer Pool の概要<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#buffer-pool-%E3%81%AE%E6%A6%82%E8%A6%81" class="hash-link" aria-label="Buffer Pool の概要 への直接リンク" title="Buffer Pool の概要 への直接リンク" translate="no">​</a></h2>
<p>InnoDB におけるデータアクセスは Page 単位で行われ、各 Page のデフォルトサイズは 16KB です。Buffer Pool はこれらの Page を管理・キャッシュするためのものです。InnoDB は連続したメモリ領域を Buffer Pool に割り当て、それを複数の Buffer Pool Instance に分割してこのメモリをより効率的に管理します。各 Instance のサイズは等しく、アルゴリズムによって 1 つの Page は必ず特定の Instance に配置されることが保証されます。このマルチ Instance 方式によって Buffer Pool の並行処理性能が向上します。</p>
<p>各 Buffer Pool Instance の内部には、それぞれ独自の管理構造が維持されています。InnoDB は 16KB の Page 単位でディスクファイルからメモリへデータを読み込み、LRU List を使ってこれらの Page をキャッシュします。頻繁にアクセスされる Page は LRU List の前方に、あまりアクセスされない Page は後方に置かれます。ある Page にアクセスするときは、まず Buffer Pool 内を検索し、ヒットしなければディスクのデータファイルを読み、取得した Page を LRU List に格納します。Instance 内に利用可能な空き Page がなくなると、LRU List 内の Page が淘汰されます。</p>
<p>Buffer Pool には Page 圧縮のロジックも含まれています。つまり実際の 16KB Page を 8KB、4KB、2KB、1KB に圧縮する仕組みですが、この部分は本記事では割愛し、デフォルトの 16KB Page を前提に主要なロジックを整理します。</p>
<p>現時点で見ると、BufferPool は階層化された構造になっています：</p>
<ul>
<li class="">BufferPool は複数の Buffer Pool Instance から構成される。</li>
<li class="">Buffer Pool Instance は複数の Buffer Chunk から構成される（デフォルトでは各 Buffer Chunk は 128MB）。Buffer Chunk は連続したメモリ領域を管理し、これを Buffer Chunk Memory と呼ぶ。</li>
<li class="">Buffer Chunk は複数の Buffer Page（略して Page、デフォルト 16KB）から構成される。</li>
</ul>
<p>この階層構造は、高い並行性のもとでの mutex 競合を減らすためのものです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-842fb6ff-945d-442d-a28e-0d7a9ef039b1-3f3a18424cb951f5053c4f5ca00a1237.webp" width="1440" height="425" class="img_ev3q"></p>
<p>本記事では主に BufferPool のデータ構造、管理方式、そして淘汰・フラッシュ戦略を解説します。まず公式が示している LRU データキャッシュの構造図を見てみましょう：</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,UklGRkwdAABXRUJQVlA4IEAdAADwqACdASp4AVsCPm00lUkkIqShIRLLEJANiWlu4XQxM6oBA0QGiX9gpS0kdbb/ngPQB/0N9E3nzIlvJn9X/GTwR/of48+eP4r8f/Qvx1/tvKriI/D/q/9g/uP7S/HL+U/zHhb8TPJ34Avxn+M/2f+x/tl/ZuHm2X/T+gF6d/JP8r/f/3C/wfojftfoJ9ev7p+Yf0Afxr+V/3780/7v///qb/aeFr9u/3/sA/yH+ef6f/A/5n/w/5n6XP5b/p/5b/Rf+H/M+2v88/xv/P/x35MfYN/Jv6J/sf7j/mf/b/pv///7PvK9hH7pf//3Xf2L//RCrjXSGVb92BV9+klwgVJGd4VtCtoVs9EOtxU77Mq1jFIpuY7vohbVFnETMjpEwm9XF18Wfx4pcijoDfENFQb5w7KFddpFXvC2dgdImFiRWWspKOxzQRBAnE0NOGO00wxcsG9890iXjdhdmV6RK7zxEyYuWDe+e6RLxu7JsdImmqA10nTlBVdrd6xiQ2O7xcGDJNjpEvG71jEhsd3i4MGSbHSJeN3rGJDY7vGqAMKFV57m0b5pygqwSLyAYT7NRFwiGy9S6HlUcetDGGZjkuEcfha+rraHReIAXDJq1MSVIDTE0wZJdXTMFozRYGNV0nR/6jt+X2K7PqQtbfyBXuaEcf4JGNV0nTlBVdrd6xismqIn2EJvgGFCq89zaN805QVYJDSblD616PEo0LRqP5EjlBRZYhXGukehvdYX2/X623J8TEXcFIHGOSnxeZlfGRI5QCxfNOUFWCQx9iu/I1hiVMskq80RWpUjiwIdiQ2ObRvmnKCrBISFsj+3JDAdq3BjlefykGvYpGBIbHR5aJGhqld0iXtMOgHau6s0y9Sql5xDjYb941/KnwKFInLjtLDp49eHrw9d823g3rboLNP5dYKNN1D1RTs2IE6WU/lRw4+9G+ZV1sPqU5bg5iz0OZtWsRuGanluwEYZdCNN0RK8gjAH89Sen8SKOXjfauXM4tjbhRQtMtxHzyEwoZ9IKFN0ZP+sAmQaoOliwIc4WQm+5Tk5LEvwtjGmDHTMw7p4ROO6qi7L6dAkVfz4gWF86EdkhpGdfTr7NJbmVnHpltQmi19JmxmqQO5+g4/67XU6IRQdQ5pRWAJ0r/b5B7d6lJ2xKd2PCGkG1wmqHCh9RxCVPvWopvaxxj13ml5Ca+FvLCmu1/vRNjE/y5WxTxWqJWHthbyzfjCBz9whC9DNiqm2EGdQQwRj6JKjazIHSJhYWbRvwGRX2ojVTpo31SHs5ciPiwKa59ffRwLsoKqdT9dHq9qCq89ZNo6wDpEwtbZLZgySPbhACPim6oen5O4pUomjiM2ZS/e3IwWtUOykAdJTbMEByqlTt8vpqIf2tNZlnAuIYv8G4nWh6YC8khkzsc/ep/kXDygJEPvplwYLiGkO5lV2WCekh8GAeg8ujp8CmtgBt0+BTPgYceg8ujp8Cmtf84zvxVBNEJohNDav9AzT+k09qRMLJWiYJ7NZmsp8JYoLgvHkovIKuSVDjnvZjukTCwbMqAF/v/gn/5GzorX95KbC+DheyQ94C9VWQLImOshXRBsETFOHIOHpPXAxd0iYWDv4a0GNje92EHBgK0RJcR0ZJv8CgqvPdImFkzmLl1Ng+p4x1T4FNdI5QVXnuj40xua+pFS82+k+BQrjXSOUFV57pEu1SECrfI7+DeGoPZW8MSGx0iYWTRMI+lIiIpu+swkmrpPLQwBufh/KasmiYWTRMLJRg4Te29rbYXGAQ4vY1FEri0YyJzcHJ2l2jV8OoOl/R68yD6ji1u5IbFIAAP7/oIFZNaArVgNsAfFGGy4Bat2YYOoB0VIqcMpAslDlez0zBNPR2ILQDJI+AFlM2mljhKdiW7alJaK7eXvxtBnTPELfVrV90VP1CKOd0Sc1VpcMKWTZDuLg0E0+hKGGCiUJCVox0pQfMM8BuNvzcE32iqLrOHYBjXrD4bEcGxKuEHmmVY06D+0lKEOCgNleiP7kP8Ax/dYqoUtUXQSAqHPnEUhC11PSt85jRouFBWo7cIbRybKyk3AiYgYxqptiPqVJkD/msUqJTzv/lM9sporyvY62EPCo/DtnDXKU9YvloCWAobg4WFEeLEECkiGl97CdGrIQUX+xTw2jTJn4DL+ZfY84T7L5AJfqiLgDvc2F2CYECLj8pr0KfyoAALGpUYtAnBxr/8xTXFLksQEPmMXhaMxWd3YEVizH3LgzWAXnTxr1waysDPfjI2dmwmo91i5bAbxL9bNnsUSn7e8chWK27M8bUGCGXHFkPmHFKnqixSt+GBnuQSvKozPmV0kHixQBNcPPh9x7JByspFKYMKKVdOntouhxJhRj8kAZ5gLOsN570pwb8Av5bknZtXSgLGWvqTZpHppMvFk7hQUsIasG9Q4Mxxk3tvgWOwTvoxX8iAPx8IG7ZiDGbpia651wbkoNQ3A8nnsrM+JXYS1CottpNa/AjJDejfOnpkbwF4Ik2mnyXN8C10ra2anCqe4hEKS/77Vh6CuwDoFmLRcZyWeTUBJ/lfmv9l3f/xyBX8ri/8ri/8ri8jxrQFEOHGvZwbGUQBlBUkhTFoiC/2xYNJIb1RAfx4ZarT6IwFzb1pIzc9FJIWoLA+UzVBwb0jky2qyXBwmJT5sgCQTUv2lJVVIYipNpgWRJA6WdKtHr1HlZ+L5N60kRM9sk7P1bc7Prluyr2S7ZDN4OVpPqXHWtIbM/KoQSN4/CNluZTVPDBYxlNWdarH6lagpBfA1SkFLMy27MyMCtpgH8Qk8fbm1x+Fsy4ouGxfdzBFj/ChPdYtGMnqd2O6YKYVXr72LDmoWgXfSjG47txXb/WfwjyC91dAjT3hRFloWMbylH17+nk2JkLjHB0fnnbhXaUXDBLDxOXoVcwVb2JhhWy/VG8W9PmPzJaiTHYinkGwhcV8X3PQfVKCVMV7CW/HoX6IUcyxsU7SAuEmORW+oDJDATn0C2xp54al7Tz2Taei6/iTgI//hTPqZE1DiCg33CYultkbDTjotVMy/ZDynuEHjxP3Xtj6RGq75aICR6YYTiQtA0uAbNy+yg6AvIssCvfR6MhFG+tli4w4G/WvfYclvlgDOjyTYp8/P5TO8gFPTdfv/GoU29G+jT3nC6989Kzi+If7W0NzxpLDac3R1yPnnOl317XaK5f9nFUkEgvaGKS+CNNTnQtQHKPrX5dL6fyBrc7ti0zet7Ow5uqpQgVhyrjR/J3JHDXxjgyVGmmRmhHR71ivAmgB+ZRzVtlEoC5Ib2DayiuF2qeKHzt9Wy8Bq5QcMAK38JtWlR8f0mVWuihO5ml54+5+JDCEdJtrIssVlVG5VXMPLcOeZJG1YJ2EkzQHmQbs7Kpierv8Hv9YSqxVmS/iYudmn253Ox2ynSFBACwN4e6eYrwOTa6JF0LiWXDr4OIOwdNFVbFLKzAGfvB8f181Lvcqr34NPOWZ1xcyOJSKZjg+Qb7va2cAM5Mbzx5oSNXYDzrn97GOUYDH6q5u2rw/sPLDp5z1AMDI4uHnUo62z7qHvnDfV9EBTSG/niDL4J/2wJoc0lC92cfE3zsyNiFrPNEO/rdD0JepRdwUper5DFDxHw6xvpuj1z+dIylmUzKhVMXpS9rnhA197SLvaIvuuzr/mLYyh01j0UWYL1Fx4Fmiz+154doX9DFE475bnEJ9Yk9UWZbej2vd2O49oKBrrAGPt6ur9pSvw0dxPmSgATlBQroQtssCWXqkT+HL2B6bCUEOdc9njMsw9Gm7nOoBVO5Sz2xsWT7muT/DtT9jCINPbUTCLfk/QNjYy+JOt0qJtH6JiPowc6AAuhzPH+lrtH+/CIYPr9QouVLa0qvAbZuVjtyZOUsrQ6WhcbmcYHMMnEdNYYVtbsd9N8UdNrhqMzMpSt4VUpiCmrKXCmzJ0ROpqeI2FoTBIAieicDwVEWWGBOnTAhyuxNJSqC1J6NWbSBm4R+d6cckdtk0ktLvepQs3lSWxxybLm8iff1QXk6UlM+3i+mMMSL8ecmv4+hm4L+WWD9xhAQAKA6l0AiXlsIbb7soOSbbQpADWiwu1tB3bZ4/OIhoS8iw3ubtp6LbGfD6meUNCfbJw8iB0AIBXAr/wGQ0IYXlAAvuJ00vgmjN6ZolcNqCH8224bu8cyZR96ytQdSOzjmpmRZjZP5GMnOriRqACH5XbH1ld+lFZQFBTe7jP3S3xEqcsOYfSW+Q6RiMiOgy6oioWxgr7V76s1Jp3EbgqVwhTYSJ++HBgeXmxPHls65jQCzoDVehV7E4/ZJv6zwJl7Hng7evWELV0k30nJp1vQ68Sot2lHXB5oLqCwrqEgtQau4c9R5nbzYnINSI7N9z4Dahg76w4TDROhU0WVNmB8lXx4292ql4Y2XCxtKMTlKbpyYwR/cBxENYngxknY1HJOoE6FHr1opL6/Cvv/i1irjPWX1jvZrvWk0Ovot3CnMm64lMmqS2XquX9QUOoAC2HbkhiMhsSz0NQdXeS1lKLfid75UddvS+c0aL4wQSjPTMrkerLm3k7+z/KcZAXrJfOQmHCh/6AXbLr/TNRJJvSkarMIYctvA1CIrQ/b4Q2dnisNdVKF7+3vDCFqTj6oPLqjrGiB4pPQFVisu0UWEVvmvgWLslpq41DhBptwrotpXSz374U+/ELYts36uKYWIH8OAx/VQEQNSvGD8JpmoPJSSpVB9jwP86HgTS8kW0qrhjw8Q4A7d24ZaoBfPHBcFjOeaksSZI0u4eBLsZoE7gkYJ0HgNeAWr+4PXVOzrIh8PyQgn6R+R1RLYr6P4pmSzExoGSWfDDIs9gGyoaY9hl79rnCAxW3+mJ3zf/na3NH5Z80qlp5WG6Pnh7nRJo0sfYz9g5M4PV4XC/Mc16/i+qVK9KqQCGassZprubBj6HfCRRGV1aA5BT3KRBW1yjR6vPVWFmF8tPdGw8hK6qhZX3oYBGrd3DPyL1lQcEWRuJCxmdK6kJ/eYxWk+aiqex/0XaZ3GOiMsH/GT6oRmvOjintR2wznkx6KOQryHkIEKlkoPc5uYVl4IwNXJoB/UZWszGF04Scm1NLPblNsL6KxuOxd5FYcCh8BBQNXgO8XxS77EGNWB/fu2ToRBOLr/6TxcdG/H1HdK9vEq7Fr0pTK0klj8D2k/1i8b0AKr62m9Zca1ug1fzKWzhxidrSkIA9t6OJUlGz7jOb7BeCKXvnISJ4nzx37VJZoD0itxrSy0Ekbk9sBsIwrPQbevyCHf8XIUIZbOVcIp/2S82U1xj+NL3BxS0ZcQgU5+8Bstfp2kxFLFLC0j8bnTwRPXoqyOHkBo8QLbnWqOAxGjU/nNpxWX/SIewyodShKZN0vOrJy1NbjsrYUD6mrkRJyz2DeDob72/kBCDqOnAKjs7Jh+HK/lmh7joGw9fBmzgQTjHotd6OuMMwg4ECTU6zpb5kacq8i3V21T0ZqnF81RIffwl9GkQkDvXsQCFfaG84DNYgo1iKYp6WkWD6C5PwFvauMfgVXS4W1xDVaTjB6ZnA/x5OhgKyZOX4gIYtxRptkUdChtsJzUF9igp6Mvb/4rhG+4y/+c69qE+kiOaj61Afdd1xNEFulN9b0rVCiCe4qH9X1xlSchaiwRqb/aF2/ZINmsNN+VCcEQjxlsnxuallVpGZNvDFCeGC8CPZETkp5EdwgZNDQ/x+JMgF/rteuTFtc09Wav0NSKxsSQVWo2+dtbjdqEoQh1LzW5COOqLK7MBSWbSOMGgCSxp5uU4OVl+ty426FJiQ35l7Z21E/hnYc28gU2Zn9sSahhvEVySrDLElbirzgGT8Q0Vpqirm23vh46ZQfwJ1Rl970y1S/L+pn0Omr68xxoDMNio3K5QA5C/9yum2GUeV9YCC9ZVEyMl3b7hn9FlsEmO2kKc3DBogYcMppJ2fAa2kOVKNYr9q8cqGf+Y7+e7uObro4k6I8qDmomlvqRYm8QmeQ0vioKrdGcCVzDttzZwD4xqEeal1UPXn9rZm8EOgM/RQkEN8SpsH1XHErjogQ7bHW4rL5Sp6OMqepDYAWost0cA+XOJIzYhGBepALp8VEYrmOdX3VlVvrhHAWKs6z6R3km3S5q8MT7/UN9OaSyGDoeb6tAeX9HYHNUDoeb97e5IS9ZBJoYRZlWIAxLgCyIs8ib1W9dOaabP3rRWS8xVy4fRf0Llbr5uITS8Lbc86r8+2HZkYokYCerUGMC9SqmR2nBCTWLy1LmhkHQ6UgtrljX1RQEy/fkH6jqpeAdW5R+/KOvfUZqHiwybVXi3XuHK7M+aVAeiqFkJWqgyU6LT2vGKrwexMFv3Ap5wDN3/p+e6S6UXpFLgaSF30QzkfO3GC+WE8KnAX1+5XjLPNDWKuLoWxyVmRsVSUVDAWyeAh4tkNSvL5ZKhKVNSboQN5qNa8fU179AS2zFQG/LD/WFzu898fgryUSG/veA7CpHPclm1/Fjqu8HrDMKKm5mKem5G99JwRDU2h747y/IsxnNWXQjbTg70l9xh2jTTuDzorgGvheZImPg5/ieB+EgCrCXiSXRojsy8FR1/tQckiHBviyy9px7avazpWjGkdBUcdlo5ZoACNTbKjZTnB76yq6yIiJnR0oh/7xM/c3oXX7dGZADOLiRgus4pvitQsflbHwE9cKfBCW946ULkBqzj5Y1ADchI16RhSYtFfaC+V99/bhUOdjo6sO32x1kofDBS80KcJm4S4CZv3LqzK6wnlULYNVbNFGWQy4KerzB9cNDdCSX8kx6kD7ltP7gTOkjwxNNM33ehJiW2n+O5anRHmM4pRKRyoOGKJxWEhVcWEJwAdeiAAdAoJACKvZdXHfriaDeCfPKn0ETQNclhBrnAAABb1Q0XePXPpB5U1TVg+4OXhIAmtzFxVv5z3IZ0ao6DgDKaVM3A2c0MbXlHJ/hUzcaknEK58FB37VCfl8EEvwmkc+yUWh4lTXj/Ng9gUiyzQb0Dwdtf6Ho7XpCl8xXDW0WVUWYSlk5jYRqQ/NcFTCJ9JhfLmwtC5cU+CdRhREzH4Ajn0Rjl28MvfH47HSe0kU0m/0s5TbuuhbwvYNL/i4picRUhWKgzDtkggsqeIgiD8CaUMq6A5lJFnnaYa/McxkxMKjqRYkrrD1kWCL1T5zsoehgdOQ2Q4vmZy40CNSBef+ZuaE+CDdX5PuxD6GgOECTQQTQ5QnC5gqLcRRv3oPRrPrhFTyRtqvQuJsYd63ppwx6aUNkmul2aZYAJQOOdJgR8S0c6d/yJBmc7UsEOtCPsVh9tipVrwslEKsc5vMvTT1UQullKdkANuKYADXsvEw0SlD4fhF/31wB8sIhbfBuUnuceruZmDr4KGkZibT0E//z+f2agZgb/q8d2lwdIoZ4Jy0X+Oze/zCsObGuedNJA3aBCMgzHCHxLfPc9wNIzQ2U0y0/FZTpWuMvGirqlhjrz+CA7WZLYDJSukxJge3W43t6jCCHQpCMOwc9WOuOpTrzyqIKlmG/howZCNf1rG98dK99b2WePr/H2Z2DcGuvjxjDOVuBF9g2PA5u7t8vnLrt39Fel6n81D1V+FJZza/CJgISdEsDerbXvY/GhpB7pt23TkXm4Zx30ThIe78rhJotUUl9A2IfdjTlcaZFjE0wjKTMZriE5NlYNBAXEEV3u3H12jOoNUGUmxebKn77zxs1uqqLe/SstCAiv7ZK5p2YhEUw2M7yDwmulYQNWjWhfkZwX/9uSFt8pbXEAmZDm8ygG9kgHgmA8FhFsOBEt9SA5E1rIZl7vyKorWve1B5kG27aHQkEvb7Fbz/Kx6JLh52CpJRYOO46ljImKSz+w0asmQSFwSVoqiEZyXrl8VBZ8olXcKyHD8/6FRfsjTc9EVzZCCZ2ANRjAeKyFs56SPl7Uf+C/lRjSKjkH4p89uAJgFF6+scifU8P//9oj5OjsWAokQttOkcSBzLK2+TZsT314vaqVrBSgOCHH6V9y08VU6nlD63ZxDS2VoWcPMOGi3bqbzx8RcolsxX+s1G3z3WUEwJdwINEMSSKhPRY9Bz9yAbZF2MVWQO8N6Kp3qonigmZYmYp4UpLkflZMwBnjLtuLYoiOJKNd71BwDWTIR+L5JAld/7ZA+8EpPYtFVn39XT7dX1b5smyrG8mItOi4WajjWsfwghYjs0/tkxkB0v3fsb3ejsR6Qy6Q3Kqfu8nICCmRBWi9OykygqeSAX+Fusg34XBFXQfWAaVGA7wy+abr5HbPrfprjsgOKPoViqVab5sK0sRypg0g8GJ/dGYHuTk8U9x7c79tl32Z/QtVlCE7V3fXbBSWLnRo+S67PPXCokf1mT4jzJCoal+m4fCbdDUcEwTMzYlKmQ4vazZ1iGFQHdnMu7N1QoFFMNCjQ8FCibgI4EPqW+Jqh30Ujfo/lqymuxy9Eg2n95Mgorlgx4PdeA0U9DWJqQC4YCB/bRp+1KMnaxzn7VuOUfOVNe5s17bj4VXiuVADdzFRCN+YugKek4pLwamkYvsdWwmG4Q7gB/mscHuBQbVu1Tz+n+U/MlwxTUE9Uj1XKwZ3GARBZ+/xOL+VMKsmAHYbAvWMomVBlghGq7AG94Ivn3pYbGXfYjfRBbAW692GNafFbZ8kFMMDHmuC6m0UKJz20ulCOlXDggajyrL4N+lQbYvtnoiIVU1jSkoSFxRsbKz8MQhVXhlrIYAdvCxaE6TU7+URSglA0IrSaUmYG5AlIqr4e4JMH2lV2+e6mSknCmDErqei6iztyzlIfW5ouYZrzpaNPxw2rM0EK/pS1H5n4SnRLfnA5tnzaDn9GdkduMSZbauhTfBRLVs+u5crmAFn4T/Zb4s9byvqjyA5Nx7wIjz2eBQbaVm5lXXlImDsmGPiCGEBsUShkf+U3Q8Six2RHAuVbELzzxQJomvbyU8SHMnen1QNRNmts3HA83KiOdq08ixfrD7WNl7o6vpTmZWELsXr+vTh5LHfaOyugNmoNAifpDCqBi5zr+yMRI9kyEPwvG+AaEk5juHyh4McRVWQI/dWAAtRs/pa81AaS/cQXChpU+1shJWZa3TrEY8iCuY2tTZEomuOOBIPIB+REL73gJDRuhggKOA8zQwUqXI59XRTM1g11ZGvCVgd4NQHVLtOkHAq1hK4os2p0m04cCvH07Mpjoh0Hsc/nUANbxMXjTXYvMJFAXDdPb2p58rwqVUNMDkWl/Ky/IkJXLhKuJGaooAhBoue0ZIexptjBkKloyZZ2r/o11wr6A2ReKqAudtDgZr2691zRM+A0bienadrAqqY4w9x/PIYDdHTvyrlLr3nAUndykBBK+aAXWsw8AR+oEXoAOJwEiSaS7Ev3BaV40g4XXcGxt2WXavUwxPtv1FRZ9IGdll2t7odlT58WEONxCAGIvTwnFGC8BybkZik9Bt98iHEDGq0IGbZXoAIpozScwRLk1W6Aslneeu6SSt2qEgyu+kcnEsUbqmsebVUROLCA9xB78SareElaeJxTfPceZSYHueAR6mLYAArPo0EZBM4wlAP6/cr1ffxrAfqnc86457PJv39iOOLOkCQJUzZDWlKmDwe7+fXakXEX8MhhsDJSq9wpAGbXxpak88pZJQGo89HjbI8kkZug15nNJpL7mHl86VUdDe8IYBTPEL3A5dSZcE9ZRZHjtvONLXQphCVB4jkfcM0erBLeJ/9FUtMQItLNg7KCOafeofMPAa8b43cdVFZuc1u0bQY5W0O3WmZJ75PfQAOMaeJdMBMs8c1ttElD2aHd96Ocs5Znu8DgL5zv1JWd7jjVgxwBlHQtPYEjt0cdSltgNn8ZaPf3bzwX4Q1LdyAOs3yIAbBLkKV+lCGmhs6KlDCcnxFAUVKwo7t3guxM/1ym/DhRGoIbOJxVEY8pSamqeMGLQvDGdk/e6iy+2ZQokLF4aWxi9VnSDFrOsqd2nmuH0pha9pFwQDpj4C5FgS6uXqhiLDgc2aeVieuINSQNWL6+JX/lq0fmPtcgPGt8B0ALZcm+AAAA=" width="376" height="603" class="img_ev3q"></p>
<p>Buffer Pool の<strong>デフォルトの総サイズは 128M</strong> で、<code>innodb-buffer-pool-size = xxxxxxx</code> によって必要に応じて変更できます（公式は、専用のデータベースサーバーであれば物理メモリの大部分を割り当ててよいと推奨しています）。Buffer Pool は New Sublist と Old Sublist に分かれています。JVM の新生代と老年代を思い出しませんか？考え方は確かに似ています。New Sublist には最も頻繁にアクティブなデータが、Old Sublist にはあまりアクセスされないデータが格納され、Old Sublist はデフォルトで BufferPool の 3/8 を占めます。</p>
<p>メモリ資源は貴重なので、ディスク上のすべてのテーブルデータをメモリに載せることは不可能であり、一定の淘汰戦略が必要になります。BufferPool は LRU アルゴリズムに MySQL 独自の変種ロジックを加えてメモリデータの淘汰を実現しており、ホット・コールドデータ処理とも呼ばれます。頻繁にアクセスされるデータは New Sublist 領域へ移動し、あまりアクセスされないデータは Old Sublist へ移動します。メモリ使用量が高くなりすぎたときは、Old Sublist のデータが優先的に淘汰されます。新しく読み込まれたページは Old Sublist の Head 位置に置かれ、そのページが頻繁にアクセスされれば New Sublist へ移動し、そうでなければ Old Sublist に留まり続けます。</p>
<p>BufferPool の内部には ChangeBuffer と呼ばれる領域もあります。図のとおりです：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-image-13-7519334f554b72ba0b825625b2d25334.webp" width="581" height="408" class="img_ev3q"></p>
<p>ChangeBuffer は、対象ページが BufferPool 内に存在しない DML 変更をキャッシュするためのものです。変更のたびにページをディスクから読み込むことを避け、対応するページが後でメモリに読み込まれたときにこれらの変更をマージすることで、ランダム IO を削減します。デフォルトでは ChangeBuffer は BufferPool の 25% を占め、最大 50% まで調整できます。</p>
<p>比較的完全な BufferPool の構造図（圧縮リストなど、描かれていないリストもいくつかあります）：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-image-17-f0b9ba6de43eca5efd0e508bef36c291.webp" width="1600" height="763" class="img_ev3q"></p>
<p>整理を始める前に、いくつかの前提概念を押さえておきましょう：</p>
<ul>
<li class=""><strong>データページ</strong>：すでにディスクのデータが読み込まれたページ。</li>
<li class=""><strong>ダーティページ</strong>：変更操作が行われたページで、ディスクへ書き戻す必要があるもの。</li>
<li class=""><strong>空きページ</strong>：データが読み込まれていないページ。</li>
<li class=""><strong>制御ブロック</strong>：各管理リストのノードに格納される BufferPool ページへのポインタ。</li>
<li class=""><strong>バッファページ</strong>：BufferPool 内のページの総称（ページサイズはすべて 16KB）。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="複数の-buffer-pool-instance-の設定">複数の Buffer Pool Instance の設定<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#%E8%A4%87%E6%95%B0%E3%81%AE-buffer-pool-instance-%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="複数の Buffer Pool Instance の設定 への直接リンク" title="複数の Buffer Pool Instance の設定 への直接リンク" translate="no">​</a></h2>
<p>MySQL サーバーの起動時に、OS へ BufferPool のメモリ領域を要求します。マルチスレッドの場面では、各リストにロック処理が必要になるため、BufferPool が非常に大きく、かつ並行アクセス量が非常に多いとき、単一の BufferPool は処理速度に悪影響を与えます。そこで BufferPool を複数の小さな BufferPool、すなわち Buffer Pool Instance に分割します。各 Instance はそれぞれ独立してメモリ領域を確保し、独立してリストを管理するため、マルチスレッドでアクセスしても互いに影響しません。インスタンス数はパラメータで変更できます：</p>
<div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># buffer pool インスタンスを 2 つ作成する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">innodb_buffer_pool_instances</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">2</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 各インスタンスが占有するメモリ = 総サイズをインスタンス数で割った値</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># innodb_buffer_pool_size / innodb_buffer_pool_instances</span><br></div></code></pre></div></div>
<p>複数のインスタンスの作成・管理自体にもオーバーヘッドがあるため、MySQL 公式では、innodb_buffer_pool_size が 1G 未満の場合はデフォルトでインスタンスは 1 つだけとなり、複数設定しても無効になると規定されています。1G を超える場合にのみ、複数インスタンスの設定が推奨されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="buffer-pool-によるページ管理">Buffer Pool によるページ管理<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#buffer-pool-%E3%81%AB%E3%82%88%E3%82%8B%E3%83%9A%E3%83%BC%E3%82%B8%E7%AE%A1%E7%90%86" class="hash-link" aria-label="Buffer Pool によるページ管理 への直接リンク" title="Buffer Pool によるページ管理 への直接リンク" translate="no">​</a></h2>
<p>データページは BufferPool の中に無秩序に置かれているわけではなく、ハッシュテーブルといくつかのリストによって組織化され、前述の各種操作をサポートできるようになっています。リストのノードの中身は Page Descriptor です：</p>
<ol>
<li class=""><strong>page hash（高速アクセス）</strong>：すべてのページは page hash と呼ばれる 1 つのハッシュテーブルで組織化されており、buffer pool 内のページへ高速にアクセスするために使われます。page hash のキーは page id です。</li>
<li class=""><strong>free list（ページの割り当て）</strong>：ページを buffer pool に読み込むときは、page descriptor として buf_block_t を割り当てる必要があります。空いていて未使用の buf_block_t をどうやって見つけるか？それらはすべて free list に繋がれています。</li>
<li class=""><strong>LRU list（ページの淘汰）</strong>：buffer pool の容量には限りがあるため、ディスクから読み込んだページを格納する空きがなくなったら、既存のページをいくつか淘汰する必要があります。ここで使われるのが LRU アルゴリズムです。</li>
<li class=""><strong>flush list（ダーティページの書き戻し）</strong>：ページがダーティページになったら、page cleaner スレッドが定期的にディスクへ書き戻す必要があります。これらのダーティページはすべて flush list 上にあります。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="free-list-によるリスト管理">Free List によるリスト管理<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#free-list-%E3%81%AB%E3%82%88%E3%82%8B%E3%83%AA%E3%82%B9%E3%83%88%E7%AE%A1%E7%90%86" class="hash-link" aria-label="Free List によるリスト管理 への直接リンク" title="Free List によるリスト管理 への直接リンク" translate="no">​</a></h2>
<p>MySQL サーバーの初回起動時には、BufferPool の初期化を行う必要があります。まず OS へ BufferPool のメモリ領域を要求し、それを複数の制御ブロックとバッファページのペアに分割します。この時点では実際のディスクページはまだ BufferPool にキャッシュされていません（まだ使われていないため）。その後プログラムの実行に伴って、ディスク上のページが次々とキャッシュされていきます。ここで問題になるのが、ディスクからページを 1 つ BufferPool に読み込むとき、どのバッファページの位置に置けばよいのか？言い換えれば、BufferPool 内のどのバッファページが空きで、どれがすでに使用中かをどう区別するのか？ということです。</p>
<p>どのバッファページが利用可能かをどこかに記録しておくのが最善です。ここでバッファページに対応する制御ブロックが大いに役立ちます。すべての空きバッファページに対応する制御ブロックをノードとして 1 つのリストに繋げます。このリストを Free リスト（空きリスト）と呼びます。初期化が完了したばかりの BufferPool ではすべてのバッファページが空きなので、各バッファページに対応する制御ブロックはすべて Free リストに追加されます。</p>
<p>Free リストをより適切に管理するために、ここでは特別にベースノードが定義されています。リストの先頭ノードのアドレス、末尾ノードのアドレス、現在のリスト内のノード数などの情報を含みます。注意すべき点として、ベースノードが占有するメモリは BufferPool 用に確保された大きな連続メモリの中には含まれず、別途確保されたメモリ領域です。</p>
<p>Free リストがあれば話は簡単です。ディスクからページを 1 つ BufferPool に読み込む必要が生じるたびに、Free リストから空きバッファページを 1 つ取り出し、そのバッファページに対応する制御ブロックの情報（そのページが属するテーブルスペースやページ番号などの情報）を書き込み、その制御ブロックを Free リストから外して、そのバッファページが使用中であることを表します。ここではっきりさせておきたいのは、リストから実際に取得するのは制御ブロックであり、制御ブロックを通じて初めて本当のページにアクセスできるということです。同様に、「BufferPool 内のバッファページを走査する」というのは実際には「各バッファページに対応する制御ブロックを走査する」ことなのです。</p>
<p>まとめ：Free リストさえきちんと管理していれば、BufferPool 内のどのページが空きかが分かります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="page-hash-によるバッファページのハッシュ処理">Page Hash によるバッファページのハッシュ処理<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#page-hash-%E3%81%AB%E3%82%88%E3%82%8B%E3%83%90%E3%83%83%E3%83%95%E3%82%A1%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E3%83%8F%E3%83%83%E3%82%B7%E3%83%A5%E5%87%A6%E7%90%86" class="hash-link" aria-label="Page Hash によるバッファページのハッシュ処理 への直接リンク" title="Page Hash によるバッファページのハッシュ処理 への直接リンク" translate="no">​</a></h2>
<p>あるページのデータにアクセスする必要があるとき、そのページはディスクから BufferPool に読み込まれます。すでに BufferPool にあれば、そのまま使えばよいのです。ここで問題になるのが、そのページが BufferPool にあるかどうかをどうやって知るのか？まさかバッファページを順番に走査するのでしょうか？BufferPool にはバッファページが大量にあるのに、全部走査していたら大変です。</p>
<p>振り返って考えてみると、私たちは実はテーブルスペース番号 + ページ番号でページを特定しています。つまりテーブルスペース番号 + ページ番号が Key で、バッファページの制御ブロックが対応する Value です。Key から Value を高速に見つけるには、当然ハッシュテーブルを使います。</p>
<p>そこで、テーブルスペース番号 + ページ番号を Key、バッファページ制御ブロックのアドレスを Value としてハッシュテーブルを作成できます。あるページのデータにアクセスする必要があるときは、まずテーブルスペース番号 + ページ番号でハッシュテーブルを検索します。対応するバッファページがあればそのまま使い、なければ Free リストから空きバッファページを 1 つ選び、ディスク上の対応するページをそのバッファページの位置に読み込みます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lru-list-によるリスト管理">LRU List によるリスト管理<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#lru-list-%E3%81%AB%E3%82%88%E3%82%8B%E3%83%AA%E3%82%B9%E3%83%88%E7%AE%A1%E7%90%86" class="hash-link" aria-label="LRU List によるリスト管理 への直接リンク" title="LRU List によるリスト管理 への直接リンク" translate="no">​</a></h2>
<p>LRU リストは BufferPool バッファ領域に対する管理方法の一つです。考えてみてください。バッファ領域にデータページを読み込み続ければ、いつかは満杯になります。そのときには不要なデータページを解放する仕組みが必要になります。その仕組みが LRU アルゴリズム（Least Recently Used、最近最も使われていないものから淘汰するアルゴリズム）です。</p>
<p>このアルゴリズムは 1 本のリストとして捉えることができ、このリストは 2 つの部分に分かれています（MySQL の変種です）。一方は使用頻度が非常に高いバッファページ、つまりホットデータを格納する部分で、Young 領域（New Sublist）と呼ばれます。もう一方は使用頻度の低いバッファページ、つまりコールドデータを格納する部分で、Old 領域（Old Sublist）と呼ばれます。InnoDB は LRU リストを比率で 2 つに分割しており、パラメータ <strong>innodb_old_blocks_pct</strong> で Old 領域の占める比率を確認できます。</p>
<p>この 2 つの領域があることで、InnoDB の設計者は BufferPool のヒット率の状況に応じた最適化ができるようになりました：</p>
<ul>
<li class="">先読み（プリフェッチ）されたページがその後アクセスされない可能性に対する最適化。設計者は、ディスク上のあるページが初めて BufferPool のバッファページに読み込まれたとき、そのバッファページに対応する制御ブロックを Old 領域の先頭に置くと定めました。こうすることで、BufferPool に先読みされたものの後続アクセスがないページは徐々に Old 領域から追い出され、Young 領域内の使用頻度の高いバッファページに影響を与えません。</li>
<li class="">フルテーブルスキャン時に短時間で大量の低頻度ページにアクセスすることに対する最適化。フルテーブルスキャンでは、初回読み込みのページは Old 領域の先頭に置かれますが、その後すぐにアクセスされ、アクセスのたびにそのページが Young 領域の先頭に置かれるため、依然として使用頻度の高いページを押し出してしまいます。設計者は、フルテーブルスキャンの過程では、あるページに多くのレコードがあり、1 レコード読むごとに 1 回ページアクセスと数えたとしても、この過程にかかる時間は非常に短いと考えました。そこで次のように定めました。Old 領域にあるバッファページへの最初のアクセス時に、対応する制御ブロックにアクセス時刻を記録します。その後のアクセス時刻が最初のアクセス時刻から一定の間隔以内であれば、そのページは Old 領域から Young 領域の先頭へ移動せず、間隔を超えた場合にのみ移動します。この間隔時間はシステム変数 innodb_old_blocks_time で制御されます。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="flush-list-によるリスト管理">Flush List によるリスト管理<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#flush-list-%E3%81%AB%E3%82%88%E3%82%8B%E3%83%AA%E3%82%B9%E3%83%88%E7%AE%A1%E7%90%86" class="hash-link" aria-label="Flush List によるリスト管理 への直接リンク" title="Flush List によるリスト管理 への直接リンク" translate="no">​</a></h2>
<p>BufferPool 内のあるバッファページのデータを変更すると、ディスク上のページと不一致になります。このようなバッファページをダーティページと呼びます。もちろん、変更のたびに即座にディスク上の対応するページへフラッシュすることもできますが、頻繁なディスク書き込みは性能を著しく悪化させます。そのため、バッファページを変更するたびに急いでフラッシュするのではなく、将来のある時点でフラッシュします。</p>
<p>しかし即座にフラッシュしないなら、後でフラッシュするときに BufferPool 内のどのページがダーティページで、どのページが一度も変更されていないページなのかをどうやって知るのでしょうか？そこで、ダーティページを格納するリストをもう 1 本作らざるを得ません。変更されたバッファページに対応する制御ブロックは、すべてノードとしてこのリストに追加されます。これらのノードに対応するバッファページはすべてディスクへフラッシュする必要があるため、Flush リストと呼ばれます。Flush リストの構造は Free リストとほぼ同じです。また、あるバッファページが空きであれば、それは決してダーティページではありません。ダーティページであれば、決して空きではありません。つまり、あるバッファページに対応する制御ブロックが Free リストのノードであると同時に Flush リストのノードであることはあり得ず、どちらか一方のリストにしか存在できません。</p>
<p><strong>まとめ</strong>：Free リストはバッファ領域内のすべての空きページからなるリスト、Flush リストは変更されたすべてのページ（ダーティページ）からなるリストと簡単に理解できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ダーティページのフラッシュ機構">ダーティページのフラッシュ機構<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#%E3%83%80%E3%83%BC%E3%83%86%E3%82%A3%E3%83%9A%E3%83%BC%E3%82%B8%E3%81%AE%E3%83%95%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E6%A9%9F%E6%A7%8B" class="hash-link" aria-label="ダーティページのフラッシュ機構 への直接リンク" title="ダーティページのフラッシュ機構 への直接リンク" translate="no">​</a></h2>
<p>ダーティページは以下のような状況でディスクへフラッシュされます：</p>
<p>1）Redo Log に書き込む余地がなくなったとき。Redo Log は永続化の保障として、更新操作のたびに必ず記録されます。Redo Log に書き込めなくなったら、ページをフラッシュしてデータをディスクへ同期し、Redo Log の CheckPoint ポインタを移動して新しい領域を空ける必要があります。このときメモリに余裕があれば、フラッシュしたページをメモリから淘汰する必要はありません。</p>
<p>2）メモリにデータページを格納する余地がなくなったとき。検索操作はディスク上のデータページをメモリへ読み込んでから返します。メモリに余地がなくなったら、最も長く使われていないデータページを淘汰しなければなりません。淘汰されるのがダーティページであれば、まずディスクへフラッシュしてから淘汰する必要があります。</p>
<p>3）データベースがアイドル状態のとき。データベースが現在アイドル状態にあると判断した場合、ダーティページのフラッシュを行います。このときメモリに余裕があれば、フラッシュ後に淘汰は行いません。</p>
<p>4）データベースの正常終了時。シャットダウン時にもページのフラッシュが行われ、それまでの変更がディスクへ永続化されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/16/mysql-buffer-pool#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>BufferPool は、InnoDB がメモリで IO を代替する中核的な設計です。データは 16KB の Page 単位でメモリにキャッシュされ、DML 操作はまずメモリを変更し、その後非同期でディスクへ書き込まれます。並行処理時のロック競合を減らすために複数の Instance に分割され、内部では Page Hash によって高速な位置特定を実現し、Free・LRU・Flush の 3 本のリストがそれぞれ空きページの割り当て、ホット・コールドデータの淘汰、ダーティページの書き戻しを担います。LRU の New/Old 分割と innodb_old_blocks_time の時間ウィンドウは、先読みとフルテーブルスキャンによるホットデータの押し流し問題を解決しています。これらのリストの連携の仕方を理解すれば、BufferPool 全体の動作メカニズムもはっきり見えてくるはずです。</p>]]></content:encoded>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[MySQL の実行チェーン]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing</guid>
            <pubDate>Tue, 15 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[1 本の SQL が MySQL 内部でたどる完全な実行チェーンを整理します。コネクタからエグゼキュータまで、Buffer Pool がディスク IO をどう減らすか、そして Redo Log・Bin Log・Undo Log それぞれの役割と 2 フェーズコミットについて解説します。]]></description>
            <content:encoded><![CDATA[<p>1 本の SQL がクライアントから送信され、データが実際にディスクへ書き込まれるまでには、コネクタ、パーサ、オプティマイザ、エグゼキュータを経由し、さらに Buffer Pool と 3 つのログの連携が欠かせません。この記事では、その実行チェーン全体を一本の流れとしてつなげて解説します。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-image-11-3b98bcb2a74b924e1f92f8c0c2e9a703.webp" width="1368" height="922" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コネクタからエグゼキュータまで">コネクタからエグゼキュータまで<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#%E3%82%B3%E3%83%8D%E3%82%AF%E3%82%BF%E3%81%8B%E3%82%89%E3%82%A8%E3%82%B0%E3%82%BC%E3%82%AD%E3%83%A5%E3%83%BC%E3%82%BF%E3%81%BE%E3%81%A7" class="hash-link" aria-label="コネクタからエグゼキュータまで への直接リンク" title="コネクタからエグゼキュータまで への直接リンク" translate="no">​</a></h2>
<p>クライアントの SQL 文が MySQL サーバーに送信されると、まず接続を確立する必要があります。コネクタはクライアントと接続を行い、ユーザーの認証情報と権限情報を検証します。権限チェックを通過すると、SQL パーサが SQL 文の意味解析を行います（SQL 文が正しいか、パースできるかをチェックします）。解析が完了すると、SQL の実行過程に対する最適化が行われます。たとえば不要な検索条件 1=1 を取り除いたり、最適なインデックスの選択や Where 条件のフィールドの並び替えを行ったりします。</p>
<p>その後、エグゼキュータがこの SQL 文を実行します。エグゼキュータは、データベースが使用しているストレージエンジンに応じて SQL を実行します。MySQL は設計上 2 つの層、すなわち Server 層とストレージエンジン層に分かれています。こうすることの利点は疎結合であることで、シーンに応じて適切なストレージエンジンを選択できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="buffer-pool">Buffer Pool<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#buffer-pool" class="hash-link" aria-label="Buffer Pool への直接リンク" title="Buffer Pool への直接リンク" translate="no">​</a></h2>
<p>よく使われる InnoDB ストレージエンジンは、ページ（16KB）単位でストレージ空間を管理しています。あらゆる CRUD 操作は最終的にページ全体を操作することになり、ページ全体を Buffer Pool にロードし、すべてのデータ操作は Buffer Pool 内で完結します。Buffer Pool は通常、サーバーメモリの 70%〜80% 程度に設定します。こうすることでデータ操作がメモリ上で直接実行され、データ操作の効率が向上します（もし DML 文のたびに IO 操作が必要になると、データベースのディスクはすぐにボトルネックになってしまいます。1 件のデータを変更するには、まずディスクから読み込み、それからディスクへフラッシュするので 2 回の IO、つまり 1 回の読み取りと 1 回の書き込みが発生します）。</p>
<p>例を挙げて説明しましょう。まず Buffer Pool がないと仮定します。user テーブルには 1 件のレコードだけがあり、そのレコードの age = 1 で、以下の 3 本の SQL を実行する必要があるとします。</p>
<p>トランザクション A：update user set age = 2（1 読み 1 書きで 2 回の IO）<br>
トランザクション B：update user set age = 3（1 読み 1 書きで 2 回の IO）<br>
トランザクション C：update user set age = 4（1 読み 1 書きで 2 回の IO）</p>
<p>毎回ディスクからデータをメモリに読み込んで変更し、それからディスクへフラッシュする必要があるため、合計で 6 回の IO が発生します。</p>
<p>Buffer Pool を導入すると、次のようになります。</p>
<p>トランザクション A：update user set age = 2（Buffer Pool に読み込んで変更、読み取り IO 1 回）<br>
トランザクション B：update user set age = 3（Buffer Pool 内のデータを変更、IO 0 回）<br>
トランザクション C：update user set age = 4（Buffer Pool 内のデータを変更しディスクへフラッシュ、書き込み IO 1 回）</p>
<p>データページのロードが必要なのは最初の 1 回だけで、その後の操作はすべてメモリ上で直接完結します。Buffer Pool を導入すると、この一連の操作は合計 2 回の IO で済みます。（ここでは基本的なロジックを簡単に示しただけで、実際の最適化ロジックはこれほど単純ではありません。とはいえ全体として、Buffer Pool はディスク上のデータをメモリにマッピングして操作することで、ディスク IO の回数を減らす仕組みです。ただし Buffer Pool はメモリ上でデータ変更操作を行うため、クラッシュや停電が発生するとデータが簡単に失われてしまいます。）</p>
<p>Buffer Pool の情報を確認するには：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 出力には Buffer Pool のサイズ、ヒット率、ダーティページ数などの情報が含まれる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">SHOW</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">ENGINE</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">INNODB</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">STATUS</span><br></div></code></pre></div></div>
<p>そこで、いくつかの非常に重要なログが補助として必要になります。Redo Log（リドゥログ）、Bin Log（バイナリログ）、Undo Log（アンドゥログ）です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redo-logリドゥログ">Redo Log（リドゥログ）<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#redo-log%E3%83%AA%E3%83%89%E3%82%A5%E3%83%AD%E3%82%B0" class="hash-link" aria-label="Redo Log（リドゥログ） への直接リンク" title="Redo Log（リドゥログ） への直接リンク" translate="no">​</a></h2>
<p>データを変更した後、変更後の値を先にディスク上の Redo Log に記録しておくことで、突然停電して Buffer Pool 内のデータがすべて失われても、電源復旧時に Redo Log をもとに Buffer Pool を復元できます。これにより、Buffer Pool によるメモリの高効率性を活かしつつ、データが失われないことも保証されます。Redo Log のフラッシュには 3 つの戦略があります。</p>
<ul>
<li class="">0 に設定：トランザクションのコミットごとにはフラッシュ操作を行わない（システムデフォルトでは Master Thread が 1 秒ごとにリドゥログの同期を行う）</li>
<li class="">1 に設定：トランザクションをコミットするたびに同期フラッシュ操作を行う。これが最も安全な設定で、このタイミングでクラッシュした場合はトランザクションの commit が成功していないことを意味するため、復元すべきデータもない（デフォルト）</li>
<li class="">2 に設定：トランザクションのコミットごとに Redo Log Buffer の内容を Page Cache に書き込むだけで同期は行わず、いつディスクへ同期するかはファイルシステム（OS）に任せる</li>
</ul>
<p>ここでの Redo Log のフラッシュはシーケンシャル書き込みの WAL（Write-ahead logging）で、日本語では「先行書き込みログ」と訳されます。ランダムなディスク書き込みに比べて効率が大幅に向上しており、Kafka や RocketMQ などの MQ もシーケンシャルなログ書き込みを採用してディスクの書き込み性能を高めています。ディスクのシーケンシャル書き込みは十分高効率とはいえ、メモリ操作とはまだ差があります。Redo Log の効率をさらに高めるため、メモリ上に Change Buffer（デフォルトサイズは 16MB、Buffer Pool 内に配置され、占有割合をパーセンテージで設定可能）を設けてメモリ上で記録を行い、トランザクションがコミットされたときにのみディスクへのシーケンシャル書き込みを行うようになっています。</p>
<p>Change Buffer は多数の Block に分かれており、各 Block のサイズは 512KB です。1 つのトランザクションが生成するすべての Redo Log は 1 つの Group と呼ばれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bin-logバイナリログ">Bin Log（バイナリログ）<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#bin-log%E3%83%90%E3%82%A4%E3%83%8A%E3%83%AA%E3%83%AD%E3%82%B0" class="hash-link" aria-label="Bin Log（バイナリログ） への直接リンク" title="Bin Log（バイナリログ） への直接リンク" translate="no">​</a></h2>
<p>Bin Log は、データベースのすべてのテーブル構造変更（CREATE、ALTER TABLE など）およびテーブルデータの変更（INSERT、UPDATE、DELETE など）を記録するバイナリログです。SELECT や SHOW といった操作は記録されません。これらの操作はデータ自体を変更しないためです。ただし、汎用クエリログ（general log）を参照すれば、MySQL が実行したすべての文を確認できます。</p>
<p>注意すべき点として、<strong>update 操作がデータの変化を引き起こさなかった場合でも、Bin Log には記録されます</strong>。</p>
<p>Redo Log と同様に、Bin Log にも独自のフラッシュ戦略があり、sync_binlog パラメータで制御します。</p>
<ul>
<li class="">0 に設定：トランザクションのコミット前に Bin Log を OS Cache へ書き込み、いつディスクへフラッシュするかはオペレーティングシステムに任せる</li>
<li class="">1 に設定：同期的なディスク書き込み方式で Bin Log を書き込み、OS Cache を使って Bin Log を書き込まない</li>
<li class="">n に設定：n 回のトランザクションコミットが行われるたびに Fsync を 1 回呼び出し、OS Cache 内の Bin Log を強制的にディスクへフラッシュする</li>
</ul>
<p>Bin Log にはよく使われるユースケースが 2 つあります。</p>
<ul>
<li class="">レプリケーション：MySQL Replication では Master 側で Bin Log を有効にし、Master が自身のバイナリログを Slave に伝達することで、Master-Slave 間のデータ一貫性を実現します。</li>
<li class="">データ復旧：mysqlbinlog ツールを使ってデータを復旧します。</li>
</ul>
<p>さて、ここで疑問が生まれます。Bin Log も Redo Log も変更後の値を記録するものですが、両者は何が違うのでしょうか？Redo Log があるのに、なぜ Bin Log も必要なのでしょうか？</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="bin-log-と-redo-log-の違い">Bin Log と Redo Log の違い<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#bin-log-%E3%81%A8-redo-log-%E3%81%AE%E9%81%95%E3%81%84" class="hash-link" aria-label="Bin Log と Redo Log の違い への直接リンク" title="Bin Log と Redo Log の違い への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">Bin Log は MySQL Server 層に属し、Redo Log は Engine 層に属する</li>
<li class="">Bin Log はすべてのエンジンで使用できるが、Redo Log は InnoDB 固有のもの</li>
<li class="">Bin Log は論理的な操作を記録し、Redo Log は更新された内容を記録する</li>
<li class="">Bin Log は追記書き込みで複数のファイルを形成し、Redo Log は固定サイズの数ファイルを循環的に書き込む</li>
<li class="">トランザクション実行中は複数の操作が継続的に Redo Log に書き込まれ、最後のコミット時にはじめて Bin Log に書き込まれる</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="undo-logアンドゥログ">Undo Log（アンドゥログ）<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#undo-log%E3%82%A2%E3%83%B3%E3%83%89%E3%82%A5%E3%83%AD%E3%82%B0" class="hash-link" aria-label="Undo Log（アンドゥログ） への直接リンク" title="Undo Log（アンドゥログ） への直接リンク" translate="no">​</a></h2>
<p>データを変更するとき、Redo Log を記録するだけでなく、対応する Undo Log も記録されます。何らかの理由でトランザクションが失敗したり、ロールバックされたりした場合、この Undo Log を使ってロールバックできます。Undo Log はセグメント（segment）方式で記録されており、各 Undo Log 操作は記録時に 1 つの Undo Log Segment を占有します。役割は、トランザクション発生前のデータのバージョンを保存することで、ロールバックに使えるだけでなく、多版型同時実行制御（MVCC）における読み取りも提供できます。注意すべき点として、Undo Log はデフォルトでグローバルテーブルスペースに格納されています。Undo Log も MySQL のテーブルに記録されていると簡単に理解してよく、Undo Log を 1 件挿入するのは通常のデータを 1 件挿入するのと似たようなものです。つまり、Undo Log を書き込む過程でも同様に Redo Log への書き込みが必要になるということです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実行チェーン">実行チェーン<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#%E5%AE%9F%E8%A1%8C%E3%83%81%E3%82%A7%E3%83%BC%E3%83%B3" class="hash-link" aria-label="実行チェーン への直接リンク" title="実行チェーン への直接リンク" translate="no">​</a></h2>
<p>以上の基礎知識を踏まえて、SQL 全体の実行チェーンを見てみましょう。</p>
<p>0、ここではコネクタ、パーサ、オプティマイザのステップは省略します（すでに上で説明しました）。</p>
<p>1、まず Undo Log がこのトランザクションのロールバック情報（トランザクション ID、ロールバックポインタ）を記録します。これはトランザクションのロールバックと、それに対応するトランザクション分離の MVCC のためです。</p>
<p>2、次に Buffer Pool 内のデータを操作します。操作と同時に、データ操作情報を Change Buffer にも追加する必要があります。もしこのデータページがまだメモリ上にない場合、<strong>データの一貫性に影響しない</strong>ことを前提に、InnoDB はこれらの更新操作を先に Change Buffer にキャッシュします。こうすることで、ディスクからこのデータページを読み込む必要がなくなります。次回のクエリでこのデータページへのアクセスが必要になったとき、データページをメモリに読み込み、それから Change Buffer 内のこのページに関する操作を実行します。（ただし、状況によっては先にデータページをメモリに読み込んでから操作する必要がある場合もあります）</p>
<p>3、データ変更の操作情報を Change Buffer に追加した後、操作情報は同期的に Redo Log へフラッシュして永続化する必要があります。これはクラッシュによる操作データの喪失を防ぐためです。（ここでいう同期とは、トランザクションがコミットされた後にはじめてディスクへのフラッシュを考えるという意味です。トランザクションがコミットされていなければフラッシュする意味がありません。MySQL のデフォルト設定では Redo Log は同期フラッシュです。非同期フラッシュに変更すると、突然のクラッシュや停電時にデータ喪失が発生し、メモリ内の Change Buffer に記録された情報はすべて失われます）</p>
<p>4、具体的な SQL 操作は Bin Log Cache にも追加する必要があります。ただし Bin Log Cache に記録されるのは論理的な操作情報（実際に実行された DML・DDL・DQL 文）だけで、データ変更情報は記録されません。Bin Log Cache は各スレッドがプライベートに持つメモリ空間（デフォルト 32K）で、トランザクション全体が生成する Bin Log Event を格納するメモリサイズを指定します。大きなトランザクションの場合、このパラメータの設定値を超える可能性が高く、その場合は Bin Log 一時ファイルを使って格納する必要があります。</p>
<p>5、クライアントがトランザクションをコミットすると、2 フェーズコミットが行われます。まず Redo Log のログレコードをフラッシュし、Redo Log に記録された状態を prepare に変更します。次に Bin Log のログレコードをフラッシュし、Bin Log のフラッシュが成功したら、対応する Redo Log の状態を prepare から commit に変更します。両者のログの一貫性を必ず保たなければならないためで、2 つのログのデータが一致しないと、マスター・スレーブ間のデータ不整合をはじめとするさまざまな問題が発生します。この時点でクライアントにトランザクションのコミット成功を応答できますが、このときデータ（ダーティページ）はまだディスクに書き込まれておらず、依然として Buffer Pool に保存されています。対応するログの記録がすべて成功しただけの状態です。</p>
<p>6、MySQL サーバーは、メモリ内の Buffer Pool のダーティページデータを非同期でフラッシュします（<strong>適切なフラッシュのタイミング</strong>）。ダーティページのデータがディスクに書き込まれてはじめて、データが本当にディスクに永続化されたことになります。非同期フラッシュ中にクラッシュや停電が発生しても、起動後に InnoDB は Redo Log をもとに、以前の Buffer Pool のデータをやり直す（redo する）ことができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="適切なフラッシュのタイミング">適切なフラッシュのタイミング<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#%E9%81%A9%E5%88%87%E3%81%AA%E3%83%95%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%9F%E3%83%B3%E3%82%B0" class="hash-link" aria-label="適切なフラッシュのタイミング への直接リンク" title="適切なフラッシュのタイミング への直接リンク" translate="no">​</a></h2>
<p>1、Redo Log に空きがなくなったとき。Redo Log は永続化の保証として、更新操作のたびに必ず記録する必要があります。Redo Log に書き込めなくなったら、ページのフラッシュを行ってデータをディスクへ同期し、Redo Log の CheckPoint ポインタを移動させて新しい領域を空けなければなりません。このときメモリに余裕があれば、フラッシュし終えたページをメモリから追い出す必要はありません。</p>
<p>2、メモリにデータページが収まらなくなったとき。クエリ操作はディスク上のデータページをメモリに読み込んでから結果を返します。メモリにデータページが収まらなくなったら、最も長く使われていないデータページを追い出さなければなりません。そのデータページがダーティページであれば、ディスクにフラッシュしてから追い出す必要があります。</p>
<p>3、データベースがアイドル状態のとき。データベースが現在アイドル状態だと判断した場合、ダーティページのフラッシュを行います。このときメモリに余裕があれば、フラッシュ後に追い出しは行いません。</p>
<p>4、データベースが正常にシャットダウンされるとき。データベースのシャットダウン時にもページのフラッシュが行われ、それまでの変更がハードディスクに永続化されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/15/mysql-zhixing#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>MySQL は一連のログのシーケンシャル書き込みによって永続化の性能を高め、さらに Buffer Pool コンポーネントでディスク上のデータをメモリにマッピングして DML 操作を行うことで、スループット性能を大幅に向上させています。同時に非同期フラッシュを採用することで、異なるテーブルや行データの変更によるディスクランダム書き込みの低性能問題を回避しています。Redo Log はクラッシュ後のデータ復旧を保証し、Bin Log はレプリケーションとデータ復旧を支え、Undo Log はロールバックと MVCC を担います。この 3 つが 2 フェーズコミットと連携することで、性能とデータ一貫性のバランスを共に保証しているのです。</p>]]></content:encoded>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[HTTP プロトコルの発展史]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/14/http</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/14/http</guid>
            <pubDate>Mon, 14 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[HTTP が 0.9 から 3.0 へと進化してきた流れを整理します。各世代のプロトコルが何を解決し、何を課題として残したのか、そして QUIC がトランスポート層をどう再設計したのかを見ていきます。]]></description>
            <content:encoded><![CDATA[<p>HTTP はブラウザとサーバー間の最も主要な通信プロトコルです。HTTP はアプリケーション層(第 7 層)のプロトコルであり、アプリケーション層で生成されたデータはトランスポート層プロトコルを媒体として、インターネット上の他のホストへと伝送されます。その媒体こそが TCP プロトコルです(3.0 では UDP に変更されました)。TCP でコネクションを確立し、その上で対応するコンテンツを伝送する仕組みになっています。</p>
<!-- -->
<p>20 世紀 60 年代、アメリカ国防総省の高等研究計画局(ARPA)が ARPA ネットを構築しました。これがインターネットの起源とされています。70 年代には、研究者たちが ARPA ネットでの実践と考察をもとに、有名な TCP/IP プロトコルを発明しました。このプロトコルは優れた階層構造と安定した性能を備えており、80 年代半ばには UNIX システムのカーネルに組み込まれ、より多くのコンピュータがネットワークに接続されるきっかけとなりました。</p>
<p>1989 年、ティム・バーナーズ=リー博士が論文を発表し、インターネット上にハイパーリンクドキュメントシステムを構築する構想を提唱しました。この論文の中で博士は、URI、HTML、HTTP という 3 つの重要な技術を確立しました。</p>
<p>この 3 つの技術によって、ハイパーテキストシステムはインターネット上で完璧に動作できるようになりました。リー博士はこのシステムを「World Wide Web」(万維網)と名付けました。こうして HTTP プロトコルはインターネット上で普及し始め、現在最もよく使われているのは HTTP/1.1 と HTTP/2.0 です。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-image-10-e734574d102a56652a23e4ce91a69df4.webp" width="944" height="356" class="img_ev3q"></p>
<p><strong>実のところ、HTTP プロトコルの各世代の進化は、いずれも前世代の HTTP の欠点を改善する形で行われてきました。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="http09">HTTP/0.9<a href="https://tanzhuo.xyz/ja/2022/11/14/http#http09" class="hash-link" aria-label="HTTP/0.9 への直接リンク" title="HTTP/0.9 への直接リンク" translate="no">​</a></h2>
<p>1991 年、HTTP(HyperText Transfer Protocol、ハイパーテキスト転送プロトコル)が正式に誕生しました。当時のバージョンは 0.9 です。名前からわかるように、このプロトコルの役割はハイパーテキストコンテンツである HTML を転送することでした。プロトコルはクライアントがリクエストを送り、サーバーがそれに応答するという通信モデルを定義しました。リクエストメッセージの内容はわずか 1 行、「GET + リクエストするファイルパス」だけです。メッセージタイプの区別がなかったため、テキスト情報しか転送できませんでした。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="http10">HTTP/1.0<a href="https://tanzhuo.xyz/ja/2022/11/14/http#http10" class="hash-link" aria-label="HTTP/1.0 への直接リンク" title="HTTP/1.0 への直接リンク" translate="no">​</a></h2>
<p>インターネットの発展とブラウザの登場に伴い、単純なテキストコンテンツだけではユーザーのニーズを満たせなくなりました。ブラウザは HTTP を通じてスクリプト、スタイル、画像、音声、動画などさまざまな種類のファイルを転送したいと考えるようになり、1996 年にアップデートされた HTTP 1.0 では次のような機能が導入されました。</p>
<ul>
<li class="">HEAD、POST などの新しいメソッドを追加</li>
<li class="">レスポンスステータスコードを追加し、想定されるエラー原因を示せるように</li>
<li class="">プロトコルバージョン番号の概念を導入</li>
<li class="">HTTP Header(ヘッダー)の概念を導入し、リクエストとレスポンスの処理をより柔軟に</li>
<li class="">転送できるデータがテキストに限定されなくなった</li>
</ul>
<p>中でも最も重要な変更はヘッダーの導入です。ヘッダーの内容はキーと値のペアの形式で設定されます。リクエストヘッダーは Accept フィールドで受け入れ可能なファイルタイプをサーバーに伝え、レスポンスヘッダーは Content-Type フィールドで返却するファイルのタイプをブラウザに伝えます。ヘッダーフィールドは異なる種類のファイル転送の問題を解決するだけでなく、キャッシュや認証情報など、他の多くの機能も実現できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="http11">HTTP/1.1<a href="https://tanzhuo.xyz/ja/2022/11/14/http#http11" class="hash-link" aria-label="HTTP/1.1 への直接リンク" title="HTTP/1.1 への直接リンク" translate="no">​</a></h2>
<p>インターネットの急速な発展に伴い、HTTP/1.0 もニーズを満たせなくなりました。最も大きな課題はコネクションの問題です。具体的には、HTTP/1.0 では通信のたびに<strong>コネクション確立</strong>、<strong>データ転送</strong>、<strong>コネクション切断</strong>という 3 つのフェーズを経る必要がありました。1 つのページが多くの外部ファイルを参照している場合、このコネクションの確立と切断のプロセスが大量のネットワークオーバーヘッドを生んでしまいます。</p>
<p>HTTP/1.1 ではデフォルトで Connection: keep-alive が使われるようになり、コネクションの確立と解放を繰り返すオーバーヘッドが回避されました。ただしサーバーは、クライアントが各リクエストのレスポンス内容を区別できるように、クライアントのリクエスト順どおりに結果を返さなければなりません。ヘッダーフィールドによって現在のリクエストのデータをすべて受信し終えたかを判断する仕組みであり、2 つのレスポンスが並行して存在することは許されません。</p>
<p><strong>HTTP/1.0 の問題を解決するため、1999 年にリリースされた HTTP/1.1 には次のような特徴があります。</strong></p>
<ul>
<li class="">持続的接続(Connection: keep-alive):TCP コネクションの再利用を導入。1 つの TCP コネクションはデフォルトでは閉じられず、複数のリクエストで再利用できる</li>
<li class="">並行コネクション:1 つのドメインに対するリクエストに複数の持続的接続を割り当て可能(持続的接続における「HOL ブロッキング(Head-of-Line Blocking)」問題を緩和)</li>
<li class="">パイプライン機構を導入し、1 つの TCP コネクションで複数のリクエストを同時に送信可能(レスポンスの順序はリクエストの順序と一致しなければならないため、あまり使われていない)</li>
<li class="">PUT、DELETE、OPTIONS、PATCH などの新しいメソッドを追加</li>
<li class="">キャッシュ用フィールドを新設(Cache-Control、ETag)</li>
<li class="">リクエストヘッダーに Range フィールドを導入し、レジューム(断点続伝)をサポート</li>
<li class="">レスポンスデータのチャンク転送(chunked)を許可し、大きなファイルの転送に有利に</li>
<li class="">Host ヘッダーを必須化し、バーチャルホスティングを可能に</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="http20">HTTP/2.0<a href="https://tanzhuo.xyz/ja/2022/11/14/http#http20" class="hash-link" aria-label="HTTP/2.0 への直接リンク" title="HTTP/2.0 への直接リンク" translate="no">​</a></h2>
<p>HTTP/1.1 は持続的接続によってコネクションの作成・切断を繰り返すことによる性能消費を大幅に減らしましたが、その並行処理能力には制限がありました。それは次の 2 点に表れます。</p>
<ul>
<li class="">HTTP/1.1 で持続的接続を使う場合、1 つのコネクションでは同時に 1 つのリクエストしか処理できません。現在のリクエストが完了するまで、他のリクエストはブロック状態になります。この状況は「HOL ブロッキング」と呼ばれます</li>
<li class="">ブラウザはサーバーの負荷を軽減するため、同一ドメインに対する HTTP コネクション数を制限しており、一般的には 6 ~ 8 本です</li>
</ul>
<p>2015 年に正式リリースされた HTTP/2 は、デフォルトで ASCII エンコードによる転送をやめ、転送効率を高めるために<strong>バイナリデータ</strong>に変更されました。</p>
<p>クライアントはリクエストを送信する際、各リクエストの内容を番号付きの異なるバイナリフレーム(Frame)にカプセル化し、これらのフレームをまとめてサーバーに送信します。サーバーはデータを受信すると、同じ番号のフレームを結合して完全なリクエスト情報に復元します。同様に、サーバーが結果を返し、クライアントがそれを受信する際も、このフレームの分割と結合のプロセスに従います。</p>
<p>バイナリフレーミングが導入されたことで、同一ドメインに対してクライアントはサーバーと 1 本のコネクションを確立するだけで通信のニーズを満たせるようになりました。このように 1 本のコネクションで複数のリクエストを送信する方式は「<strong>多重化(マルチプレキシング)</strong>」と呼ばれ、それぞれの経路は stream(ストリーム)と呼ばれます。</p>
<p>HTTP/2.0 の主な変更点は次のとおりです。</p>
<ul>
<li class="">データはバイナリプロトコルで転送され、プレーンテキストではなくなった</li>
<li class="">多重化を導入し、1.1 のパイプラインを廃止</li>
<li class="">専用アルゴリズムでヘッダーを圧縮し、転送データ量を削減</li>
<li class="">データフレームに優先度を設定し、サーバーが特定のリクエストを優先処理できるように</li>
<li class="">サーバーからクライアントへのデータのプッシュ(サーバープッシュ)を許可</li>
<li class="">ヘッダーフィールドはすべて小文字に。疑似ヘッダーの概念を導入し、ヘッダーフィールドの前にコロン始まりで記述</li>
<li class="">セキュリティを強化し、「事実上」暗号化通信(TLS)を必須に</li>
</ul>
<p>HTTP/2.0 はリリースから何年も経っていますが、HTTP/1.1 があまりにも定番かつ強力だったため、現在でも HTTP/2.0 の普及率はそれほど高くなく、多くのウェブサイトが依然として HTTP/1.1 を使っています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="http30">HTTP/3.0<a href="https://tanzhuo.xyz/ja/2022/11/14/http#http30" class="hash-link" aria-label="HTTP/3.0 への直接リンク" title="HTTP/3.0 への直接リンク" translate="no">​</a></h2>
<p>もちろん HTTP/2 も完璧ではありません。クライアントまたはサーバーの通信中にパケットロスが発生したり、どちらかのネットワークが中断したりすると、TCP コネクション全体が一時停止してしまいます。</p>
<p>HTTP/2 はバイナリフレーミングによる多重化を採用しているため、通常は 1 本の TCP コネクションだけで転送を行います。パケットロスやネットワーク中断が発生すると、後続のすべてのデータがブロックされてしまいます。一方 HTTP/1.1 の場合は複数の TCP コネクションを開けるため、どれか 1 本の TCP に問題が発生しても他のコネクションには影響せず、残りの TCP コネクションで正常にデータを転送し続けられます。この状況では HTTP/2 のパフォーマンスがかえって HTTP/1 に劣ってしまうのです。</p>
<p>2018 年、HTTP/3 は下層で依存していた TCP を UDP に変更し、この問題を根本的に解決しました。UDP は TCP と比べて、データ転送時にコネクションを確立する必要がなく、複数のパケットを同時に送信できるため転送効率が非常に高いのが最大の特徴です。欠点は、相手が確実にデータを受け取れることを保証する確認応答の仕組みがないことです。そこで Google は独自路線を取り、UDP ベースの <strong>QUIC プロトコル</strong>を開発して HTTP/3 に採用しました。現在、多くのブラウザやハードウェアベンダーが HTTP/3.0 プロトコルへの対応を始めています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quic-プロトコル">QUIC プロトコル<a href="https://tanzhuo.xyz/ja/2022/11/14/http#quic-%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB" class="hash-link" aria-label="QUIC プロトコル への直接リンク" title="QUIC プロトコル への直接リンク" translate="no">​</a></h2>
<p>百度百科:<a href="https://baike.baidu.com/item/QUIC/17341272?fr=aladdin&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">QUIC_百度百科 (baidu.com)</a></p>
<p>QUIC は quick と同じ発音で、つまり<strong>速い</strong>という意味です。Google が提唱した UDP ベースの転送プロトコルであり、そのため QUIC は「<strong>Quick UDP Internet Connections(高速 UDP インターネット接続)</strong>」とも呼ばれています。QUIC の第一の特徴は速さです。なぜ速いと言えるのか、いったいどこが速いのでしょうか?</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-より高速なコネクション確立">1) より高速なコネクション確立<a href="https://tanzhuo.xyz/ja/2022/11/14/http#1-%E3%82%88%E3%82%8A%E9%AB%98%E9%80%9F%E3%81%AA%E3%82%B3%E3%83%8D%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E7%A2%BA%E7%AB%8B" class="hash-link" aria-label="1) より高速なコネクション確立 への直接リンク" title="1) より高速なコネクション確立 への直接リンク" translate="no">​</a></h3>
<p>ご存じのとおり、HTTP プロトコルはトランスポート層で TCP を使ってメッセージを転送しており、さらに HTTPS や HTTP/2.0 では TLS プロトコルによる暗号化も行われます。そのため、ハンドシェイクによるコネクション遅延が発生します。TCP の 3 ウェイハンドシェイク(1 RTT)に加えて TLS ハンドシェイク(2 RTT)が必要で、下図のようになります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2025-11-a602adffbd744406a31ccf802fb74569-e939433e4bbc48bcff4c9b3fe613dac5.webp" width="1069" height="601" class="img_ev3q"></p>
<p>多くの短時間接続のシナリオでは、このハンドシェイク遅延の影響は大きく、しかも取り除くことができません。RTT との戦いは、人類と効率の果てなき闘いなのです。</p>
<p>これに対して QUIC のハンドシェイクはより高速です。トランスポート層プロトコルとして UDP を使うことで、3 ウェイハンドシェイクの時間遅延を削減しているからです。さらに QUIC の暗号化には TLS プロトコルの最新バージョンである TLS 1.3 が採用されています。従来の TLS 1.1-1.2 と比べて、TLS 1.3 はクライアントが TLS ハンドシェイクの完了を待たずにアプリケーションデータの送信を開始でき、1 RTT および 0 RTT をサポートするため、<strong>高速なコネクション確立</strong>を実現できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-hol-ブロッキングがない">2) HOL ブロッキングがない<a href="https://tanzhuo.xyz/ja/2022/11/14/http#2-hol-%E3%83%96%E3%83%AD%E3%83%83%E3%82%AD%E3%83%B3%E3%82%B0%E3%81%8C%E3%81%AA%E3%81%84" class="hash-link" aria-label="2) HOL ブロッキングがない への直接リンク" title="2) HOL ブロッキングがない への直接リンク" translate="no">​</a></h3>
<p>前述のとおり、HTTP/2.0 はアプリケーション層の HOL ブロッキング問題を解決しましたが、コネクション自体は依然として TCP に基づいており、トランスポート層でのリクエストブロッキング問題は解決できませんでした。</p>
<p>一方 UDP にはそもそもコネクション確立という概念がなく、さらに QUIC が使用する stream は互いに分離されているため、ある stream のデータ処理が他の stream をブロックすることはありません。したがって UDP を使うことで HOL ブロッキングは発生しなくなります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-信頼性とより正確な-rtt">3) 信頼性とより正確な RTT<a href="https://tanzhuo.xyz/ja/2022/11/14/http#3-%E4%BF%A1%E9%A0%BC%E6%80%A7%E3%81%A8%E3%82%88%E3%82%8A%E6%AD%A3%E7%A2%BA%E3%81%AA-rtt" class="hash-link" aria-label="3) 信頼性とより正確な RTT への直接リンク" title="3) 信頼性とより正確な RTT への直接リンク" translate="no">​</a></h3>
<p>TCP ではデータの信頼性を保証するために、<strong>シーケンス番号+確認応答番号</strong>の仕組みが使われています。synchronize sequence number を持つパケットがサーバーに送信されると、サーバーは一定時間内に応答を返します。この時間内に応答がなければ、クライアントはサーバーがパケットを受信して応答を返すまで、そのパケットを再送し続けます。</p>
<blockquote>
<p>では、TCP はどのように再送タイムアウト時間を判断しているのでしょうか?</p>
</blockquote>
<p>TCP は一般的に<strong>適応的再送アルゴリズム</strong>を採用しており、このタイムアウト時間はラウンドトリップタイム RTT に応じて動的に調整されます。しかし再送時には毎回同じシーケンス番号を使うため、応答が元のパケットに対するものなのか再送パケットに対するものなのかを区別できず、RTT の計算結果があまり正確でなくなってしまいます。</p>
<p>QUIC は TCP プロトコルを使っていませんが、信頼性も保証しています。QUIC が信頼性を実現する仕組みは <strong>Packet Number</strong> です。このシーケンス番号は synchronize sequence number の代替と考えることができ、こちらも単調増加します。syn と異なるのは、サーバーがパケットを受信したかどうかにかかわらず、この Packet Number は必ず +1 されるという点です。syn の場合は、サーバーが ack 応答を返した後にのみ +1 されます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2025-11-quic-35e9b29c19f3401c32ef72eaa77d6abb.webp" width="1600" height="946" class="img_ev3q"></p>
<p>たとえば PN = 10 のパケットが送信中に何らかの理由でなかなかサーバーに届かなかった場合、クライアントは PN = 11 のパケットを再送します。しばらくしてクライアントが PN = 10 に対する応答を受け取り、応答メッセージを返送すると、このときの RTT は PN = 10 のパケットがネットワーク内で生存していた時間となり、比較的正確に計算できます。</p>
<blockquote>
<p>QUIC はパケットの信頼性を保証しますが、では、データの信頼性はどのように保証されるのでしょうか?</p>
</blockquote>
<p>QUIC は <strong>stream offset</strong> という概念を導入しました。1 つの stream で複数の stream offset を転送でき、各 stream offset は実質的に 1 つの PN で識別されるデータです。ある PN で識別されるデータが失われても、PN を +1 して再送されるのは元の PN が示していたのと同じデータであり、すべての PN で識別されるデータがサーバーに届いた時点で再構築が行われます。これによってデータの信頼性が保証されます。サーバーに到達した stream offset は順序どおりに組み立てられるため、データの順序性も同時に保証されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-プラガブルな輻輳制御">4) プラガブルな輻輳制御<a href="https://tanzhuo.xyz/ja/2022/11/14/http#4-%E3%83%97%E3%83%A9%E3%82%AC%E3%83%96%E3%83%AB%E3%81%AA%E8%BC%BB%E8%BC%B3%E5%88%B6%E5%BE%A1" class="hash-link" aria-label="4) プラガブルな輻輳制御 への直接リンク" title="4) プラガブルな輻輳制御 への直接リンク" translate="no">​</a></h3>
<p>よく知られているように、TCP プロトコルの具体的な実装はオペレーティングシステムのカーネルが担っており、アプリケーションは利用することしかできず、カーネルを変更することはできません。モバイル端末やますます多くのデバイスがインターネットに接続されるようになり、パフォーマンスは非常に重要な評価指標になってきました。モバイルネットワークは急速に発展していますが、ユーザー側の更新は非常に緩慢です。開発終了から何年も経っているにもかかわらず、いまだに多くの地域のコンピュータで XP システムが使われているのを目にします。サーバー側のシステムはユーザーのアップグレードには依存しませんが、OS のアップグレードは低レイヤーのソフトウェアやランタイムライブラリの更新を伴うため、やはり保守的で緩慢です。</p>
<p>QUIC プロトコルの重要な特徴のひとつが**プラガビリティ(差し替え可能性)**であり、動的な更新とアップグレードが可能です。QUIC はアプリケーション層で輻輳制御アルゴリズムを実装しており、OS やカーネルのサポートを必要としません。輻輳制御アルゴリズムを切り替える場合も、サーバーで再読み込みするだけでよく、停止や再起動は不要です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-フロー制御とセキュリティ">5) フロー制御とセキュリティ<a href="https://tanzhuo.xyz/ja/2022/11/14/http#5-%E3%83%95%E3%83%AD%E3%83%BC%E5%88%B6%E5%BE%A1%E3%81%A8%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3" class="hash-link" aria-label="5) フロー制御とセキュリティ への直接リンク" title="5) フロー制御とセキュリティ への直接リンク" translate="no">​</a></h3>
<p>TCP のフロー制御は<strong>スライディングウィンドウ</strong>によって実現されていることはご存じのとおりです。QUIC もフロー制御を実装しており、同様にウィンドウ更新 <code>window_update</code> を使って、受け入れ可能なバイト数を相手に伝えます。</p>
<p>TCP プロトコルのヘッダーは暗号化も認証もされていないため、転送中に改ざんされる可能性があります。これに対して QUIC では、メッセージのヘッダーはすべて認証され、メッセージ本体も暗号化処理されています。そのため QUIC のメッセージに何らかの変更が加えられれば、受信側は即座にそれを検知でき、セキュリティが保証されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-quic-の利点まとめ">6) QUIC の利点まとめ<a href="https://tanzhuo.xyz/ja/2022/11/14/http#6-quic-%E3%81%AE%E5%88%A9%E7%82%B9%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="6) QUIC の利点まとめ への直接リンク" title="6) QUIC の利点まとめ への直接リンク" translate="no">​</a></h3>
<p>まとめると、QUIC には次のような利点があります。</p>
<ul>
<li class="">UDP プロトコルを使うため、3 ウェイハンドシェイクによるコネクション確立が不要で、TLS のコネクション確立時間も短縮される。</li>
<li class="">HOL ブロッキング問題を解決した。</li>
<li class="">動的なプラガビリティを実現し、アプリケーション層で輻輳制御アルゴリズムを実装しているため、いつでも切り替えられる。</li>
<li class="">メッセージヘッダーとメッセージ本体をそれぞれ認証・暗号化処理し、セキュリティを保証する。</li>
<li class="">コネクションのスムーズなマイグレーションが可能。</li>
</ul>
<p>コネクションのスムーズなマイグレーションとは、スマートフォンやモバイルデバイスが 4G 回線と WiFi などのネットワーク間を切り替える際に、切断や再接続が発生せず、ユーザーが何も意識することなくシームレスに信号を切り替えられることを指します。現在、QUIC プロトコルは RFC 9000 として標準化されています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/14/http#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>HTTP の進化を振り返ってみましょう。0.9 は HTML テキストしか転送できず、1.0 はヘッダーを導入して転送コンテンツを多様化し、1.1 は持続的接続でコネクションを繰り返し確立するオーバーヘッドを解決し、2.0 はバイナリフレーミングと多重化で並行処理のボトルネックを突破しました。しかし下層が TCP である限り、トランスポート層の HOL ブロッキングは根絶できません。そこで HTTP/3 は UDP ベースの QUIC に切り替え、コネクション確立、信頼性のある転送、輻輳制御をすべてアプリケーション層に移して再設計しました。各世代のプロトコルは前世代の弱点を補う形で進化してきたのです。HTTP/3.0 が今後ますます多くのシーンで使われていくことでしょう。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[JVM スレッドプールの拡張メカニズム]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong</guid>
            <pubDate>Mon, 14 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[HotSpot では Java スレッドとカーネルスレッドが 1 対 1 でマッピングされ、作成・破棄のコストが高くつきます。ThreadPoolExecutor のコアパラメータ、タスク処理と拡張・回収のフロー、そして無界キューによって設定が無効になる落とし穴を整理します。]]></description>
            <content:encoded><![CDATA[<p><strong>HotSpot VM のスレッドモデル</strong> では、Java スレッドとオペレーティングシステムのスレッドは 1 対 1 のマッピング関係にあります（JDK19 以降で仮想スレッドが導入されましたが、従来のスレッドは依然としてこのモデルです）。つまり、1 本の Java スレッドは 1 本の OS カーネルスレッド（KLT / LWP）に対応します。</p>
<!-- -->
<p>Java がスレッドを作成するとき、OS カーネルの API を呼び出して対応するカーネルスレッドを作成する必要があり、OS はそのためにスタック空間やスケジューリング情報などのリソースを割り当てなければなりません。Java スレッドが終了すると、対応するカーネルスレッドも回収されます。したがって <strong>スレッドの作成と破棄のコストは高く、スレッド数も無限に増やすことはできません</strong>。</p>
<p>スレッド数が多すぎると：</p>
<ol>
<li class="">スレッド作成が大きなシステムオーバーヘッドをもたらす</li>
<li class="">CPU が複数のスレッド間で頻繁に <strong>コンテキストスイッチ（Context Switch）</strong> を行う</li>
<li class="">大量のスレッドが <strong>システム性能の低下、さらには OOM</strong> を引き起こす可能性がある</li>
</ol>
<p>そのため、実際の開発では通常 <strong>スレッドプール（ThreadPoolExecutor）</strong> を使ってスレッドのライフサイクルを統一的に管理します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="threadpoolexecutor-のコアパラメータ">ThreadPoolExecutor のコアパラメータ<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#threadpoolexecutor-%E3%81%AE%E3%82%B3%E3%82%A2%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF" class="hash-link" aria-label="ThreadPoolExecutor のコアパラメータ への直接リンク" title="ThreadPoolExecutor のコアパラメータ への直接リンク" translate="no">​</a></h2>













































<table><thead><tr><th>パラメータ</th><th>型</th><th>説明</th></tr></thead><tbody><tr><td>corePoolSize</td><td>int</td><td>コアスレッド数。スレッドプール内に長期的に保持されるスレッドの数</td></tr><tr><td>maximumPoolSize</td><td>int</td><td>最大スレッド数。タスクキューが満杯のときに作成を許可する最大スレッド数</td></tr><tr><td>keepAliveTime</td><td>long</td><td>非コアスレッドの最大アイドル生存時間</td></tr><tr><td>unit</td><td>TimeUnit</td><td>keepAliveTime の時間単位</td></tr><tr><td>workQueue</td><td>BlockingQueue</td><td>タスクキュー。実行待ちのタスクを格納する</td></tr><tr><td>threadFactory</td><td>ThreadFactory</td><td>スレッド作成ファクトリ。スレッド名や優先度などのカスタマイズに使う</td></tr><tr><td>handler</td><td>RejectedExecutionHandler</td><td>拒否ポリシー。タスクを実行できないときの処理方法</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドプールのタスク処理フロー">スレッドプールのタスク処理フロー<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%97%E3%83%BC%E3%83%AB%E3%81%AE%E3%82%BF%E3%82%B9%E3%82%AF%E5%87%A6%E7%90%86%E3%83%95%E3%83%AD%E3%83%BC" class="hash-link" aria-label="スレッドプールのタスク処理フロー への直接リンク" title="スレッドプールのタスク処理フロー への直接リンク" translate="no">​</a></h2>
<p>新しいタスクがスレッドプールに提出されると、スレッドプールは以下の順序で処理します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1まずコアスレッドでタスクを実行">1）まずコアスレッドでタスクを実行<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#1%E3%81%BE%E3%81%9A%E3%82%B3%E3%82%A2%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%A7%E3%82%BF%E3%82%B9%E3%82%AF%E3%82%92%E5%AE%9F%E8%A1%8C" class="hash-link" aria-label="1）まずコアスレッドでタスクを実行 への直接リンク" title="1）まずコアスレッドでタスクを実行 への直接リンク" translate="no">​</a></h3>
<p>現在の実行スレッド数が <code>corePoolSize</code> <strong>より小さい</strong> 場合、スレッドプールは直接新しいスレッドを作成してタスクを実行します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2コアスレッドが満杯--タスクはキューへ">2）コアスレッドが満杯 → タスクはキューへ<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#2%E3%82%B3%E3%82%A2%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%8C%E6%BA%80%E6%9D%AF--%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AF%E3%82%AD%E3%83%A5%E3%83%BC%E3%81%B8" class="hash-link" aria-label="2）コアスレッドが満杯 → タスクはキューへ への直接リンク" title="2）コアスレッドが満杯 → タスクはキューへ への直接リンク" translate="no">​</a></h3>
<p>現在のスレッド数が <strong>すでに</strong> <code>corePoolSize</code> に達している場合、新しいタスクはすぐにはスレッド作成をトリガーせず、<code>BlockingQueue</code> の <strong>タスクキュー</strong> に入って実行を待ちます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3キューが満杯--スレッドプールを拡張">3）キューが満杯 → スレッドプールを拡張<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#3%E3%82%AD%E3%83%A5%E3%83%BC%E3%81%8C%E6%BA%80%E6%9D%AF--%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%97%E3%83%BC%E3%83%AB%E3%82%92%E6%8B%A1%E5%BC%B5" class="hash-link" aria-label="3）キューが満杯 → スレッドプールを拡張 への直接リンク" title="3）キューが満杯 → スレッドプールを拡張 への直接リンク" translate="no">​</a></h3>
<p>タスクキューが最大容量に達しており、かつ現在のスレッド数が <code>maximumPoolSize</code> <strong>より小さい</strong> 場合、スレッドプールは <strong>新しいスレッドを作成してタスクを処理</strong> します。これらのスレッドは <strong>非コアスレッド</strong> と呼ばれます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4最大スレッド数を超過--拒否ポリシー">4）最大スレッド数を超過 → 拒否ポリシー<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#4%E6%9C%80%E5%A4%A7%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E6%95%B0%E3%82%92%E8%B6%85%E9%81%8E--%E6%8B%92%E5%90%A6%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC" class="hash-link" aria-label="4）最大スレッド数を超過 → 拒否ポリシー への直接リンク" title="4）最大スレッド数を超過 → 拒否ポリシー への直接リンク" translate="no">​</a></h3>
<p>キューが満杯で、かつ現在のスレッド数がすでに <code>maximumPoolSize</code> に達している場合、スレッドプールは <strong>RejectedExecutionHandler 拒否ポリシー</strong> を発動します。</p>
<p>主なポリシー：</p>
<ul>
<li class="">AbortPolicy（デフォルト、そのまま例外をスロー）</li>
<li class="">CallerRunsPolicy</li>
<li class="">DiscardPolicy</li>
<li class="">DiscardOldestPolicy</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="keepalivetime-の役割">keepAliveTime の役割<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#keepalivetime-%E3%81%AE%E5%BD%B9%E5%89%B2" class="hash-link" aria-label="keepAliveTime の役割 への直接リンク" title="keepAliveTime の役割 への直接リンク" translate="no">​</a></h2>
<p>スレッドプール内の <strong>非コアスレッド</strong> が <code>keepAliveTime</code> の時間内に <strong>1 つもタスクを実行しなかった</strong> 場合、そのスレッドは破棄されます。つまりコアスレッドはデフォルトでは破棄されず、非コアスレッドはアイドル時間が <code>keepAliveTime</code> を超えると回収されます。これによりスレッドプールは、<strong>ピーク時に拡張し、低負荷時にスレッドを回収する</strong> ことができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="非常にハマりやすい落とし穴">非常にハマりやすい落とし穴<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#%E9%9D%9E%E5%B8%B8%E3%81%AB%E3%83%8F%E3%83%9E%E3%82%8A%E3%82%84%E3%81%99%E3%81%84%E8%90%BD%E3%81%A8%E3%81%97%E7%A9%B4" class="hash-link" aria-label="非常にハマりやすい落とし穴 への直接リンク" title="非常にハマりやすい落とし穴 への直接リンク" translate="no">​</a></h2>
<p><code>workQueue</code> に <strong>無界キュー（LinkedBlockingQueue のデフォルトコンストラクタなど）</strong> を使うと、タスクは延々とキューに入り続け、キューはほぼ満杯になりません。その結果、スレッドプールは永遠に拡張のステップまで到達せず、<code>maximumPoolSize</code> と <code>keepAliveTime</code> は <strong>基本的に効きません</strong>。これは多くの本番システムで <strong>スレッドプールの設定が無効になる</strong> よくある原因でもあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/14/jvm-thread-kuorong#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>スレッドプールの拡張経路は、コアスレッド → タスクキュー → 非コアスレッド → 拒否ポリシー、と要約できます。<code>maximumPoolSize</code> はキューが満杯になって初めて効き、<code>keepAliveTime</code> は非コアスレッドの回収タイミングを決めます。スレッドプールを設定する際は、キューの種類の選択がスレッド数のパラメータと同じくらい重要です。無界キューは拡張と拒否ポリシーを有名無実化してしまいます。この一連のチェーン全体を理解すれば、本番で起きる「スレッドプールのパラメータが効かない」という問題の大半に答えが見つかります。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[ブルームフィルタ徹底解説]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/13/bu-long-guo-lu-qi</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/13/bu-long-guo-lu-qi</guid>
            <pubDate>Sun, 13 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[ビットマップと複数ハッシュから出発して、ブルームフィルタの判定の特性、偽陽性率の由来とトレードオフ、そしてなぜ元の構造は削除をサポートせず、カウンティングブルームフィルタがどうそれを補うのかを整理します。]]></description>
            <content:encoded><![CDATA[<p>キャッシュを扱う人ならたいてい避けて通れない問題があります。リクエストされた key がキャッシュにもデータベースにも存在しないのに、クエリが毎回データベースまで突き抜けてしまうという問題です。この種のリクエストを防ぐには、まず「この key がそもそも存在するのか」を高速に判定できなければなりません。ブルームフィルタはまさにそのために生まれました。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景の紹介">背景の紹介<a href="https://tanzhuo.xyz/ja/2022/11/13/bu-long-guo-lu-qi#%E8%83%8C%E6%99%AF%E3%81%AE%E7%B4%B9%E4%BB%8B" class="hash-link" aria-label="背景の紹介 への直接リンク" title="背景の紹介 への直接リンク" translate="no">​</a></h2>
<p>redis キャッシュのシナリオでは、誰かがまったく存在しない key を大量にリクエストしてくると、キャッシュは永遠にヒットせず、負荷がすべてデータベースに集中します。これがよく言われるキャッシュ貫通（キャッシュペネトレーション）です。この種のリクエストを遮断する最も素朴な発想は、正当な key をすべて集合に保存しておき、クエリの前に判定することです。しかし key の数が多くなると、HashSet のような構造で全量の key を保存するのはメモリコスト的に受け入れがたくなります。ブルームフィルタは極めて小さな空間と引き換えに「誤差はあり得るが十分に速い」存在判定を提供するもので、まさにこのニーズにぴったりはまります。</p>
<p>ブルームフィルタ（Bloom Filter）は 1970 年に Bloom によって提案されたもので、実際には非常に長いバイナリベクトルと一連のランダム写像関数で構成されています。ブルームフィルタの利用シーンは一般に redis のキャッシュ貫通の防止であり、ブルームフィルタを使えば空間を大きく節約でき、要素の存在有無をすばやく特定できます。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="763" height="403" class="img_ev3q"></p>
<p>ブルームフィルタは key をハッシュしてビットマップ（Bitmap、実体は bit 配列）内の対応するインデックスを特定します。この配列の各位置は 0 と 1 の 2 つの状態しか持たず、各位置は 1 ビット（bit）しか占有しません。0 は要素が存在しないこと、1 は要素が存在することを表します。</p>
<p>具体的には、要素を書き込むとき、k 個の異なるハッシュ関数でそれぞれ key を計算して k 個のインデックスを得て、ビットマップ内のこれら k 個の位置をすべて 1 にします。クエリのときも同じ流れをたどります。k 個の位置のうちどれか 1 つでも 0 なら、この key は一度も書き込まれていないことを意味します。k 個の位置がすべて 1 のときに限り、「存在する可能性がある」と判定します。プロセス全体はハッシュ計算とビット演算だけで、時間計算量は O(k) であり、格納済みの要素数には依存しません。これがクエリの速さの理由でもあります。</p>
<p><strong>注意：2 つの異なる key をハッシュした結果、対応するインデックスが一部重複することがあります。これによりメモリ使用量を削減できますが、ハッシュ衝突が発生する確率もあり、本来存在しない key をハッシュした結果、ビットマップ上でたまたますべて 1 になっているケースが起こり得ます。</strong></p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="742" height="390" class="img_ev3q"></p>
<p>以上の現象から、ブルームフィルタの観点で 2 つの大きな特徴を導き出せます。</p>
<p><strong>1、ブルームフィルタがある要素は存在すると判定した場合、その要素は存在する可能性がある。</strong></p>
<p><strong>2、ブルームフィルタがある要素は存在しないと判定した場合、その要素は確実に存在しない。</strong></p>
<p>この 2 つの特徴が使い方を決めます。確実に存在しないリクエストを外側で遮断する「前段フィルタ」に適している一方、「存在する」という結論を鵜呑みにしてはいけません。存在すると判定された後も、キャッシュまたはデータベースでの二次確認が必要です。</p>
<p>なぜならブルームフィルタには常に誤判定率が存在するからです。ハッシュ衝突を 100% 回避することは不可能です。ブルームフィルタではこの誤判定率を偽陽性確率、すなわち False Positive Probability、略して fpp と呼びます。</p>
<p>誤判定率の大きさは主に 3 つの要因で決まります。ビットマップの長さ m、ハッシュ関数の個数 k、そしてすでに書き込まれた要素数 n です。書き込まれる要素が多いほどビットマップ内で 1 になったビットが増え、未知の key の k 個の位置が「たまたますべて 1」になる確率も高くなります。そのためブルームフィルタは通常、作成時に想定要素数と期待する fpp の指定を求め、実装側（Guava の BloomFilter など）が適切な m と k を逆算します。</p>
<p>fpp を抑えるには、ビットマップの長さを増やすか、ハッシュ回数を増やして衝突確率を下げることができますが、ビットマップの拡大にはより多くのメモリ空間が必要で、ハッシュ回数の増加にはより多くの CPU リソースが必要です。リソースコストの見積もりをきちんと行い、適切な方式を選ぶ必要があります。これは本質的に、空間・CPU・正確性の三者間のトレードオフであり、タダの昼食はありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="対応する-key-を削除するには">対応する key を削除するには？<a href="https://tanzhuo.xyz/ja/2022/11/13/bu-long-guo-lu-qi#%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B-key-%E3%82%92%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF" class="hash-link" aria-label="対応する key を削除するには？ への直接リンク" title="対応する key を削除するには？ への直接リンク" translate="no">​</a></h2>
<p>ここまで見てきたように、ブルームフィルタで要素の存在を判定するのは、対応するインデックス位置が 1 かどうかで判断するものです。しかし要素を削除したいとき、1 をそのまま 0 に変えることはできません。その位置には他の要素が存在している可能性があるからです。**したがって元のブルームフィルタは削除操作をサポートできません。**削除をサポートしたい場合はどうすればよいのでしょうか。最も簡単な方法はカウンターを追加することです。つまりビット配列の各位置について、存在しなければ 0、存在するなら要素の個数を具体的な数値として保存し、単に 1 を保存するだけにはしません。ここで問題になるのは、本来なら 1 を保存するのに 1 ビットで足りたところ、たとえば 2 という具体的な数値を保存するには 2 ビット必要になることです。そのためカウンター付きのブルームフィルタはより大きな空間を占有します。</p>
<p>この改良版は一般にカウンティングブルームフィルタ（Counting Bloom Filter）と呼ばれます。書き込み時は k 個の位置のカウンターをそれぞれ 1 増やし、削除時はそれぞれ 1 減らします。カウンターが 0 になったときに初めて、その位置が本当に空になったことを意味します。代価は空間の膨張（各位置が 1 bit から数 bit になる）に加えて、もう 1 つ隠れたリスクがあります。一度も書き込まれていない要素を誤って削除すると、カウンターが誤って減らされ、本来存在する要素を「消してしまう」可能性があり、偽陰性を引き起こします。したがって削除操作は、書き込み済みと確認できた要素に対してのみ実行すべきです。</p>
<p>業務上、削除の必要性が高くないなら、もっと手軽な方法もあります。削除はせず、定期的に最新の全量データで新しいブルームフィルタを再構築し、古いものと置き換えるのです。再構築中は旧フィルタがサービスを継続し、切り替えはアトミックに行えます。多くのキャッシュ貫通対策のシーンで使われているのがまさにこの考え方です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/11/13/bu-long-guo-lu-qi#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）容量は十分に見積もること。ブルームフィルタは作成後にビットマップの長さが固定され、書き込まれる要素が想定規模を超えると fpp が著しく悪化します。しかもオンラインで拡張することはできず、再構築するしかありません。</p>
<p>2）「存在する」は本当に存在することを意味しない。誤判定で通過した key は依然としてデータベースに到達します。ブルームフィルタは貫通を大幅に減らせるだけで、100% なくすことはできません。データベース側のセーフティネット（空値のキャッシュなど）は依然として必要です。</p>
<p>3）ハッシュ関数は書き込み側と一致させること。複数のサービスが 1 つのブルームフィルタを共有する場合（redis に置く場合など）、各側のハッシュ実装とパラメータは完全に同一でなければなりません。そうでなければ判定結果は誤ったものになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/13/bu-long-guo-lu-qi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ブルームフィルタは 1 つのビットマップと k 個のハッシュ関数によって、極めて小さな空間占有と O(k) のクエリで存在判定を実現します。その代価は、偽陽性が存在すること、そして元の構造では削除をサポートしないことです。「存在しないと言えば確実に存在しない、存在すると言えば可能性があるにすぎない」という 2 つの判定特性が、前段フィルタとしての適性を決めており、典型的なシーンがキャッシュ貫通の防止です。削除が必要ならカウンティングブルームフィルタに切り替えるか、いっそ定期再構築の道を選べばよいでしょう。そして容量の見積もりと fpp の設定については、メモリ・CPU・正確性の間で、自分の業務に合わせてそろばんを弾く必要があります。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[隠れたパフォーマンスキラー「False Sharing」]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang</guid>
            <pubDate>Thu, 03 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[CPU の多段キャッシュと Cache Line から説き起こし、マルチコア並行環境における False Sharing（偽共有）問題の成因と、キャッシュラインパディングなどの一般的な回避手段を分析します。]]></description>
            <content:encoded><![CDATA[<p>CPU の製造プロセスの発展に伴い、現在のハイエンド CPU はすでに数十コア・百数十スレッドを備えており、CPU には 1 次・2 次・3 次キャッシュが設計されています。CPU のコアはこれらのキャッシュを持つことでデータ処理を高速化し、メモリへのアクセス頻度を減らすことができ、CPU の計算性能をさらに高められるのです。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cpu-のキャッシュ構造とアクセスレイテンシ">CPU のキャッシュ構造とアクセスレイテンシ<a href="https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang#cpu-%E3%81%AE%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E6%A7%8B%E9%80%A0%E3%81%A8%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%AC%E3%82%A4%E3%83%86%E3%83%B3%E3%82%B7" class="hash-link" aria-label="CPU のキャッシュ構造とアクセスレイテンシ への直接リンク" title="CPU のキャッシュ構造とアクセスレイテンシ への直接リンク" translate="no">​</a></h2>
<p>CPU のキャッシュ構造、およびメモリ・ハードディスクとの階層関係は下図のとおりです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-image-7-55a2ece8a91bbe178b8999954644bf4f.webp" width="1156" height="1166" class="img_ev3q"></p>
<p>よく知られているように、CPU が一度メモリにアクセスするコストは非常に大きく、ディスク上のデータを一度取得しようとすればさらに長い待ち時間が必要になります。現在では mmap のような技術によってこの状況は緩和されていますが、全体として CPU の計算性能はコンピュータ構造全体における天井であり、他のハードウェアはデータ転送速度の面で比べると足を引っ張っている格好です。CPU が各階層のストレージにアクセスするレイテンシを見てみましょう。</p>









































<table><thead><tr><th>ストレージ</th><th>記憶媒体</th><th>媒体コスト(米ドル)</th><th>ランダムアクセスレイテンシ</th></tr></thead><tbody><tr><td>L1 Cache</td><td>SRAM</td><td>7</td><td>1ns</td></tr><tr><td>L2 Cache</td><td>SRAM</td><td>7</td><td>4ns</td></tr><tr><td>Memory</td><td>DRAM</td><td>0.015</td><td>100ns</td></tr><tr><td>Disk</td><td>SSD (NAND)</td><td>0.0004</td><td>150us</td></tr><tr><td>Disk</td><td>HDD</td><td>0.00004</td><td>10ms</td></tr></tbody></table>
<p>見てのとおり、外部ストレージデバイスは容量が大きくコストが低いほど多くのデータを格納できますが、アクセス速度も遅くなります。アクセス速度が速いデバイスほど製造コストは高くなります。CPU が L1 Cache にアクセスする速度はメモリへのアクセスより 100 倍も速く、これこそが CPU の中に L1〜L3 Cache が存在する理由です——Cache を CPU とメモリの間のキャッシュ層として、メモリへのアクセス頻度を減らしているのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュラインcache-line">キャッシュライン（Cache Line）<a href="https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%83%A9%E3%82%A4%E3%83%B3cache-line" class="hash-link" aria-label="キャッシュライン（Cache Line） への直接リンク" title="キャッシュライン（Cache Line） への直接リンク" translate="no">​</a></h2>
<p>CPU がメモリから Cache へデータを読み込むとき、1 バイトずつ読み込むのではなく、ブロック単位でまとめて読み込みます。このブロック単位のデータは Cache Line（キャッシュライン）と呼ばれます。つまり <strong>Cache Line とは、CPU がメモリから Cache へデータを読み込む単位</strong> なのです。</p>
<p>Cache Line のサイズについては、Linux システムでは次のコマンドで確認できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># cpu0 の L1 キャッシュラインサイズを確認する。単位はバイト</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ </span><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain"> /sys/devices/system/cpu/cpu0/cache/index0/coherency_line_size</span><br></div></code></pre></div></div>
<p>主流の CPU の Cache Line は一般的に 64 バイトですが、Apple が開発した M1 チップの Cache Line はすでに 128 バイトになっています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="false-sharing-とは何か">False Sharing とは何か<a href="https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang#false-sharing-%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B" class="hash-link" aria-label="False Sharing とは何か への直接リンク" title="False Sharing とは何か への直接リンク" translate="no">​</a></h2>
<p>メモリ上のデータは Cache Line 単位で CPU Cache にコピーされることが分かりました。次は、Cache の False Sharing とは何か、そしてこの問題をどう回避するかを紹介します。</p>
<p>デュアルコアの CPU があり、2 つのコアがそれぞれ異なるスレッドを並列に実行していて、同時にメモリから 2 つの異なるデータ、すなわち型が <code>long</code> の変数 A と B（各 8 バイト）を読み取るとします。CPU1 は A を取得して変更したく、CPU2 は B を取得して変更したいのですが、この 2 つのデータのアドレスは <strong>物理メモリ（DRAM）上で連続しています</strong>。Cache Line のサイズが 64 バイトで、変数 A が Cache Line の先頭位置にある場合、B は A のアドレスのすぐ後ろに続くため、2 つのデータは <strong>同一の Cache Line 内</strong> に位置することになります。そして Cache Line は CPU がメモリから Cache へデータを読み込む単位なので、この 2 つのデータは 2 つの CPU コアそれぞれの Cache に同時に読み込まれます。</p>
<p>ここで一つの問題を考えてみましょう。この 2 つの異なるコアのスレッドがそれぞれ A と B のデータを変更した場合、たとえば CPU1 のスレッドは変数 A だけを変更し、CPU2 のスレッドは変数 B だけを変更したら、何が起こるでしょうか？</p>
<p>このとき、両コアそれぞれの Cache Line 内のデータは不一致になり、最終的な計算結果に影響する可能性が高くなります。両者の Cache Line のデータ一貫性を保証するために、マルチコアキャッシュコヒーレンシの MESI プロトコルが登場しました。</p>
<p>MESI に従うため、あるコアがデータを変更したら、他のコアに通知せざるを得ません。「あなたが持っている Cache Line はすでに期限切れです。メモリからもう一度取得し直してください」と。これがパフォーマンスの損失につながります。</p>
<p>複数の CPU コアが、同一の Cache Line 内にある互いに無関係な変数を並行して変更するとき、False Sharing（偽共有）の問題が発生します。それぞれが操作しているのは明らかに異なるデータなのに、同じキャッシュラインを共有しているために互いに足を引っ張り合うのです。あるコアが Cache Line 内のデータを更新すると、他のコアは行全体のデータを再ロードしなければならず、この過程にはかなりの時間がかかります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="false-sharing-を回避するには">False Sharing を回避するには<a href="https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang#false-sharing-%E3%82%92%E5%9B%9E%E9%81%BF%E3%81%99%E3%82%8B%E3%81%AB%E3%81%AF" class="hash-link" aria-label="False Sharing を回避するには への直接リンク" title="False Sharing を回避するには への直接リンク" translate="no">​</a></h2>
<p>最も一般的な手段は Cache Line パディング（Padding）です。原理は、変数の間に無用なデータを詰め込み、ホットな変数を異なるキャッシュラインに隔離することで、異なる CPU コアがアクセスするデータが同一の Cache Line 上に載らないようにし、False Sharing を回避するというものです。</p>
<p>このほかにも、プログラミングレベルのテクニックがいくつかあります。たとえば、頻繁に変更が必要なデータを意図的に異なる Cache Line に配置する、あるいはスレッドローカルストレージ（Thread Local Storage、TLS）を使って異なるスレッド間のデータ競合を回避する、などです。ハードウェアレベルでは、Intel が Cache Allocation Technology（CAT）を提供しており、プログラマが Cache の割り当て方式を明示的に制御できます。さらに Cache Partitioning という技術もあり、Cache を複数の領域に分割して各領域を異なるスレッドに割り当てて使用させることで、同様に異なるスレッド間のデータ競合と False Sharing の問題を回避できます。</p>
<p>False Sharing のほかにも、Cache に関連する注意すべきパフォーマンス問題がいくつかあります。たとえば Cache Miss です。CPU が Cache に存在しないデータにアクセスする必要があるとき Cache Miss が発生し、このとき CPU はメモリからデータを読み取る必要があり、この過程には長い時間がかかります。Cache Miss を減らすには、プリフェッチ（Prefetching）や適切なキャッシュ置換アルゴリズム（Cache Replacement Algorithm）などの手段を活用できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2022/11/03/wei-gong-xiang#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>Cache はコンピュータアーキテクチャにおいて性能を高める鍵となる一環ですが、読み書きの単位としての Cache Line は、False Sharing のような隠れた罠ももたらしました。複数のコアが同一キャッシュライン内の異なる変数を並行して変更すると、MESI プロトコルの一貫性維持のオーバーヘッドによって性能は目に見えて低下します。回避の考え方は実に直接的です。パディングやデータレイアウトによって、ホットな変数それぞれにキャッシュラインを 1 本ずつ独占させればよいのです。高並行なコードを書くときは、データがメモリ上でどのように配置されるかに気を配ることで、この種の目に見えないパフォーマンスキラーを避けられることが多いのです。</p>]]></content:encoded>
            <category>Java</category>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[セキュアコンテナランタイム Kata]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa</guid>
            <pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[従来のコンテナはホストとカーネルを共有するため、分離性の面でセキュリティ上の懸念があります。本記事では、軽量な仮想マシンによってカーネルレベルの分離を実現する Kata のアプローチと、その特徴・課題を紹介します。]]></description>
            <content:encoded><![CDATA[<p>従来の軽量コンテナは Linux の namespace と cgroup によってコンテナを分離しており、軽量・シンプル・高効率というメリットをもたらす一方で、セキュリティ上の懸念も抱えています。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="カーネル共有のリスク">カーネル共有のリスク<a href="https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa#%E3%82%AB%E3%83%BC%E3%83%8D%E3%83%AB%E5%85%B1%E6%9C%89%E3%81%AE%E3%83%AA%E3%82%B9%E3%82%AF" class="hash-link" aria-label="カーネル共有のリスク への直接リンク" title="カーネル共有のリスク への直接リンク" translate="no">​</a></h2>
<p>コンテナはシステム内の他のプロセスからリソースが分離された実行環境を提供しますが、ホストマシンとはカーネルを共有しています。もしあるコンテナが攻撃を受け、そこからホストのカーネルまで落とされてしまえば、他のコンテナも必然的にすべてクラッシュし、その被害は計り知れません。</p>
<p>では、安全でありながら効率も比較的高いコンテナ技術はないのでしょうか。そこで登場したのが Kata コンテナ技術です。</p>
<p><a href="https://katacontainers.io/" target="_blank" rel="noopener noreferrer" class="">Kata Containers - Open Source Container Runtime Software</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="kata-の実現アプローチ">Kata の実現アプローチ<a href="https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa#kata-%E3%81%AE%E5%AE%9F%E7%8F%BE%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81" class="hash-link" aria-label="Kata の実現アプローチ への直接リンク" title="Kata の実現アプローチ への直接リンク" translate="no">​</a></h2>
<p>Kata は実際には、軽量な仮想マシンを作成することでコンテナ間のリソース分離を実現し、その仮想マシンの中でコンテナランタイムを動かします。これによりコンテナは専用カーネル内で実行され、ネットワーク・I/O・メモリの分離が提供されるほか、仮想化 VT 拡張を通じてハードウェアによる強制的な分離も利用できます。セキュリティを提供しながら、依然として高いパフォーマンスを保っています。一部の性能を犠牲にする必要はあるものの（現在のバージョンはすでに 3.0 に達しており、パフォーマンスは大幅に向上しています）、そのメリットは明白です。単一のコンテナに問題が発生したり悪意ある攻撃を受けたりしても、ホストマシンに影響が及ぶことは決してなく、間接的に他の稼働中のコンテナも保護されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="オープンソースリポジトリ">オープンソースリポジトリ<a href="https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa#%E3%82%AA%E3%83%BC%E3%83%97%E3%83%B3%E3%82%BD%E3%83%BC%E3%82%B9%E3%83%AA%E3%83%9D%E3%82%B8%E3%83%88%E3%83%AA" class="hash-link" aria-label="オープンソースリポジトリ への直接リンク" title="オープンソースリポジトリ への直接リンク" translate="no">​</a></h2>
<p>Github : <a href="https://github.com/kata-containers/kata-containers" target="_blank" rel="noopener noreferrer" class="">https://github.com/kata-containers/kata-containers</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-image-80526d4327e0fdb37b1097e86ec4fe97.webp" width="640" height="309" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="特徴">特徴<a href="https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa#%E7%89%B9%E5%BE%B4" class="hash-link" aria-label="特徴 への直接リンク" title="特徴 への直接リンク" translate="no">​</a></h2>
<p>Kata Containers には、さらに次のような特徴があります。</p>
<ol>
<li class="">柔軟性：Kata Containers は複数のオペレーティングシステムとコンテナランタイム環境で動作します。Linux、Windows、FreeBSD など複数の OS をサポートし、Docker、CRI-O、containerd といった複数のコンテナランタイム環境と互換性があります。</li>
<li class="">拡張性：Kata Containers は大規模なデプロイにも容易にスケールできます。Kubernetes などのコンテナオーケストレーションプラットフォームに統合でき、大量のコンテナを管理できます。</li>
<li class="">インストールの容易さ：Kata Containers のインストールは非常に簡単です。パッケージ管理ツール（apt、yum、zypper など）でインストールでき、コンテナの設定と起動を素早く行える便利なコマンドラインツールも用意されています。</li>
<li class="">コミュニティサポート：Kata Containers には、世界中の開発者が共同でメンテナンス・発展させている活発なオープンソースコミュニティがあります。このコミュニティはさまざまなドキュメント、チュートリアル、サポートを提供しており、Kata Containers をより深く理解し活用する助けになります。</li>
<li class="">セキュリティ：専用カーネル内で実行され、ネットワーク・I/O・メモリの分離を提供し、仮想化 VT 拡張を備えたハードウェアによる強制的な分離も利用できます。</li>
<li class="">互換性：OCI コンテナフォーマット、Kubernetes CRI インターフェース、そして従来の仮想化技術といった業界標準をサポートしています。</li>
<li class="">軽量：フル機能の VM の中にコンテナをネストさせる必要がありません。</li>
<li class="">高パフォーマンス：標準的な Linux コンテナに近いパフォーマンスを提供します（それでも性能の損失は存在します）。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="課題">課題<a href="https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa#%E8%AA%B2%E9%A1%8C" class="hash-link" aria-label="課題 への直接リンク" title="課題 への直接リンク" translate="no">​</a></h2>
<p>Kata Containers の弱点の一部を挙げます。</p>
<ol>
<li class="">性能の損失：Kata Containers のパフォーマンスは大幅に向上しているものの、従来の Linux コンテナと比べると、依然として一定の性能損失があります。これは Kata Containers が軽量な仮想マシンでコンテナ分離を実現しており、その分の追加オーバーヘッドが生じるためです。</li>
<li class="">インストールと設定の複雑さ：従来の Linux コンテナと比べると、Kata Containers のインストールと設定はより複雑になる場合があります。QEMU や KVM といった仮想化ソフトウェア、そして Kata Containers ランタイムなどのコンポーネントをインストール・設定する必要があります。</li>
<li class="">ハードウェア要件の高さ：Kata Containers は仮想化 VT 拡張をサポートするハードウェアを必要とするため、古いサーバーでの利用が制限される可能性があります。</li>
<li class="">エコシステムの未成熟さ：Kata Containers には活発なオープンソースコミュニティがあるものの、他のコンテナ技術と比べると、エコシステムはまだ十分に成熟していない可能性があります。つまり、特定のニーズを満たすためには、ユーザー自身がツールやプラグインを書く必要が出てくるかもしれません。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/01/an-quan-rong-qi-zhi-k-a-fa#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Kata の核心となる考え方は、軽量な仮想マシンと引き換えにカーネルレベルの分離を得ることです。各コンテナは専用カーネル内で動作するため、単一のコンテナが侵害されてもホストや他のコンテナには波及しません。その代償として、一定の性能損失、より複雑なインストール・設定、そしてハードウェア仮想化機能への要件があります。Kata は従来のコンテナを置き換えるものではなく、新しいトレードオフの選択肢を提供するものです。マルチテナントを扱うワークロードや高い分離性が求められる場合には、その部分に Kata を導入する価値があります。逆にそうでなければ、標準的な Linux コンテナのほうが依然としてシンプルな選択肢です。</p>]]></content:encoded>
            <category>Docker</category>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[Istio のトラフィックミラーリング]]></title>
            <link>https://tanzhuo.xyz/ja/2022/11/01/is-ti-o</link>
            <guid>https://tanzhuo.xyz/ja/2022/11/01/is-ti-o</guid>
            <pubDate>Tue, 01 Nov 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[k8s クラスタで Istio の Envoy プロキシを利用してトラフィックミラーリングを実現:実際のリクエストを複製してシャドウサービスに送り、レスポンスは破棄されるため業務に完全に透過的。原理、VirtualService の設定の書き方、実際にハマったポイントを整理します。]]></description>
            <content:encoded><![CDATA[<p>k8s クラスタに Service Mesh として Istio をデプロイした後は、Envoy プロキシがトラフィックを転送する特性を利用して、設定ファイルに数行追加するだけでトラフィックミラーリングを有効にできます。ミラーリングされたトラフィックのレスポンスはクライアントに返らず、既存の業務コードに対して完全に透過的です。うまく使えば本番環境で大きな価値を発揮します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="トラフィックミラーリングとは">トラフィックミラーリングとは<a href="https://tanzhuo.xyz/ja/2022/11/01/is-ti-o#%E3%83%88%E3%83%A9%E3%83%95%E3%82%A3%E3%83%83%E3%82%AF%E3%83%9F%E3%83%A9%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF" class="hash-link" aria-label="トラフィックミラーリングとは への直接リンク" title="トラフィックミラーリングとは への直接リンク" translate="no">​</a></h2>
<p>トラフィックミラーリングはシャドウトラフィック（Shadow Traffic）とも呼ばれます。Envoy がクライアントのリクエストを実サービスに転送すると同時に、コピーを 1 つ作って指定のミラーサービスに送信します。ポイントは 2 つあります。</p>
<p>1）ミラーリクエストは「送りっぱなし」（fire-and-forget）です。Envoy はミラーサービスのレスポンスを待たず、ましてやそれをクライアントに返すこともありません。ミラーサービスの処理が遅くても、エラーになっても、ダウンしても、メインの経路には影響しません。</p>
<p>2）ミラーリングはトラフィックが業務コンテナに入る前に Sidecar プロキシによって行われるため、業務コードには一切の変更が不要で、ミラートラフィックを受け取るサービスをもう 1 セットデプロイするだけで済みます。</p>
<p>公式ドキュメントはこちら:<a href="https://istio.io/latest/zh/docs/tasks/traffic-management/mirroring/" target="_blank" rel="noopener noreferrer" class="">镜像</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="典型的なユースケース">典型的なユースケース<a href="https://tanzhuo.xyz/ja/2022/11/01/is-ti-o#%E5%85%B8%E5%9E%8B%E7%9A%84%E3%81%AA%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9" class="hash-link" aria-label="典型的なユースケース への直接リンク" title="典型的なユースケース への直接リンク" translate="no">​</a></h2>
<p>1、テスト環境:テストバージョンのコンテナサービスが本番インスタンスの実トラフィックを使うことができ、本番の重要な経路には影響しません。例えばステージング環境でリアルタイムのテスト検証をより適切に行えるようになり、リリース後に発生するさまざまな異常を減らし、開発チームにリリースへのより強い自信を与えてくれます 😂。リリースはほぼ見通しの立った状態にでき、夜遅くまで残業してデプロイする必要もなくなります。</p>
<p>2、データ収集:リクエスト情報を同期的に収集できるので、別のコンテナでリスク管理分析やログ記録を行い、対応するユーザープロファイル情報を得ることができます。</p>
<p>3、パフォーマンスと互換性の検証:新バージョンのサービス、リファクタリング後の API、フレームワークや依存を入れ替えた実装は、まずミラートラフィックでしばらく「並走」させ、新旧両側のログとメトリクスを比較し、挙動が一致することを確認してから正式にトラフィックを切り替えられます。</p>
<p>4、問題の再現:本番でまれに発生する異常なリクエストはテスト環境で再現するのが難しいものですが、詳細ログやデバッグスイッチを有効にしたインスタンスにトラフィックをミラーリングすれば、一次情報の現場をそのまま手に入れられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="設定方法">設定方法<a href="https://tanzhuo.xyz/ja/2022/11/01/is-ti-o#%E8%A8%AD%E5%AE%9A%E6%96%B9%E6%B3%95" class="hash-link" aria-label="設定方法 への直接リンク" title="設定方法 への直接リンク" translate="no">​</a></h2>
<p>Istio ではトラフィックミラーリングは VirtualService の <code>mirror</code> フィールドで宣言します。<code>httpbin</code> サービスに v1、v2 の 2 つのバージョンがあり、v1 に来るトラフィックのコピーを v2 にミラーリングしたい場合、書き方はおおよそ次のようになります。</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> networking.istio.io/v1beta1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> VirtualService</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> httpbin</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">hosts</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> httpbin</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">http</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">route</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 実トラフィック:すべて v1 に転送し、レスポンスをクライアントに返す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">destination</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">host</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> httpbin</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">subset</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> v1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">weight</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">100</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ミラートラフィック:コピーを v2 に送り、レスポンスは破棄される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">mirror</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">host</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> httpbin</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">subset</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> v2</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ミラーリングのサンプリング比率。未指定ならデフォルト 100%</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">mirrorPercentage</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">100.0</span><br></div></code></pre></div></div>
<p>各フィールドの意味は次のとおりです。</p>
<p>1）<code>route.destination</code> はメインルートで、実リクエストを誰に送るかを決めます。クライアントが受け取るのはこのレスポンスです。</p>
<p>2）<code>mirror</code> はミラーリング先を指定します。同じく host + subset の組み合わせで、subset は事前に DestinationRule で定義しておく必要があります。</p>
<p>3）<code>mirrorPercentage</code> はミラーリングのサンプリング比率を制御します。ミラーリング先のキャパシティが本番より小さい場合は、いきなり 100% にするより、1%、10% のように段階的に増やしていくほうがずっと安全です。</p>
<p>設定を書き終えたら <code>kubectl apply -f</code> で反映され、業務 Pod の再起動は一切不要です。これも Sidecar 方式の利点の一つです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/11/01/is-ti-o#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）ミラートラフィックの Host ヘッダには <code>-shadow</code> サフィックスが付加されます。例えば <code>httpbin:8000</code> は <code>httpbin-shadow:8000</code> になります。ミラーサービスが Host の検証やルーティングをしている場合は、この差異を事前に処理しておく必要があります。逆に、このサフィックスはログの中で実トラフィックとシャドウトラフィックを区別するのにも使えます。</p>
<p>2）ミラーリングされるのはリクエストであって、冪等性ではありません。書き込み系 API がミラーリングされると、シャドウ環境でもう一度実行されます。ミラーサービスが同じデータベース、同じ下流につながっていると、重複書き込みやサードパーティへの重複呼び出しを引き起こす可能性があります。ミラーサービスには独立したデータソースをつなぐか、ミラー側で書き込み操作をインターセプトするかのどちらかが必要です。</p>
<p>3）ミラーリングは送信帯域と Envoy のオーバーヘッドを増幅します。リクエストボディが大きい、QPS が高いサービスでは特に顕著なので、まず <code>mirrorPercentage</code> を小さい比率にして検証してから増やしましょう。</p>
<p>4）ミラーサービスのエラーはクライアントに影響しません。これは利点であると同時に盲点でもあります——シャドウ環境が落ちてもユーザーは気づかないため、別途監視とアラートを設定する必要があります。さもないと「並走」がとっくに止まっていたのに誰も気づかない、ということになりかねません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/11/01/is-ti-o#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>トラフィックミラーリングは「本番トラフィックで検証する」というリスクの高い行為を、低コストな操作に変えてくれます。VirtualService の設定を一つ書くだけで、業務コードはゼロ変更、メイン経路への影響もゼロです。ステージング検証、リスク管理分析、問題の再現といったシナリオはどれも恩恵を受けられます。主に気をつけるべきは、書き込み操作の副作用、Host ヘッダの <code>-shadow</code> サフィックス、そしてミラー側自身の監視です。この数点をきちんと処理すれば、常備しておく価値のある非常に優れたツールになります。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[分散トランザクションと Seata]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/31/seata</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/31/seata</guid>
            <pubDate>Mon, 31 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[マイクロサービスアーキテクチャでは各ビジネスモジュールが独立した DB ストレージノードを持ち、DB を跨ぐ呼び出しが分散トランザクション問題をもたらします。本記事では Seata の役割分担と、AT、TCC、Saga、XA という 4 つのモードの原理とトレードオフを整理します。]]></description>
            <content:encoded><![CDATA[<p>エンタープライズアプリケーション開発では、分散フレームワークの発展に伴い、本番環境には多くのデータベースインスタンスが存在するようになりました。特にマイクロサービスの分野では、通常、各ビジネス Service モジュールごとに自身のビジネス専用の DB ストレージノードを対応させ、さらにそのストレージノードを高可用構成でデプロイします。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="トランザクション問題">トランザクション問題<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="トランザクション問題 への直接リンク" title="トランザクション問題 への直接リンク" translate="no">​</a></h2>
<p>Service のビジネスロジックを書くとき、1 つのビジネス操作はしばしば他のビジネスモジュールをリモート呼び出しし、生成されたデータは異なるストレージノードに永続化されます。複数のデータベースインスタンス間でトランザクションの ACID 特性を保証しようとすると、分散トランザクションの問題に直面します。</p>
<p>分散トランザクションが実現すべきことは、次の 2 点に要約できます。</p>
<p>1）ビジネスの呼び出しチェーン全体がすべて成功した場合、その呼び出しチェーンに対応するデータベースすべてでトランザクションをコミットする。</p>
<p>2）呼び出しチェーンの中で例外がスローされた場合、その呼び出しチェーンに対応するデータベースすべてでロールバックを行う。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="seata-の役割分担">Seata の役割分担<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#seata-%E3%81%AE%E5%BD%B9%E5%89%B2%E5%88%86%E6%8B%85" class="hash-link" aria-label="Seata の役割分担 への直接リンク" title="Seata の役割分担 への直接リンク" translate="no">​</a></h2>
<p>Seata の分散トランザクションソリューションには、一般に次のような役割があります。</p>
<ul>
<li class="">TC (Transaction Coordinator): トランザクションコーディネーター。</li>
<li class="">TM (Transaction Manager): トランザクションマネージャー。AP 側に組み込まれます。</li>
<li class="">RM (Resource Manager): リソースマネージャー。ブランチトランザクション処理のリソースを管理し、TC と通信してブランチトランザクションの登録と状態報告を行い、ブランチトランザクションのコミットまたはロールバックを駆動します。</li>
<li class="">AP (Application Program): RM にアクセスするアプリケーション。</li>
</ul>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-11-145942191-7a2d469f-94c8-4cd2-8c7e-46ad75683636-604f44d73d72d865af48e8fc188c9a9e.webp" width="1534" height="908" class="img_ev3q"></p>
<p><strong>図中の TC トランザクションコーディネーターコンポーネントは単独でデプロイし、マイクロサービスクラスタ全体のレジストリに参加させる必要があります。現在一般的なレジストリはすべてサポートされており、Nacos、Zookeeper、Etcd、Eureka、Consul などが使えます。同時に AP 側で対応する接続設定を行う必要があります。TC モジュールは高可用性を必ず保証しなければなりません。このモジュールがダウンすれば、分散トランザクションは成り立たなくなります。</strong></p>
<p><a href="http://seata.io/zh-cn/docs/user/txgroup/transaction-group-and-ha.html" target="_blank" rel="noopener noreferrer" class="">トランザクショングループと高可用性</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="剛性トランザクションと柔軟トランザクション">剛性トランザクションと柔軟トランザクション<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#%E5%89%9B%E6%80%A7%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A8%E6%9F%94%E8%BB%9F%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3" class="hash-link" aria-label="剛性トランザクションと柔軟トランザクション への直接リンク" title="剛性トランザクションと柔軟トランザクション への直接リンク" translate="no">​</a></h2>
<p>剛性トランザクション: 通常ビジネス側の改修は不要で、強一貫性を持ち、ロールバックと分離性をネイティブにサポートします。並行性は低く、短いトランザクションに適しています。対応する方式: XA プロトコル（2PC、JTA、JTS）、3PC。</p>
<p>柔軟トランザクション: ビジネス側の改修が必要で、結果整合性となり、補償インターフェースとリソースロックインターフェースの実装が必要です。高い並行性に対応でき、長いトランザクションに適しています。対応する方式: TCC/FMT、Saga（ステートマシンモード、Aop モード）、ローカルトランザクションメッセージ、メッセージトランザクション（ハーフメッセージ）。</p>
<p>では、長いトランザクションとは何で、短いトランザクションとは何でしょうか。</p>
<p>長いトランザクション: あるリソース（X ロック、ギャップロック、テーブルロック、ページロック）を長時間占有し、更新するデータが多い、または実行速度が遅く、ビジネスフローが長いものです。他のトランザクションがそのリソースにアクセスするとブロックされて待たされ、デッドロックを引き起こしやすくなります。</p>
<p>短いトランザクション: リソースの占有がごく短時間で、更新するデータが少ない、または実行がすぐ完了し、ビジネスフローが短く、トランザクションがすぐに終わるため、デッドロックが発生する確率は小さくなります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-つのトランザクションモード">4 つのトランザクションモード<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#4-%E3%81%A4%E3%81%AE%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B6%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="4 つのトランザクションモード への直接リンク" title="4 つのトランザクションモード への直接リンク" translate="no">​</a></h2>
<p>現在 Seata は 4 種類の分散トランザクション管理モードをサポートしています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="at-モード">AT モード<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#at-%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="AT モード への直接リンク" title="AT モード への直接リンク" translate="no">​</a></h3>
<p>AT モードは Seata の中で最も特色のあるモードと言えます。要するに最適化された 2PC であり、二相コミットの一種です。このモードを使うには、ローカルデータベースノードに undolog テーブルを作成する必要があります。プロキシの DataSourceProxy がアプリケーションの実行する DML SQL 文をインターセプトし、SQL のセマンティクスを解析してクエリ SQL に変換し、実行前の対応するデータのミラーを undolog テーブルに保存して、第二段階のロールバック用とします。以上のステップはすべてプロキシパターンで完結するため、ビジネスロジックコードへの侵入はゼロで実現されています。</p>
<p>しかしこの方式は完璧ではなく、パフォーマンスの損失がかなり大きくなります。1 回の DML SQL につき、セマンティクス変換とクエリ SQL の実行が 1 回、そしてデータミラーの undolog テーブルへの追加が発生します。第二段階のロールバックが発生した場合はさらにローカルロックを取得し、元のデータミラーへ巻き戻す必要があります。加えて、もう一つ深刻な問題があります。ロールバックの際に、他のローカルトランザクションが Seata のグローバルトランザクション管理を通さずグローバルロックを取得しないまま、ローカルトランザクションで undolog 内のあるデータミラーに対応するデータを直接更新してしまうと、Seata はロールバック時にロールバックミラーと現在のローカルデータが一致しないことを検出し、データ喪失の問題が発生します。これは CAS における ABA 問題に少し似ています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tcc-モード">TCC モード<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#tcc-%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="TCC モード への直接リンク" title="TCC モード への直接リンク" translate="no">​</a></h3>
<p>TCC モードは比較的シンプルで、実際にはこれも 2PC の二相コミットです。AT モードとの違いは、第一段階の prepare（準備）、第二段階の commit、そして rollback のロジックを自分で実装する必要がある点で、Seata のトランザクションマネージャーがこれらの実装メソッドを順に呼び出します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="saga-モード">Saga モード<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#saga-%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="Saga モード への直接リンク" title="Saga モード への直接リンク" translate="no">​</a></h3>
<p>Saga は一連のローカルトランザクションで構成されます。各ローカルトランザクションはデータベースを更新した後、メッセージまたはイベントを発行し、Saga 内の次のローカルトランザクションの実行をトリガーします。あるローカルトランザクションがビジネスルールを満たせず失敗した場合、Saga はその失敗したトランザクションより前にすでにコミット済みのすべてのトランザクションに対して補償操作を実行します。</p>
<p>そのため Saga は実装が最も手間のかかるモードと言えますが、Saga ベースのステートマシンによって、分散トランザクションの各ステップを非常に柔軟に制御できます。長いトランザクションのシーンでは、なおさら自分できめ細かく各ケースを制御する必要があります。分散トランザクション全体の状態に注目するのではなく、各イベントの処理と、自身のメソッドの冪等性さえきちんと担保すればよいのです。ただし Saga のトランザクションには段階的コミットという概念がなく、すべてローカルで直接コミットされます。ロールバックの途中で他のトランザクションがそれらのデータを読み取り、更新してしまうと、ダーティリードやダーティライトが発生します。この状況については、自前で txid の仕組みを実装して回避する必要があるだろうと考えています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="xa-モード">XA モード<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#xa-%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="XA モード への直接リンク" title="XA モード への直接リンク" translate="no">​</a></h3>
<p>XA モードはまずデータベースが XA プロトコルをサポートしていることを前提とします。その実装には 2PC、3PC があり、完全にデータベース自身が提供するため、アプリケーション側は何の処理も必要ありません。本質的には AT モードとよく似ていますが、違いは、データベースが XA トランザクションを実行する過程で、強一貫性のためにデータリソース（X ロック、ギャップロック、テーブルロック、ページロック。データ範囲によって異なります）を占有し続ける点です。長いトランザクションの場合、高並行のシーンでは非常にデッドロックが発生しやすく、データベースのパフォーマンスもかなり大きく低下します。すべて短いトランザクションであれば、このモードを使うことができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/31/seata#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>分散トランザクションの本質は、DB を跨ぐ呼び出しチェーンを、一緒にコミットするか、一緒にロールバックするかのどちらかにすることです。Seata は TC、TM、RM の役割分担によってこれをプロダクトとして仕上げ、AT、TCC、Saga、XA の 4 つのモードを提供しています。AT はデータミラーによってゼロ侵入を実現し、ほとんどの一般的なシーンに適しています。TCC と Saga は補償ロジックをビジネス側に委ねることで、より高い並行性と柔軟性を手に入れます。XA はデータベースのネイティブサポートに依存し、短いトランザクションにのみ適しています。選定の際は、まずビジネスが長いトランザクションか短いトランザクションか、強一貫性を求めるのか結果整合性で許容できるのかを見極めたうえで、当てはまるものを選びましょう。</p>]]></content:encoded>
            <category>DB</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Helm パッケージ管理ツール]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/27/ku-be</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/27/ku-be</guid>
            <pubDate>Thu, 27 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[クラスタ内の yaml ファイルが保守しきれないほど増えたとき、Helm は Chart によって設定を再利用可能でバージョン管理できるアプリケーションパッケージにまとめます。この記事では Helm が解決する問題、中核概念、よく使うコマンドを整理し、あわせて UI 管理ツールも紹介します。]]></description>
            <content:encoded><![CDATA[<p>ご存じのとおり、k8s は複数の node 上でコンテナリソースを管理できますが、各コンテナは対応する pod-yaml ファイルによって管理する必要があります。</p>
<!-- -->
<p>この記事で話したいのは、実は yaml ファイルが制御不能になる問題です。アプリケーション 1 つに Deployment を 1 枚、Service を 1 枚書くだけなら大した負担ではありません。しかしクラスタ内のアプリケーションが増えると、設定ファイルの数は目に見える速さで膨れ上がります。イメージタグを 1 つ変えるのに何枚ものファイルをめくり、環境を 1 セット増やすには設定一式をコピーしてパラメータを 1 つずつ書き換える。この段階まで来ると、人力での yaml 保守はもはや現実的ではありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="大量の-yaml-の話から始めよう">大量の yaml の話から始めよう<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#%E5%A4%A7%E9%87%8F%E3%81%AE-yaml-%E3%81%AE%E8%A9%B1%E3%81%8B%E3%82%89%E5%A7%8B%E3%82%81%E3%82%88%E3%81%86" class="hash-link" aria-label="大量の yaml の話から始めよう への直接リンク" title="大量の yaml の話から始めよう への直接リンク" translate="no">​</a></h2>
<p>まず問題の整理から。ある namespace に数十個の pod を組み合わせてデプロイする必要があるとします。例えば mysql クラスタ、kafka クラスタ。しかも kafka クラスタをデプロイするには先に zookeeper クラスタが必要で、アプリケーションには Redis クラスタや MQ キューのクラスタも必要、さらにアプリケーション自体のデプロイもあり、マイクロサービスは 50～100 個、あるいはそれ以上に及ぶこともあります。</p>
<p>こんなシナリオで yaml 設定ファイルを書けと言われたら、おそらく断りたくなるでしょう。仮に一度デプロイし、苦労の末に完了したとしても、リモートのデータセンターのクラスタにもう一度デプロイしなければならないとしたらどうしますか？ ましてや、複数の異なるデータセンターのクラスタで設定情報を完全に一致させるにはどうすればいいのでしょうか？</p>
<p>これらの yaml の間には暗黙のデプロイ順序があり、大量の重複した設定項目もあります。イメージレジストリのアドレス、リソース制限、ストレージクラス名などは、ほぼすべてのファイルに書き写さなければなりません。共通パラメータが 1 つ変わったとき、どこか 1 箇所でも修正が漏れれば、それは潜在的なリスクです。</p>
<p>というわけで問題は明白です。すべての yaml 設定ファイルを一元管理でき、かつ再利用できるツールが必要なのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="helmk8s-のパッケージマネージャー">Helm：k8s のパッケージマネージャー<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#helmk8s-%E3%81%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC" class="hash-link" aria-label="Helm：k8s のパッケージマネージャー への直接リンク" title="Helm：k8s のパッケージマネージャー への直接リンク" translate="no">​</a></h2>
<p>そこで Helm が誕生しました。その位置づけは CentOS における Yum パッケージ管理ツール、あるいは Debian における apt パッケージ管理ツールに似ています。ただし今回のパッケージ管理は k8s 環境をベースにしています。</p>
<p>Helm における各パッケージは Chart と呼ばれ、1 つの Chart は 1 つのディレクトリです。ディレクトリの中核的な内容は 3 つの部分にまとめられます。<code>Chart.yaml</code> はこのパッケージの名前やバージョンなどのメタ情報を記述し、<code>values.yaml</code> はデフォルトの設定値を格納してインストール時に外部から上書きでき、<code>templates/</code> ディレクトリにはテンプレート構文を含む k8s リソースファイルが置かれます。インストール時、Helm は values をテンプレートにレンダリングして本物の yaml を生成し、それをクラスタに適用します。一度のインストールの結果は Release と呼ばれます。</p>
<p>設定再利用の鍵はこのテンプレート機構にあります。異なるデータセンター、異なる環境が同じ Chart を共有し、それぞれは小さな values ファイルを 1 枚保守するだけ。データセンター間の設定不一致の問題は自然に解消されます。Chart は依存関係も宣言でき、「kafka をデプロイする前に zookeeper が必要」といった関係はサブ Chart に任せられるので、順序を人が覚えておく必要もなくなります。</p>
<p>アプリケーションの公開者は Helm によってアプリをパッケージ化し、依存関係を管理し、バージョンを管理してソフトウェアリポジトリに公開し一元管理できます。k8s へのデプロイが必要になったら、コマンド 1 つで pod アプリケーション一式をデプロイできます。</p>
<p>日常的に使うコマンドは多くありません：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># Chart リポジトリを追加する（よく使われる bitnami リポジトリを例に）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">helm repo </span><span class="token function" style="color:hsl(207, 82%, 66%)">add</span><span class="token plain"> bitnami https://charts.bitnami.com/bitnami</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ローカルにキャッシュされたリポジトリインデックスを更新する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">helm repo update</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># コマンド 1 つでアプリ一式をデプロイ。Chart が必要なリソースをすべて自動作成する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">helm </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> my-mysql bitnami/mysql</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 現在の namespace にデプロイ済みの Release を確認する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">helm list</span><br></div></code></pre></div></div>
<p>デプロイ後のアップグレードとロールバックも Helm が引き受け、変更のたびに新しいリビジョンとして記録されます：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># カスタムの values ファイルでデフォルト設定を上書きしてアップグレードする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">helm upgrade my-mysql bitnami/mysql </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-f</span><span class="token plain"> my-values.yaml</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># アップグレードに問題があれば前のリビジョンにロールバックする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">helm rollback my-mysql</span><br></div></code></pre></div></div>
<p>Helm の公式サイト：</p>
<p><a href="https://helm.sh/" target="_blank" rel="noopener noreferrer" class="">Helm</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="gui-管理kubeapps">GUI 管理：Kubeapps<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#gui-%E7%AE%A1%E7%90%86kubeapps" class="hash-link" aria-label="GUI 管理：Kubeapps への直接リンク" title="GUI 管理：Kubeapps への直接リンク" translate="no">​</a></h2>
<p>公式の中国語ドキュメントはかなり詳しく書かれているので、興味があれば読んでみてください。ここではさらに、Helm の UI 管理ツールを 1 つおすすめします。企業の Helm パッケージ情報をより手軽に管理できます。</p>
<p>kubeapps</p>
<p><a href="https://kubeapps.dev/" target="_blank" rel="noopener noreferrer" class="">Kubeapps - Kubeapps</a></p>
<p>Kubeapps 自体がクラスタ内にデプロイされる Web アプリケーションで、画面上でリポジトリの Chart を閲覧し、フォームに設定を入力してインストールを完了でき、既存の Release の確認やアップグレードもできます。全員にコマンドラインでクラスタを操作させるわけにいかないチームにとって、この種のツールはかなりの手間を省いてくれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-カスタム設定は自分の-values-ファイルに置く">1) カスタム設定は自分の values ファイルに置く<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#1-%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%A0%E8%A8%AD%E5%AE%9A%E3%81%AF%E8%87%AA%E5%88%86%E3%81%AE-values-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E7%BD%AE%E3%81%8F" class="hash-link" aria-label="1) カスタム設定は自分の values ファイルに置く への直接リンク" title="1) カスタム設定は自分の values ファイルに置く への直接リンク" translate="no">​</a></h3>
<p>デフォルト値の上書きには、Chart 内の <code>values.yaml</code> を直接書き換えるのではなく、できるだけ <code>-f</code> で独立した values ファイルを渡すようにしましょう。前者は Chart のアップグレード時にそのまま使い回せますが、後者の変更は Chart の更新時に上書きされて失われやすいのです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-アンインストールはよく考えてから">2) アンインストールはよく考えてから<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#2-%E3%82%A2%E3%83%B3%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%AF%E3%82%88%E3%81%8F%E8%80%83%E3%81%88%E3%81%A6%E3%81%8B%E3%82%89" class="hash-link" aria-label="2) アンインストールはよく考えてから への直接リンク" title="2) アンインストールはよく考えてから への直接リンク" translate="no">​</a></h3>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p><code>helm uninstall</code> は Release に関連するリソースを一括で削除します。mysql のようなステートフルなアプリケーションでは、実行前にストレージボリュームの保持ポリシーを確認し、データごと消してしまわないようにしてください。</p></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-chart-のバージョンとアプリのバージョンは別物">3) Chart のバージョンとアプリのバージョンは別物<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#3-chart-%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%A8%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3%E3%81%AF%E5%88%A5%E7%89%A9" class="hash-link" aria-label="3) Chart のバージョンとアプリのバージョンは別物 への直接リンク" title="3) Chart のバージョンとアプリのバージョンは別物 への直接リンク" translate="no">​</a></h3>
<p><code>Chart.yaml</code> の version は Chart 自体のパッケージバージョンを指し、appVersion のほうが中のソフトウェアのバージョンに対応します。アップグレード前にどちらが変わるのかをよく確認し、テンプレートを変えただけのつもりが実はアプリまでバージョンアップしていた、という事態を避けましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/27/ku-be#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Helm はあちこちに散らばった yaml を Chart にまとめ、テンプレート + values という方式で設定の再利用と複数環境の一貫性の問題を解決し、デプロイ・アップグレード・ロールバックをコマンド 1 つに収束させました。規模が小さいうちは手書きの yaml でもなんとかなりますが、アプリケーションが増えてきたら、早めに Helm というパッケージ管理のレイヤーを導入することで、後々の大量の反復作業を省けます。さらに Kubeapps のような UI ツールを組み合わせれば、チームで協働する際の敷居もぐっと下がります。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[Kubernetes Pod Yamlファイル解説]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2</guid>
            <pubDate>Thu, 27 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Pod YAML をフィールドごとに注釈:metadata、containers、プローブ、リソース制限、volumes などよく使う設定の意味と使い方を解説し、スペルの落とし穴と kubectl explain の活用テクニックも整理します。]]></description>
            <content:encoded><![CDATA[<p>Pod は Kubernetes における最小のスケジューリング単位であり、ほぼすべてのワークロードは最終的に Pod の YAML 定義に行き着きます。このノートでは Pod YAML の全フィールドの注釈を整理しました。正確な情報は <code>kubectl explain</code> のクエリ結果を基準にしてください。例:<code>kubectl explain pod.spec.volumes</code>。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-pod-yaml-を読み解く必要があるのか">なぜ Pod YAML を読み解く必要があるのか<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#%E3%81%AA%E3%81%9C-pod-yaml-%E3%82%92%E8%AA%AD%E3%81%BF%E8%A7%A3%E3%81%8F%E5%BF%85%E8%A6%81%E3%81%8C%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ Pod YAML を読み解く必要があるのか への直接リンク" title="なぜ Pod YAML を読み解く必要があるのか への直接リンク" translate="no">​</a></h2>
<p>普段 Deployment や StatefulSet でアプリケーションをデプロイしていますが、それらの <code>spec.template</code> 部分は本質的に Pod のテンプレートです。コンテナが起動しない、プローブが失敗する、マウントパスが間違っているといった問題のトラブルシューティングは、最終的に Pod 定義に立ち返ってフィールドを一つずつ確認することになります。毎回ドキュメントをめくるより、よく使うフィールドの意味を一度まとめて把握しておく方が効率的です。</p>
<p>フィールドに自信がないときは、API 自身に聞くのが最も確実です:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># あるフィールドのドキュメント説明を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl explain pod.spec.containers.livenessProbe</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># あるフィールド配下の全サブフィールドを再帰的に一覧表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl explain pod.spec.volumes </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--recursive</span><br></div></code></pre></div></div>
<p><code>kubectl explain</code> はクラスターの OpenAPI Schema を直接読み取るため、現在のクラスターバージョンと厳密に一致しており、どこかから書き写した注釈表よりもずっと正確です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="全フィールド注釈リファレンス">全フィールド注釈リファレンス<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#%E5%85%A8%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E6%B3%A8%E9%87%88%E3%83%AA%E3%83%95%E3%82%A1%E3%83%AC%E3%83%B3%E3%82%B9" class="hash-link" aria-label="全フィールド注釈リファレンス への直接リンク" title="全フィールド注釈リファレンス への直接リンク" translate="no">​</a></h2>
<p>以下は注釈付きの完全なリファレンスです(注釈はクイックリファレンス用であり、正確には <code>kubectl explain</code> を基準にしてください):</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token key atrule" style="color:hsl(29, 54%, 61%)">apiVersion</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> v1            //バージョン</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">kind</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> pod                 //リソースタイプ、pod</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">metadata</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                 //メタデータ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String            //メタデータ、podの名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">namespace</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String       //メタデータ、podのネームスペース</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">labels</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                 //メタデータ、ラベルのリスト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //メタデータ、ラベルの名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">annotations</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">            //メタデータ、カスタムアノテーションのリスト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //メタデータ、カスタムアノテーションの名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                     //pod内のコンテナの詳細定義</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">containers</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">             //pod内のコンテナのリスト、複数のコンテナを持てる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String          //コンテナ名</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">image</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String         //コンテナのイメージ名</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">imagesPullPolicy</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">Always</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token plain">Never</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token plain">IfNotPresent</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">//イメージ取得のポリシー</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">常にプル、プルしない、ローカルにイメージがあればプルしない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">command</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">String</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">     //コンテナの起動コマンドリスト(未設定の場合はイメージビルド時の起動コマンドを使用)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">args</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">String</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">        //コンテナの起動引数リスト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">workingDir</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String    //コンテナの作業ディレクトリ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">volumeMounts</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">         //コンテナ内部にマウントするストレージボリュームの設定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //Pod定義の共有ストレージボリューム名を利用。volumes</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">部分で定義した共有ストレージボリューム名を使うこと</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">mountPath</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String   //ストレージボリュームをコンテナ内でMountする絶対パス。512文字未満にすること</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">readOnly</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> boolean   //読み取り専用モードかどうか。デフォルトは読み書きモード</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">ports</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                //コンテナが公開するポート番号のリスト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //ポートの名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">containerPort</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> int  //コンテナが公開するポート</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">hostPort</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> int       //コンテナのあるホストがリッスンするポート(コンテナ公開ポートをホストのポートにマッピング)。デフォルトはcontainerPortと同じ。hostPortを設定すると、同一ホスト上でそのコンテナの2つ目のレプリカを起動できない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">protocol</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String    //ポートのプロトコル。TCPとUDPをサポート、デフォルトはTCP</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">env</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                  //コンテナ実行前に設定する環境変数のリスト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //環境変数の名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String       //環境変数の値</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">resources</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">            //リソース制限とリソース要求の設定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">limits</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">             //リソース制限の設定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">cpu</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Srting       //CPU制限。単位はcore数。docker run </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">cpu</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">sharesパラメータに使われる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">memory</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String    //メモリ制限。単位はMiB、GiBなど。docker run </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain">memoryパラメータに使われる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">requeste</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">           //リソース要求の設定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">cpu</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String       //cpu要求。単位はcore数。コンテナ起動時の初期利用可能量</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">memory</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String    //メモリ要求。単位はMiBまたはGiB。コンテナ起動時の初期利用可能量</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">livenessProbe</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">        //pod内コンテナのヘルスチェック設定。指定回数応答がなければ、そのコンテナを自動的に再起動する。プローブ方式はexec、httpGet、tcpSocket</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">exec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">               //execプローブ方式</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">command</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">String</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> //exec方式で指定するコマンドまたはスクリプト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">httpGet</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">            //httpgetでヘルスチェック。pathとportの指定が必要</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">path</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String      //URLパス(対応するドメインまたはIPアドレスの部分を除いたもの)</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">port</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> number      //対応するポート  </span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">host</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String      //ドメインまたはIPアドレス</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">scheme</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Srtring   //対応する検査プロトコル。httpなど</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">httpHeaders</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">      //ヘッダー情報を指定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> Stirng    //ヘッダー情報の名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">          </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">value</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String   //ヘッダー情報の値</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">tcpSocket</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">          //tcpSocketでヘルスチェック</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">port</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> number      //プローブ対象のポート番号</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">initialDelaySeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> 0//コンテナ起動完了後、最初のプローブまでの時間。単位はs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">timeoutSeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> 0     //プローブの応答待ちタイムアウト時間。単位はs、デフォルトは1。タイムアウトするとコンテナは不健全と判断され再起動される</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">periodSeconds</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> 0      //定期プローブの間隔設定。単位はs、デフォルトは10</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">successThreshold</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> 0   //何回成功したら成功と見なすか</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">failureThreshold</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> 0   //何回失敗したら失敗と見なすか</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">securityContext</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">        //セキュリティ設定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">privileged</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> false     //</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">restartPolicy</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">Always</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token plain">Never</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">|</span><span class="token plain">OnFailure</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">//再起動ポリシー。終了したら必ず再起動 / 正常終了(終了コード0)以外の非0終了コードで終了した場合のみ再起動 / Pod終了後に終了コードをmasterへ報告しPodを再起動しない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">nodeSelector</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> object      //NodeのLabelを設定。key</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">value形式で指定し、PodはこれらのLabelを持つNodeにスケジューリングされる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">imagePullSecrets</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">         //イメージのpull時に使うSecrets名。name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">sercretkey形式で指定</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String            //参照名</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">hostNetwork</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> false        //ホストネットワークモードを使うかどうか。デフォルトはfalse。trueにするとホストのネットワークを使用しdockerブリッジを使わない。このPodは同一マシン上で2つ目のレプリカを起動できない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">volumes</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                  //このpodに定義する共有ストレージボリュームのリスト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String            //共有ストレージボリューム名。1つのPod内で各ストレージボリュームに名前を定義し、spec</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.containers</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.volumeMounts</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.nameから参照する。タイプは多数あり、emptyDir、hostPathなど</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">emptyDir</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain">            //emptyDirタイプのストレージボリューム。一時ディレクトリでPodとライフサイクルを共にする。空オブジェクト</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">hostPath</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">               //hostPathタイプのストレージボリューム。Podのあるホストのディレクトリをマウントすることを表し、volumes</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain">.hostNetwork.pathで指定する</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">path</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> string          //Podのあるホストのディレクトリ。コンテナ内でmountされるディレクトリに使われる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">secret</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                 //secretタイプのストレージボリューム。クラスターで事前定義されたsecretオブジェクトをコンテナ内部にマウントすることを表す</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">secretName</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String    //ストレージボリューム名</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">items</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                 //Secretオブジェクト内の特定のKeyのみをマウントしたい場合に使用</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">key</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String         //keyの値</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">path</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //マッピングファイルの相対パス</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">configMap</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">              //configMapタイプのストレージボリューム。クラスターで事前定義されたconfigMapオブジェクトをコンテナ内部にマウントすることを表す</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">name</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String          //使用するconfigMapの名前</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">items</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">                //ConfigMapオブジェクト内の特定のKeyのみをマウントしたい場合に使用</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">key</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String         //keyを定義</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">path</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> String        //マッピングファイルの相対パス</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="いくつかの重要フィールドの詳しい説明">いくつかの重要フィールドの詳しい説明<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#%E3%81%84%E3%81%8F%E3%81%A4%E3%81%8B%E3%81%AE%E9%87%8D%E8%A6%81%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%81%AE%E8%A9%B3%E3%81%97%E3%81%84%E8%AA%AC%E6%98%8E" class="hash-link" aria-label="いくつかの重要フィールドの詳しい説明 への直接リンク" title="いくつかの重要フィールドの詳しい説明 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-command-と-args">1) command と args<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#1-command-%E3%81%A8-args" class="hash-link" aria-label="1) command と args への直接リンク" title="1) command と args への直接リンク" translate="no">​</a></h3>
<p><code>command</code> はコンテナランタイムの entrypoint に対応し、<code>args</code> はそれに渡す引数に対応します。両方とも書かない場合はイメージ付属の ENTRYPOINT と CMD が使われ、<code>args</code> だけ書いた場合はイメージの ENTRYPOINT が保持されて CMD が上書きされ、<code>command</code> を書くとイメージ内の ENTRYPOINT と CMD はどちらも効かなくなります。「コンテナが起動直後に終了する」問題を調査するときは、まずこの 2 つのフィールドがイメージのデフォルト起動コマンドを意図せず上書きしていないかを確認しましょう。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-resources-と-limits">2) resources:requests と limits<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#2-resources-%E3%81%A8-limits" class="hash-link" aria-label="2) resources:requests と limits への直接リンク" title="2) resources:requests と limits への直接リンク" translate="no">​</a></h3>
<p><code>requests</code> はスケジューリングの根拠です——スケジューラーはこれを基にノードの残リソースが足りるかを判断します。<code>limits</code> は実行時の上限です——メモリが limit を超えるとコンテナは OOMKill され、CPU が limit を超えるとスロットリングされます(コンテナは kill されません)。両方とも未設定の場合、Pod は BestEffort の QoS クラスで動作し、ノードのリソースが逼迫したときに真っ先に追い出されます。プロダクション環境では少なくとも requests は設定することをおすすめします。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-ヘルスプローブ">3) ヘルスプローブ<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#3-%E3%83%98%E3%83%AB%E3%82%B9%E3%83%97%E3%83%AD%E3%83%BC%E3%83%96" class="hash-link" aria-label="3) ヘルスプローブ への直接リンク" title="3) ヘルスプローブ への直接リンク" translate="no">​</a></h3>
<p><code>livenessProbe</code> はコンテナがまだ生きているかを判定し、失敗が <code>failureThreshold</code> 回に達すると kubelet がコンテナを再起動します。これ以外に <code>readinessProbe</code>(準備完了かを判定。失敗しても Pod が Service のエンドポイントから外されるだけで、コンテナは再起動されません)があり、フィールド構造は完全に同じです。<code>initialDelaySeconds</code> にはアプリケーションの起動時間を十分に確保してください。そうしないと、アプリが起動しきる前にプローブに「死んでいる」と判定され、再起動を繰り返すループに陥ります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-volumes-と-volumemounts">4) volumes と volumeMounts<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#4-volumes-%E3%81%A8-volumemounts" class="hash-link" aria-label="4) volumes と volumeMounts への直接リンク" title="4) volumes と volumeMounts への直接リンク" translate="no">​</a></h3>
<p>ストレージは「2 段階」の宣言です。まず <code>spec.volumes</code> でボリューム(emptyDir、hostPath、secret、configMap などのタイプ)を定義し、次にコンテナの <code>volumeMounts</code> で <code>name</code> によって参照し、マウントパスを指定します。両側の <code>name</code> は完全に一致していなければならず、これは初心者が最もよく遭遇するエラーの原因のひとつです。emptyDir は Pod の削除とともに破棄されるため一時データにしか向きません。hostPath はホストのディレクトリを直接マウントするため、Pod が別ノードに移るとデータが「消えた」ように見えます。一般にログ収集のようなノードレベルのユースケースにのみ使います。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>上の注釈表は界隈で広く出回っていますが、書き写された版にはいくつかスペルの問題が混入しており、そのままコピーすると apply でエラーになるか、フィールドが静かに無視されます:</p>
<ul>
<li class=""><code>kind: pod</code> は <code>kind: Pod</code> が正しく、リソースタイプは先頭が大文字です。</li>
<li class=""><code>imagesPullPolicy</code> の正しいフィールド名は <code>imagePullPolicy</code> で、s は付きません。</li>
<li class=""><code>resources</code> 配下の <code>requeste</code> の正しい書き方は <code>requests</code> です。</li>
<li class="">hostPath のパスフィールドは <code>volumes[].hostPath.path</code> であり、注釈に書かれている <code>hostNetwork.path</code> は誤記です。</li>
</ul>
<p>フィールド名のスペルを間違えた場合、クラスターの検証ポリシーによっては、そのまま拒否されることもあれば、未知のフィールドとして無視されることもあります——後者の方が厄介で、設定が効いているように見えて実際には効いていません。改めて強調しておきます。作業前に <code>kubectl explain</code> でフィールド名を確認するか、<code>kubectl apply --dry-run=server -f pod.yaml</code> で API Server に先に検証させましょう。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>YAML のコメントには <code>//</code> ではなく <code>#</code> を使うべきです。上の表の <code>//</code> コメントはクイックリファレンス用の注記にすぎず、実際の YAML ファイルにコピーする前には削除するか <code>#</code> に書き換える必要があります。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/27/kubernetes-2#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Pod YAML のフィールドは多いものの、よく使うものは実はいくつかの領域に集中しています。metadata の labels、containers のイメージと起動コマンド、resources、3 種類のプローブ、volumes のマウントです。注釈表はフィールドの位置を素早く特定するのに便利ですが、フィールド名とデフォルト値は必ず <code>kubectl explain</code> の出力を基準にしてください——現在のクラスターの Schema を読むため、古くなることも、書き写しのミスもありません。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[Fiddler のインストールと HTTPS リクエストのキャプチャ]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu</guid>
            <pubDate>Thu, 20 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Fiddler のインストール、システムプロキシの設定、そして偽造証明書の設定によって HTTPS 暗号化リクエストをキャプチャする原理と操作手順を紹介します。]]></description>
            <content:encoded><![CDATA[<p>ご存じのとおり Fiddler は非常に実用的なパケットキャプチャツール（主に HTTP リクエスト向け）で、公式サイトからダウンロードできる拡張機能も豊富です。開発の中では、インターフェースのデータを確認したり、API の安全性をテストしたり（署名なしで値を改変できるかどうかなど）するために、キャプチャツールが必要になる場面は避けられません。ここでは Fiddler のインストールと、証明書を設定して HTTPS の暗号化された情報をキャプチャする方法を紹介します。</p>
<!-- -->
<p>API 開発をしていると、最もよくある要望は「リクエストは本当に送信されたのか、パラメータは正しいのか、サーバーは何を返したのか」を知りたいというものです。ブラウザの F12 ではブラウザ自身が送ったリクエストしか見えず、アプリ、デスクトッププログラム、バックエンドサービス間の呼び出しは見えません。一方ログにはたいてい業務フィールドしか記録されておらず、本当に確認したい生のメッセージが抜けています。キャプチャツールはまさにこの隙間を埋めるものです。プロキシとしてクライアントとサーバーの間に座り、すべてのリクエストとレスポンスを丸ごとあなたの前に並べてくれます。Fiddler はこの種のツールの中で、設定コストが最も低く、HTTP のサポートが最も扱いやすいものの一つです。</p>
<p>厄介なのは HTTPS です。今どきの API は基本的に HTTPS で、メッセージは全経路で暗号化されているため、プロキシがトラフィックを捕まえても、そこにあるのは暗号文の山だけです。平文を見るには、Fiddler に「中間者（マン・イン・ザ・ミドル）」として TLS ハンドシェイクに介入させる必要があります。これが本記事でしっかり説明したい部分です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ダウンロードとインストール">ダウンロードとインストール<a href="https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu#%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%A8%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="ダウンロードとインストール への直接リンク" title="ダウンロードとインストール への直接リンク" translate="no">​</a></h2>
<p>Fiddler の公式サイト：</p>
<p><a href="https://www.telerik.com/fiddler" target="_blank" rel="noopener noreferrer" class="">Fiddler | Web Debugging Proxy and Troubleshooting Solutions</a></p>
<p>対応するバージョンを選んでダウンロードします。個人のデバッグ用途なら無料の Fiddler Classic で十分で、エンタープライズ版の登録は不要です。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-18-a3c29b8f26df9a8ea23e14cbb0b92512.webp" width="1600" height="783" class="img_ev3q"></p>
<p>インストールが完了したら起動します：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-19-be4e61d0ca02ec3e5799e4a490c813e8.webp" width="1600" height="869" class="img_ev3q"></p>
<p>この時点で、何も設定しなくてもすでに HTTP リクエストを直接キャプチャできます（HTTP は平文で転送されるためです。もちろんリクエストがシステムプロキシを経由する必要はあり、Firefox ブラウザは「手動でプロキシを設定」する必要があります）。しかしプロジェクトで HTTPS リクエストを使っている場合、HTTPS リクエストの安全性によりすべてのデータが暗号化されています。では、どうやってその中の情報をキャプチャすればよいのでしょうか？</p>
<p>ここでプロキシの仕組みを補足しておきます。Fiddler は起動時に自身をシステムプロキシとして登録し、デフォルトで <code>127.0.0.1:8888</code> をリッスンします。システムプロキシを経由するプログラム（ほとんどの Windows アプリ、Chrome、Edge）は、自動的にトラフィックを Fiddler へ転送します。一方 Firefox は独自のプロキシ設定を持っており、システムプロキシに従わないため、ネットワーク設定で「手動でプロキシを設定」し、同じアドレスとポートを指定する必要があります：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">HTTP 代理:  127.0.0.1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">端口:       8888</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="https-キャプチャの原理">HTTPS キャプチャの原理<a href="https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu#https-%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E3%81%AE%E5%8E%9F%E7%90%86" class="hash-link" aria-label="HTTPS キャプチャの原理 への直接リンク" title="HTTPS キャプチャの原理 への直接リンク" translate="no">​</a></h2>
<p>まず Fiddler の証明書を設定する必要があります。なぜこんなものを設定するのか？と疑問に思う人もいるでしょう。まずは簡単に原理を見てみましょう：</p>
<p>1、Fiddler が私たち自身のクライアントの HTTPS リクエストを傍受します。</p>
<p>2、傍受したリクエストをリクエスト先のサーバーへ転送します。</p>
<p>3、リクエスト先のサーバーが応答し、証明書ファイルを生成して私たちのクライアントへ送ります。Fiddler がこれを傍受し、サーバーが応答した証明書ファイルを取得したうえで、私たちが設定した偽造証明書を私たち自身のクライアントへ送ります。</p>
<p>4、クライアントは偽造証明書を受け取り、再度データをリクエストします。データはすでに偽造証明書に基づいて暗号化されています。</p>
<p>5、Fiddler は傍受してその中のすべてのリクエスト情報を取得し、さらにステップ 3 で傍受した本物の証明書ファイルを使って情報を暗号化し、リクエスト先のサーバーへ送信します。</p>
<p>要するに、Fiddler はデータを中間で転送する役割を果たしています。だからこそ、クライアントが送信するデータとサーバーが応答するデータの情報を取得できるのです。</p>
<p>平たく言えば、これは一種の「善意の中間者攻撃」です。HTTPS の安全性は証明書の信頼チェーンに依存しています。クライアントはサーバー証明書を受け取ると、それが自分の信頼するルート証明機関によって発行されたものかどうかを検証します。Fiddler が証明書をすり替えてもクライアントに拒否されないのは、Fiddler のルート証明書をシステムの「信頼されたルート証明機関」にインストールしたことが前提です。一度インストールすれば、Fiddler がその場で発行する偽造証明書は検証を通過し、クライアントはそれを使って後続のデータを暗号化するようになり、平文が Fiddler の手に落ちるというわけです。これが、以下のステップ 2「ルート証明書を信頼する」が省略できない理由でもあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="証明書の設定">証明書の設定<a href="https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu#%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="証明書の設定 への直接リンク" title="証明書の設定 への直接リンク" translate="no">​</a></h2>
<p><strong>Fiddler の証明書設定：</strong></p>
<p>1、Tools -&gt; Options -&gt; HTTPS</p>
<p>Decrypt HTTPS traffic をクリックし、from all processes を選択します。</p>
<p>このステップは HTTPS 復号のメインスイッチを入れる操作です。<code>from all processes</code> はすべてのプロセスの HTTPS トラフィックを復号することを意味します。特定のプログラムだけキャプチャしたい場合は、<code>from browsers only</code> に変えるか、後述のプロセスフィルタを使うことで、無関係なトラフィックでリストが埋め尽くされるのを避けられます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-20-404125e5766d5ffd58605f55e31804eb.webp" width="1600" height="869" class="img_ev3q"></p>
<p>2、Actions -&gt; Trust Root Certificate</p>
<p>このステップこそ、先ほど原理のところで述べた鍵となる操作です。Fiddler のルート証明書をシステムの信頼ストアにインストールします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-21-621c2ed823d08158c78ad432ddde9c39.webp" width="1600" height="869" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-22-7f14841bfa953c40fea063f5d9037588.webp" width="1600" height="869" class="img_ev3q"></p>
<p>3、yes をクリックすると証明書の設定は完了です。キャプチャを開始すれば HTTPS リクエストの内容情報を確認できます。</p>
<p>ダイアログで <code>Yes</code> をクリックして信頼を確認すれば、証明書のインストールは完了です。メイン画面に戻ると、以前は暗号化された文字化けとして表示されていた HTTPS セッションで、平文のリクエストヘッダ、リクエストボディ、レスポンスボディが見えるようになります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-23-507eb4cb4ef1dcdbd24c643d2132007d.webp" width="1600" height="869" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">特定のアプリのパケットがキャプチャできない：たいていはそのアプリがシステムプロキシを経由していないか、証明書検証の回避策が取られているためです。Android 7 以降、アプリはデフォルトでユーザーがインストールした証明書を信頼しません。アプリ自身の設定か別の手段が必要で、Fiddler で証明書をインストールするだけではキャプチャできません。</li>
<li class="">クライアントが SSL Pinning（証明書ピンニング）をしている場合：この種のアプリはサーバー証明書のフィンガープリントをコードに埋め込んでおり、Fiddler がすり替えた証明書を見ると接続を直ちに拒否します。通常の設定では対処できません。</li>
<li class="">Firefox でキャプチャできない：Firefox 独自のプロキシ設定が <code>127.0.0.1:8888</code> を指しているか確認してください。このステップを忘れないように。</li>
</ul>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>Fiddler のルート証明書をシステムにインストールするということは、このマシンにすべての HTTPS を復号できる穴を開けるのと同じことです。デバッグが終わったら、Tools -&gt; Options -&gt; HTTPS で <code>Remove Interception Certificates</code> をクリックするか、システムの証明書管理から <code>DO_NOT_TRUST_FiddlerRoot</code> を削除することを忘れずに。開発マシンに長期間残しておかないようにしましょう。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/20/fiddler-an-zhuang-bing-zhua-qu-httpsqing-qiu#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>HTTP の平文キャプチャはほぼ設定不要で、HTTPS のキャプチャの核心はたった 2 つ。Decrypt HTTPS traffic を有効にすることと、Fiddler のルート証明書を信頼することです。「中間者 + 偽造証明書」という本筋を理解していれば、パケットがキャプチャできないときも、どこで引っかかっているのか——プロキシを経由していないのか、証明書が信頼されていないのか、それとも相手が Pinning をしているのか——おおよそ判断できるようになります。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Redis でよく使う UI ツール]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju</guid>
            <pubDate>Thu, 20 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Redis のコマンドラインでのデータ確認は直観的とは言えません。よく使う Redis の GUI クライアント——RDM、AnotherRedisDesktopManager、QuickRedis——を記録し、選び方のアドバイスと注意点も添えます。]]></description>
            <content:encoded><![CDATA[<p>Redis はクライアントでコマンドを実行してデータ情報を確認できますが、GUI ほど効率的ではありません。そこで、よく使われる Redis の UI ツールを何点か記録しておきます。</p>
<!-- -->
<p>日常の開発で Redis を確認する場面は実は細々としています。ある key がちゃんと書き込まれたかの確認、シリアライズ後の value がどんな形かの確認、TTL が想定通りかのチェックなどです。<code>redis-cli</code> でももちろん全部できますが、<code>keys</code> / <code>scan</code>、<code>type</code>、<code>ttl</code>、<code>get</code> / <code>hgetall</code> の間を行ったり来たりするのは効率が良くありません。Hash や ZSet のような構造化データに出会うと、プレーンテキストの出力は読むのも一苦労です。手に馴染む GUI クライアントがあれば、これらの操作を一つの画面に集約でき、キー一覧、データ構造、TTL が一目瞭然になります。というわけで、使ったことのある何点かを記録しておけば、今後マシンを替えたときにそのままインストールできて便利です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-desktop-manager">Redis Desktop Manager<a href="https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju#redis-desktop-manager" class="hash-link" aria-label="Redis Desktop Manager への直接リンク" title="Redis Desktop Manager への直接リンク" translate="no">​</a></h2>
<p>有償で、半月の試用ができます。ネット上でクラック版を見つけることもできますが、バージョンは最新でない可能性があります。</p>
<p>RDM は老舗のツールと言えます。初期のバージョンはオープンソースで無料でしたが、後に有償サブスクリプションに転換して RESP.app に改名しました。多くの人が他の無料クライアントに乗り換えた理由でもあります。機能面では比較的充実しています。SSH トンネル、SSL 接続をサポートし、内蔵コンソールで直接コマンドを叩くことができ、各種データ型の表示もかなり成熟しています。会社が費用を負担してくれるなら、今でも堅実な選択肢です。</p>
<p><a href="https://resp.app/" target="_blank" rel="noopener noreferrer" class="">RESP.app (formerly Redis Desktop Manager) - GUI for Redis ® available on Windows, macOS, iPad and Linux.</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="anotherredisdesktopmanager">AnotherRedisDesktopManager<a href="https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju#anotherredisdesktopmanager" class="hash-link" aria-label="AnotherRedisDesktopManager への直接リンク" title="AnotherRedisDesktopManager への直接リンク" translate="no">​</a></h2>
<p>オープンソースで無料、使いやすく、UI もシンプルで綺麗です。</p>
<p>名前からして RDM の有償化に対するちょっとした皮肉が込められています。売りは安定性です。大量の key をロードしてもフリーズやクラッシュをしません。この点は本番データベースの key が数百万に達するときに特に重要で、一括で全量を取得するのではなく <code>scan</code> による漸進的な走査を使っています。日常的に必要な機能は一通り揃っています。複数接続の管理、SSH/SSL、クラスタモード、コマンドラインターミナル、ダークテーマ、そして key を区切り文字でツリー状に折りたたんで表示できます。私自身、現在メインで使っているのはこれです。</p>
<p><a href="https://github.com/qishibo/AnotherRedisDesktopManager" target="_blank" rel="noopener noreferrer" class="">GitHub - qishibo/AnotherRedisDesktopManager: 🚀🚀🚀A faster, better and more stable redis desktop manager [GUI client], compatible with Linux, Windows, Mac. What’s more, it won’t crash when loading massive keys.</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quickredis">QuickRedis<a href="https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju#quickredis" class="hash-link" aria-label="QuickRedis への直接リンク" title="QuickRedis への直接リンク" translate="no">​</a></h2>
<p>QuickRedis は永久無料の Redis 可視化管理ツールです。直接接続、Sentinel、クラスタモードをサポートし、億単位の key に対応、さらに心躍る UI を備えています。QuickRedis は Windows、Mac OS X、Linux で動作します。QuickRedis は効率化ツールです。他の人が必死にコマンドを叩いている間に、あなたはもうお茶を飲んでいる、というわけです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-6874747-d065e1e6a63c9c853df4c8faa4fb757b.webp" width="1600" height="936" class="img_ev3q"></p>
<p>Sentinel とクラスタモードへの対応はすぐに使える状態で、コマンドラインのように自分で <code>MOVED</code> リダイレクトを処理したりマスターノードを手動で探したりする必要がありません。本番環境のクラスタに接続する際にはかなり手間が省けます。画面はツリーディレクトリ形式で、key をコロン区切りで階層に組織します。業務プレフィックスで key を命名する習慣があるなら、とても快適に使えるでしょう。</p>
<p><a href="https://github.com/quick123official/quick_redis_blog" target="_blank" rel="noopener noreferrer" class="">GitHub - quick123official/quick_redis_blog: QuickRedis is a free forever Redis Desktop manager. It supports direct connection, sentinel, and cluster mode, supports multiple languages, supports hundreds of millions of keys, and has an amazing UI. Supports both Windows, Mac OS X and Linux platform.</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）本番環境への接続は控えめに。GUI ツールでは数クリックで key の削除や value の変更ができてしまい、誤操作のコストはコマンドラインより低いのです。本番接続には読み取り専用アカウントだけを使うか、少なくとも接続名で明確に区別を付けて、テスト環境での操作の癖を本番に持ち込まないようにすることをおすすめします。</p>
<p>2）全量スキャンに注意。一部のクライアントはキー一覧の表示に <code>scan</code> ではなく <code>keys *</code> を使っており、大きなデータベースでは Redis のメインスレッドをブロックしてしまいます。ツール選びの際にはこの点に気をつけましょう。AnotherRedisDesktopManager はこの点で評判が良いです。</p>
<p>3）value の表示が文字化けするのは、たいていツールの問題ではありません。ビジネスコードが JDK シリアライズや Protobuf で書き込んだバイナリデータは、クライアントはバイト列をそのまま表示することしかできません。一部のツールはデシリアライズ方式の選択や JSON の整形をサポートしているので、使う前にそのオプションがあるか確認するとよいでしょう。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>リモートの Redis に接続するときは、6379 ポートを直接公開インターネットにさらすのではなく、SSH トンネル経由を優先しましょう。上記のツールはいずれも SSH 接続の設定をサポートしています。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/20/redis-chang-yong-uigong-ju#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>三つのツールのうち、RDM（RESP.app）は機能が最も充実していますが有償です。AnotherRedisDesktopManager はオープンソースで無料、安定していて手がかからず、デフォルトの選択肢に適しています。QuickRedis も同じく無料で、ツリー型の UI と Sentinel・クラスタ対応が光ります。ツール自体に絶対的な優劣はありません。手に馴染む一つを選んで、データ確認の時間を節約できればそれで十分です。</p>]]></content:encoded>
            <category>Redis</category>
        </item>
        <item>
            <title><![CDATA[OSI 7階層ネットワークプロトコル]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi</guid>
            <pubDate>Mon, 17 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[OSI 7階層モデルの整理:各層が解決する問題、データ単位の名称、対応する代表的な機器とプロトコル、そして TCP/IP モデルとの関係。]]></description>
            <content:encoded><![CDATA[<p>ネットワーク障害を調査するとき、最もよく使う考え方が層ごとの切り分けです。ケーブルはつながっているか、IP に ping は通るか、ポートは Listen しているか、アプリケーションは何を返しているか。この考え方の源流が OSI 7階層モデルです。ここでは各層が解決する問題を一通りおさらいし、自分用の備忘録としてまとめます。</p>
<!-- -->
<p>まず、なぜ層に分けるのかという話から。ネットワーク通信で扱うべきことは非常に多岐にわたります。信号を媒体上でどう伝送するか、エラーが起きたらどうするか、ネットワークをまたいで相手をどう見つけるか、アプリケーション同士でデータ形式をどう取り決めるか。これらの問題を責務ごとに切り分け、各層は下位層が提供するサービスにのみ依存し、上位層にはインターフェースだけを公開する。そうすることで、各層を独立して設計・置き換えできるようになります。OSI モデルは通信の全過程を 7 つの層に分割し、下から順に、物理層、データリンク層、ネットワーク層、トランスポート層、セッション層、プレゼンテーション層、アプリケーション層となっています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="物理層">物理層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E7%89%A9%E7%90%86%E5%B1%A4" class="hash-link" aria-label="物理層 への直接リンク" title="物理層 への直接リンク" translate="no">​</a></h2>
<p>二つのハードウェア間でどう通信するかという問題を解決します。代表的な物理媒体には光ファイバー、ケーブル、リピーターなどがあります。主に物理デバイスの規格を定義する層で、LAN ケーブルのコネクタ形状、光ファイバーのインターフェース種別、各種伝送媒体の伝送速度などが対象です。</p>
<p>主な役割はビット列の伝送です(1 と 0 を電流の強弱に変換して伝送し、宛先に届いたら再び 1 と 0 に戻す、いわゆる D/A 変換と A/D 変換です)。この層のデータはビットと呼ばれます。</p>
<p>言い換えると、物理層が関心を持つのは電気的特性と機械的特性だけです。電圧が何ボルトなら 1 なのか、コネクタはどんな形か、1 秒間に何ビット送れるのか。伝えている内容が何であるかはまったく関知しません。NIC の物理ポートやハブはこの層で動作します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データリンク層">データリンク層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%AA%E3%83%B3%E3%82%AF%E5%B1%A4" class="hash-link" aria-label="データリンク層 への直接リンク" title="データリンク層 への直接リンク" translate="no">​</a></h2>
<p>コンピュータネットワークにはさまざまな干渉が存在するため、物理リンクは信頼できるものではありません。この層の主な機能は、各種の制御プロトコルによって、誤りのある物理チャネルを、誤りなくデータフレームを確実に伝送できるデータリンクへと変えることです。</p>
<p>具体的な仕事としては、物理層から届くビット列の形のデータを受け取り、フレームにカプセル化して上位層に渡します。同様に、上位層から来たデータフレームをビット列の形に分解して物理層へ転送します。この層のデータはフレームと呼ばれます。</p>
<p>フレームにカプセル化することで初めて、「ひとまとまりのデータがどこから始まりどこで終わるのか」という境界が生まれ、フレーム末尾にチェックサムフィールドを付けて誤り検出ができるようになります。壊れたフレームは受信時にそのまま破棄されます。この層では MAC アドレスで同一リンク上のデバイスを識別します。イーサネットやスイッチはこの層で動作するため、スイッチはレイヤー 2(L2)デバイスとも呼ばれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ネットワーク層">ネットワーク層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF%E5%B1%A4" class="hash-link" aria-label="ネットワーク層 への直接リンク" title="ネットワーク層 への直接リンク" translate="no">​</a></h2>
<p>ネットワークに複数のコンピュータがある場合、送信先のコンピュータをどう見つけるか。途中に複数のノードがある場合、どの経路を選ぶか。これがルーティングの仕事です。</p>
<p>この層の主なタスクは、経路選択アルゴリズムを使って、パケット(この層のデータ単位で、上位層のデータをまとめたもの)が通信サブネットを通過する最適な経路を選ぶことです。この層で定義されるのが IP アドレスで、IP アドレスによるアドレッシングのために IP プロトコルが生まれました。</p>
<p>データリンク層との違いは有効範囲にあります。MAC アドレスは同一リンク内でのみ有効なのに対し、IP アドレスはネットワーク全体でのアドレッシングに使えます。パケットがルーターを一つ通過するたびに、リンク層のフレームは剥がされて再カプセル化されますが、IP 層の送信元アドレスと宛先アドレスは終始変わりません。注意すべきは、IP プロトコル自体は「ベストエフォート」だということです。パケットを宛先に向けて送ることだけを担当し、到達も順序も保証しません。信頼性の問題は上位層に委ねられます。ルーターは典型的なレイヤー 3(L3)デバイスです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="トランスポート層">トランスポート層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%83%88%E3%83%A9%E3%83%B3%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88%E5%B1%A4" class="hash-link" aria-label="トランスポート層 への直接リンク" title="トランスポート層 への直接リンク" translate="no">​</a></h2>
<p>大量のデータを送信すると、パケットロスが発生する可能性があります。受信側のコンピュータは、すべてのパケットを完全に受け取れたかどうかを伝える必要があります。欠けていれば、どのパケットが失われたかを知らせ、すべて受信できるまで再送してもらいます。</p>
<p>簡単に言えば、トランスポート層の主な機能は、データ伝送サービスの品質を監視し、データの正しい伝送を保証することです。</p>
<p>トランスポート層はもう一つの問題も解決します。IP アドレスで特定できるのはホスト 1 台までですが、1 台のホスト上では多くのプロセスが動いています。データは結局誰に渡せばよいのか。答えはポート番号です。IP とポートの組み合わせで初めて特定のプロセスまで到達できます。この層で最も有名なプロトコルが TCP と UDP です。TCP はコネクション指向で、確認応答・再送・フロー制御によって信頼性を保証します。UDP はコネクションレスで、信頼性は保証しませんがオーバーヘッドが小さく、遅延に敏感で多少のロスが許容されるシーンに向いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="セッション層">セッション層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E5%B1%A4" class="hash-link" aria-label="セッション層 への直接リンク" title="セッション層 への直接リンク" translate="no">​</a></h2>
<p>ここまでで、正しいコンピュータに、正しくカプセル化された情報を送ることはできるようになりました。しかし、毎回トランスポート層プロトコルを呼び出してパッキングし、さらに IP プロトコルを呼び出して経路を探す、というわけにはいきません。そこで、パケットの送受信とアドレッシングを自動化する機能が必要になります。こうして登場したのがセッション層です。その役割は、アプリケーション間の通信の確立と管理です。</p>
<p>担当するのはセッションの確立・維持・終了です。通信する双方が「対話を始める」ところから「対話を終える」ところまで、その全過程をこの層が管理します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プレゼンテーション層">プレゼンテーション層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%83%97%E3%83%AC%E3%82%BC%E3%83%B3%E3%83%86%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%B1%A4" class="hash-link" aria-label="プレゼンテーション層 への直接リンク" title="プレゼンテーション層 への直接リンク" translate="no">​</a></h2>
<p>プレゼンテーション層はデータ形式の変換を担当します。アプリケーションが扱う情報をネットワーク伝送に適した形式に変換したり、下位層から来たデータを上位層が処理できる形式に変換したりします。</p>
<p>典型的な仕事には、文字エンコーディングの変換、データの暗号化・復号、圧縮・展開があります。2 台のマシンの内部データ表現は異なる場合がありますが、プレゼンテーション層は一方が送出したデータをもう一方が正しく理解できることを保証します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アプリケーション層">アプリケーション層<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E5%B1%A4" class="hash-link" aria-label="アプリケーション層 への直接リンク" title="アプリケーション層 への直接リンク" translate="no">​</a></h2>
<p>アプリケーション層は、コンピュータのユーザーおよび各種アプリケーションとネットワークとの間のインターフェースです。その機能は、ユーザーに直接サービスを提供し、ユーザーがネットワーク上で行いたいさまざまな作業を実現することです。</p>
<p>普段最もよく付き合うプロトコルはすべてこの層にあります。Web を閲覧する HTTP、ファイルを転送する FTP、メールを送る SMTP、名前解決を行う DNS などです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-----1--96518b37b423d77157eafd5d667beaaa.webp" width="1120" height="1587" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tcpip-モデルとの関係">TCP/IP モデルとの関係<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#tcpip-%E3%83%A2%E3%83%87%E3%83%AB%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82" class="hash-link" aria-label="TCP/IP モデルとの関係 への直接リンク" title="TCP/IP モデルとの関係 への直接リンク" translate="no">​</a></h2>
<p>OSI は概念モデルであり、実際のインターネットで使われているプロトコルスタックは TCP/IP です。TCP/IP は通常 4 層に分けられます。ネットワークインターフェース層が OSI の物理層とデータリンク層に、インターネット層がネットワーク層に、トランスポート層がトランスポート層に対応し、セッション層・プレゼンテーション層・アプリケーション層は一つのアプリケーション層に統合されています。実際のプロトコルで独立したセッション層・プレゼンテーション層のプロトコルをほとんど見かけないのはこのためで、それらの責務の多くはアプリケーション層プロトコルやアプリケーション自身が担っています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1)各層のデータ単位は混同しやすいので注意。物理層はビット、データリンク層はフレーム、ネットワーク層はパケット、トランスポート層はセグメントです。トラブルシューティングでも面接でもよく使います。</p>
<p>2)機器と層の対応関係は覚えておく価値があります。ハブは物理層、スイッチはデータリンク層、ルーターはネットワーク層で動作します。「L2 スイッチ」「L3 スイッチ」という呼び方はここから来ています。</p>
<p>3)あるプロトコルがどの層に属するかは、名前の印象ではなく、それが解決する問題で判断します。たとえば DNS はインフラではありますがアプリケーション層プロトコルですし、ARP は IP から MAC への対応付けを行うもので、ネットワーク層とデータリンク層の間で動作します。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>層ごとに問題を切り分ける順序は固定してしまうとよいです。まず物理的な接続(ケーブル、NIC のランプ)、次にリンクと IP(ゲートウェイへの ping、対象への ping)、その次にトランスポート層(ポートは Listen しているか、ファイアウォールは通しているか)、最後にアプリケーション自身のログを見る。層を追って消去法で絞り込む方が、当てずっぽうに試すよりずっと速く解決できます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/17/7ceng-wang-luo-xie-yi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>7階層モデルの核心は責務の分離です。各層は一種類の問題だけを解決し、上位にサービスを提供し、下位のサービスを利用します。物理層はビットを運び、データリンク層は一区間のリンクの信頼性を保証し、ネットワーク層はネットワーク全体のアドレッシングとルーティングを担い、トランスポート層はエンドツーエンドの伝送品質を保証し、上の三層はアプリケーションそのものに奉仕します。各層が解決する問題を理解すれば、個々のプロトコルを見たときにどこに位置づくべきか、そしてトラブル時にどの層から調べればよいかが分かるようになります。</p>]]></content:encoded>
            <category>Linux</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[Docker 基本コマンド]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling</guid>
            <pubDate>Mon, 17 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Docker で日常的に最もよく使うコマンド群の整理:イメージの取得、コンテナの起動、イメージ・コンテナ一覧の確認、削除、停止、再起動、コンテナへの接続。イメージとコンテナの関係と、ハマりやすいポイントも補足します。]]></description>
            <content:encoded><![CDATA[<p>Docker を使い始めた頃、いちばんよくある戸惑いは概念ではなく、「この操作、結局どのコマンドを打てばいいのか」というものです。この記事では日常で最も使用頻度の高い基本コマンドを整理し、それぞれに書式と例を付けました。手元でさっと参照できるようにしています。</p>
<!-- -->
<p>個々のコマンドを見る前に、まず一つ整理しておきましょう。<strong>イメージ(image)とコンテナ(container)は別物です</strong>。イメージは読み取り専用のテンプレートで、プログラムの実行に必要なファイルシステムと設定を含みます。一方コンテナはイメージの実行インスタンスで、イメージの上に書き込み可能なレイヤーを 1 枚重ねたものです。同じイメージから、互いに干渉しない複数のコンテナを起動できます。以下のコマンドは基本的にこの 2 つのオブジェクトを軸に展開されます。イメージを操作するもの(pull、images、rmi)と、コンテナを操作するもの(run、ps、rm、kill、restart、start、exec)があり、対象を区別すればコマンドを混同しにくくなります。</p>
<p>また、Docker デーモンはデフォルトで root 権限で動作するため、一般ユーザーが docker コマンドを実行するには <code>sudo</code> を付ける必要があります(またはユーザーを docker グループに追加します)。そのため以下の例の多くには <code>sudo</code> が付いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ヘルプ">ヘルプ<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%83%98%E3%83%AB%E3%83%97" class="hash-link" aria-label="ヘルプ への直接リンク" title="ヘルプ への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-h</span><br></div></code></pre></div></div>
<p>コマンドやオプションを忘れたら、まずヘルプを見ましょう。個別のサブコマンドのヘルプも確認できます。たとえば <code>docker run --help</code> を実行すると、そのサブコマンドがサポートするすべてのオプションが一覧表示されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="イメージの取得">イメージの取得<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E5%8F%96%E5%BE%97" class="hash-link" aria-label="イメージの取得 への直接リンク" title="イメージの取得 への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式:NAME はイメージ名、TAG はバージョンタグ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> pull NAME</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">:TAG</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例:centos の latest タグを取得</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> pull centos:latest</span><br></div></code></pre></div></div>
<p><code>pull</code> はイメージレジストリ(デフォルトは Docker Hub)からイメージをローカルにダウンロードします。TAG を書かない場合、デフォルトで <code>latest</code> を取得します。イメージはレイヤーごとに保存されており、取得時には各レイヤーそれぞれのダウンロード進捗が表示されます。すでにローカルに存在するレイヤーはそのまま再利用され、重複してダウンロードされることはありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="container-の起動">Container の起動<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#container-%E3%81%AE%E8%B5%B7%E5%8B%95" class="hash-link" aria-label="Container の起動 への直接リンク" title="Container の起動 への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> run </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> IMAGE </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">COMMAND</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">ARG</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例:イメージから対話型コンテナを起動(原文のイメージ名は contos だが、正しくは centos)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-t</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-i</span><span class="token plain"> contos /bin/bash</span><br></div></code></pre></div></div>
<p><code>run</code> がやっていることは実は 2 段階です。まずイメージから新しいコンテナを作成し、それから起動します。よく使うオプションをいくつか挙げます。</p>
<p>1)<code>-i</code>:標準入力を開いたままにし、コンテナに入力を送れるようにする;</p>
<p>2)<code>-t</code>:疑似ターミナルを割り当てる。<code>-i</code> と組み合わせることで対話可能なシェルが得られる;</p>
<p>3)イメージ名の後に続く <code>/bin/bash</code> は、コンテナ起動後に実行されるコマンドです。このコマンドが終了すると、コンテナも停止します。</p>
<p>ローカルに該当イメージがない場合、<code>run</code> は先に自動で pull を 1 回実行します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="イメージ一覧の確認">イメージ一覧の確認<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E4%B8%80%E8%A6%A7%E3%81%AE%E7%A2%BA%E8%AA%8D" class="hash-link" aria-label="イメージ一覧の確認 への直接リンク" title="イメージ一覧の確認 への直接リンク" translate="no">​</a></h2>
<p>ローカルのすべての images を一覧表示します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> images </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">NAME</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例:centos 関連のイメージだけを見る</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> images centos</span><br></div></code></pre></div></div>
<p>出力にはイメージのリポジトリ名、タグ、イメージ ID、作成日時、サイズが表示されます。後でイメージを削除するときに使うのは、ここに表示される名前または ID です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナ一覧の確認">コンテナ一覧の確認<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E4%B8%80%E8%A6%A7%E3%81%AE%E7%A2%BA%E8%AA%8D" class="hash-link" aria-label="コンテナ一覧の確認 への直接リンク" title="コンテナ一覧の確認 への直接リンク" translate="no">​</a></h2>
<p>これまでに作成したすべての container を確認できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">ps</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例:-a で停止中のものも含むすべてのコンテナを一覧表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">ps</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-a</span><br></div></code></pre></div></div>
<p><code>docker ps</code> はデフォルトで<strong>実行中</strong>のコンテナしか表示しないことに注意してください。終了した直後のコンテナは <code>ps</code> では見えないため、コンテナが「消えた」と勘違いしやすいのですが、<code>-a</code> を付ければ全部見えます。出力の CONTAINER ID と NAMES はどちらも、以降のコマンドでコンテナを指定するのに使えます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="イメージの削除">イメージの削除<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%AE%E5%89%8A%E9%99%A4" class="hash-link" aria-label="イメージの削除 への直接リンク" title="イメージの削除 への直接リンク" translate="no">​</a></h2>
<p>ダウンロード済みのイメージをローカルから削除します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式:一度に複数削除できる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> rmi IMAGE </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">IMAGE</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> rmi centos:latest</span><br></div></code></pre></div></div>
<p>そのイメージから作成されたコンテナが(たとえ停止状態でも)残っている場合、<code>rmi</code> はエラーを出して削除を拒否します。先に関連コンテナを <code>rm</code> で削除する必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナインスタンスの削除">コンテナインスタンスの削除<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%81%AE%E5%89%8A%E9%99%A4" class="hash-link" aria-label="コンテナインスタンスの削除 への直接リンク" title="コンテナインスタンスの削除 への直接リンク" translate="no">​</a></h2>
<p>1 つまたは複数のコンテナインスタンスを削除します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">rm</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> CONTAINER </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">CONTAINER</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><br></div></code></pre></div></div>
<p>すべての実行中コンテナを削除します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 原文の書き方は以下のとおり。実際に使うときは中のコマンドを $() またはバッククォートでコマンド置換すること:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># sudo docker rm $(sudo docker ps -aq)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">rm</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">ps</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-aq</span><br></div></code></pre></div></div>
<p>ここで <code>docker ps -aq</code> の <code>-q</code> はコンテナ ID のみを出力するという意味で、これを <code>docker rm</code> の引数として渡すことで一括削除を実現しています。実行中のコンテナはそのまま <code>rm</code> できません。先に停止するか、<code>-f</code> を付けて強制削除します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実行中のコンテナを停止する">実行中のコンテナを停止する<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E5%AE%9F%E8%A1%8C%E4%B8%AD%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E5%81%9C%E6%AD%A2%E3%81%99%E3%82%8B" class="hash-link" aria-label="実行中のコンテナを停止する への直接リンク" title="実行中のコンテナを停止する への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">kill</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> CONTAINER </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">CONTAINNER</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">..</span><span class="token plain">.</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例:コンテナ ID は一意に特定できれば先頭数文字だけで OK</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">kill</span><span class="token plain"> 026e</span><br></div></code></pre></div></div>
<p><code>kill</code> はデフォルトでコンテナ内のメインプロセスに SIGKILL を送るため、プロセスは即座に強制終了され、後始末の機会がありません。プログラムにグレースフルに終了する機会を与えたいなら <code>docker stop</code> の方が適切です。stop は先に SIGTERM を送り、一定時間待ってから SIGKILL を送ります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実行中のコンテナを再起動する">実行中のコンテナを再起動する<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E5%AE%9F%E8%A1%8C%E4%B8%AD%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E5%86%8D%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B" class="hash-link" aria-label="実行中のコンテナを再起動する への直接リンク" title="実行中のコンテナを再起動する への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> restart </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> contains</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">CONTAINER</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> restart 026e</span><br></div></code></pre></div></div>
<p><code>restart</code> は停止してから起動するのと同等で、コンテナの書き込み可能レイヤーのデータはそのまま保持されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="停止済みのコンテナを起動する">停止済みのコンテナを起動する<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E5%81%9C%E6%AD%A2%E6%B8%88%E3%81%BF%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%82%92%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B" class="hash-link" aria-label="停止済みのコンテナを起動する への直接リンク" title="停止済みのコンテナを起動する への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 書式</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> start </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">OPTIONS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"> CONTAINER </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token plain">CONTAINER</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">..</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 例</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> start 026e</span><br></div></code></pre></div></div>
<p><code>start</code> と <code>run</code> の違いに注意してください。<code>run</code> は<strong>新しい</strong>コンテナを作成しますが、<code>start</code> はすでに存在する停止中のコンテナを再び立ち上げるだけで、それまでにコンテナ内で行った変更はすべて残っています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="指定したコンテナに入る">指定したコンテナに入る<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AB%E5%85%A5%E3%82%8B" class="hash-link" aria-label="指定したコンテナに入る への直接リンク" title="指定したコンテナに入る への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">exec</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-it</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">コンテナ名または ID</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> /bin/bash</span><br></div></code></pre></div></div>
<p><code>exec</code> は実行中のコンテナの中で追加のプロセスを起動します。ここではもう一つ bash を開いています。<code>attach</code> よりもこちらの方がよく使われます。<code>exec</code> で入ったシェルで <code>exit</code> しても、新しく開いたプロセスが終了するだけで、コンテナ本体の実行には影響しません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1)<strong>イメージ ID とコンテナ ID を混同しない</strong>。<code>rmi</code> はイメージ削除、<code>rm</code> はコンテナ削除です。引数を間違えると「該当オブジェクトが存在しない」というエラーになります。</p>
<p>2)<strong>コンテナの停止は削除ではない</strong>。停止したコンテナは名前とディスク上の書き込み可能レイヤーを占有し続けます。新しいコンテナが同じ名前を使うと作成に失敗するので、先に古いものを <code>rm</code> する必要があります。</p>
<p>3)<strong><code>kill</code> より <code>stop</code> を優先する</strong>。プロセスがハングして応答しない場合を除き、アプリケーションに SIGTERM を受けて後始末する機会を与えましょう。</p>
<p>4)<strong>コンテナ内のデータは揮発性</strong>。コンテナを <code>rm</code> すると、書き込み可能レイヤーのデータも一緒に消えます。重要なデータはデータボリュームでホストマシンにマウントすべきです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/17/docker-ji-ben-ming-ling#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>この一連のコマンドは Docker の最小のワークフローをカバーしています。<code>pull</code> でイメージを取得し、<code>run</code> でコンテナを起動し、<code>images</code> / <code>ps</code> で状態を確認し、<code>start</code> / <code>stop</code>(<code>kill</code>)/ <code>restart</code> でライフサイクルを制御し、<code>exec</code> でコンテナに入って問題を調査し、最後に <code>rm</code> / <code>rmi</code> でコンテナとイメージを片付ける。「イメージはテンプレート、コンテナはインスタンス」という軸さえ押さえれば、あとは慣れの問題です。</p>]]></content:encoded>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[IDEA 定番プラグインと開発ツール]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian</guid>
            <pubDate>Mon, 17 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[日常の Java 開発でよく使う IDEA プラグインとツールを整理:SonarQube、FindBugs、CheckStyle、Maven Helper、Lombok、Key Promoter X など、ユースケースの説明付き。]]></description>
            <content:encoded><![CDATA[<p>IDEA はそれ自体で十分に強力ですが、適切なプラグインをいくつか入れるだけで、コード品質チェック、依存関係の調査、フォーマット規約といった日常の雑務にかかる時間を大きく削減できます。この記事では、私が Java 開発でよく使うプラグインとツールを用途別に分類して整理します。必要に応じて選んで使ってください。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="静的コード品質管理ツールのデプロイプラットフォーム">静的コード品質管理ツールのデプロイプラットフォーム<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E9%9D%99%E7%9A%84%E3%82%B3%E3%83%BC%E3%83%89%E5%93%81%E8%B3%AA%E7%AE%A1%E7%90%86%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0" class="hash-link" aria-label="静的コード品質管理ツールのデプロイプラットフォーム への直接リンク" title="静的コード品質管理ツールのデプロイプラットフォーム への直接リンク" translate="no">​</a></h2>
<p><a href="https://www.sonarqube.org/" target="_blank" rel="noopener noreferrer" class="">Code Quality and Code Security | SonarQube</a></p>
<p>SonarQube は厳密には IDEA プラグインではなく、独立してデプロイするコード品質管理プラットフォームです。静的解析でコード内のバグ、脆弱性、コードスメル（code smell）をスキャンし、定量的な品質レポートを出してくれます。一般的な使い方は、チーム内にサービスを一式デプロイし、CI フローと組み合わせてコミットやマージのたびに自動スキャンを走らせ、品質問題をマージ前に食い止めるというものです。IDEA 側にも対応するプラグインがあり、サーバーに接続してローカルでスキャン結果を確認できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コード品質チェックプラグイン-findbugs">コード品質チェックプラグイン FindBugs<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%82%B3%E3%83%BC%E3%83%89%E5%93%81%E8%B3%AA%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3-findbugs" class="hash-link" aria-label="コード品質チェックプラグイン FindBugs への直接リンク" title="コード品質チェックプラグイン FindBugs への直接リンク" translate="no">​</a></h2>
<p>IDEA IDE にプラグインとして直接インストールできます。</p>
<p><a href="https://findbugs.sourceforge.net/" target="_blank" rel="noopener noreferrer" class="">FindBugs™ - Find Bugs in Java Programs (sourceforge.net)</a></p>
<p>FindBugs の原理は、コンパイル後のバイトコードを解析して既知の欠陥パターンとマッチングすることです。例えば null ポインタのデリファレンス、リソースのクローズ漏れ、equals と hashCode の不整合などです。SonarQube とは位置づけが補完的で、SonarQube がチームレベルの継続的な品質管理に向いているのに対し、FindBugs はローカルでコードを書き終えた後にさっと一回スキャンする用途に向いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="翻訳プラグイン英語が苦手な人の必需品">翻訳プラグイン（英語が苦手な人の必需品）<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E7%BF%BB%E8%A8%B3%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E8%8B%B1%E8%AA%9E%E3%81%8C%E8%8B%A6%E6%89%8B%E3%81%AA%E4%BA%BA%E3%81%AE%E5%BF%85%E9%9C%80%E5%93%81" class="hash-link" aria-label="翻訳プラグイン（英語が苦手な人の必需品） への直接リンク" title="翻訳プラグイン（英語が苦手な人の必需品） への直接リンク" translate="no">​</a></h2>
<p>Translation はコード開発ビューの中で直接翻訳できます。</p>
<p>変数名、コメント、ドキュメント文字列を選択してショートカットキーを押すだけで訳文が表示され、ブラウザに切り替えて辞書を引く必要がありません。英語のソースコードコメントを読むときや、変数名を考えるときに特に便利です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コードフォーマット規約プラグイン">コードフォーマット規約プラグイン<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E8%A6%8F%E7%B4%84%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3" class="hash-link" aria-label="コードフォーマット規約プラグイン への直接リンク" title="コードフォーマット規約プラグイン への直接リンク" translate="no">​</a></h2>
<p>CheckStyle-IDEA は、コードのフォーマット、命名規則、Javadoc、クラス設計などの面からコードの規約とスタイルをチェックすることで、開発者がコーディング規約をより良く遵守できるよう効果的に支援します。</p>
<p>FindBugs とは重点が異なります。FindBugs が探すのは「バグを生みそうなコード」であり、CheckStyle が管理するのは「規約に沿っていないコード」です。チームで 1 つのルールファイル（例えばアリババや Google の Java スタイル規約をベースにカスタマイズしたもの）を共有すれば、全員が生み出すコードのスタイルが揃い、レビュー時にフォーマットの問題で何度もやり取りする必要がなくなります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="java-maven-パッケージ競合検出ツール">Java Maven パッケージ競合検出ツール<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#java-maven-%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E7%AB%B6%E5%90%88%E6%A4%9C%E5%87%BA%E3%83%84%E3%83%BC%E3%83%AB" class="hash-link" aria-label="Java Maven パッケージ競合検出ツール への直接リンク" title="Java Maven パッケージ競合検出ツール への直接リンク" translate="no">​</a></h2>
<p>Maven Helper はプロジェクト全体の pom.xml 内の依存関係の競合を検出できます。</p>
<p>Maven の依存関係は推移的なので、プロジェクトが大きくなると、同じパッケージが異なる依存経由で異なるバージョンとして間接的に取り込まれることはほぼ必然です。デフォルトの <code>mvn dependency:tree</code> の出力は非常に読みにくいですが、Maven Helper なら IDEA で pom.xml を開くだけで依存関係分析ビューが追加され、競合する依存は赤くマークされ、右クリックで不要なバージョンを exclude できます。バージョン競合が原因の NoSuchMethodError や ClassNotFoundException といった問題を調査するときに非常に役立ちます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コードミニマップ表示プラグイン">コードミニマップ表示プラグイン<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9F%E3%83%8B%E3%83%9E%E3%83%83%E3%83%97%E8%A1%A8%E7%A4%BA%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3" class="hash-link" aria-label="コードミニマップ表示プラグイン への直接リンク" title="コードミニマップ表示プラグイン への直接リンク" translate="no">​</a></h2>
<p>CodeGlance は VSCode のミニマップ表示に似たものです。</p>
<p>エディタの右側にファイル全体のサムネイルを表示します。長いファイルの中でジャンプしたり、おおよその位置を特定したりするとき、スクロールバーをドラッグするより直感的です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プロジェクトホットデプロイプラグイン有料">プロジェクトホットデプロイプラグイン（有料）<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%83%9B%E3%83%83%E3%83%88%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E6%9C%89%E6%96%99" class="hash-link" aria-label="プロジェクトホットデプロイプラグイン（有料） への直接リンク" title="プロジェクトホットデプロイプラグイン（有料） への直接リンク" translate="no">​</a></h2>
<p>JRebel はコードや mapper.xml を修正した後の再起動を減らし、作業効率を高めます。</p>
<p><a href="https://tanzhuo.xyz/idea-2020-jrebelpo-jie/" target="_blank" rel="noopener noreferrer" class="">IntelliJ IDEA 2020 JRebel破解</a></p>
<p>従来の開発フローでは、Java コードを 1 行変えるだけでアプリケーションの再起動が必要で、プロジェクトが大きくなると 1 回の再起動に数十秒から数分かかることもあります。JRebel のアプローチは JVM レベルでクラスのホットスワップを行うというもので、コードを修正しても再起動なしで反映され、MyBatis の mapper.xml の変更までカバーできます。Spring Boot 標準の devtools による再起動方式よりカバー範囲が広いのが特徴です。欠点は商用の有料製品であり、個人で使うにはコストが安くないことです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="json-文字列オブジェクトを-java-ファイルに変換するプラグイン">JSON 文字列オブジェクトを Java ファイルに変換するプラグイン<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#json-%E6%96%87%E5%AD%97%E5%88%97%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%92-java-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%99%E3%82%8B%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3" class="hash-link" aria-label="JSON 文字列オブジェクトを Java ファイルに変換するプラグイン への直接リンク" title="JSON 文字列オブジェクトを Java ファイルに変換するプラグイン への直接リンク" translate="no">​</a></h2>
<p>GsonFormat</p>
<p>サードパーティ API と連携するとき、JSON レスポンスを受け取って対応する Java Bean を手書きするのは遅いうえにフィールドの書き間違いも起きがちです。GsonFormat なら JSON 文字列をそのまま貼り付けるだけで、フィールドと型が正しく対応したエンティティクラスを自動生成でき、ネスト構造も処理できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="開発効率ツール">開発効率ツール<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E9%96%8B%E7%99%BA%E5%8A%B9%E7%8E%87%E3%83%84%E3%83%BC%E3%83%AB" class="hash-link" aria-label="開発効率ツール への直接リンク" title="開発効率ツール への直接リンク" translate="no">​</a></h2>
<p>Lombok は Java 開発においてアノテーションで JavaBean の記述を簡略化し、冗長なボイラープレートコードをなくすために生まれたプラグインで、書くクラスをより簡潔にしてくれます。</p>
<p>その原理は、コンパイル時にアノテーションプロセッサでコードを生成することです。<code>@Getter</code>/<code>@Setter</code> は読み書きメソッドを、<code>@Builder</code> はビルダーを、<code>@Slf4j</code> はロガーオブジェクトの宣言を生成します。ソースコード上ではこれらのメソッドは見えませんが、コンパイルされた class ファイルにはすべて含まれています。注意点として、Lombok は両側の連携が必要です。プロジェクトに依存関係を追加し、IDEA にプラグインをインストールする——どちらか一方が欠けても画面中が赤いエラーだらけになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ショートカットキー提示プラグインほぼ必須">ショートカットキー提示プラグイン（ほぼ必須）<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%82%B7%E3%83%A7%E3%83%BC%E3%83%88%E3%82%AB%E3%83%83%E3%83%88%E3%82%AD%E3%83%BC%E6%8F%90%E7%A4%BA%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%BB%E3%81%BC%E5%BF%85%E9%A0%88" class="hash-link" aria-label="ショートカットキー提示プラグイン（ほぼ必須） への直接リンク" title="ショートカットキー提示プラグイン（ほぼ必須） への直接リンク" translate="no">​</a></h2>
<p>Key Promoter X は IntelliJ 系製品（IDEA、Android Studio、CLion など）向けのプラグインで、作業中のマウス操作から基本的なキーボードショートカットを学ぶのを助けてくれます。IDE 内のボタンをマウスで操作すると、Key Promoter X が本来使うべきキーボードショートカットを表示してくれます。これにより、面倒なマウス操作をキー操作に置き換える方法を簡単に学べ、より速いマウスレス開発への移行を後押ししてくれます。現在、ツールバーボタン、メニューボタン、ツールウィンドウとその中の操作をサポートしています。</p>
<p>プラグインのページ:<a href="https://plugins.jetbrains.com/plugin/9792-key-promoter-x" target="_blank" rel="noopener noreferrer" class="">https://plugins.jetbrains.com/plugin/9792-key-promoter-x</a></p>
<p>この種の「教育型」プラグインの良さは受動的な学習にあります。ショートカット一覧をわざわざ暗記する必要はなく、マウスで頻繁にクリックしている操作ほど繰り返し提示してくれるので、最もよく使うものから自然に覚えられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）プラグインは多ければ良いというものではありません。各プラグインはメモリを消費し、インデックス作成や起動速度を遅くします。IDEA が重くなったら、まずプラグインの入れすぎを疑い、使わないものは早めに無効化しましょう。</p>
<p>2）Lombok 関連のコンパイルエラーは、まず依存関係とプラグインの両方が入っているか、そしてアノテーション処理（Annotation Processing）が有効になっているかを確認してください。</p>
<p>3）品質チェック系のプラグイン（FindBugs、CheckStyle）は大きなプロジェクトでフルスキャンすると時間がかかるため、変更したモジュールやファイルだけに増分チェックをかけるのがおすすめです。</p>
<p>4）依存関係の競合を Maven Helper で調査した後の exclude は慎重に。除外するバージョンが本当に他のモジュールから必要とされていないかを確認しないと、問題をコンパイル時から実行時に先送りしてしまう可能性があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/17/idea-chang-yong-cha-jian#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>この 10 個のツールは日常のニーズをおおよそ数カテゴリでカバーしています。品質と規約（SonarQube、FindBugs、CheckStyle）、依存関係管理（Maven Helper）、コーディング効率（Lombok、GsonFormat、Translation、CodeGlance）、開発体験（JRebel、Key Promoter X）です。一度に全部入れる必要はありません。自分のプロジェクトの痛点に合わせて選んで入れ、使い慣れてから買い足していくのが、プラグインの正しい付き合い方です。</p>]]></content:encoded>
            <category>Java</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[JVM GC の知識ポイント]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/17/jvm-gc</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/17/jvm-gc</guid>
            <pubDate>Mon, 17 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[JVM でよく使われるガベージコレクタには Serial、Parallel、ParNew、CMS、G1、ZGC などがあります。各コレクタは並行能力、停止時間、スループット、適用シナリオがそれぞれ異なります。]]></description>
            <content:encoded><![CDATA[<p>JVM でよく使われるガベージコレクタには、主に Serial、Parallel、ParNew、CMS、G1、ZGC などがあります。各コレクタは <strong>並行能力、停止時間、スループット、適用シナリオ</strong> がそれぞれ異なります。以下、代表的なガベージコレクタを整理して説明します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="serial-コレクタ">Serial コレクタ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#serial-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="Serial コレクタ への直接リンク" title="Serial コレクタ への直接リンク" translate="no">​</a></h2>
<p>対応パラメータ：<code>-XX:+UseSerialGC -XX:+UseSerialOldGC</code></p>
<p>Serial コレクタは JVM の中で最も基礎的で歴史の長いガベージコレクタであり、<strong>シングルスレッドでガベージコレクションを実行</strong> します。ガベージコレクション実行時、JVM は <strong>STW（Stop The World）</strong> を発動します。つまりすべてのユーザースレッドを一時停止し、ガベージコレクションが完了してからビジネススレッドの実行を再開します。</p>
<p>アルゴリズムの実装面では、Serial コレクタは <strong>新生代（Young 世代）でマーク・コピーアルゴリズム（Copying）</strong> を、<strong>老年代（Old 世代）でマーク・コンパクトアルゴリズム（Mark-Compact）</strong> を使用します。回収プロセス全体で GC スレッドが 1 本しかないため、実装はシンプルで安定していますが、ヒープメモリが大きい場合や CPU コア数が多い場合には、ガベージコレクションの効率が明らかに低下します。</p>
<p>そのため Serial コレクタは、<strong>メモリ規模が小さく、CPU コア数が少ないアプリケーション環境</strong>、例えば初期の Client モード JVM やリソース制約のあるシステムに向いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="parallel-scavenge-コレクタ">Parallel Scavenge コレクタ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#parallel-scavenge-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="Parallel Scavenge コレクタ への直接リンク" title="Parallel Scavenge コレクタ への直接リンク" translate="no">​</a></h2>
<p>対応パラメータ：<code>-XX:+UseParallelGC -XX:+UseParallelOldGC</code></p>
<p>Parallel Scavenge コレクタは Serial コレクタの <strong>マルチスレッド版</strong> であり、その核心的な目標は <strong>システムのスループット（Throughput）を高める</strong> ことです。ガベージコレクション時には複数の GC スレッドを並列に実行し、全体の回収効率を向上させます。</p>
<p>デフォルトでは GC スレッド数は CPU コア数とほぼ同じですが、パラメータ <code>-XX:ParallelGCThreads</code> でスレッド数を手動指定することもできます。Parallel コレクタのアルゴリズムは Serial コレクタとほぼ同じで、<strong>新生代ではマーク・コピーアルゴリズム、老年代ではマーク・コンパクトアルゴリズム</strong> を使用します。</p>
<p>Parallel コレクタは <strong>停止時間よりもスループットを重視</strong> するため、<strong>バックグラウンド計算型サービスやバッチ処理システム</strong> に非常に適しています。ヒープメモリ規模が中程度（例えば 3～4GB 前後）のアプリケーションシナリオでは、このコレクタで良好なパフォーマンスを得られます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="parnew-コレクタ">ParNew コレクタ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#parnew-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="ParNew コレクタ への直接リンク" title="ParNew コレクタ への直接リンク" translate="no">​</a></h2>
<p>対応パラメータ：<code>-XX:+UseParNewGC</code></p>
<p>ParNew コレクタは <strong>Serial コレクタのマルチスレッド版</strong> と理解できますが、その主な用途は単独での使用ではなく、<strong>CMS コレクタと組み合わせて使う</strong> ことにあります。ParNew は <strong>新生代のガベージコレクション</strong> を担当し、CMS は <strong>老年代のガベージコレクション</strong> を担当します。</p>
<p>実装方式として、ParNew は <strong>マルチスレッド並列回収メカニズム</strong> を採用し、新生代では引き続き <strong>マーク・コピーアルゴリズム</strong> を使用します。CMS は低停止時間を目標とするコレクタであり、Serial コレクタはシングルスレッドであるため、実際に CMS を使う際は通常 ParNew を組み合わせて新生代の回収効率を高めます。</p>
<p>この組み合わせ（ParNew + CMS）は、<strong>初期の Web サービスや停止時間に敏感なシステムで多く使われていました</strong>。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cms-コレクタ">CMS コレクタ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#cms-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="CMS コレクタ への直接リンク" title="CMS コレクタ への直接リンク" translate="no">​</a></h2>
<p>対応パラメータ：<code>-XX:+UseConcMarkSweepGC</code></p>
<p>CMS（Concurrent Mark Sweep）コレクタは主に <strong>老年代のガベージコレクション</strong> に使用され、その設計目標は <strong>ガベージコレクション時の停止時間を可能な限り減らす</strong> ことです。CMS の重要な特徴のひとつは、<strong>GC スレッドとユーザースレッドの並行実行を許可する</strong> ことで、アプリケーションの一時停止時間を削減できる点です。</p>
<p>CMS は <strong>マーク・スイープアルゴリズム（Mark-Sweep）</strong> を使用し、<strong>トリカラーマーキング（三色マーキング法）</strong> に基づいてオブジェクトの到達可能性分析を行います。回収プロセス全体は通常、次の 5 つのフェーズに分かれます：</p>
<ol>
<li class=""><strong>初期マーク（Initial Mark）</strong>
STW を発動し、GC Roots が直接参照するオブジェクトのみをマークします。オブジェクト数が少ないため実行は非常に高速です。</li>
<li class=""><strong>並行マーク（Concurrent Mark）</strong>
ユーザースレッドと並行して実行し、オブジェクトグラフ全体を走査してすべての到達可能なオブジェクトをマークします。</li>
<li class=""><strong>再マーク（Remark）</strong>
再び STW を発動し、並行マーク中にオブジェクト参照の変化によって生じたずれを修正します。</li>
<li class=""><strong>並行スイープ（Concurrent Sweep）</strong>
ユーザースレッドと並行して実行し、マークされなかったオブジェクトを掃除します。</li>
<li class=""><strong>並行リセット（Concurrent Reset）</strong>
CMS の内部状態をリセットし、次回の GC に備えます。</li>
</ol>
<p>CMS のフロー図：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10----b8e31d638671b82e0bfc6e3e2f62f4b7.webp" width="1105" height="396" class="img_ev3q"></p>
<p>CMS は回収プロセスを 1 つのステップから複数のステップに分割し、マークとスイープの両方を細分化したうえで、最も時間のかかるオブジェクトのマークステップをユーザースレッドとの並行実行に回しました。これによりアプリケーションは STW の停止をほとんど感知しなくなります。初期マークと再マークの 2 フェーズにごく短い STW があるだけで、それ以外のプロセスはすべて並行して進みます。こうして CMS は <strong>最短の回収停止</strong> という目標を達成しました。ただし停止時間が最短であることは、回収プロセス全体の所要時間が短いことを意味しません。実際には CMS の全体的な回収時間は他のガベージコレクタより長く、あくまで停止時間が最小であるにすぎません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="g1-コレクタ">G1 コレクタ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#g1-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="G1 コレクタ への直接リンク" title="G1 コレクタ への直接リンク" translate="no">​</a></h2>
<p>対応パラメータ：<code>-XX:+UseG1GC</code></p>
<p>G1（Garbage First）コレクタは <strong>JDK9 以降のデフォルトのガベージコレクタ</strong> であり、主に <strong>大容量メモリのサーバー環境</strong> で使用されます。新生代と老年代で物理的に区分けする従来のガベージコレクタと異なり、G1 はヒープ全体を複数の <strong>Region（領域）</strong> に分割します。</p>
<p>各 Region は必要に応じて動的に <strong>Eden、Survivor、Old 領域</strong> として使用できます。G1 は各 Region 内のゴミの比率を統計し、<strong>ゴミが最も多く、回収の効果が最も高い Region</strong> を優先的に回収します。これが「Garbage First」という名前の由来です。</p>
<p>G1 のもうひとつの重要な特徴は <strong>予測可能な停止時間モデル</strong> です。パラメータ <code>-XX:MaxGCPauseMillis</code> で期待する最大停止時間を設定でき、G1 はその目標に応じて回収戦略を自動調整します。</p>
<p>全体の回収フローは <strong>初期マーク、並行マーク、最終マーク、Region 回収</strong> などのフェーズを含みます。CMS と比べて、G1 はメモリフラグメンテーションの削減、大容量ヒープメモリのサポート、停止時間の制御においてより優れたパフォーマンスを発揮するため、現代の JVM 環境で広く使用されています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zgc-コレクタ">ZGC コレクタ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#zgc-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF" class="hash-link" aria-label="ZGC コレクタ への直接リンク" title="ZGC コレクタ への直接リンク" translate="no">​</a></h2>
<p>ZGC は <strong>JDK11 で導入された低レイテンシガベージコレクタ</strong> で、その核心的な目標は <strong>ミリ秒レベルの GC 停止時間の実現</strong> です。ほとんどの場合、ZGC の STW 停止時間は <strong>10ms 以内</strong> に抑えられ、TB クラスのヒープメモリでも安定を保てます。</p>
<p>ZGC の実装はいくつかのキー技術に依存しています。<strong>カラードポインタ（Colored Pointer）</strong>、<strong>ロードバリア（Load Barrier）</strong>、そして Region ベースの <strong>ZPage メモリ管理メカニズム</strong> です。これらの技術により、ZGC はほとんどの時間、ユーザースレッドと並行してガベージコレクションを実行できます。</p>
<p>ZGC の主な回収プロセスは <strong>並行マーク、並行リロケーション、並行参照修正</strong> を含みます。大部分の作業が並行フェーズで完了するため、アプリケーションスレッドへの影響は非常に小さくなります。</p>
<p>そのため ZGC は、<strong>超大容量メモリのサーバー、リアルタイムシステム、金融取引などレイテンシに極めて敏感なアプリケーションシナリオ</strong> に非常に適しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="主要-gc-コレクタの比較">主要 GC コレクタの比較<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#%E4%B8%BB%E8%A6%81-gc-%E3%82%B3%E3%83%AC%E3%82%AF%E3%82%BF%E3%81%AE%E6%AF%94%E8%BC%83" class="hash-link" aria-label="主要 GC コレクタの比較 への直接リンク" title="主要 GC コレクタの比較 への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>Serial</strong>：シングルスレッド回収、小規模アプリケーション向け</li>
<li class=""><strong>Parallel</strong>：マルチスレッド回収、スループット優先</li>
<li class=""><strong>ParNew + CMS</strong>：低停止時間の構成（JDK8 でよく見られる）</li>
<li class=""><strong>G1</strong>：現代 JVM のデフォルトコレクタ、大容量メモリサービス向け</li>
<li class=""><strong>ZGC</strong>：超低レイテンシガベージコレクタ、TB クラスのメモリシステム向け</li>
</ul>





































































<table><thead><tr><th>コレクタ</th><th>種類</th><th>スレッドモード</th><th>主要アルゴリズム</th><th>停止の特徴</th><th>適用シナリオ</th></tr></thead><tbody><tr><td>Serial</td><td>新生代 + 老年代</td><td>シングルスレッド</td><td>コピー + マーク・コンパクト</td><td>STW 時間が長め</td><td>小規模アプリ、シングルコア CPU</td></tr><tr><td>Parallel Scavenge</td><td>新生代</td><td>マルチスレッド</td><td>コピーアルゴリズム</td><td>スループット優先</td><td>バックグラウンド計算、バッチ処理</td></tr><tr><td>Parallel Old</td><td>老年代</td><td>マルチスレッド</td><td>マーク・コンパクト</td><td>スループット優先</td><td>高スループットサービス</td></tr><tr><td>ParNew</td><td>新生代</td><td>マルチスレッド</td><td>コピーアルゴリズム</td><td>停止が短め</td><td>CMS との組み合わせ用</td></tr><tr><td>CMS</td><td>老年代</td><td>並行</td><td>マーク・スイープ</td><td>低停止</td><td>Web サービス、低レイテンシシステム</td></tr><tr><td>G1</td><td>全ヒープ（Region）</td><td>並行 + 並列</td><td>コピー + マーク・コンパクト</td><td>予測可能な停止</td><td>大容量メモリサーバー</td></tr><tr><td>ZGC</td><td>全ヒープ（Region）</td><td>高度な並行</td><td>マーク + リロケーション</td><td>&lt;10ms の停止</td><td>超大容量ヒープ、低レイテンシシステム</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="発展golang-gc-の基本メカニズム">発展：Golang GC の基本メカニズム<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#%E7%99%BA%E5%B1%95golang-gc-%E3%81%AE%E5%9F%BA%E6%9C%AC%E3%83%A1%E3%82%AB%E3%83%8B%E3%82%BA%E3%83%A0" class="hash-link" aria-label="発展：Golang GC の基本メカニズム への直接リンク" title="発展：Golang GC の基本メカニズム への直接リンク" translate="no">​</a></h2>
<p>Go は <strong>バージョン 1.5 からトリカラーマーキング並行 GC（Tri-color Mark and Sweep）を導入</strong> し、その後のバージョンで継続的に最適化を重ね、現在では成熟した <strong>並行マーク + 並行スイープ + ライトバリア（Write Barrier）</strong> の GC 体系が形成されています。</p>
<p>Golang GC の核心的な特徴は以下のとおりです：</p>
<ul>
<li class=""><strong>トリカラーマーキングアルゴリズム（Tri-color Marking）</strong></li>
<li class=""><strong>並行マーク（Concurrent Mark）</strong></li>
<li class=""><strong>ライトバリア（Write Barrier）によるオブジェクト参照の一貫性保証</strong></li>
<li class=""><strong>STW 時間が極めて短い（通常 &lt; 1ms）</strong></li>
<li class=""><strong>GC の自動トリガー（ヒープ増加比率に基づく）</strong></li>
</ul>
<p>Golang GC の主なフローは以下のとおりです：</p>
<ol>
<li class=""><strong>STW Start</strong>：すべての Goroutine を一時停止し、GC Root を初期化します。</li>
<li class=""><strong>並行マーク（Concurrent Mark）</strong>：GC がユーザー Goroutine と並行して実行され、トリカラーマーキングでオブジェクト参照を走査します。</li>
<li class=""><strong>マーク終了（Mark Termination）</strong>：短時間の STW で、すべてのオブジェクトのマーク完了を保証します。</li>
<li class=""><strong>並行スイープ（Concurrent Sweep）</strong>：マークされなかったオブジェクトを回収し、プログラムと並行して実行されます。</li>
</ol>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="jvm-gc-と-golang-gc-の核心的な違い">JVM GC と Golang GC の核心的な違い<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#jvm-gc-%E3%81%A8-golang-gc-%E3%81%AE%E6%A0%B8%E5%BF%83%E7%9A%84%E3%81%AA%E9%81%95%E3%81%84" class="hash-link" aria-label="JVM GC と Golang GC の核心的な違い への直接リンク" title="JVM GC と Golang GC の核心的な違い への直接リンク" translate="no">​</a></h3>
<p>JVM は <strong>複数のガベージコレクタ（Serial、CMS、G1、ZGC など）を採用し、世代別回収メカニズムをサポートしており、異なるアルゴリズムとパラメータの組み合わせによってさまざまなシナリオに適応</strong> します。一方 Golang は設計当初から統一されたトリカラーマーキング並行 GC（Tri-color Mark &amp; Sweep）体系を採用し、世代別回収を行わず、並行マーク、ライトバリア、極めて短い STW によって低レイテンシの回収を実現しています。そのため全体構造がよりシンプルで、チューニングコストも低くなっています。</p>
<p>なぜ Go は世代別 GC をしないのでしょうか？それは Go チームが、世代別 GC はレイテンシに敏感なシステムでは効果が明確でない一方、ランタイムの複雑さを増すと考えているからです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/17/jvm-gc#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Serial から ZGC まで、JVM ガベージコレクタの進化の主軸は、実のところスループットと停止時間の間で絶えずトレードオフを図ることでした。シングルスレッドの Serial はシンプルさと安定性が持ち味で、Parallel はスループットを追求し、ParNew + CMS は並行実行によって低停止を実現し、G1 は Region 化によって停止を予測可能にし、ZGC はカラードポインタとロードバリアによって停止をミリ秒レベルまで抑え込みました。コレクタを選ぶ際は新しいものを追いかける必要はなく、鍵はやはりアプリケーションのヒープ規模とレイテンシ要件です。Golang の統一された非世代別 GC 設計と比較すると、2 つのランタイムが複雑さと柔軟性においてどう異なるトレードオフをしているかも、よりはっきり見えてきます。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[StarVCenter プライベートクラウド構築]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu</guid>
            <pubDate>Mon, 17 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[StarVCenter は中国産のハイパーコンバージドクラウドプラットフォーム IaaS ソフトウェアで、コンピューティング、ストレージ、ネットワークを同一のハードウェアノードに統合します。本記事ではそのハイパーコンバージドの特徴、無料ポリシー、高度な機能を紹介し、デプロイ前の準備と注意事項を整理します。]]></description>
            <content:encoded><![CDATA[<p>StarVCenter は中国産のハイパーコンバージドクラウドプラットフォーム IaaS ソフトウェアです。ハイパーコンバージド技術を提供しており、コンピューティング、ストレージ、ネットワークなどのリソースを 1 つのハードウェアノードに統合し、完全な仮想化インフラを構成します。このハイパーコンバージドアーキテクチャはリソース利用率を高め、デプロイと保守のコストを削減しつつ、より高い性能と信頼性を提供できます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜプライベートクラウドに注目するのか">なぜプライベートクラウドに注目するのか<a href="https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu#%E3%81%AA%E3%81%9C%E3%83%97%E3%83%A9%E3%82%A4%E3%83%99%E3%83%BC%E3%83%88%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%81%AB%E6%B3%A8%E7%9B%AE%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜプライベートクラウドに注目するのか への直接リンク" title="なぜプライベートクラウドに注目するのか への直接リンク" translate="no">​</a></h2>
<p>自宅や小規模なマシンルームに自分だけのプライベートクラウドを構築しようとすると、製品選定の問題は避けて通れません。商用仮想化ソリューションはライセンス費用が安くありませんし、KVM を純粋に手作業で保守するのは統一された管理画面がなく、仮想マシンが増えてくると作成・移行・監視がどれも煩雑になります。この両者の中間に位置するハイパーコンバージドソフトウェアが、ちょうどこの空白を埋めてくれます。グラフィカルな集中管理を備えつつ、集中型ストレージ機器を別途購入する必要もありません。StarVCenter は、中国産ソリューションを調査していたときに見つけた選択肢のひとつで、コミュニティ版は個人や小規模なユースケースには無料なので、記録しておく価値があります。</p>
<p>公式ドキュメント:<a href="http://www.starvcs.com/doc/vcenter/index.html" target="_blank" rel="noopener noreferrer" class="">StarVCenter帮助文档 · StarVCenter帮助文档 (starvcs.com)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハイパーコンバージドとは何か">ハイパーコンバージドとは何か<a href="https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu#%E3%83%8F%E3%82%A4%E3%83%91%E3%83%BC%E3%82%B3%E3%83%B3%E3%83%90%E3%83%BC%E3%82%B8%E3%83%89%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B" class="hash-link" aria-label="ハイパーコンバージドとは何か への直接リンク" title="ハイパーコンバージドとは何か への直接リンク" translate="no">​</a></h2>
<p>従来の仮想化アーキテクチャでは、コンピューティングノードとストレージ機器は分離されています。サーバーが仮想マシンを動かし、データは集中型のストレージアレイに置かれ、両者は専用のストレージネットワークで接続されます。このアーキテクチャは性能も信頼性も悪くないのですが、機器が高価で配線も複雑なため、小規模なデプロイにはとても不向きです。</p>
<p>ハイパーコンバージド(HCI)の考え方は、ストレージをサーバーローカルに戻すことです。各ノードは計算能力を提供すると同時にローカルディスクも供出し、ソフトウェア層が各ノードのディスクを 1 つの分散ストレージプールに集約します。仮想マシンのディスクデータは複数のノードに分片(シャーディング)して保存され、レプリカも保持されます。これにより、あるノードが故障してもデータは失われず、拡張時にはサーバーを 1 台追加するだけで計算能力とストレージ容量が同時に増えます。</p>
<p>StarVCenter のハイパーコンバージド技術には以下の特徴があります。</p>
<ol>
<li class="">ハードウェアノードの統合:コンピューティング、ストレージ、ネットワークなどのリソースを 1 つのハードウェアノードに統合し、完全な仮想化インフラを構成します。</li>
<li class="">デプロイの簡素化:自動化されたデプロイと設定により、仮想化環境全体の構築と保守を簡素化します。</li>
<li class="">高い性能と信頼性:コンピューティング、ストレージ、ネットワークなどのリソースを統合することで、システムの性能と信頼性を高めます。</li>
<li class="">柔軟な拡張:オンラインでのスケールアウト・スケールインをサポートし、ビジネスの必要に応じてリソースを柔軟に調整できます。</li>
</ol>
<p>全体として、StarVCenter のハイパーコンバージド技術は、効率的な仮想化インフラのデプロイと管理を実現し、リソース利用率を高めてコストを削減する手助けをしてくれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="無料ポリシーと高度な機能">無料ポリシーと高度な機能<a href="https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu#%E7%84%A1%E6%96%99%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC%E3%81%A8%E9%AB%98%E5%BA%A6%E3%81%AA%E6%A9%9F%E8%83%BD" class="hash-link" aria-label="無料ポリシーと高度な機能 への直接リンク" title="無料ポリシーと高度な機能 への直接リンク" translate="no">​</a></h2>
<p>StarVCenter は徹底的な使いやすさを追求しており、コミュニティでの無料利用(物理サーバー 20 台以内)、コミュニティによる無料技術サポート、永久無料アップグレードを提供しています。ホームラボや小規模チームにとって、物理サーバー 20 台という上限は実質的に無制限と言ってよいでしょう。</p>
<p>基本的な仮想化管理機能のほかに、StarVCenter は次のような高度な機能も提供しています。</p>
<ol>
<li class="">マルチクラウド管理:パブリッククラウドとプライベートクラウドを含む複数のクラウドプラットフォームの管理をサポートし、クラウドプラットフォーム横断のリソース管理とスケジューリング機能を提供します。</li>
<li class="">コンテナ管理:Docker や Kubernetes などのコンテナ化アプリケーションの管理をサポートし、コンテナのデプロイ、管理、監視を実現できます。</li>
<li class="">自動化運用:自動デプロイ、構成管理、障害診断、性能最適化などの自動化運用機能をサポートし、運用効率を高め、人為的ミスを減らします。</li>
<li class="">セキュリティ管理:アクセス制御、監査、ログ管理などを含むセキュリティ管理機能を提供し、仮想化環境の安全性を保障します。</li>
</ol>
<p>プロジェクトホワイトペーパー:<a href="http://www.starvcs.com/white_paper.pdf" target="_blank" rel="noopener noreferrer" class="">http://www.starvcs.com/white_paper.pdf</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイ前の準備">デプロイ前の準備<a href="https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E5%89%8D%E3%81%AE%E6%BA%96%E5%82%99" class="hash-link" aria-label="デプロイ前の準備 への直接リンク" title="デプロイ前の準備 への直接リンク" translate="no">​</a></h2>
<p>実際にデプロイに取りかかる前に、いくつか先に確認しておくとよいことがあります。</p>
<ol>
<li class="">BIOS で CPU のハードウェア仮想化支援(Intel VT-x または AMD-V)を有効にします。これはあらゆる仮想化プラットフォームの前提条件です。</li>
<li class="">ネットワークの設計を事前に済ませておきます。ハイパーコンバージドアーキテクチャでは、管理トラフィック、業務トラフィック、ストレージトラフィックを分離するのが望ましく、少なくともどの NIC・どのネットワークセグメントをそれぞれに使うのかを明確にしておきましょう。後から調整するのはとても面倒です。</li>
<li class="">ディスクの設計も事前に考えておきます。システムディスクとデータディスクを分け、分散ストレージに参加するディスクは事前にパーティションを切ったり RAID を組んだりしないこと。具体的な要件は公式ドキュメントに従ってください。</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>この種のプラットフォームに初めて触れる場合は、まず仮想マシン上でネステッド仮想化による実験的なデプロイを行い、インストール手順と管理画面に慣れてから、物理サーバーにインストールすることをおすすめします。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">コミュニティ版の無料枠は物理サーバー 20 台以内です。この規模を超える場合は公式に問い合わせる必要があるため、選定時には将来の拡張予測も織り込んでおきましょう。</li>
<li class="">ハイパーコンバージドの信頼性はマルチレプリカから生まれます。単一ノードのデプロイではその核心的な価値を体験できません。障害ドメインもレプリカ機構も、少なくとも 3 ノードあってはじめて意味を持ちます。</li>
<li class="">分散ストレージはディスクとネットワークの品質にかなり敏感です。重要なデータを載せる前にバックアップをしっかり取り、まだ検証していないストレージプールにすべての卵を入れないようにしましょう。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/17/si-you-yun-bu-shu#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>StarVCenter は中国産のハイパーコンバージドという選択肢を提供してくれます。コンピューティング、ストレージ、ネットワークを同じノード群に収め、デプロイも運用も従来の 3 層アーキテクチャよりシンプルで、コミュニティ版の無料ポリシーは小規模なユースケースにも十分フレンドリーです。</p>
<p>現在、筆者の家には Inspur のブレードサーバーが 1 台しかありません。ラックサーバーが複数台そろったら、StarVCenter でプライベートクラウドを構築してみるつもりです 😒。さらに K8S と組み合わせたら最高ではないでしょうか。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Zookeeper 可視化 UI ツール]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju</guid>
            <pubDate>Sun, 16 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[よく使われる Zookeeper 可視化ツールを整理します。有料の Zookeeper Assistant、オープンソースの PrettyZoo、そして zktools と zkui。使用感と選び方のアドバイス付きです。]]></description>
            <content:encoded><![CDATA[<p>仕事の効率を高めるために、よく使われる Zookeeper UI ツールをいくつか紹介します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-ui-ツールが必要なのか">なぜ UI ツールが必要なのか<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#%E3%81%AA%E3%81%9C-ui-%E3%83%84%E3%83%BC%E3%83%AB%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ UI ツールが必要なのか への直接リンク" title="なぜ UI ツールが必要なのか への直接リンク" translate="no">​</a></h2>
<p>Zookeeper 付属のコマンドラインクライアント <code>zkCli.sh</code> でも基本操作は事足りますが、使い勝手はよくありません。ノードツリーを見るには一階層ずつ <code>ls</code> し、ノードデータを見るには別途 <code>get</code> する必要があり、ノードが増えると全体構造を直感的に把握するのが難しくなります。レジストリセンター、分散ロック、設定ノードといった問題を日常的に調査する際、Znode のツリー構造、ノードデータ、stat メタ情報を直接表示してくれるグラフィカルツールがあれば、コマンドを叩く時間をかなり節約できます。</p>
<p>Zookeeper のデータモデル自体は非常にシンプルです。Znode で構成される 1 本の木で、各ノードは少量のデータを保存でき、バージョン番号、ACL、作成/更新時刻などの stat 情報が付属します。そのためこの種の UI ツールのコア機能もだいたい似たり寄ったりで、主にノードの CRUD、データ編集、接続管理です。違いはむしろインターフェースの使用感やメンテナンスの活発さに表れます。以下は私が触れてきた数種類です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zookeeper-assistant有料">Zookeeper Assistant（有料）<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#zookeeper-assistant%E6%9C%89%E6%96%99" class="hash-link" aria-label="Zookeeper Assistant（有料） への直接リンク" title="Zookeeper Assistant（有料） への直接リンク" translate="no">​</a></h2>
<p><a href="http://www.redisant.cn/za" target="_blank" rel="noopener noreferrer" class="">ZooKeeper Assistant - ZooKeeper可視化管理・監視ツール</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-1665904032647-77776a146fa2d03b97094cfb91e011db.webp" width="1550" height="925" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-17-1aeb7611ecabc744bb394677184f3ed7.webp" width="1600" height="681" class="img_ev3q"></p>
<p>Redisant 社のデスクトップクライアントで、インターフェースのスタイルは同社の他製品と一貫しています。左側にノードツリー、右側にデータパネルという構成で、ノードデータとメタ情報が一目瞭然です。</p>
<p>有料である点については、個人的には必要ないと感じています。ZK の機能は複雑ではなく、主に Znode ノード情報の CRUD だからです。とはいえ UI インターフェースを丁寧に作り込んでいるのは見て取れるので、その点は称賛に値します。会社が経費で落としてくれて、すぐ使える体験を求めるなら検討の余地はありますが、個人使用なら以下の無料の選択肢で十分です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="prettyzooオープンソース無料">PrettyZoo（オープンソース・無料）<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#prettyzoo%E3%82%AA%E3%83%BC%E3%83%97%E3%83%B3%E3%82%BD%E3%83%BC%E3%82%B9%E7%84%A1%E6%96%99" class="hash-link" aria-label="PrettyZoo（オープンソース・無料） への直接リンク" title="PrettyZoo（オープンソース・無料） への直接リンク" translate="no">​</a></h2>
<p>これは私自身も使っています。無料で、UI も見栄えがします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-1665904111228-14856e4b6a0f693574abc8ea019fd310.webp" width="1331" height="747" class="img_ev3q"></p>
<p>GitHub アドレス：<a href="https://github.com/vran-dev/PrettyZoo" target="_blank" rel="noopener noreferrer" class="">vran-dev/PrettyZoo: 😉 Pretty nice Zookeeper GUI, Support Win / Mac / Linux Platform (github.com)</a></p>
<p>JavaFX ベースで開発されたデスクトップアプリで、Windows、Mac、Linux の 3 プラットフォームすべてにインストーラがあります。基本的なノードツリーの閲覧とデータ編集に加えて、ノードの stat 情報も確認でき、コマンドライン端末も内蔵しているので、グラフィカルインターフェースでカバーできない操作は直接コマンドを叩いて補えます。私にとっては、「無料 + クロスプラットフォーム + 見た目が悪くない + メンテナンスが続いている」という点を合わせれば、デフォルトの選択にするのに十分です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zktools">zktools<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#zktools" class="hash-link" aria-label="zktools への直接リンク" title="zktools への直接リンク" translate="no">​</a></h2>
<p>長らくメンテナンスされておらず、GitHub 上ではすでに 4 年前の産物になっていますが、それでも作者の尽力には感謝します。</p>
<p><a href="https://github.com/zhangyawei117/zktools" target="_blank" rel="noopener noreferrer" class="">GitHub - zhangyawei117/zktools: zk可視化ツール zktools</a></p>
<p>選択肢が少なかった時代には多くの人を助けてくれましたが、今はより活発な代替品があるので、新規ユーザーには基本的におすすめしません。ここに載せているのは主に敬意を表するためです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zkui">zkui<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#zkui" class="hash-link" aria-label="zkui への直接リンク" title="zkui への直接リンク" translate="no">​</a></h2>
<p>Web アプリケーションとして開発された ZKUI ツールです。最後の更新も 2 年前で、個人的にはほとんど使っていません。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="1110" height="378" class="img_ev3q"></p>
<p>GitHub アドレス：<a href="https://github.com/DeemOpen/zkui" target="_blank" rel="noopener noreferrer" class="">https://github.com/DeemOpen/zkui</a></p>
<p>前述のデスクトップクライアントと異なり、zkui はサーバーサイドにデプロイする Web アプリで、チームメンバーがブラウザからアクセスします。「1 か所にデプロイして、複数人で共用する」シナリオに向いています。その代償として、サービスを自分で保守する必要があり、ログインや権限の問題も考えなければなりません。個人が日常的にノードを見るだけなら、デスクトップクライアントのほうが手軽です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）本番環境の ZK に接続するときは特に慎重に。この種のツールではノードの削除やデータの変更がワンクリックで済んでしまい、二重確認という心理的ブレーキがありません。レジストリセンターのノードを誤って削除すれば、影響を受けるのは一群のサービスかもしれません。本番環境では「見る」だけに使い、書き込み操作はスクリプトや変更フローを通すことをおすすめします。</p>
<p>2）ACL に注意。クラスタに ACL 認証が設定されている場合、ツールで接続しても一部のノードしか見えなかったり、データが読めなかったりすることがあります。これはツールのバグではないので、まず接続時に正しい認証情報を渡しているかを確認しましょう。</p>
<p>3）長期間メンテナンスされていないツール（上記の zktools、zkui など）は、新しいバージョンの Zookeeper や新しい JDK で互換性の問題が起きる可能性があります。奇妙なエラーに遭遇したら、まずバージョンを疑い、クラスタを疑うのは後にしましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/16/zookeeper-ke-shi-hua-uigong-ju#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>これらのツールの中で、私の選択は明確です。日常の個人使用ならそのまま PrettyZoo。無料でクロスプラットフォーム、見た目もよく、今もメンテナンスされています。商用ソフトウェアの体験を求めて予算に余裕があるなら Zookeeper Assistant を見てみるとよいでしょう。チームで 1 つの入り口を共有する必要がある場合に、Web 版の zkui を検討すればよいと思います。ツール自体はどれも複雑ではないので、手に馴染むものを 1 つ選んで、浮いた時間を本当の問題に使えばよいのです。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Zookeeper クラスター構築]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun</guid>
            <pubDate>Sat, 15 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Debian 11 上に Zookeeper 3.8.0 で 5 ノードクラスターを構築:Znode と ZAB プロトコルの基本概念から、zoo.cfg の設定、myid の設定、Leader 選出実験まで。]]></description>
            <content:encoded><![CDATA[<p>この記事では、Debian 上に 5 ノードの Zookeeper クラスターを構築する一連の流れを記録します。まず Znode や ZAB プロトコルといった基礎概念を整理し、そのあと実際に設定を行い、Leader ダウン時の選出実験を一度やってみます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイ環境">デプロイ環境<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E7%92%B0%E5%A2%83" class="hash-link" aria-label="デプロイ環境 への直接リンク" title="デプロイ環境 への直接リンク" translate="no">​</a></h2>
<p>オペレーティングシステム:Debian 11.5.0</p>
<p>実行環境:OpenJDK-1.8.0_332 64-Bit Server VM</p>
<p>Zookeeper バージョン:3.8.0</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="公式の紹介">公式の紹介<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E5%85%AC%E5%BC%8F%E3%81%AE%E7%B4%B9%E4%BB%8B" class="hash-link" aria-label="公式の紹介 への直接リンク" title="公式の紹介 への直接リンク" translate="no">​</a></h2>
<p>Zookeeper 公式ドキュメントのアドレス(ダウンロードした zip アーカイブ内の \docs ディレクトリにもドキュメントが同梱されています):<a href="https://zookeeper.apache.org/doc/r3.8.0/index.html" target="_blank" rel="noopener noreferrer" class="">ZooKeeper: Because Coordinating Distributed Systems is a Zoo (apache.org)</a></p>
<p>ZooKeeper は、分散アプリケーション向けの分散型オープンソースコーディネーションサービスです。シンプルなプリミティブのセットを公開しており、分散アプリケーションはこれらのプリミティブをベースに、より高レベルの同期、設定の維持管理、グループ・ネーミングサービスを実装できます。プログラミングしやすいように設計されており、ファイルシステムのディレクトリツリー構造に似たデータモデルを使用します。Java で動作し、Java と C のバインディングを提供しています。</p>
<p>ZooKeeper の実装は、高パフォーマンス、高可用性、厳密に順序付けられたアクセスを非常に重視しています。高パフォーマンスであることは大規模分散システムで使えることを意味し、信頼性はそれが単一障害点にならないことを意味し、厳密な順序付けはクライアント側で複雑な同期プリミティブを実装できることを意味します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-zkservice-7e54d04876edac10ed3249727ed99fb4.webp" width="600" height="185" class="img_ev3q"></p>
<p>公式の紹介から、Zookeeper は主に <strong>meta 情報の保存と設定情報の同期</strong>に用いられる、<strong>高パフォーマンス・高可用・単一障害点のない</strong>アプリケーションサービスである、と結論づけられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="個人的な理解">個人的な理解<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E5%80%8B%E4%BA%BA%E7%9A%84%E3%81%AA%E7%90%86%E8%A7%A3" class="hash-link" aria-label="個人的な理解 への直接リンク" title="個人的な理解 への直接リンク" translate="no">​</a></h2>
<p>日本語に訳すと「動物園の飼育員」です。Apache のプロジェクトはほとんどが動物をロゴや名前に使っているので、Zookeeper のロゴは飼育員のような姿で、シャベルまで持っています(doge)。Zookeeper 自体のアーキテクチャ設計もシンプルで、とっつきやすいものです。</p>
<p><img decoding="async" loading="lazy" src="data:image/gif;base64,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" width="79" height="112" class="img_ev3q"></p>
<p>Apache Zookeeper は Apache Hadoop のアーキテクチャ全体の一員でもあり、Hadoop アーキテクチャの中で統一的な設定管理、ネーミングサービス、重要データの保存などの機能を担うミドルウェアです。一言でまとめると:Zookeeper = ファイルシステム + 通知メカニズム、です。</p>
<p>Zookeeper(以下 ZK)自体のデータ保存方式は Key-Value 形式に似ていますが、ZK ではデータが Znode という形で保存され、Linux のファイルシステム管理に似ています。Key はすべてファイルパスの形で表現され、多階層のディレクトリを持ちます。これは Redis の Key ともよく似ていますが、Redis の Key が : 記号で階層を分ける習慣なのに対し、ZK は完全に Linux の / 記号で階層を分けます。ZK のデータはメモリ上に保存され、ハードディスクに永続化されます。メモリ内で Znode のツリー状データ構造(ファイルシステム)を維持しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データ構造">データ構造<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0" class="hash-link" aria-label="データ構造 への直接リンク" title="データ構造 への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="442" height="253" class="img_ev3q"></p>
<p>ZK の Value は豊富なデータ構造を提供していません(Redis が list、set、hashmap、zset、string など多様な構造を提供しているのとは違います)。ZK の Value は String 型のみです。ZK の位置付けはそもそもデータベースではなく、アーキテクチャ内の重要な設定情報を保存し、通知と同期の機能を提供するために設計されたものです。<strong>そのため各 Znode の最大保存データは 1M です</strong>。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="znode-のタイプ">Znode のタイプ<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#znode-%E3%81%AE%E3%82%BF%E3%82%A4%E3%83%97" class="hash-link" aria-label="Znode のタイプ への直接リンク" title="Znode のタイプ への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-永続ノード">1) 永続ノード<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#1-%E6%B0%B8%E7%B6%9A%E3%83%8E%E3%83%BC%E3%83%89" class="hash-link" aria-label="1) 永続ノード への直接リンク" title="1) 永続ノード への直接リンク" translate="no">​</a></h3>
<p>永続ノードは作成後、人為的に Delete コマンドで削除しない限り、ずっと存在し続けます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-一時ノード">2) 一時ノード<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#2-%E4%B8%80%E6%99%82%E3%83%8E%E3%83%BC%E3%83%89" class="hash-link" aria-label="2) 一時ノード への直接リンク" title="2) 一時ノード への直接リンク" translate="no">​</a></h3>
<p>一時ノードは接続クライアントのセッションに依存します。作成後、クライアントが接続を維持し続けていれば一時ノードは存在し続け、クライアントの接続が切断されると一時ノードは削除されます。この仕組みの使いどころが見えてきませんか?レジストリセンター(サービスレジストリ)の機能にぴったりです——Dubbo が起動すれば接続し、ダウンすれば Znode ノードが削除され、他のクライアントに通知されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-シーケンシャルノード">3) シーケンシャルノード<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#3-%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB%E3%83%8E%E3%83%BC%E3%83%89" class="hash-link" aria-label="3) シーケンシャルノード への直接リンク" title="3) シーケンシャルノード への直接リンク" translate="no">​</a></h3>
<p>シーケンシャルノードを作成すると、ノードパスの後ろに自動的に 10 桁の数字がカウンターとして追加されます。</p>
<p>例えば /node というシーケンシャルノードを作成すると、file path は /node0000000001 になります。</p>
<p>次にもう一度 /node ノードを作成すると、カウンターに +1 して新しい Znode ノードが作成されます(古い /node0000000001 は削除されず、そのまま残ります)。このときの file path は /node0000000002 です。</p>
<p>ただし、カウンターの値が 2147483647 を超えるとオーバーフローが発生します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-一時シーケンシャルノード">4) 一時シーケンシャルノード<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#4-%E4%B8%80%E6%99%82%E3%82%B7%E3%83%BC%E3%82%B1%E3%83%B3%E3%82%B7%E3%83%A3%E3%83%AB%E3%83%8E%E3%83%BC%E3%83%89" class="hash-link" aria-label="4) 一時シーケンシャルノード への直接リンク" title="4) 一時シーケンシャルノード への直接リンク" translate="no">​</a></h3>
<p>一時ノードの特性 + シーケンシャルノードの特性です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zk-の特性">ZK の特性<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#zk-%E3%81%AE%E7%89%B9%E6%80%A7" class="hash-link" aria-label="ZK の特性 への直接リンク" title="ZK の特性 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">順序一貫性 - クライアントからの更新は、その送信順序どおりに適用される。</li>
<li class="">原子性 - 更新は成功するか失敗するかのどちらか。部分的な結果は存在しない。</li>
<li class="">単一システムイメージ - (グローバルなデータ一貫性)クラスター内の各 Server は同一のデータレプリカを保持しており、Client がどの Server に接続してもデータは一貫している【理論上は一貫しているものの、データ同期の過程で一時的に不一致になる瞬間はある。ただし sync コマンドで解決できる】。</li>
<li class="">信頼性 - 更新が適用されると、クライアントが更新を上書きするまで、その時点からずっと存在し続ける。</li>
<li class="">適時性 - システムのクライアントビューが特定の時間範囲内で最新であることを保証する。</li>
</ul>
<p><strong>このうち原子性は ZAB プロトコルによって保証されています。ZAB プロトコルの正式名称は Zookeeper Atomic Broadcast(Zookeeper アトミックブロードキャスト)です。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="zab-プロトコル">ZAB プロトコル<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#zab-%E3%83%97%E3%83%AD%E3%83%88%E3%82%B3%E3%83%AB" class="hash-link" aria-label="ZAB プロトコル への直接リンク" title="ZAB プロトコル への直接リンク" translate="no">​</a></h2>
<p>ZAB には 2 つのモードがあります。1 つはメッセージブロードキャストモード、もう 1 つはクラッシュリカバリーモードです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="メッセージブロードキャストモード">メッセージブロードキャストモード<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%83%A1%E3%83%83%E3%82%BB%E3%83%BC%E3%82%B8%E3%83%96%E3%83%AD%E3%83%BC%E3%83%89%E3%82%AD%E3%83%A3%E3%82%B9%E3%83%88%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="メッセージブロードキャストモード への直接リンク" title="メッセージブロードキャストモード への直接リンク" translate="no">​</a></h3>
<p>Zookeeper クラスターにおけるデータレプリカの伝搬戦略には、メッセージブロードキャストモードが採用されています。Zookeeper のデータレプリカの同期方式は 2PC に似ていますが、同じではありません。2PC ではコーディネーターがすべての参加者から ACK 確認メッセージのフィードバックを受け取るのを待ってから commit メッセージを送信する必要があり、すべての参加者が全員成功するか全員失敗するかを要求するため、2PC は深刻なブロッキング問題を引き起こします。</p>
<p>一方 Zookeeper で Leader が Follower の ACK フィードバックを待つというのは、過半数の Follower から成功のフィードバックがあれば十分という意味で、すべての Follower からのフィードバックを受け取る必要はありません。これにより待ち時間を減らすことができます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラッシュリカバリーモード">クラッシュリカバリーモード<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%82%AF%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E3%83%AA%E3%82%AB%E3%83%90%E3%83%AA%E3%83%BC%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="クラッシュリカバリーモード への直接リンク" title="クラッシュリカバリーモード への直接リンク" translate="no">​</a></h3>
<p>Leader サーバーがクラッシュしたり、ネットワークの問題で Leader サーバーが過半数の Follower との連絡を失ったりすると、クラッシュリカバリーモードに入り、新しい Leader を選出し直します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスター構築5-ノード">クラスター構築(5 ノード)<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E6%A7%8B%E7%AF%895-%E3%83%8E%E3%83%BC%E3%83%89" class="hash-link" aria-label="クラスター構築(5 ノード) への直接リンク" title="クラスター構築(5 ノード) への直接リンク" translate="no">​</a></h2>
<p>ZK のクラスターには最低 3 つのノードが必要で、ノード数は<strong>奇数</strong>でなければなりません。許容できるダウン台数は<strong>ノード数 / 2</strong> です。3 台なら 1 台のダウンを許容してサービスを継続でき、5 台なら 2 台のダウンを許容してサービスを継続できます。従うルールは、<strong>過半数のノードが正常であれば、クラスター全体は外部へのサービス提供を継続できる</strong>というものです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="zk-クラスターにおける-3-つのロール">ZK クラスターにおける 3 つのロール<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#zk-%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B-3-%E3%81%A4%E3%81%AE%E3%83%AD%E3%83%BC%E3%83%AB" class="hash-link" aria-label="ZK クラスターにおける 3 つのロール への直接リンク" title="ZK クラスターにおける 3 つのロール への直接リンク" translate="no">​</a></h3>
<p>Leader(リーダー):クラスター全体で Leader に選出されるノードは 1 つだけで、書き込み操作ができるのは Leader だけです。(この設計により ZK の書き込みパフォーマンスがボトルネックになる運命にありますが、ほとんどがメモリ操作であり、各 Znode ノードのデータ量も非常に小さいため、それでも高い書き込みパフォーマンスを維持できます。)</p>
<p>Follower(フォロワー):Follower はクラスター内に複数存在でき、Leader とハートビート接続を維持する必要があります。このノードは読み取りのみ可能で書き込みはできず、Leader ノードがダウンした際には新しい Leader の選出投票に参加する必要があります。</p>
<p>Observer(オブザーバー):Observer ノードはクラスター全体の中で最も末端の存在で、読み取りのみ可能で、Leader 選出に参加できません。なぜ Observer のようなロールが必要なのでしょうか?ZK クラスターの書き込み操作は過半数の Follower の同期確認があって初めて書き込み成功となるため、ZK クラスター内のノードが多いほど書き込みパフォーマンスは低下します。ZK クラスターの読み取り能力を高めつつ書き込みパフォーマンスに影響を与えないために、Observer のようなノードが設計されたのです。Observer は Leader のデータを同期し、読み取り操作を提供することだけを担当します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-428604b1d2f3ba5ce2ecc4fde939121b-fad0baa9cebc4c2505c42de855a2494b.webp" width="500" height="350" class="img_ev3q"></p>
<p>公式が示している ZK クラスターのノード数とパフォーマンスのグラフを見ると、ノード数の増加に伴い、ZK クラスターが処理するリクエスト数も上昇しています。ただし個人的には、正確に言えば上昇するのは読み取り能力で、書き込み能力は低下するはずだと思います。ノードが多いほど、Leader がデータを書き込む際に ZAB プロトコルで他の Follower ノードに同期して確認を取る必要があり、過半数の Follower ノードがデータ同期に成功して初めて書き込み成功となるため、ノード数の増加は必然的に書き込み能力の低下を招きます。この設計は ZK が大量書き込みのシナリオに向いていないことも示しており、公式が示す読み書き比率は 7:3 か 8:2 が最適とされています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="zoocfg-の設定このチュートリアルではダウンロードと解凍は省略します">zoo.cfg の設定(このチュートリアルではダウンロードと解凍は省略します)<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#zoocfg-%E3%81%AE%E8%A8%AD%E5%AE%9A%E3%81%93%E3%81%AE%E3%83%81%E3%83%A5%E3%83%BC%E3%83%88%E3%83%AA%E3%82%A2%E3%83%AB%E3%81%A7%E3%81%AF%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E3%81%A8%E8%A7%A3%E5%87%8D%E3%81%AF%E7%9C%81%E7%95%A5%E3%81%97%E3%81%BE%E3%81%99" class="hash-link" aria-label="zoo.cfg の設定(このチュートリアルではダウンロードと解凍は省略します) への直接リンク" title="zoo.cfg の設定(このチュートリアルではダウンロードと解凍は省略します) への直接リンク" translate="no">​</a></h3>
<p>zip アーカイブを解凍したら、conf フォルダに入り、zoo_sample.cfg を zoo.cfg にリネームするか、コピーして zoo.cfg という名前にします。</p>
<p>zoo.cfg 内のいくつかのデフォルト設定を変更します。最も重要なのは dataDir のフォルダパスの変更です。ZK はデフォルトで tmp パスに保存しますが、Linux システムは不定期にこの一時ディレクトリを空にするため、他のフォルダパスに変更する必要があります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-1665826644086-c156c215baba24939267268b3a9cf3ea.jpg" width="1294" height="892" class="img_ev3q"></p>
<p>変更が終わったら保存を忘れずに。私が設定したパスは /opt/zookeeper/data で、以降の各ノードでも設定が必要です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="myid-ファイルの作成">myid ファイルの作成<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#myid-%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90" class="hash-link" aria-label="myid ファイルの作成 への直接リンク" title="myid ファイルの作成 への直接リンク" translate="no">​</a></h3>
<p>変更後の dataDir パスのフォルダ内に myid ファイルを作成し、ファイル内に現在のノードの一意な識別 ID を書き込みます。</p>
<p><img decoding="async" loading="lazy" src="data:image/jpeg;base64,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" width="494" height="49" class="img_ev3q"></p>
<p>以降の各ノードでも、それぞれのノードに対応する識別 ID を設定する必要があり、重複は許されません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスターノードリストの設定">クラスターノードリストの設定<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%8E%E3%83%BC%E3%83%89%E3%83%AA%E3%82%B9%E3%83%88%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="クラスターノードリストの設定 への直接リンク" title="クラスターノードリストの設定 への直接リンク" translate="no">​</a></h3>
<p>各ノードの zoo.cfg に server ノードのアドレスを追加します:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-1-0293760e7e0428371702a80c7559a9d5.png" width="1113" height="1010" class="img_ev3q"></p>
<p>server.1 の 1 は myid の識別子を表し、その後ろの値にはノードの ip でも hostname でも書けます。2888 ポートはデータ同期に、3888 ポートは選出投票に使われます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="全ノードの起動">全ノードの起動<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E5%85%A8%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E8%B5%B7%E5%8B%95" class="hash-link" aria-label="全ノードの起動 への直接リンク" title="全ノードの起動 への直接リンク" translate="no">​</a></h3>
<p>半数未満のノードしか起動していない場合、クラスターは一切のサービスを提供しません:</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="1082" height="182" class="img_ev3q"></p>
<p>このとき例外がスローされ ZK に接続できないため、すべてのノードを起動する必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスターステータスの検証">クラスターステータスの検証<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%82%B9%E3%83%86%E3%83%BC%E3%82%BF%E3%82%B9%E3%81%AE%E6%A4%9C%E8%A8%BC" class="hash-link" aria-label="クラスターステータスの検証 への直接リンク" title="クラスターステータスの検証 への直接リンク" translate="no">​</a></h3>
<p>すべてのノードが正常に起動した後:</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="790" height="174" class="img_ev3q"></p>
<p>zkServer.sh の status コマンドで、現在のノードのロールと起動ステータスを確認できます。</p>
<p>zkCli.sh でクラスターに接続し、コマンドを実行して正常かどうかテストします:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-5-0820db64e27c2089db99d00fd216ab45.png" width="1133" height="646" class="img_ev3q"></p>
<p>さらに他のノードでもコマンドテストを行います:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-6-ac15ca49fe2fa89961cabb52a29c41b5.png" width="1097" height="574" class="img_ev3q"></p>
<p>すべて正常です。これで最もシンプルな ZK クラスターの構築に成功しました。今回の構築は最も基本的な流れに基づいたものであり、今後は設定の変更を通じてクラスター全体のディテールをさらにチューニングし、<strong>ssl 通信とパスワードを追加してクラスター全体のセキュリティを強化する</strong>ことができます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="リスク実験">リスク実験<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%83%AA%E3%82%B9%E3%82%AF%E5%AE%9F%E9%A8%93" class="hash-link" aria-label="リスク実験 への直接リンク" title="リスク実験 への直接リンク" translate="no">​</a></h3>
<p><strong>テストの強度をさらに上げて、Leader ノードを停止させ、クラスターが自動的に新しい Leader ノードを選出するかどうかを確認してみます。</strong></p>
<p>Leader ノードを見つけて、そのノードのサービスを停止します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-15-d408ba5e077788935ad4ebe8f7cdcd02.png" width="1023" height="267" class="img_ev3q"></p>
<p>現在の Leader ノードは debian4 サーバーなので、すぐに zkServer.sh stop コマンドを実行してその ZK サービスを停止します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-1665900721220-61d412e62856fbc99e948132720c2acf.jpg" width="1010" height="363" class="img_ev3q"></p>
<p>zkServer.sh status コマンドで確認すると、debian5 サーバーが新しい Leader ノードに選出されており、クラスターの選出が完全に正常であることを示しています。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-16-eea82f99208fce0d17c1684ccbddce2b.png" width="1060" height="272" class="img_ev3q"></p>
<p>公式の設定パラメータ:<a href="https://zookeeper.apache.org/doc/r3.8.0/zookeeperAdmin.html#sc_zkMulitServerSetup" target="_blank" rel="noopener noreferrer" class="">动物园管理员:因为协调分布式系统是一个动物园 (apache.org)</a></p>
<p><a href="https://tanzhuo.xyz/zookeeper-ke-shi-hua-uigong-ju/" target="_blank" rel="noopener noreferrer" class="">Zookeeper 可視化 UI ツール</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/15/da-jian-zookeeperji-qun#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回の構築は最も基礎的な流れで進めました。dataDir を変更し、myid を書き、zoo.cfg にすべての server ノードを登録し、1 台ずつ起動する。クラスターが外部にサービスを提供できるかどうかのコアルールはただ 1 つ——過半数のノードが生存していること。リスク実験でもこれが検証できました。Leader を停止すると、残りのノードがすぐに新しい Leader を選出し、サービスは中断しませんでした。Znode モデルと ZAB プロトコルの過半数確認メカニズムを理解すれば、これらの設定項目の背後にある設計意図もすべて腑に落ちます。プロダクション環境で使う前には、ssl と認証の設定も忘れずに追加しましょう。</p>]]></content:encoded>
            <category>Java</category>
            <category>技術</category>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Debian に Containerd をインストールする]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang</guid>
            <pubDate>Sat, 15 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Debian 11 に Containerd をインストールする一連の流れ:cri-containerd-cni リリースパッケージのダウンロード、環境変数と中国国内向けミラーアクセラレータの設定、最後に systemd でサービスを起動して自動起動を設定するまで。]]></description>
            <content:encoded><![CDATA[<p>Containerd 公式サイト:<a href="https://containerd.io/" target="_blank" rel="noopener noreferrer" class="">containerd – An industry-standard container runtime with an emphasis on simplicity, robustness and portability</a></p>
<!-- -->
<p>公式インストールガイド:<a href="https://github.com/containerd/containerd/blob/main/docs/getting-started.md" target="_blank" rel="noopener noreferrer" class="">containerd/docs/getting-started.md (github.com)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="インストール環境">インストール環境<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E7%92%B0%E5%A2%83" class="hash-link" aria-label="インストール環境 への直接リンク" title="インストール環境 への直接リンク" translate="no">​</a></h2>
<p>Debian 11.5.0</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="containerd-リリースパッケージのダウンロード">Containerd リリースパッケージのダウンロード<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#containerd-%E3%83%AA%E3%83%AA%E3%83%BC%E3%82%B9%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AE%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89" class="hash-link" aria-label="Containerd リリースパッケージのダウンロード への直接リンク" title="Containerd リリースパッケージのダウンロード への直接リンク" translate="no">​</a></h2>
<p>ダウンロード先:<a href="https://github.com/containerd/containerd/releases" target="_blank" rel="noopener noreferrer" class="">https://github.com/containerd/containerd/releases</a></p>
<p>containerd は runc を呼び出す必要があるため、本来は先に runc をインストールしなければなりません。ただし containerd は関連する依存関係を同梱した圧縮パッケージ cri-containerd-cni を提供しており、これを使えば一つずつインストールする手間が省けます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-9-f8bc3132a7d5be8680b45c4f5bb2f749.webp" width="1291" height="1099" class="img_ev3q"></p>
<p>コマンドを実行してダウンロードします。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">wget</span><span class="token plain"> https://github.com/containerd/containerd/releases/download/v1.6.8/cri-containerd-cni-1.6.8-linux-amd64.tar.gz</span><br></div></code></pre></div></div>
<p>中国国内でダウンロードが遅すぎる場合は、こちらのアドレス(国内プロキシサイト)を試せます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">wget</span><span class="token plain"> https://download.fastgit.org/containerd/containerd/releases/download/v1.6.8/cri-containerd-cni-1.6.8-linux-amd64.tar.gz</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="圧縮パッケージをルートディレクトリに展開する">圧縮パッケージをルートディレクトリに展開する<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#%E5%9C%A7%E7%B8%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%82%92%E3%83%AB%E3%83%BC%E3%83%88%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AB%E5%B1%95%E9%96%8B%E3%81%99%E3%82%8B" class="hash-link" aria-label="圧縮パッケージをルートディレクトリに展開する への直接リンク" title="圧縮パッケージをルートディレクトリに展開する への直接リンク" translate="no">​</a></h2>
<p>圧縮パッケージの中はすでにシステムの階層構造どおりにディレクトリとファイルが組まれているので、そのまま <code>/</code> ルートディレクトリに展開すれば OK です。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">tar</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-C</span><span class="token plain"> / </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-xzf</span><span class="token plain"> cri-containerd-cni-1.6.8-linux-amd64.tar.gz</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="環境変数の設定">環境変数の設定<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="環境変数の設定 への直接リンク" title="環境変数の設定 への直接リンク" translate="no">​</a></h2>
<p><code>/usr/local/bin</code> と <code>/usr/local/sbin</code> を <code>~/.bashrc</code> ファイルの <code>PATH</code> 環境変数に追加します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">vim</span><span class="token plain"> ~/.bashrc</span><br></div></code></pre></div></div>
<p>以下の内容を追加します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># シェルが containerd 関連の実行ファイルを見つけられるようにする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">export</span><span class="token plain"> </span><span class="token assign-left variable environment constant" style="color:hsl(29, 54%, 61%)">PATH</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token environment constant" style="color:hsl(29, 54%, 61%)">$PATH</span><span class="token plain">:/usr/local/bin:/usr/local/sbin</span><br></div></code></pre></div></div>
<p>追加後、コマンドを実行して反映させます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">source</span><span class="token plain"> ~/.bashrc</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="containerd-デフォルト設定ファイルの生成">containerd デフォルト設定ファイルの生成<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#containerd-%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%94%9F%E6%88%90" class="hash-link" aria-label="containerd デフォルト設定ファイルの生成 への直接リンク" title="containerd デフォルト設定ファイルの生成 への直接リンク" translate="no">​</a></h2>
<p>設定ファイルは etc ディレクトリの下に置きます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain"> /etc/containerd</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># デフォルト設定を config.toml として書き出す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">containerd config default </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> /etc/containerd/config.toml</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="中国国内向けミラーアクセラレータの設定">中国国内向けミラーアクセラレータの設定<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#%E4%B8%AD%E5%9B%BD%E5%9B%BD%E5%86%85%E5%90%91%E3%81%91%E3%83%9F%E3%83%A9%E3%83%BC%E3%82%A2%E3%82%AF%E3%82%BB%E3%83%A9%E3%83%AC%E3%83%BC%E3%82%BF%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="中国国内向けミラーアクセラレータの設定 への直接リンク" title="中国国内向けミラーアクセラレータの設定 への直接リンク" translate="no">​</a></h3>
<p>デフォルト設定ファイルを生成したら、手動で国内向けミラーアクセラレータのアドレスを設定します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">vim</span><span class="token plain"> /etc/containerd/config.toml</span><br></div></code></pre></div></div>
<p>以下の行を探します
<strong>[plugins."io.containerd.grpc.v1.cri".registry.mirrors]</strong></p>
<p>だいたい 153 行目あたりです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-11-405c4770b2814e518247e3a124317568.png" width="1136" height="798" class="img_ev3q"></p>
<p>以下の内容を追加します。インデントに注意してください。</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># docker.io と k8s.gcr.io をそれぞれ対応する中国国内ミラーに向ける</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token table class-name" style="color:hsl(29, 54%, 61%)">plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key property" style="color:hsl(355, 65%, 65%)">endpoint</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token string" style="color:hsl(95, 38%, 62%)">"https://kvuwuws2.mirror.aliyuncs.com"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token table class-name" style="color:hsl(29, 54%, 61%)">plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token key property" style="color:hsl(355, 65%, 65%)">endpoint</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token string" style="color:hsl(95, 38%, 62%)">"https://registry.aliyuncs.com/k8sxio"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-12-8d68b3bb6dbc5662e9ee1c379201e1a5.png" width="1165" height="662" class="img_ev3q"></p>
<p>設定したらファイルを保存します。</p>
<p>設定の中の root はコンテナ情報の保存先であり、必要に応じて変更できます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-image-13-8de1c0fc48a088a3650e2f3bdeaf9831.png" width="1032" height="392" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="containerd-サービスの起動と自動起動の設定">containerd サービスの起動と自動起動の設定<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#containerd-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E8%B5%B7%E5%8B%95%E3%81%A8%E8%87%AA%E5%8B%95%E8%B5%B7%E5%8B%95%E3%81%AE%E8%A8%AD%E5%AE%9A" class="hash-link" aria-label="containerd サービスの起動と自動起動の設定 への直接リンク" title="containerd サービスの起動と自動起動の設定 への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># --now は自動起動を設定すると同時にサービスを即時起動することを意味する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">systemctl </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">enable</span><span class="token plain"> containerd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--now</span><br></div></code></pre></div></div>
<p>サービスが正常に起動したか確認しましょう。</p>
<p><img decoding="async" loading="lazy" src="data:image/jpeg;base64,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" width="926" height="234" class="img_ev3q"></p>
<p>これで containerd サービスのインストールと起動が完了しました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/15/debian-an-zhuang#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Debian への containerd のインストールは、実のところ 4 ステップです。公式の cri-containerd-cni リリースパッケージをダウンロードし、ルートディレクトリに展開し、PATH とデフォルト設定ファイルを設定し、systemd でサービスを起動する。この圧縮パッケージには runc と CNI プラグインが同梱されているので、依存関係を個別にインストールする手間が省けます。中国国内の環境ならついでにミラーアクセラレータも設定しておくと、イメージ取得の体験が格段に良くなります。今後 Kubernetes と連携する場合も、このインストール方法をそのまま流用できます。</p>]]></content:encoded>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[よく使われる CI/CD ツール]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd</guid>
            <pubDate>Fri, 14 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[代表的な CI/CD ツールを一覧で紹介：Jenkins、GitLab CI、CircleCI、Buddy、Jenkins X、そして Alibaba Cloud の雲效（Yunxiao）。それぞれの特徴と適したシーンを簡潔にまとめます。]]></description>
            <content:encoded><![CDATA[<p>CI/CD は継続的インテグレーションと継続的デリバリー/デプロイを組み合わせたもので、ソフトウェアのデリバリーをより速く、より安定させることを目指します。この記事では、市場でよく使われている CI/CD ツールを整理し、選定時の参考になるようにまとめます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>CI/CD はソフトウェア開発プロセスの一つで、継続的インテグレーション（Continuous Integration）と継続的デリバリー/デプロイ（Continuous Delivery/Deployment）という 2 つの概念を組み合わせたものです。ソフトウェアのデリバリーとデプロイを加速し、ソフトウェアの品質と安定性を高めることを目的としています。</p>
<p>継続的インテグレーションとは、開発者がコミットしたコードをメインのコードベースに自動的に統合し、その後一連のテストとチェックを実行して、コードの品質と安定性を担保することを指します。これによりコードの競合を回避し、マージの複雑さを下げると同時に、コードの問題を早期に発見・解決でき、コードの保守性とテスト容易性が向上します。</p>
<p>継続的デリバリー/デプロイとは、テストとチェックを通過したコードを本番環境に自動的にデプロイし、迅速なソフトウェアのデリバリーとデプロイを実現することを指します。これには自動化ツールとプロセスの助けが必要で、自動テスト、自動ビルド、自動デプロイなどが含まれます。人為的な操作によるミスや遅延を減らし、ソフトウェアデリバリーの速度と品質を高めます。</p>
<p>CI/CD は開発チームが市場のニーズやユーザーのフィードバックにより速く応えることを助け、開発サイクルとコストを削減すると同時に、チームのコラボレーション効率とコード管理の信頼性を高めます。CI/CD の実現には一連のツールと技術が必要で、バージョン管理ツール、自動ビルドツール、自動テストツール、コンテナ技術、クラウドコンピューティングなどが含まれます。現在、市場には Jenkins、GitLab CI、Travis CI、CircleCI など多くの CI/CD ツールやプラットフォームがあり、豊富な機能とプラグインを提供し、多様な言語とフレームワークでの開発をサポートしています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="circleci">CircleCI<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#circleci" class="hash-link" aria-label="CircleCI への直接リンク" title="CircleCI への直接リンク" translate="no">​</a></h2>
<p>クローズドソースは有料、オープンソースは無料：オープンソースプロジェクトは無料で利用できます。価格は必要な並行ビルドインスタンスの数によって決まり、単一インスタンスは無料です。</p>
<p><a href="https://circleci.com/" target="_blank" rel="noopener noreferrer" class="">Continuous Integration and Delivery</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="jenkins">Jenkins<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#jenkins" class="hash-link" aria-label="Jenkins への直接リンク" title="Jenkins への直接リンク" translate="no">​</a></h2>
<p>無料で、最もよく使われている CI/CD ツールの一つ。Java ベースで開発されています。</p>
<p><a href="https://www.jenkins.io/zh/" target="_blank" rel="noopener noreferrer" class="">Jenkins</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="buddy">Buddy<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#buddy" class="hash-link" aria-label="Buddy への直接リンク" title="Buddy への直接リンク" translate="no">​</a></h2>
<p>アカウント登録後 14 日間無料で利用できます。UI が比較的モダンで、機能が豊富かつ習得も簡単です。</p>
<p><a href="https://buddy.works/" target="_blank" rel="noopener noreferrer" class="">Buddy: The DevOps Automation Platform</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="gitlab">GitLab<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#gitlab" class="hash-link" aria-label="GitLab への直接リンク" title="GitLab への直接リンク" translate="no">​</a></h2>
<p>コミュニティ版は無料です。GitLab は単なるコードリポジトリ管理ツールではなく、CI/CD 機能も内蔵しており、パイプラインスクリプトを直接記述して、コンパイル・パッケージング・リリースなどのタスクを完結できます。</p>
<p><a href="https://about.gitlab.com/" target="_blank" rel="noopener noreferrer" class="">The One DevOps Platform</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="jenkins-x">Jenkins X<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#jenkins-x" class="hash-link" aria-label="Jenkins X への直接リンク" title="Jenkins X への直接リンク" translate="no">​</a></h2>
<p>Kubernetes クラスタにより良く溶け込むために、Helm、Draft、GitOps、さらに Nexus、chartmuseum、monocular など数多くのシステムやツールを統合しています。これによりビルド・コンパイル環境の自動構築、コンテナイメージの生成、パイプライン、自動デプロイを実現し、シンプルな Review を通じて環境間の自動リリースを行えます。</p>
<p><a href="https://www.jenkins.io/zh/projects/jenkins-x/" target="_blank" rel="noopener noreferrer" class="">Jenkins X</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="雲效yunxiao">雲效（Yunxiao）<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#%E9%9B%B2%E6%95%88yunxiao" class="hash-link" aria-label="雲效（Yunxiao） への直接リンク" title="雲效（Yunxiao） への直接リンク" translate="no">​</a></h2>
<p>Alibaba Cloud が構築したワンストップのクラウドネイティブ開発プラットフォームです。git リポジトリ、Bug 管理、タスク割り当て、パイプラインリリースなどの機能を内蔵し、統合度が非常に高く、クラウド上のあらゆるリソースと連携しているため、企業が迅速に開発・デプロイを行うのに適しています。</p>
<p><a href="https://www.aliyun.com/product/yunxiao?channel=yy_sem_key_yunxiao&amp;utm_content=se_1009966124&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">阿里云云效-DevOps自动化运维-企业级DevOps平台-项目协作工具-阿里云</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/14/chang-jian-de-ci-cd#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>これらのツールに絶対的な優劣はなく、むしろシーンとのマッチングの問題です。個人やオープンソースプロジェクトなら、CircleCI のようなオープンソース向けに無料のホスティングサービスをまず検討するとよいでしょう。チームがすでに GitLab でコードを管理しているなら、内蔵の GitLab CI をそのまま使うのが最も手軽です。Jenkins はエコシステムとプラグイン数で勝り、自前でパイプラインを組む自由度が最も高いツールです。インフラがすでに Kubernetes 上で動いているなら、Jenkins X のほうがクラウドネイティブなワークフローにフィットします。中国国内の企業でリソースが Alibaba Cloud 上にあるなら、雲效のワンストップな統合も試す価値があります。</p>]]></content:encoded>
            <category>CI/CD</category>
        </item>
        <item>
            <title><![CDATA[Debian apt に Alibaba Cloud ミラーを追加する]]></title>
            <link>https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt</link>
            <guid>https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt</guid>
            <pubDate>Fri, 14 Oct 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Debian デフォルトの apt ソースを Alibaba Cloud ミラーに置き換える：sources.list のバックアップと編集、ミラーアドレスの書き込み、apt-get update でのインデックス更新。ソースの書式とミラー変更時のよくある落とし穴も解説します。]]></description>
            <content:encoded><![CDATA[<p>新規インストールした Debian はデフォルトで公式ソースを使いますが、中国国内からソフトウェアパッケージをダウンロードすると数十 KB/s しか出ないことが多く、何かをインストールしようとするとプログレスバーが延々と止まったままになります。apt ソースを Alibaba Cloud の国内ミラーに切り替えるのは、マシンを手に入れたら私がたいてい最初にやることです。ここに手順と注意点を書き留めておきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜミラーを変えるのか">なぜミラーを変えるのか<a href="https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt#%E3%81%AA%E3%81%9C%E3%83%9F%E3%83%A9%E3%83%BC%E3%82%92%E5%A4%89%E3%81%88%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜミラーを変えるのか への直接リンク" title="なぜミラーを変えるのか への直接リンク" translate="no">​</a></h2>
<p>apt でソフトウェアをインストールするとき、<code>/etc/apt/sources.list</code> に設定されたリポジトリのアドレスからパッケージとインデックスをダウンロードします。公式ソースのサーバーは国外にあるため、中国国内からのアクセスはレイテンシが高く帯域も細く、<code>apt-get update</code> も <code>apt-get install</code> も非常に遅くなり、タイムアウトで失敗することさえあります。Alibaba Cloud、清華大学、中国科学技術大学などのミラーサイトは公式ソースから定期的にリポジトリ全体を同期しており、パッケージの内容は同一で、アドレスが国内ノードに変わっただけです。速度は桁違いに向上します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ソースリストの書式">ソースリストの書式<a href="https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt#%E3%82%BD%E3%83%BC%E3%82%B9%E3%83%AA%E3%82%B9%E3%83%88%E3%81%AE%E6%9B%B8%E5%BC%8F" class="hash-link" aria-label="ソースリストの書式 への直接リンク" title="ソースリストの書式 への直接リンク" translate="no">​</a></h2>
<p>手を動かす前に、まず sources.list の各行の意味を理解しておきましょう。書式は決まっています：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb [仓库地址] [发行版代号] [组件...]</span><br></div></code></pre></div></div>
<ul>
<li class=""><code>deb</code> はバイナリパッケージのリポジトリを表し、<code>deb-src</code> は対応するソースコードリポジトリです。普段ソフトウェアをインストールするだけなら <code>deb-src</code> は設定しなくても構いません；</li>
<li class="">ディストリビューションのコードネームは Debian のバージョンに対応します。例えば <code>stretch</code> は Debian 9 です。ミラーを変えるときはコードネームが自分のシステムのバージョンと一致していなければなりません。<code>cat /etc/os-release</code> で確認できます；</li>
<li class="">コンポーネントのうち <code>main</code> は公式のメインリポジトリで、<code>contrib</code> と <code>non-free</code> には非自由ソフトウェアに依存するパッケージや、それ自体が非自由なパッケージ（一部のドライバやファームウェアなど）が含まれます。通常はすべて付けておきます；</li>
<li class=""><code>stretch/updates</code> はセキュリティ更新のリポジトリ、<code>stretch-updates</code> は通常の更新、<code>stretch-backports</code> は新しいバージョンから移植（バックポート）されたパッケージを提供します。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="操作手順">操作手順<a href="https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt#%E6%93%8D%E4%BD%9C%E6%89%8B%E9%A0%86" class="hash-link" aria-label="操作手順 への直接リンク" title="操作手順 への直接リンク" translate="no">​</a></h2>
<p>1）ソースリストのファイルを開く</p>
<p>vim /etc/apt/sources.list</p>
<p>変更前にバックアップを取っておくことをおすすめします。問題が起きてもすぐ元に戻せます：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 既存のソース設定をバックアップする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cp</span><span class="token plain"> /etc/apt/sources.list /etc/apt/sources.list.bak</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ソースリストを編集する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">vim</span><span class="token plain"> /etc/apt/sources.list</span><br></div></code></pre></div></div>
<p>2）Alibaba Cloud ミラーのアドレスを追加する</p>
<p>既存の内容をコメントアウトするか空にして、以下の行を書き込みます（stretch すなわち Debian 9 の例）：</p>
<p>deb <a href="http://mirrors.aliyun.com/debian/" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian/</a> stretch main non-free contrib<br>
deb-src <a href="http://mirrors.aliyun.com/debian/" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian/</a> stretch main non-free contrib<br>
deb <a href="http://mirrors.aliyun.com/debian-security" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian-security</a> stretch/updates main<br>
deb-src <a href="http://mirrors.aliyun.com/debian-security" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian-security</a> stretch/updates main<br>
deb <a href="http://mirrors.aliyun.com/debian/" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian/</a> stretch-updates main non-free contrib<br>
deb-src <a href="http://mirrors.aliyun.com/debian/" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian/</a> stretch-updates main non-free contrib<br>
deb <a href="http://mirrors.aliyun.com/debian/" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian/</a> stretch-backports main non-free contrib<br>
deb-src <a href="http://mirrors.aliyun.com/debian/" target="_blank" rel="noopener noreferrer" class="">http://mirrors.aliyun.com/debian/</a> stretch-backports main non-free contrib</p>
<p>コピーしやすいプレーンテキスト版：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb http://mirrors.aliyun.com/debian/ stretch main non-free contrib</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb-src http://mirrors.aliyun.com/debian/ stretch main non-free contrib</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb http://mirrors.aliyun.com/debian-security stretch/updates main</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb-src http://mirrors.aliyun.com/debian-security stretch/updates main</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb-src http://mirrors.aliyun.com/debian/ stretch-updates main non-free contrib</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">deb-src http://mirrors.aliyun.com/debian/ stretch-backports main non-free contrib</span><br></div></code></pre></div></div>
<p>3）ソースを更新する</p>
<p>保存して終了したら、ローカルのパッケージインデックスを更新して新しい設定を反映させます：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">apt-get</span><span class="token plain"> update</span><br></div></code></pre></div></div>
<p>このステップでは Alibaba Cloud ミラーからすべてのリポジトリのインデックスファイルを取得し直します。出力の中に <code>mirrors.aliyun.com</code> のアドレスが見えるはずです。以降 <code>apt-get install</code> を実行すれば、ダウンロードは国内ミラー経由になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">コードネームは必ずシステムのバージョンと対応させること。stretch のソースを別のバージョンに書き込むと、軽くて update の 404 エラー、悪くすると異なるバージョンのパッケージが混在して依存関係が壊れます。他のバージョンでは、上記のすべての <code>stretch</code> を対応するコードネームに置き換えるだけです。</li>
<li class="">比較的新しい Debian バージョンでは、セキュリティソースの書き方が <code>コードネーム/updates</code> から <code>コードネーム-security</code> に変わりました。古い記事をそのまま写すとリポジトリが見つからないエラーに遭遇します。ミラーサイトのトップページに掲載されている設定を正としてください。</li>
<li class="">update 時に GPG 署名関連のエラーが出る場合、たいていは対応する鍵がないかシステム時刻がずれています。まず時刻を確認してから鍵を調べましょう。検証をスキップして強行インストールするのはやめてください。</li>
<li class="">イントラネットや安定性への要求が高い環境では、ソースを変更した後、まず <code>apt-get update</code> して小さなパッケージを 1 つインストールして検証し、問題がないことを確認してから大規模なインストールやアップグレードを行いましょう。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/10/14/debian-tian-jia-a-li-yuan-apt#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ミラーの変更自体は 3 ステップだけです。<code>/etc/apt/sources.list</code> をバックアップして編集し、ミラーのアドレスを書き込み、<code>apt-get update</code> でインデックスを更新する。鍵となるのはコードネームとシステムのバージョンを一致させることと、変更前にバックアップを残しておくこと。問題が起きてもいつでも元に戻せます。この考え方は清華ミラーや中国科学技術大学ミラーにもそのまま使えます。ドメイン名を変えるだけです。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[JUC 復習（その二）ロックを知る]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er</guid>
            <pubDate>Sat, 27 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[JUC 復習の第 2 回：フェアロック、リエントラントロック、読み書きロックなど代表的なロックの概念を整理し、CAS アルゴリズムの原理と ABA 問題、そして AQS の位置づけを紹介します。]]></description>
            <content:encoded><![CDATA[<p>ロックは並行プログラミングにおいて避けて通れないテーマです。今回はフェアロックや読み書きロックから CAS、AQS まで、代表的なロックの概念をまとめて振り返ります。JUC 復習シリーズの第 2 回です。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ロックの基本概念">ロックの基本概念<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AE%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5" class="hash-link" aria-label="ロックの基本概念 への直接リンク" title="ロックの基本概念 への直接リンク" translate="no">​</a></h2>
<p>ロック機構によって、マルチスレッド環境において同一の時間・空間内でクリティカルセクションのコードに入れるスレッドを 1 つに限定でき、クリティカルセクション内で操作されるデータの一貫性が保証されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="代表的なロック">代表的なロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#%E4%BB%A3%E8%A1%A8%E7%9A%84%E3%81%AA%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="代表的なロック への直接リンク" title="代表的なロック への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-フェアロック">1) フェアロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#1-%E3%83%95%E3%82%A7%E3%82%A2%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="1) フェアロック への直接リンク" title="1) フェアロック への直接リンク" translate="no">​</a></h3>
<p>特定のスレッドが飢餓状態に陥ること、つまり CPU タイムスライスが割り当てられずに命令をいつまでも実行できなくなることを防ぎます。公平性を保証するにはスレッドの状態を追加で管理する必要があり、スレッドのコンテキストスイッチが増えてオーバーヘッドが大きくなり、スループットは低下します。ReentrantLock は初期化パラメータでフェアロックに設定できますが、Synchronized はデフォルトで非フェアロックであり、フェアロックに変更することはできません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-非フェアロック">2) 非フェアロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#2-%E9%9D%9E%E3%83%95%E3%82%A7%E3%82%A2%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="2) 非フェアロック への直接リンク" title="2) 非フェアロック への直接リンク" translate="no">​</a></h3>
<p>計算リソースを直接奪い合うため、スレッドの飢餓が発生しやすくなります。その一方でスループットは高く、スレッドの切り替えも頻繁ではありません。先に CPU タイムスライスを獲得したスレッドが実行されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-リエントラントロック">3) リエントラントロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#3-%E3%83%AA%E3%82%A8%E3%83%B3%E3%83%88%E3%83%A9%E3%83%B3%E3%83%88%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="3) リエントラントロック への直接リンク" title="3) リエントラントロック への直接リンク" translate="no">​</a></h3>
<p>再帰ロックとも呼ばれ、すでに取得済みのロックオブジェクトを再度取得でき、ある程度デッドロックを回避できます。ReentrantLock と Synchronized はどちらもリエントラントロックです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-非リエントラントロック">4) 非リエントラントロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#4-%E9%9D%9E%E3%83%AA%E3%82%A8%E3%83%B3%E3%83%88%E3%83%A9%E3%83%B3%E3%83%88%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="4) 非リエントラントロック への直接リンク" title="4) 非リエントラントロック への直接リンク" translate="no">​</a></h3>
<p>スレッドが実行中にロックを取得した後、同期ブロック内で同じロックを再度取得しようとするとデッドロックになります。非リエントラントロックは同期ブロック内で 1 回しか取得できません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-ミューテックスロック相互排他ロック">5) ミューテックスロック（相互排他ロック）<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#5-%E3%83%9F%E3%83%A5%E3%83%BC%E3%83%86%E3%83%83%E3%82%AF%E3%82%B9%E3%83%AD%E3%83%83%E3%82%AF%E7%9B%B8%E4%BA%92%E6%8E%92%E4%BB%96%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="5) ミューテックスロック（相互排他ロック） への直接リンク" title="5) ミューテックスロック（相互排他ロック） への直接リンク" translate="no">​</a></h3>
<p>あるスレッドがリソースを占有している間、他のスレッドはサスペンドされ、CPU リソースを消費しません。ロックが解放されると、CPU がサスペンド中のスレッドをスケジューリングします。高頻度で操作されないリソースに適しており、そうでない場合はスレッドの頻繁なスケジューリングにより効率が低下します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-読み書きロック">6) 読み書きロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#6-%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="6) 読み書きロック への直接リンク" title="6) 読み書きロック への直接リンク" translate="no">​</a></h3>
<p>ReentrantReadWriteLock 読み書きロックは、読み取りロックと書き込みロックの 2 つの部分で構成され、読み取りロックと書き込みロックは互いに排他的です。共有リソースを読み取るだけなら読み取りロックを、共有リソースを変更する必要があるなら書き込みロックを使います。読み取りが多く書き込みが少ないシナリオに適しています。書き込みロックがどのスレッドにも保持されていない場合、複数のスレッドが読み取りロックを並行して保持できますが、書き込みロックがスレッドに保持されると、他のスレッドによる読み取りロック・書き込みロックの取得操作はすべてブロックされます。</p>
<p>**書き込み優先：**書き込みスレッドが書き込みロックを取得し続けると、読み取りスレッドが「飢餓」に陥ります。</p>
<p>**読み取り優先：**読み取りスレッドが読み取りロックを取得し続けると、書き込みスレッドは永遠にロックを取得できず、書き込みスレッドが「飢餓」に陥ります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-フェア読み書きロック">7) フェア読み書きロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#7-%E3%83%95%E3%82%A7%E3%82%A2%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="7) フェア読み書きロック への直接リンク" title="7) フェア読み書きロック への直接リンク" translate="no">​</a></h3>
<p>フェア読み書きロックの比較的シンプルな実現方法は、ロックを取得するスレッドをキューで並ばせることです。書き込みスレッドか読み取りスレッドかを問わず、先入れ先出しの原則に従ってロックを取得させます。これにより読み取りスレッドは引き続き並行実行でき、「飢餓」現象をある程度緩和できます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-スタンプ付きロック">8) スタンプ付きロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#8-%E3%82%B9%E3%82%BF%E3%83%B3%E3%83%97%E4%BB%98%E3%81%8D%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="8) スタンプ付きロック への直接リンク" title="8) スタンプ付きロック への直接リンク" translate="no">​</a></h3>
<p>StampedLock は ReentrantReadWriteLock よりも高速なロックで、楽観的読み取り、悲観的読み取りロック、書き込みロックをサポートします。ReentrantReadWriteLock と異なり、StampedLock は複数のスレッドが楽観的読み取りを申請している最中でも、1 つのスレッドによる書き込みロックの申請を許可します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="並行同期を捉える視点">並行同期を捉える視点<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#%E4%B8%A6%E8%A1%8C%E5%90%8C%E6%9C%9F%E3%82%92%E6%8D%89%E3%81%88%E3%82%8B%E8%A6%96%E7%82%B9" class="hash-link" aria-label="並行同期を捉える視点 への直接リンク" title="並行同期を捉える視点 への直接リンク" translate="no">​</a></h2>
<p>以下の「ロック」は具体的なロック実装ではなく、並行同期を捉える視点、あるいはロックの状態を表すものです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-楽観的ロック">1) 楽観的ロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#1-%E6%A5%BD%E8%A6%B3%E7%9A%84%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="1) 楽観的ロック への直接リンク" title="1) 楽観的ロック への直接リンク" translate="no">​</a></h3>
<p>バージョン番号や CAS アルゴリズムで実現でき、競合の発生確率が低いことを前提とします。その動作方式は、リソースを変更する際に自分だけが変更していると仮定し、変更完了後に検証を行い、問題があれば再度変更するというものです。なお「ロック」と呼ばれてはいますが、楽観的ロックは一切ロックをかけないため、ロックフリープログラミングとも呼ばれます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-悲観的ロック">2) 悲観的ロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#2-%E6%82%B2%E8%A6%B3%E7%9A%84%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="2) 悲観的ロック への直接リンク" title="2) 悲観的ロック への直接リンク" translate="no">​</a></h3>
<p>複数のスレッドがリソースを変更することを前提とし、変更のたびにロックをかけます。一般にデータベース自体のロック機構は悲観的ロックの仕組みをベースに実装されています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-セグメントロック">3) セグメントロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#3-%E3%82%BB%E3%82%B0%E3%83%A1%E3%83%B3%E3%83%88%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="3) セグメントロック への直接リンク" title="3) セグメントロック への直接リンク" translate="no">​</a></h3>
<p>セグメントロックの設計目的は、ロックの操作粒度を細かくすることです。たとえば配列全体を更新する必要がなく、配列内の 1 要素だけを更新する場合は、その要素にだけロックをかければ十分です。JDK1.7 の ConcurrentHashMap はセグメントロックの形で並行操作を実現しており、Segment に対してロックをかけることでより高い並行効率を達成していましたが、JDK1.8 では CAS アルゴリズムに置き換えられました。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-バイアスロック">4) バイアスロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#4-%E3%83%90%E3%82%A4%E3%82%A2%E3%82%B9%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="4) バイアスロック への直接リンク" title="4) バイアスロック への直接リンク" translate="no">​</a></h3>
<p>ある同期コードが常に 1 つのスレッドからのみアクセスされる場合、そのスレッドが自動的にロックを取得するようにして、ロック取得のコストを下げる仕組みです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-軽量ロック">5) 軽量ロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#5-%E8%BB%BD%E9%87%8F%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="5) 軽量ロック への直接リンク" title="5) 軽量ロック への直接リンク" translate="no">​</a></h3>
<p>現在のロックがバイアスロックの状態で別のスレッドからアクセスされると、バイアスロックは軽量ロックに昇格します。他のスレッドはスピンによってロックの取得を試み、ブロックされることはありません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-重量ロック">6) 重量ロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#6-%E9%87%8D%E9%87%8F%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="6) 重量ロック への直接リンク" title="6) 重量ロック への直接リンク" translate="no">​</a></h3>
<p>現在のロックが軽量ロックの状態で、別のスレッドが一定回数スピンしてもロックを取得できない場合、軽量ロックは重量ロックに昇格し、他のスレッドをブロックします。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-スピンロック">7) スピンロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#7-%E3%82%B9%E3%83%94%E3%83%B3%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="7) スピンロック への直接リンク" title="7) スピンロック への直接リンク" translate="no">​</a></h3>
<p>ロックを取得しようとするスレッドがすぐにはブロックされず、ループしながらロックの取得を試みる方式です。ただし CPU リソースをより多く消費します。つまり空回りし続けるわけです。通常はスピン回数を設定し、指定回数に達するとスレッドをサスペンドします。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-デッドロック">8) デッドロック<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#8-%E3%83%87%E3%83%83%E3%83%89%E3%83%AD%E3%83%83%E3%82%AF" class="hash-link" aria-label="8) デッドロック への直接リンク" title="8) デッドロック への直接リンク" translate="no">​</a></h3>
<p>デッドロックはロックの一種ではなく、現象です。実行中の 2 つのスレッドが、互いに相手が保持しているロックを取得しなければ命令を続行できない状態になると、デッドロックが発生します。プログラムがフリーズし、処理を先に進められなくなります。デッドロックは <code>jstack</code> コマンドで調査できます。まず <code>jps -l</code> コマンドでアプリケーションの pid（プロセス番号）を取得し、次に <code>jstack &lt;プロセス番号&gt;</code> でデッドロックを調査します。また <code>jconsole</code> コマンドで UI ツールを開いてデッドロック情報を確認することもできます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cas-アルゴリズムとは">CAS アルゴリズムとは<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#cas-%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0%E3%81%A8%E3%81%AF" class="hash-link" aria-label="CAS アルゴリズムとは への直接リンク" title="CAS アルゴリズムとは への直接リンク" translate="no">​</a></h2>
<p>CAS は Compare And Swap の略で、その名のとおり<strong>比較と交換</strong>を意味します。このアルゴリズムはロックフリーアルゴリズムの一種で、ロックをかけずにスレッドセーフを保証できます。つまりスレッドをブロックすることなく変数の同期を実現するため、<strong>ノンブロッキング同期</strong>の範疇に属します。</p>
<p>CAS アルゴリズムには 3 つのオペランドが関わります。メモリ値 <strong>V</strong>、比較値 <strong>A</strong>、交換値 <strong>B</strong> です。具体的な操作手順は、V が A と等しい場合に限り、CAS アルゴリズムがアトミックに B で V を置き換え、そうでなければ何も実行しない、というものです。一般に CAS アルゴリズムは<strong>スピン</strong>操作であり、成功するまで繰り返しリトライします。</p>
<p><strong>CAS アルゴリズムには次の特徴があります。ABA 問題、スピンによるオーバーヘッド、単一の共有変数に対するアトミック操作しか保証しないこと、です。</strong></p>
<ul>
<li class="">ABA 問題。変数 V を最初に読み取ったときの値が A で、代入直前の確認でも A のままだったとしても、他のスレッドに変更されていないとは言い切れません。その間に別の値に変更され、その後 A に戻された可能性があるからです。この場合、CAS アルゴリズムは一度も変更されていないと誤認してしまいます。これが CAS アルゴリズムの ABA 問題と呼ばれるものです。実際の利用シーンでは判断が必要で、ABA 問題の影響が小さいシーンでは対処しなくても構いません。そうでなければ、変更のたびにバージョン番号を付与する方式で ABA 問題を解決するか、あるいは悲観的ロックを採用します。</li>
<li class="">スピンによるオーバーヘッド。前述のとおり、CAS アルゴリズムはスピン操作であり、壁にぶつかっても引き返さない精神で、失敗すると成功するまでリトライし続けます。しかしこのスピンが長時間続くと CPU に大きな負担がかかります。これがいわゆるスピンのオーバーヘッドです。</li>
<li class="">単一の共有変数に対するアトミック操作しか保証しないこと。CAS アルゴリズムは単一の共有変数に対してのみ有効で、複数の共有変数にまたがる操作では同期を保証できません。解決方法の一つは、それらの共有変数を 1 つのオブジェクトにまとめてから CAS アルゴリズムで処理することです。</li>
</ul>
<p>低レイヤーのアセンブリ命令は <code>lock cmpxchgl</code> で、このうち lock プレフィックスが特に重要です。その命令の説明は次のとおりです。</p>
<p>1）メモリに対する読み取り・変更・書き込み操作がアトミックに実行されることを保証します。Pentium およびそれ以前のプロセッサでは、lock プレフィックス付きの命令は実行中にバスをロックし、他のプロセッサが一時的にバス経由でメモリにアクセスできなくなります。当然これは高価なオーバーヘッドをもたらします。Pentium 4、Intel Xeon、および P6 プロセッサ以降、Intel は従来のバスロックをベースに非常に意義のある最適化を行いました。lock プレフィックス命令の実行中に、アクセス対象のメモリ領域（area of memory）がすでにプロセッサ内部のキャッシュでロックされており（つまり、そのメモリ領域を含むキャッシュラインが排他状態または変更済み状態にある）、かつそのメモリ領域が単一のキャッシュライン（cache line）内に完全に収まっている場合、プロセッサはその命令を直接実行します。命令の実行中はそのキャッシュラインがロックされ続けるため、他のプロセッサはその命令がアクセスするメモリ領域を読み書きできず、命令実行のアトミック性が保証されます。この操作過程はキャッシュロック（cache locking）と呼ばれ、lock プレフィックス命令の実行コストを大幅に削減します。ただし、マルチプロセッサ間の競合が激しい場合や、命令がアクセスするメモリアドレスがアラインされていない場合は、依然としてバスがロックされます。</p>
<p>2）この命令と、その前後の読み取り・書き込み命令とのリオーダリングを禁止します。</p>
<p>3）ライトバッファ内のすべてのデータをメモリにフラッシュします。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="aqs-とは">AQS とは<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#aqs-%E3%81%A8%E3%81%AF" class="hash-link" aria-label="AQS とは への直接リンク" title="AQS とは への直接リンク" translate="no">​</a></h2>
<p>Abstract Queued Synchronizer（抽象キュー同期器）のことで、JUC 内のすべての Lock インターフェース実装クラスは AQS 抽象クラスをベースに実装されています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/27/jucfu-xi-pian-er#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回は代表的なロックの概念を一度に整理しました。フェア・非フェア、リエントラント・非リエントラントはロックの振る舞いの特性であり、読み書きロックと StampedLock は読み取りが多く書き込みが少ないシナリオ向け、そして楽観的ロック・悲観的ロック、バイアスロックから重量ロックへの昇格は、むしろ並行同期を捉える視点やロックの状態を表すものです。CAS は JUC のロックフリープログラミングの礎であり、その 3 つのオペランドと ABA 問題を理解しておくことは非常に重要です。AQS については、JUC の各種 Lock 実装に共通する骨格であり、その内部実装は別の記事で改めて掘り下げる価値があります。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[JUC 復習編（一）スレッドの基礎]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi</guid>
            <pubDate>Fri, 26 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[JUC 復習の第一回：JVM メモリモデルとスレッドスタックから始めて、スレッドモデル、スレッドの 5 つの状態、そしてスレッドプールの作成方法と拒否ポリシーを整理します。]]></description>
            <content:encoded><![CDATA[<p>Java の JUC の章は知識ポイントが非常に多いため、自分が身につけた知識をより良く整理する目的で、ブログを書くという形で低レイヤーの原理から順に整理し、並行プログラミングに対する自分なりの理解をまとめていきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="並行プログラミングとは何か">並行プログラミングとは何か<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E4%B8%A6%E8%A1%8C%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF%E4%BD%95%E3%81%8B" class="hash-link" aria-label="並行プログラミングとは何か への直接リンク" title="並行プログラミングとは何か への直接リンク" translate="no">​</a></h2>
<p>その理由はコンピュータの発展の歴史から語る必要があります。ムーアの法則によれば、CPU の演算能力は時間とともに向上していきますが、ムーアの法則はすぐに物理的な限界に近づき、シングルコア CPU の演算能力はボトルネックにぶつかりました。人々はより高い演算能力を求めて、マルチコア CPU アーキテクチャの設計へと舵を切り、マルチコアとハイパースレッディング技術によって CPU の演算能力は大幅に向上しました。原理としては、複数の人が同時に異なる命令を実行するようなもので、全体の効率は当然ひとりで実行するよりも高くなります。（あくまで全体の効率が高くなるという意味であり、マルチスレッドを使えば必ずシングルスレッドのアプリケーションより命令実行効率が高くなるという意味ではありません）</p>
<p>マルチコア CPU の計算リソースをより有効に活用する（CPU を絞り尽くす）ために、マルチスレッド開発が生まれました。マルチスレッドは特定のシナリオでは確かにプログラム全体の効率を高めますが、<strong>銀の弾丸ではありません</strong>。スレッドセーフティ、ロック機構、デッドロック、スレッドコンテキストスイッチのオーバーヘッドといった一連の問題も同時に付いてきます。</p>
<p><strong>さて、ここまでスレッドについて色々と述べてきましたが、そもそもスレッドとは何なのでしょうか？</strong></p>
<p>スレッドとは、オペレーティングシステムが演算スケジューリングを行える最小単位です。プロセスの中に含まれ、プロセスにおける実際の実行単位です。1 本のスレッドとは、プロセス内の単一の順序を持つ制御フローのことで、1 つのプロセス内では複数のスレッドを並行して動かすことができ、各スレッドは並列に異なるタスクを実行します。そして 1 つのプロセスには少なくとも 1 本のスレッドが実行されています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="jvm-におけるスレッドの位置">JVM におけるスレッドの位置<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#jvm-%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AE%E4%BD%8D%E7%BD%AE" class="hash-link" aria-label="JVM におけるスレッドの位置 への直接リンク" title="JVM におけるスレッドの位置 への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-56-6dc9d01d54aecf1fb5b2c41ecf1908a7.webp" width="1006" height="603" class="img_ev3q"></p>
<p>JVM メモリモデルの図から、スレッド Thread が JVM 全体のどこに位置するのかを知ることができます。VM Stack（仮想マシンスタック）空間の中で、各スレッドは自分専用のスレッドスタックを持ち、スレッドスタックには多数のスタックフレーム Stack Frame が格納され、スタックフレームの中にはさらに LVA、OS、FD などの情報が格納されています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lvaローカル変数配列local-variable-array">LVA：ローカル変数配列（Local Variable Array）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#lva%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E5%A4%89%E6%95%B0%E9%85%8D%E5%88%97local-variable-array" class="hash-link" aria-label="LVA：ローカル変数配列（Local Variable Array） への直接リンク" title="LVA：ローカル変数配列（Local Variable Array） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">スタックフレームのローカル変数部分は、インデックス 0 から始まるバイト配列で構成されます。</li>
<li class="">すべてのメソッド引数とローカル変数が含まれます。</li>
<li class="">配列内の各スロット（エントリ）のサイズは 4 バイトです。</li>
<li class="">int、float および参照型は、配列内でそれぞれ 1 つのエントリ（スロット）、すなわち 4 バイトを占有します。</li>
<li class="">double と long 型のデータは、配列内で連続する 2 つのエントリ、合計 8 バイトを占有します。</li>
<li class="">byte、short、char 型は格納前に int 型へ変換され、1 スロット、すなわち 4 バイトを占有します。</li>
<li class="">ただし boolean 型の値の格納方法は JVM によって実装が異なります。ほとんどの JVM 実装では、boolean 型はローカル変数配列内で 1 スロットを占有します。</li>
<li class="">引数は、メソッドで宣言された順序どおりに、最初にローカル変数配列へ配置されます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="osオペランドスタックoperand-stack">OS：オペランドスタック（Operand Stack）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#os%E3%82%AA%E3%83%9A%E3%83%A9%E3%83%B3%E3%83%89%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AFoperand-stack" class="hash-link" aria-label="OS：オペランドスタック（Operand Stack） への直接リンク" title="OS：オペランドスタック（Operand Stack） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">JVM はオペランドスタックを実行時のワークスペースとして、言い換えれば計算の中間結果を格納する場所として使用します。</li>
<li class="">オペランドスタックもローカル変数配列と同様に配列として構成されます。ただしインデックスでアクセスするのではなく、いくつかの命令によってアクセスされます。これらの命令は値をスタックにプッシュしたり、スタックから値をポップして必要な操作を行ったりできます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="fdフレームデータ領域frame-data">FD：フレームデータ領域（Frame Data）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#fd%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%87%E3%83%BC%E3%82%BF%E9%A0%98%E5%9F%9Fframe-data" class="hash-link" aria-label="FD：フレームデータ領域（Frame Data） への直接リンク" title="FD：フレームデータ領域（Frame Data） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">フレームデータ領域には、すべてのシンボル参照（定数プールの解決）と、通常メソッドの return 位置が含まれます。この return は特定のメソッドに関連付けられ、呼び出し元へ戻るために使われます。</li>
<li class="">また、例外テーブルへの参照も含まれており、例外発生時に catch ブロックの情報を提供できます。</li>
</ul>
<p>JDK1.8 では、デフォルトのスレッドスタック空間は 1MB で、<code>-XX:ThreadStackSize=256k</code> によってスレッドスタックのメモリ空間サイズを調整できます。スレッドスタック空間が不足すると、StackOverflowError 例外がスローされます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドスタック">スレッドスタック<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF" class="hash-link" aria-label="スレッドスタック への直接リンク" title="スレッドスタック への直接リンク" translate="no">​</a></h2>
<p>**スレッドスタック：**スレッドが関数を実行するたびにスタックフレームが 1 つプッシュされます。スレッドスタックは先入れ後出し（FILO）に従い、先に入ったものがスタックの底に、後から入ったものがスタックの頂上に置かれ、スタックの頂上から順に実行されます。例えば、A 関数が B 関数を呼び出し、B 関数がさらに C 関数を呼び出す場合、スタック構造は次の図のようになります。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="457" height="471" class="img_ev3q"></p>
<p>スタック構造はシンプルで、ガベージコレクタも必要ありません。スタックフレームは実行が完了すると即座にメモリが破棄されるためです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドモデル">スレッドモデル<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%A2%E3%83%87%E3%83%AB" class="hash-link" aria-label="スレッドモデル への直接リンク" title="スレッドモデル への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-対-1-スレッドモデル1-つの-lwp-が-1-つの-klt-に対応">1 対 1 スレッドモデル（1 つの LWP が 1 つの KLT に対応）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#1-%E5%AF%BE-1-%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%A2%E3%83%87%E3%83%AB1-%E3%81%A4%E3%81%AE-lwp-%E3%81%8C-1-%E3%81%A4%E3%81%AE-klt-%E3%81%AB%E5%AF%BE%E5%BF%9C" class="hash-link" aria-label="1 対 1 スレッドモデル（1 つの LWP が 1 つの KLT に対応） への直接リンク" title="1 対 1 スレッドモデル（1 つの LWP が 1 つの KLT に対応） への直接リンク" translate="no">​</a></h3>
<p>JDK1.8 で使用されているのはこのモデルです。</p>
<p>**KLT：カーネルレベルスレッド（Kernel Level Thread）**このスレッド実装は、オペレーティングシステムのカーネルが直接スレッドのサポート、スレッド切り替え、およびスケジューラの操作によるスケジューリングを行うもので、スレッドが各プロセッサにマッピングされます。</p>
<p>**LWP：軽量プロセス（Light Weight Process）**プログラムは通常、カーネルスレッドを直接使うのではなく、カーネルスレッドの高級インターフェースである LWP を介してカーネルスレッドをスケジューリング・操作します。LWP と通常のプロセスの違いは、最小限の実行コンテキストとスケジューラに必要な統計情報しか持たないことにあり、これが「軽量」と呼ばれる理由です。また、LWP と KLT の関係は 1:1 で存在します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-58-eb43fb8e4e480589dae73d43e0fd3a74.webp" width="951" height="636" class="img_ev3q"></p>
<p>メリット：</p>
<p>1）実装がシンプルで、大部分のマルチスレッドシナリオに適用でき、現在主流の JVM はいずれもこの方式を採用しています。</p>
<p>デメリット：</p>
<p>1）ユーザースレッドのブロックとウェイクアップが直接カーネルスレッドにマッピングされるうえ、スレッド数の増加に伴い、スレッド間の頻繁な切り替えによって CPU のオーバーヘッドが大きくなります。JDK1.8 では CAS アルゴリズムが導入され、スレッド間の頻繁な切り替えとスレッドのロック取得を回避することで、JVM の並行性能は確かに大幅に向上しました。</p>
<p>2）オペレーティングシステムのカーネルが作成できるスレッド数には限りがあり、アプリケーションが過剰なスレッドを作成すると、システムの性能が大幅に低下します。CPU のタイムスライスの大部分が、過剰なスレッドの切り替えによって消費されてしまうのです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="多対多スレッドモデルlwp-の上に仮想的なユーザースレッド-ut-を構築">多対多スレッドモデル（LWP の上に仮想的なユーザースレッド UT を構築）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E5%A4%9A%E5%AF%BE%E5%A4%9A%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%A2%E3%83%87%E3%83%ABlwp-%E3%81%AE%E4%B8%8A%E3%81%AB%E4%BB%AE%E6%83%B3%E7%9A%84%E3%81%AA%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89-ut-%E3%82%92%E6%A7%8B%E7%AF%89" class="hash-link" aria-label="多対多スレッドモデル（LWP の上に仮想的なユーザースレッド UT を構築） への直接リンク" title="多対多スレッドモデル（LWP の上に仮想的なユーザースレッド UT を構築） への直接リンク" translate="no">​</a></h3>
<p>現在、Go 言語と JDK19 バージョンで使用されているのはこのモデルです。</p>
<p>**UT：ユーザースレッド（User Thread）**ユーザー空間に構築され、システムカーネルはユーザースレッドの存在を感知できません。スレッドの作成、破棄、切り替えのオーバーヘッドが小さいのが特徴です。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-59-36418701a1fc2ee67b61c7e5c35ed456.webp" width="796" height="657" class="img_ev3q"></p>
<p>多対多モデルは 2 レベルスレッドモデルとも呼ばれ、各スレッドモデルの長所を十分に取り入れつつ、それらの短所を可能な限り回避しています。このモデルでは、ユーザースレッドとカーネルスレッドは多対多（M : N、通常 M &gt;= N）のマッピングモデルになります。</p>
<p>メリット：</p>
<p>1）アプリケーション自身が UT 間の切り替えを管理するため、下層の CPU は頻繁にスレッドを切り替える必要がなく、現在のスレッドをより連続的に実行でき、プログラムの効率が向上します。</p>
<p>2）UT のスケジューリング優先度をプログラム自身でコントロールできます。</p>
<p>3）並行数を大幅に高められます。このモデルなら UT を 1 万本以上起動しても十分にサービスを提供でき、高並行シナリオに生まれつき適しています。</p>
<p>デメリット：</p>
<p>1）実装が比較的複雑です。例えば Go 言語が採用している GMP スレッドモデルはこの多対多方式で実装されており、これが goroutine で高い並行性を実現できる理由のひとつでもあります。現在、Java の Loom プロジェクトもこの方面を探求しており、JDK19 ですでに実装されているので、ぜひ試してみてください。<a href="https://openjdk.org/jeps/425" target="_blank" rel="noopener noreferrer" class="">JEP 425：仮想スレッド（プレビュー版） (openjdk.org)</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドの-5-つの状態">スレッドの 5 つの状態<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AE-5-%E3%81%A4%E3%81%AE%E7%8A%B6%E6%85%8B" class="hash-link" aria-label="スレッドの 5 つの状態 への直接リンク" title="スレッドの 5 つの状態 への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-62-fbfa9c8007363de3c757b9ae4a73abee.webp" width="1019" height="670" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-新規new">1) 新規（New）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#1-%E6%96%B0%E8%A6%8Fnew" class="hash-link" aria-label="1) 新規（New） への直接リンク" title="1) 新規（New） への直接リンク" translate="no">​</a></h3>
<p>スレッドオブジェクトが作成されると、新規状態に入ります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-実行可能runnable">2) 実行可能（Runnable）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#2-%E5%AE%9F%E8%A1%8C%E5%8F%AF%E8%83%BDrunnable" class="hash-link" aria-label="2) 実行可能（Runnable） への直接リンク" title="2) 実行可能（Runnable） への直接リンク" translate="no">​</a></h3>
<p>「実行可能状態」とも呼ばれます。スレッドオブジェクトが作成された後、他のスレッドがそのオブジェクトの start() メソッドを呼び出すことでスレッドが起動されます（例：thread.start()）。実行可能状態にあるスレッドは、いつでも CPU にスケジューリングされて実行される可能性がありますが、具体的にいつ実行されるかは CPU 自身が決定します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-実行中running">3) 実行中（Running）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#3-%E5%AE%9F%E8%A1%8C%E4%B8%ADrunning" class="hash-link" aria-label="3) 実行中（Running） への直接リンク" title="3) 実行中（Running） への直接リンク" translate="no">​</a></h3>
<p>スレッドが CPU の実行権を取得して実行します。注意すべき点は、スレッドは実行可能状態からのみ実行中状態に入れるということです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-ブロックblocked">4) ブロック（Blocked）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#4-%E3%83%96%E3%83%AD%E3%83%83%E3%82%AFblocked" class="hash-link" aria-label="4) ブロック（Blocked） への直接リンク" title="4) ブロック（Blocked） への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">(01) 待機ブロック -- スレッドの wait() メソッドを呼び出して、ある処理の完了をスレッドに待たせます。notify() メソッドでそのスレッドをウェイクアップでき、ウェイクアップ後にスレッドは実行可能状態に入ります。</li>
<li class="">(02) 同期ブロック -- スレッドが synchronized 同期ロックの取得に失敗した場合（ロックが他のスレッドに占有されているため）、同期ブロック状態に入ります。</li>
<li class="">(03) その他のブロック -- スレッドの sleep() や join() を呼び出したり、I/O リクエストを発行したりすると、スレッドはブロック状態に入ります。sleep() のタイムアウト、join() の対象スレッドの終了またはタイムアウト、あるいは I/O 処理の完了時に、スレッドは再び実行可能状態に戻ります。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-終了dead">5) 終了（Dead）<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#5-%E7%B5%82%E4%BA%86dead" class="hash-link" aria-label="5) 終了（Dead） への直接リンク" title="5) 終了（Dead） への直接リンク" translate="no">​</a></h3>
<p>スレッドの実行が完了したか、例外によって run() メソッドを抜けた場合、そのスレッドはライフサイクルを終えます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="java-スレッドプールの活用">Java スレッドプールの活用<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#java-%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%97%E3%83%BC%E3%83%AB%E3%81%AE%E6%B4%BB%E7%94%A8" class="hash-link" aria-label="Java スレッドプールの活用 への直接リンク" title="Java スレッドプールの活用 への直接リンク" translate="no">​</a></h2>
<p>上記のスレッド状態からわかるように、スレッドの作成と破棄を絶えず繰り返すと、新規と終了の 2 つの状態でより多くの計算リソースが消費されます。スレッドをより有効に使うために、スレッドリソースを管理するスレッドプールが登場しました。スレッドプールでは、まず指定した数のスレッドを事前に作成しておき、使用が必要になったときにそこからスレッドを取り出して実行し、対応するタスクの実行が終わったらスレッドプールに戻します。こうすることで、作成と破棄の繰り返しをうまく回避し、プログラムの効率を高めることができます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-60-11f1b3fb7fc52f39dfcec972cc9df124.webp" width="894" height="682" class="img_ev3q"></p>
<p>メリット：</p>
<p>1）アプリケーション内のスレッド数とタスク数をより良く管理できます。</p>
<p>2）タスクの実行速度が上がり、つまりリクエストの応答時間が短縮されます。（作成と破棄のステップがないため）</p>
<p>3）マルチスレッド並列計算による効率向上や、その他の拡張機能を利用できます。</p>
<p>4）スレッドを再利用することで、システムの計算リソースのオーバーヘッドを削減できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドプールの作成方法">スレッドプールの作成方法<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%97%E3%83%BC%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90%E6%96%B9%E6%B3%95" class="hash-link" aria-label="スレッドプールの作成方法 への直接リンク" title="スレッドプールの作成方法 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">ThreadPoolExecutor で手動でスレッドプールを作成する。</li>
<li class="">Executors エグゼキュータで自動的にスレッドプールを作成する。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="スレッドプールを作成する-7-つの方法">スレッドプールを作成する 7 つの方法<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%97%E3%83%BC%E3%83%AB%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B-7-%E3%81%A4%E3%81%AE%E6%96%B9%E6%B3%95" class="hash-link" aria-label="スレッドプールを作成する 7 つの方法 への直接リンク" title="スレッドプールを作成する 7 つの方法 への直接リンク" translate="no">​</a></h3>
<p>1）Executors.newFixedThreadPool：固定サイズのスレッドプールを作成します。並行するスレッド数を制御でき、超過したスレッドはキューで待機します。
2）Executors.newCachedThreadPool：キャッシュ可能なスレッドプールを作成します。スレッド数が処理に必要な数を超えた場合、一定期間キャッシュした後に回収され、スレッド数が足りない場合は新しいスレッドを作成します。
3）Executors.newSingleThreadExecutor：単一スレッドのスレッドプールを作成します。先入れ先出しの実行順序を保証できます。
4）Executors.newScheduledThreadPool：遅延タスクを実行できるスレッドプールを作成します。
5）Executors.newSingleThreadScheduledExecutor：遅延タスクを実行できる単一スレッドのスレッドプールを作成します。
6）Executors.newWorkStealingPool：プリエンプティブに実行するスレッドプールを作成します（タスクの実行順序は不定）。
7）ThreadPoolExecutor：手動でスレッドプールを作成する方式で、作成時に最大 7 つのパラメータを設定できます：<strong>コアスレッド数、最大スレッド数、ブロッキングキューの種類（有界、無界）、生存時間、時間の単位、スレッドファクトリ、拒否ポリシー</strong>。</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> corePoolSize</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">                    </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// コアスレッド数</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">int</span><span class="token plain"> maximumPoolSize</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">                 </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 最大スレッド数</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">long</span><span class="token plain"> keepAliveTime</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">                  </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 非コアスレッドのアイドル生存時間</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token class-name" style="color:hsl(29, 54%, 61%)">TimeUnit</span><span class="token plain"> unit</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">                       </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 生存時間の単位</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token class-name" style="color:hsl(29, 54%, 61%)">BlockingQueue</span><span class="token generics punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token generics class-name" style="color:hsl(29, 54%, 61%)">Runnable</span><span class="token generics punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"> workQueue</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">   </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// ブロッキングキュー（有界 / 無界）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token class-name" style="color:hsl(29, 54%, 61%)">ThreadFactory</span><span class="token plain"> threadFactory</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain">         </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// スレッドファクトリ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token class-name" style="color:hsl(29, 54%, 61%)">RejectedExecutionHandler</span><span class="token plain"> handler     </span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 拒否ポリシー</span><br></div></code></pre></div></div>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>スレッドプールの作成には、最後の ThreadPoolExecutor 方式の使用をおすすめします。これを使えばスレッドプールの動作ルールを明確にでき、リソース枯渇のリスクを回避できるからです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="拒否ポリシー">拒否ポリシー<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E6%8B%92%E5%90%A6%E3%83%9D%E3%83%AA%E3%82%B7%E3%83%BC" class="hash-link" aria-label="拒否ポリシー への直接リンク" title="拒否ポリシー への直接リンク" translate="no">​</a></h2>
<p>拒否ポリシーは全部で 4 種類あります：</p>
<p>1）ThreadPoolExecutor.AbortPolicy：デフォルトの拒否ポリシーで、タスクを拒否して例外をスローします。
2）ThreadPoolExecutor.CallerRunsPolicy：呼び出し元のスレッドで直接タスクを実行します。
3）ThreadPoolExecutor.DiscardPolicy：タスクをそのまま拒否し、エラーはスローしません。
4）ThreadPoolExecutor.DiscardOldestPolicy：拒否ポリシーが発動した後、新しいタスクが追加され続ける限り、ブロッキングキュー内の最も古いタスクを破棄し続け、新しいタスクを追加します。</p>
<p><strong>スレッドの最大実行タスク数 = スレッド数 + キューの長さ。</strong></p>
<p>スレッドプールの最大スレッド数が 10、キューの長さが 20 であれば、そのスレッドプールは最大で同時に 30 個のタスクを受け付けられることを意味し、超過したタスクには対応する拒否ポリシーが適用されます。もちろん、RejectedExecutionHandler インターフェースを自分で実装して拒否処理をハンドリングすることもできます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/26/jucfu-xi-pian-yi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回は JVM メモリモデルを入り口に、スレッドスタックの内部構造、1 対 1 と多対多という 2 つのスレッドモデルのトレードオフ、そしてスレッドの 5 つの状態を整理しました。スレッドの作成と破棄のコストを理解すれば、スレッドプールが存在する意味も理解できます。実際の開発では ThreadPoolExecutor で手動でスレッドプールを作成し、コアパラメータと拒否ポリシーを明確にして、Executors のデフォルト設定がもたらすリソースリスクを回避することをおすすめします。これらの基礎概念は、この後のロック機構や JUC の各種シンクロナイザを理解するための前提となります。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Kubernetes に Istio をデプロイする]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/22/kubernetes</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/22/kubernetes</guid>
            <pubDate>Mon, 22 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Istio のアーキテクチャとコアコンポーネントを紹介し、Kubernetes クラスタで Helm を使って Istio をインストールし、Bookinfo サンプルアプリケーションをデプロイする過程を記録します。]]></description>
            <content:encoded><![CDATA[<p>kubernetes はすでに運用上の大部分の問題を解決しており、動的スケーリング、スケジューリング、イメージ管理、コンテナダウン時の再起動、コンピュートノードの監視など、優れた自動運用の仕組みを備えています。しかし k8s は、アプリケーション層での監視やトラフィック管理、アプリケーション間の相互呼び出しの調整、サービスレジストリ、設定センターといった機能は提供していません。そこで登場したのが、Service Mesh アーキテクチャの代表的プロダクトである Istio です。</p>
<!-- -->
<p>Istio は Google、IBM、Lyft の三社が共同開発したフレームワークで、主な役割は k8s 環境においてマイクロサービス間のトラフィックを制御、接続、保護、設定、観測することです。これにより、マイクロサービスを開発する際に、煩雑で複雑、保守性が低く強く結合した低レベルのマイクロサービス SDK を捨てられるようになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="istio-のアーキテクチャ">Istio のアーキテクチャ<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#istio-%E3%81%AE%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" class="hash-link" aria-label="Istio のアーキテクチャ への直接リンク" title="Istio のアーキテクチャ への直接リンク" translate="no">​</a></h2>
<p><strong>アーキテクチャ上、Istio はデータプレーンとコントロールプレーンに分かれます。</strong></p>
<p>データプレーンの核心となる発想が Sidecar（サイドカーパターン）です。同一 pod 内に proxy 代理コンテナを追加してビジネスコンテナのトラフィックを引き受け、ビジネスコードに一切侵入することなく、さまざまな機能を実現します。例えば、ロードバランシング、トラフィックの暗号化、監視、ポリシー（カナリアリリース、Blue-Green デプロイ）、サーキットブレーカー、フォールバック、リモートサービス呼び出し、設定センター、サービスレジストリなど、よく使われるマイクロサービス機能です（ただし分散トランザクションの管理は担当しません。これは理屈から言っても、確かに Istio が引き受けるべき範疇ではないでしょう）。</p>
<p>コントロールプレーンは、プロキシを管理・設定してトラフィックをルーティングする役割を担います。さらにコントロールプレーンは Mixer を設定してポリシーの適用とテレメトリデータの収集・表示を行い、サービス呼び出しチェーンの分析やサービストポロジー図などの機能を提供します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-50-29cab4357ba6d65501863969da0b1a2a.webp" width="1057" height="715" class="img_ev3q"></p>
<p>ビジネスコードが SpringCloud、Dubbo、Motan、ServiceComb といった強い結合を持つ特定のマイクロサービス SDK に依存しなくなるため、ビジネスシーンに応じて開発言語を選び、異なる言語の開発チームがそれぞれビジネス開発を進められるようになり、全体の効率が上がります。細々としたサービス登録、設定、リモート呼び出しの管理は、すべて Istio に任せてしまえばよいのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンポーネント紹介">コンポーネント紹介<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E7%B4%B9%E4%BB%8B" class="hash-link" aria-label="コンポーネント紹介 への直接リンク" title="コンポーネント紹介 への直接リンク" translate="no">​</a></h2>
<p>citadel：身元認証と証明書管理を担うコアセキュリティコンポーネント。</p>
<p>galley：設定管理を担うコンポーネント。設定ファイルのフォーマットや内容の正しさを検証し、それらの設定情報を pilot や mixer に提供します。</p>
<p>pilot：制御の中枢で、サービスディスカバリとルールの変換・配布を含みます。</p>
<p>proxy：C++ で開発された Envoy と Pilot-agent によって実装され、動的サービスディスカバリ、ロードバランシング、TLS、サーキットブレーカー、ヘルスチェック、トラフィック分割、カナリアなどの機能を提供します。さらにテレメトリデータを生成し、マイクロサービスに可観測性をもたらします。</p>
<p>Ingressgateway：入口の gateway です。つまり、メッシュ外からメッシュ内のサービスへのアクセスはこの gateway を通じて行われます。</p>
<p><strong>詳細な中国語公式ドキュメント：</strong><a href="https://istio.io/latest/zh/docs/" target="_blank" rel="noopener noreferrer" class=""><strong>Istio / ドキュメント</strong></a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイの流れ">デプロイの流れ<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%AE%E6%B5%81%E3%82%8C" class="hash-link" aria-label="デプロイの流れ への直接リンク" title="デプロイの流れ への直接リンク" translate="no">​</a></h2>
<p>ここでは最も簡単な Helm によるインストールを使います。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1なぜ-helm-を使うのか">1）なぜ Helm を使うのか<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#1%E3%81%AA%E3%81%9C-helm-%E3%82%92%E4%BD%BF%E3%81%86%E3%81%AE%E3%81%8B" class="hash-link" aria-label="1）なぜ Helm を使うのか への直接リンク" title="1）なぜ Helm を使うのか への直接リンク" translate="no">​</a></h3>
<p>Helm は K8S クラスタにおけるパッケージ管理ツールの役割を担います。Service、Pod、Ingress といった K8S の各種リソースを宣言するには Yaml ファイルを書く必要があります。しかしマイクロサービスが増え、さらに redis クラスタ、mysql 高可用クラスタ、Hadoop クラスタなどが加わると、書くべき Yaml ファイルはどんどん増え、書き間違いのリスクも伴います。より重要なのは、ビジネスの成長に伴って他の地域にも K8S クラスタをデプロイした場合、マルチリージョン・アクティブ構成では同じ Pod 群をもう一度デプロイし直す必要が出てくることです。このようなとき Helm はこの種の問題をうまく解決してくれます。Helm の Chart には、一つの完全なサービスに必要なすべての Yaml ファイル設定が含まれているのです。</p>
<p>Helm の利点：</p>
<ul>
<li class="">yaml ファイルの効率的な再利用を実現する。</li>
<li class="">大量の yaml ファイルを一つのまとまりとして管理できる。</li>
<li class="">アプリケーションレベルのバージョン管理を実現する。</li>
</ul>
<p>その後は企業内部の Chart リポジトリを構築することもできます。そうすれば、企業が何かのサービスを必要とするときに、煩雑な Yaml ファイルをもう書く必要はなく、Helm で Chart を実行するだけで K8S クラスタに対応する Pod をデプロイし、目的の機能サービスを手に入れられます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2helm-のインストール">2）Helm のインストール<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#2helm-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="2）Helm のインストール への直接リンク" title="2）Helm のインストール への直接リンク" translate="no">​</a></h3>
<p>Helm を使うにはまずインストールが必要です（公式サイト）：<a href="https://helm.sh/docs/" target="_blank" rel="noopener noreferrer" class="">Helm | ドキュメント (helm.sh)</a></p>
<p>インストール時は Helm と Kubernetes のバージョンサポートに注意してください：<a href="https://helm.sh/zh/docs/topics/version_skew/" target="_blank" rel="noopener noreferrer" class="">Helm | Helm バージョンサポートポリシー</a></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3istio-のインストール">3）Istio のインストール<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#3istio-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="3）Istio のインストール への直接リンク" title="3）Istio のインストール への直接リンク" translate="no">​</a></h3>
<p><strong>インストールの実行：<a href="https://istio.io/latest/zh/docs/setup/install/helm/" target="_blank" rel="noopener noreferrer" class="">Istio / Helm によるインストール</a></strong></p>
<p>Istio コンポーネント用の名前空間 <code>istio-system</code> を作成します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ kubectl create namespace istio-system</span><br></div></code></pre></div></div>
<p>Istio base chart をインストールします。これには Istio コントロールプレーンが使用するクラスタスコープのリソースが含まれています。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 公式サイトには対応する chart リポジトリが記載されていないので、ここで補足しておく</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ helm repo </span><span class="token function" style="color:hsl(207, 82%, 66%)">add</span><span class="token plain"> istio https://istio-release.storage.googleapis.com/charts</span><br></div></code></pre></div></div>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ helm </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> istio-base istio/base </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> istio-system</span><br></div></code></pre></div></div>
<p>Istio discovery chart をインストールします。これは <code>istiod</code> サービスのデプロイに使われます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ helm </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> istiod istio/istiod </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> istio-system </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--wait</span><br></div></code></pre></div></div>
<p>（オプション）Istio のイングレスゲートウェイをインストールします。</p>
<p><strong>Linux カーネルは 4.11 以上が必須です。お使いの Linux カーネルバージョンが要件を満たしているか各自確認してください。</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># イングレスゲートウェイ用の名前空間を作成し、sidecar の自動注入を有効化する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ kubectl create namespace istio-ingress</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ kubectl label namespace istio-ingress istio-injection</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">enabled</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">$ helm </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> istio-ingress istio/gateway </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> istio-ingress </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--wait</span><br></div></code></pre></div></div>
<p>Istio ingress のデプロイが完了したら、Istio 公式の demo プロジェクトである Bookinfo マイクロサービスをデプロイして、Istio のさまざまな能力を体験できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bookinfo">Bookinfo<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#bookinfo" class="hash-link" aria-label="Bookinfo への直接リンク" title="Bookinfo への直接リンク" translate="no">​</a></h2>
<p>公式サイト：<a href="https://istio.io/latest/zh/docs/examples/bookinfo/" target="_blank" rel="noopener noreferrer" class="">Istio / Bookinfo アプリケーション</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-noistio-6757dcc885fcdd61bb4011d88efc5dd7.svg" width="662" height="453" class="img_ev3q"></p>
<p>Bookinfo マイクロサービスは Istio によるサイドカーデプロイを採用しており、4 種類の異なる言語のプログラムで構成されています。さらに Java で開発された Reviews サービスは 3 つの異なるバージョンのアプリケーションを提供しており、マルチバージョン管理、トラフィック管理、移行、統合における Istio の能力をよく体現しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/22/kubernetes#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Istio はサービスガバナンスをビジネスコードから切り離します。データプレーンは Sidecar でトラフィックを引き受け、コントロールプレーンはルーティングとポリシーを一元的に配布し、ビジネス側はもはや特定言語のマイクロサービス SDK に縛られません。デプロイ面では Helm の助けを借りれば、install コマンド三つで base、istiod、イングレスゲートウェイをインストールでき、大量の Yaml を手書きするよりずっと楽です。インストール後に公式の Bookinfo サンプルを一通り動かせば、多言語・マルチバージョンのトラフィック管理能力はほぼ体験できます。その先は自分たちのビジネスと組み合わせて、カナリアリリースやサーキットブレーカーといった能力を少しずつ活用していきましょう。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[kubectl よく使うコマンド]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/21/k</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/21/k</guid>
            <pubDate>Sun, 21 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[日常運用で頻繁に使う kubectl コマンドを整理:リソースの確認、作成と削除、ノードのメンテナンス、設定のエクスポート。さらに Pod の強制削除や drain によるノード退避などの注意点も補足します。]]></description>
            <content:encoded><![CDATA[<p>日々 Kubernetes クラスタと付き合っていると、ほとんどの操作は kubectl を避けて通れません。コマンド自体は難しくないのですが、難しいのは使いたいときに思い出せないことです。特にノードのメンテナンスや強制削除といった、頻度は低いけれど重要な操作ほどそうです。この記事では、普段最もよく使うコマンドをシーン別に整理しました。忘れたらここに戻って見返します。</p>
<!-- -->
<p>kubectl のすべての操作は、本質的には kube-apiserver への REST リクエストです。参照は GET、設定の適用は望ましい状態(desired state)への宣言的な更新、削除はオブジェクトを etcd から取り除き、各コントローラーが実際の状態を収束させる、という流れです。この点を理解しておくと、多くのコマンドの挙動が説明しやすくなります。</p>
<p>まずは最も基本的なところから。パラメータの書き方が不確かなときは、ヘルプ情報が常に最初の入り口です。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ヘルプ情報を表示。サブコマンドに対しても使える(例: kubectl get --help)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--help</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスタとノードの確認">クラスタとノードの確認<a href="https://tanzhuo.xyz/ja/2022/08/21/k#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%A8%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E7%A2%BA%E8%AA%8D" class="hash-link" aria-label="クラスタとノードの確認 への直接リンク" title="クラスタとノードの確認 への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># クラスタのノード数と各ノードの状態(Ready / NotReady)を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get nodes</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># node ノードの詳細情報を表示:pod 情報とハードウェアリソースの使用状況</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ノードのリソース不足や Pod がスケジュールできない問題の調査でよく使う</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl describe </span><span class="token function" style="color:hsl(207, 82%, 66%)">node</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">ノード名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><br></div></code></pre></div></div>
<p><code>describe node</code> の出力では、Conditions、Allocated resources、Events の 3 セクションが最も情報量が多く、ノードに異常があるときはまずここを見ましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="namespace-と-pod-の確認">Namespace と Pod の確認<a href="https://tanzhuo.xyz/ja/2022/08/21/k#namespace-%E3%81%A8-pod-%E3%81%AE%E7%A2%BA%E8%AA%8D" class="hash-link" aria-label="Namespace と Pod の確認 への直接リンク" title="Namespace と Pod の確認 への直接リンク" translate="no">​</a></h2>
<p>Kubernetes は Namespace でリソースを分離しています。参照系コマンドの多くは <code>-n</code> で Namespace を指定する必要があり、指定しない場合はデフォルトで default になります。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># Namespace を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get namespace</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 対応する Namespace 配下の pod 情報を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get pod </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">Namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># すべての Namespace 配下の pod を確認。-A は --all-namespaces の短縮形</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get pods </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-A</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># すべての Namespace 配下の pod を確認(完全なパラメータ表記は --all-namespaces)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get pod --all-namespace</span><br></div></code></pre></div></div>
<p>具体的な Pod を特定した後のトラブルシューティングは、主に describe と logs の 2 つのコマンドで行います。片方はイベントを、もう片方はログを見るためのものです。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 1 つまたは複数のリソースオブジェクトの詳細情報を表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># スケジュール失敗、イメージの pull 失敗などの原因はすべて Events に出てくる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl describe</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># pod リソースオブジェクト内の 1 つのコンテナのログを出力</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># マルチコンテナ Pod では -c でコンテナ名を指定する必要がある</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl logs</span><br></div></code></pre></div></div>
<p>ひとつ経験則を。Pod が起動しないときはまず describe でイベントを見て、起動しているのに挙動がおかしいときに logs でログを見る。この順序を逆にすると、たいてい無駄に時間を費やすことになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リソースの作成と削除">リソースの作成と削除<a href="https://tanzhuo.xyz/ja/2022/08/21/k#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E4%BD%9C%E6%88%90%E3%81%A8%E5%89%8A%E9%99%A4" class="hash-link" aria-label="リソースの作成と削除 への直接リンク" title="リソースの作成と削除 への直接リンク" translate="no">​</a></h2>
<p>kubectl のリソース管理には 2 つのスタイルがあります。<code>create</code> は命令的で、クラスタに何を作るかを直接指示します。<code>apply</code> は宣言的で、yaml に記述した望ましい状態をクラスタに提出し、既存であれば差分更新を行います。日常的な設定ファイルのメンテナンスには apply がおすすめで、同じファイルを何度でも実行できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># yaml/json ファイルまたは標準入力からリソースオブジェクトを作成</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl create</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># pod 設定ファイルを適用し、リソースを設定。適用済みのファイルなら差分更新になる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl apply </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-f</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">pod.yaml</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># pod 設定ファイルの適用を取り消し、リソースを削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl delete </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-f</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">pod.yaml</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 指定した Namespace 配下の Deployment を削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 注意:Deployment を削除すると、それが管理する ReplicaSet と Pod もカスケード削除される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl delete Deployments </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">Namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">Deployment名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><br></div></code></pre></div></div>
<p>一括クリーンアップと強制削除のコマンドです。破壊力は順に大きくなっていきます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># Terminating 状態のままの pod を強制削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --grace-period=0 は graceful shutdown の待機をスキップすることを意味する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl delete pod </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">podname</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--force</span><span class="token plain"> --grace-period</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ある namespace 配下のすべての pod を削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Deployment などのコントローラーに管理されている Pod は自動的に再作成される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl delete </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--all</span><span class="token plain"> pods </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># namespace ごと削除。配下のすべてのリソースがまとめてカスケード削除される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl delete ns </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><br></div></code></pre></div></div>
<p>さらに変更系のコマンドが 2 つあります。リソースにラベルを付けたり、設定を一時的に変更したりするのに使います。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># リソースのラベルを設定。ラベルは Service がバックエンドを選択したり、ノードアフィニティでスケジュールしたりする際の根拠になる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl label</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># デフォルトエディタでサーバー上に定義されたリソースオブジェクトを編集。保存すると即時反映される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl edit</span><br></div></code></pre></div></div>
<p><code>edit</code> は緊急デバッグに向いていますが、変更はローカルの yaml ファイルには同期されません。変更後は忘れずに設定ファイルへ反映しましょう。そうしないと次回の apply で上書きされてしまいます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ノードのメンテナンス">ノードのメンテナンス<a href="https://tanzhuo.xyz/ja/2022/08/21/k#%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E3%83%A1%E3%83%B3%E3%83%86%E3%83%8A%E3%83%B3%E3%82%B9" class="hash-link" aria-label="ノードのメンテナンス への直接リンク" title="ノードのメンテナンス への直接リンク" translate="no">​</a></h2>
<p>ノードをメンテナンス(カーネルアップグレード、ハードウェア交換)のためにオフラインにする標準フローは、まず cordon、それから drain です。cordon はノードをスケジュール不可としてマークするだけで、既存の Pod には影響しません。drain はそれに加えて、ノード上の既存 Pod を退避させ、コントローラーが他のノードで再作成します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ノードを退避準備:ノードをスケジュール不可としてマークし、新しい Pod はこのノードにスケジュールされなくなる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl cordon </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">ノード名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ノードの pod を退避</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl drain</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># そのノードのすべての pod を退避</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --ignore-daemonsets:DaemonSet 管理の Pod をスキップ(もともと各ノードに 1 つずつ存在し、退避しても再作成される)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># --delete-local-data:emptyDir のローカルデータを使う Pod も併せて削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl drain </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">ノード名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> --delete-local-data </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--force</span><span class="token plain"> --ignore-daemonsets</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># メンテナンス完了後、クラスタから node ノードを削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl delete nodes </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">ノード名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リソース設定のエクスポート">リソース設定のエクスポート<a href="https://tanzhuo.xyz/ja/2022/08/21/k#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E8%A8%AD%E5%AE%9A%E3%81%AE%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88" class="hash-link" aria-label="リソース設定のエクスポート への直接リンク" title="リソース設定のエクスポート への直接リンク" translate="no">​</a></h2>
<p>本番環境で手動変更したリソースの現在の状態を yaml ファイルとして固定化したいときは、<code>-o yaml</code> とリダイレクトを組み合わせてエクスポートできます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 既存の pod を yaml 設定ファイルとしてエクスポート</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get deployment </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">Namespace</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">pod名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-o</span><span class="token plain"> yaml </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">ファイル名</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain">.yaml</span><br></div></code></pre></div></div>
<p>エクスポートされた yaml には status、resourceVersion、uid といったクラスタランタイムのフィールドが含まれます。別の場所で apply する前に、これらをクリーンアップして spec 関連の部分だけを残すのがよいでしょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/08/21/k#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1)<code>--all-namespace</code> という表記は覚え間違えやすく、完全なパラメータは <code>--all-namespaces</code>(s 付き)です。日常的には短縮形の <code>-A</code> を使うのが一番手軽です。</p>
<p>2)<code>--force --grace-period=0</code> による強制削除は、API Server にオブジェクトを即時に取り除かせるだけで、コンテナのプロセスが本当に終了したとは限りません。ステートフルなサービスでは慎重に使いましょう。同一インスタンスが「スプリットブレイン」的に 2 つ動いてしまう恐れがあります。</p>
<p>3)Namespace の削除はカスケード操作で、配下の Deployment、Service、ConfigMap がすべて一緒に消えます。実行前に <code>kubectl get all -n &lt;namespace&gt;</code> で中に何が残っているかを一度確認しましょう。</p>
<p>4)<code>drain</code> の際に <code>--ignore-daemonsets</code> を付けないと、DaemonSet 管理の Pod に遭遇した時点でコマンドがエラーで中断します。また <code>--delete-local-data</code> は新しいバージョンでは <code>--delete-emptydir-data</code> に改名されています。旧パラメータでエラーになったら新しい名前に替えてください。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>drain は実際に業務 Pod を退避させます。本番環境で実行する前に、レプリカ数が十分か、他のノードに余裕があるかを確認し、サービス容量の急落を避けてください。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/21/k#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>このリストは、確認、作成と削除、ノードのメンテナンス、設定のエクスポートという 4 つの高頻度シーンをカバーしています。コマンドを全部覚えられなくても大丈夫です。2 つの流れさえ覚えておけば十分です。参照とトラブルシューティングは <code>get → describe → logs</code>、ノードのオフライン化は <code>cordon → drain → delete node</code>。それ以外は、<code>--help</code> でいつでも調べられます。</p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[K8S Kubeadm による構築の流れ]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/20/k8s</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/20/k8s</guid>
            <pubDate>Sat, 20 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[kubeadm で Kubernetes 1.24 高可用性クラスタを構築する一連の流れの記録:事前の環境準備、Containerd ランタイム、クラスタの初期化、CNI ネットワークコンポーネント、そしてハマったポイント。]]></description>
            <content:encoded><![CDATA[<p>最近 kubeadm で K8S クラスタを構築した流れと、途中で遭遇したいくつかの問題を、ノートとして記録しておきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイ環境">デプロイ環境<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E7%92%B0%E5%A2%83" class="hash-link" aria-label="デプロイ環境 への直接リンク" title="デプロイ環境 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">構築バージョン:kubernetes-1.24.4(HA)</li>
<li class="">コンテナランタイム:containerd-1.6.8</li>
<li class="">OS:CentOS7</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="前提条件">前提条件<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6" class="hash-link" aria-label="前提条件 への直接リンク" title="前提条件 への直接リンク" translate="no">​</a></h2>
<p>1)クラスタノードの計算リソースは 2 コア CPU・2GB RAM 以上のマシンであること。ノード間は内部ネットワークまたはパブリックネットワークで到達可能であること。</p>
<p>2)各マシンの hostname と MAC アドレスは重複してはならず、product_uuid も一意である必要があります。hostname で対応するノードにアクセスできるよう、/etc/hosts ファイルを設定します。</p>
<p>3)kubernetes 1.24 以降、デフォルトのコンテナランタイムは Docker ではなく Containerd に変わりました。docker を使いたい場合は <a href="https://github.com/Mirantis/cri-dockerd" target="_blank" rel="noopener noreferrer" class="">cri-dockerd</a> をインストールする必要があります。</p>
<p>4)HA アーキテクチャには 3 つの master コントロールプレーンノードが必須です。高可用性には外部 etcd とスタック型 etcd の 2 種類のトポロジーがあり、アーキテクチャは下図のとおりです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="外部-etcd">外部 etcd<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E5%A4%96%E9%83%A8-etcd" class="hash-link" aria-label="外部 etcd への直接リンク" title="外部 etcd への直接リンク" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-46-2b17f77d2252cf1f1b37c1c98c81e317.png" width="1281" height="879" class="img_ev3q"></p>
<p>メリット:コントロールプレーンインスタンスや etcd メンバーを失ったときの影響が小さく、スタック型 HA トポロジーのようにクラスタの冗長性に影響することがありません。</p>
<p>デメリット:より多くのマシンノードが必要で、etcd HA クラスタを個別にデプロイするための 3 台のサーバーが必要です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="スタック型-etcd">スタック型 etcd<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%82%B9%E3%82%BF%E3%83%83%E3%82%AF%E5%9E%8B-etcd" class="hash-link" aria-label="スタック型 etcd への直接リンク" title="スタック型 etcd への直接リンク" translate="no">​</a></h3>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-45-18ff9d55a0109ad3d07f388f439d385a.webp" width="1307" height="837" class="img_ev3q"></p>
<p>メリット:このトポロジーはコントロールプレーンと etcd メンバーを同一ノード上に結合します。外部 etcd クラスタを使う場合に比べてセットアップが簡単で、レプリカ管理もしやすくなります。</p>
<p>デメリット:スタック型クラスタには結合による同時障害のリスクがあります。1 つのノードに障害が発生すると、etcd メンバーとコントロールプレーンインスタンスの両方が失われ、冗長性が損なわれます。コントロールプレーンノードを増やすことでこのリスクを下げられます。</p>
<p><strong>load balancer:</strong> 内部ロードバランサーで、それ自体の HA を保証する必要があります。worker ノードのために apiServer をリバースプロキシし、apiServer への統一されたアクセス IP を提供するとともに、master の可用性を検知できます。</p>
<p>5)Linux のスワップパーティションを無効化します。(スワップパーティション:メモリ不足時にハードディスクをストレージとして使う仕組みで、システム性能は低下しますが、より多くのメモリデータを保持でき、メモリが満杯になってもサーバーがすぐに固まらないという利点があります)</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># CentOS でスワップパーティションを無効化する:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 恒久的に無効化:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 1、vim /etc/fstab で設定ファイルに入り、以下の設定をコメントアウトする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">/dev/mapper/cl-swap     swap     swap    defaults    </span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 2、コメントアウトして保存する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 一時的に無効化:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># swapoff -a コマンドを実行する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">swapoff </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-a</span><br></div></code></pre></div></div>
<p>6)ファイアウォールを無効化するか、公式サイト指定のポートを開放します</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイアウォールの状態を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--state</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 1、ファイアウォールのポートを開放。master ノードには 6443 2379-2380 10250 10251 10252 が必要</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">6443</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">2379</span><span class="token plain">-2380/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">10250</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">10251</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">10252</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイアウォールのポートを開放。node-worker ノードには 10250 30000-32767 が必要</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">10250</span><span class="token plain">/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--permanent</span><span class="token plain"> --add-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">30000</span><span class="token plain">-32767/tcp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイアウォール設定のリロード</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--reload</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ポートが正しく設定されたか確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">firewall-cmd </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--zone</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">public --list-ports</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 2、ファイアウォールを無効化する選択肢もある!安全ではないが、多くの問題を回避できる。たとえば後述の CNI ネットワークコンポーネントに必要なポートなど</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># firewall を停止</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">systemctl stop firewalld.service</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># firewall の自動起動を無効化</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">systemctl disable firewalld.service</span><br></div></code></pre></div></div>
<p>7)yum リポジトリ情報 /etc/yum.repos.d/kubernetes.repo を設定します(公式リポジトリは使えない可能性があり、VPN 経由のアクセスが必要になるため、ここでは中国国内の Alibaba リポジトリを使用します)</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;&lt;</span><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> /etc/yum.repos.d/kubernetes.repo</span><span class="token string" style="color:hsl(95, 38%, 62%)"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">[kubernetes]</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">name=Kubernetes</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">enabled=1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">gpgcheck=1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">repo_gpgcheck=1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><br></div></code></pre></div></div>
<p>8)カーネルのフォワーディングとブリッジフィルタリングを有効にし、ipset と ipvsadm をインストールして、対応するカーネルモジュールを有効化します</p>
<p>公式ドキュメント:<a href="https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/" target="_blank" rel="noopener noreferrer" class="">コンテナランタイム | Kubernetes</a></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ロードするカーネルモジュールを宣言する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;&lt;</span><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation function" style="color:hsl(207, 82%, 66%)">tee</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> /etc/modules-load.d/k8s.conf</span><span class="token string" style="color:hsl(95, 38%, 62%)"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">overlay</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">br_netfilter</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> modprobe overlay</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> modprobe br_netfilter</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ブリッジフィルタリングとカーネルフォワーディングを有効化する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;&lt;</span><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> </span><span class="token string bash punctuation function" style="color:hsl(207, 82%, 66%)">tee</span><span class="token string bash punctuation" style="color:hsl(220, 14%, 71%)"> /etc/sysctl.d/k8s.conf</span><span class="token string" style="color:hsl(95, 38%, 62%)"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">net.bridge.bridge-nf-call-ip6tables = 1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">net.bridge.bridge-nf-call-iptables = 1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">net.ipv4.ip_forward = 1</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">vm.swappiness = 0</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 設定を反映させる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sysctl</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--system</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ipset ipvsadm をインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">yum </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-y</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> ipset ipvsadm</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain"> /etc/sysconfig/modules/</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ipvs に必要なカーネルモジュールを宣言する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> /etc/sysconfig/modules/ipvs.modules </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;&lt;</span><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">#!/bin/bash</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">modprobe -- ip_vs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">modprobe -- ip_vs_rr</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">modprobe -- ip_vs_wrr</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">modprobe -- ip_vs_sh</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">modprobe -- nf_conntrack</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token string" style="color:hsl(95, 38%, 62%)">EOF</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 実行権限を付与して実行する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">chmod</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">755</span><span class="token plain"> /etc/sysconfig/modules/ipvs.modules </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&amp;&amp;</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">bash</span><span class="token plain"> /etc/sysconfig/modules/ipvs.modules</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 反映されたか確認する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">lsmod </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain"> ip_vs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># コンソール出力</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ip_vs_sh               </span><span class="token number" style="color:hsl(29, 54%, 61%)">16384</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ip_vs_wrr              </span><span class="token number" style="color:hsl(29, 54%, 61%)">16384</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ip_vs_rr               </span><span class="token number" style="color:hsl(29, 54%, 61%)">16384</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ip_vs                 </span><span class="token number" style="color:hsl(29, 54%, 61%)">180224</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">6</span><span class="token plain"> ip_vs_rr,ip_vs_sh,ip_vs_wrr</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">nf_conntrack          </span><span class="token number" style="color:hsl(29, 54%, 61%)">176128</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token plain"> ip_vs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">nf_defrag_ipv6         </span><span class="token number" style="color:hsl(29, 54%, 61%)">24576</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">2</span><span class="token plain"> nf_conntrack,ip_vs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">libcrc32c              </span><span class="token number" style="color:hsl(29, 54%, 61%)">16384</span><span class="token plain">  </span><span class="token number" style="color:hsl(29, 54%, 61%)">2</span><span class="token plain"> nf_conntrack,ip_vs</span><br></div></code></pre></div></div>
<p>9)SELinux を permissive モードに設定します(実質的に無効化するのと同等です)</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> setenforce </span><span class="token number" style="color:hsl(29, 54%, 61%)">0</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナランタイムのインストール">コンテナランタイムのインストール<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="コンテナランタイムのインストール への直接リンク" title="コンテナランタイムのインストール への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="containerd">Containerd<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#containerd" class="hash-link" aria-label="Containerd への直接リンク" title="Containerd への直接リンク" translate="no">​</a></h3>
<p>参考:<a href="https://blog.csdn.net/weixin_39246554/article/details/120930966" target="_blank" rel="noopener noreferrer" class="">実戦:centos7 での containerd のインストール-20211023_一念一生～one のブログ-CSDN ブログ_centos で containerd をインストール</a></p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>Containerd 1.6 以降のバージョンでは、ローカル依存の libseccomp を 2.4 以上にアップグレードする必要があります。さもないとコンテナが起動できない事態が起こり得ます。私自身が実際にハマったポイントで、エラー情報が非常に見つけにくいうえ、CentOS の yum リポジトリには最高でも 2.3.1 しかないため、公式から自分でダウンロードしてインストールするしかありません。</p></div></div>
<p>関連コマンド:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 現在の libseccomp バージョンを確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rpm</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-qa</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain"> libseccomp</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 旧バージョンをアンインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rpm</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-e</span><span class="token plain"> libseccomp-devel-2.3.1-4.el7.x86_64 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--nodeps</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rpm</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-e</span><span class="token plain"> libseccomp-2.3.1-4.el7.x86_64 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--nodeps</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 新バージョンの libseccomp をダウンロード</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">wget</span><span class="token plain"> http://rpmfind.net/linux/centos/8-stream/BaseOS/x86_64/os/Packages/libseccomp-2.5.1-1.el8.x86_64.rpm</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># libseccomp をインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rpm</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-ivh</span><span class="token plain"> libseccomp-2.5.1-1.el8.x86_64.rpm</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスタの初期化">クラスタの初期化<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AE%E5%88%9D%E6%9C%9F%E5%8C%96" class="hash-link" aria-label="クラスタの初期化 への直接リンク" title="クラスタの初期化 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1kubeadmkubectlkubelet-の三大コンポーネントをインストールする">1)kubeadm、kubectl、kubelet の三大コンポーネントをインストールする<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#1kubeadmkubectlkubelet-%E3%81%AE%E4%B8%89%E5%A4%A7%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%82%92%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E3%81%99%E3%82%8B" class="hash-link" aria-label="1)kubeadm、kubectl、kubelet の三大コンポーネントをインストールする への直接リンク" title="1)kubeadm、kubectl、kubelet の三大コンポーネントをインストールする への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># コンポーネントをインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> yum </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-y</span><span class="token plain"> kubelet-1.24.4 kubeadm-1.24.4 kubectl-1.24.4 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--disableexcludes</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">kubernetes</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># kubelet の自動起動を設定</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> systemctl </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">enable</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--now</span><span class="token plain"> kubelet</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2コントロールプレーンノードを初期化する">2)コントロールプレーンノードを初期化する<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#2%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%97%E3%83%AC%E3%83%BC%E3%83%B3%E3%83%8E%E3%83%BC%E3%83%89%E3%82%92%E5%88%9D%E6%9C%9F%E5%8C%96%E3%81%99%E3%82%8B" class="hash-link" aria-label="2)コントロールプレーンノードを初期化する への直接リンク" title="2)コントロールプレーンノードを初期化する への直接リンク" translate="no">​</a></h3>
<p>kubeadm でノードを初期化します。コマンドは kubeadm init【オプション】です。</p>
<p><strong>より詳しいオプションは公式ドキュメントを参照してください:<a href="https://kubernetes.io/zh-cn/docs/reference/setup-tools/kubeadm/" target="_blank" rel="noopener noreferrer" class="">Kubeadm | Kubernetes</a></strong></p>
<p>ここでは私のローカル環境の設定値を使っています。本番環境では対応する IP アドレスを本番の内部ネットワーク IP に変えるだけで OK です。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># コマンドでコントロールプレーンを初期化できる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> kubeadm init </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--control-plane-endpoint </span><span class="token string" style="color:hsl(95, 38%, 62%)">"k8s-cluster:6443"</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--apiserver-advertise-address</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.5.27 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--apiserver-bind-port</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">6443</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--image-repository registry.aliyuncs.com/google_containers </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--kubernetes-version v1.24.4 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--cert-dir</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/etc/kubernetes/pki </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--service-cidr</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">10.96</span><span class="token plain">.0.0/12 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--pod-network-cidr</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token number" style="color:hsl(29, 54%, 61%)">172.24</span><span class="token plain">.0.0/16 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">--upload-certs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 初期化ファイルを使う方法もある。まず初期化ファイルを生成し、自分のシナリオに合わせてパラメータを修正する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubeadm config print init-defaults  </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> kubeadm-config.yaml</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubeadm init </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--config</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> kubeadm-config.yaml --upload-certs</span><br></div></code></pre></div></div>
<p>主要なオプションの説明:</p>
<p>--control-plane-endpoint:内部の <strong>load balancer</strong> の IP アドレス、または内部ネットワークの DNS ドメイン名情報を記入します。worker ノードはこのドメイン名または IP を通じてクラスタ内部の master ノードにアクセスし、master の高可用性を確保します。</p>
<p>--apiserver-advertise-address:現在のコントロールプレーンの内部ネットワーク IP アドレスを記入します。</p>
<p>--pod-network-cidr:pod の内部ネットワークセグメントの IP。</p>
<p>--service-cidr:service の内部ネットワークセグメントの IP。</p>
<p>--image-repository:イメージリポジトリのアドレス。中国国内向けに設定しています。私はここで Alibaba のリポジトリを使い、取得速度を向上させました。</p>
<p><strong>初期化時、kubeadm は k8s.gcr.io/pause イメージを取得する必要がありますが、中国国内ではブロックされているため、ダウンロードに失敗して初期化がこける可能性が高いです。初期化に失敗した場合はコンテナランタイムのログを確認し、対応するイメージを取得できていないことが分かれば、この問題です(私は数日間ハマって原因を探しました)。解決方法:</strong></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># コンテナランタイムのログを確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">journalctl </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-xeu</span><span class="token plain"> containerd</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># k8s.gcr.io は海外ネットワークに接続しないと取得できないため、k8s の基盤コンテナ pause がしばしば取得できない。</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># crictl で中国国内のイメージリポジトリから取得し、ctr tag を使って解決できる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 国内ミラーからイメージを取得</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">crictl pull registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># タグを付け替える</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ctr </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-n</span><span class="token plain"> k8s.io image tag registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6 k8s.gcr.io/pause:3.6</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3初期化成功後の出力">3)初期化成功後の出力<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#3%E5%88%9D%E6%9C%9F%E5%8C%96%E6%88%90%E5%8A%9F%E5%BE%8C%E3%81%AE%E5%87%BA%E5%8A%9B" class="hash-link" aria-label="3)初期化成功後の出力 への直接リンク" title="3)初期化成功後の出力 への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Your Kubernetes control-plane has initialized successfully</span><span class="token operator" style="color:hsl(207, 82%, 66%)">!</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">To start using your cluster, you need to run the following as a regular user:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token function" style="color:hsl(207, 82%, 66%)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain"> </span><span class="token environment constant" style="color:hsl(29, 54%, 61%)">$HOME</span><span class="token plain">/.kube</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">cp</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-i</span><span class="token plain"> /etc/kubernetes/admin.conf </span><span class="token environment constant" style="color:hsl(29, 54%, 61%)">$HOME</span><span class="token plain">/.kube/config</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">chown</span><span class="token plain"> </span><span class="token variable" style="color:hsl(207, 82%, 66%)">$(</span><span class="token variable function" style="color:hsl(207, 82%, 66%)">id</span><span class="token variable" style="color:hsl(207, 82%, 66%)"> </span><span class="token variable parameter variable" style="color:hsl(207, 82%, 66%)">-u</span><span class="token variable" style="color:hsl(207, 82%, 66%)">)</span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">:</span><span class="token variable" style="color:hsl(207, 82%, 66%)">$(</span><span class="token variable function" style="color:hsl(207, 82%, 66%)">id</span><span class="token variable" style="color:hsl(207, 82%, 66%)"> </span><span class="token variable parameter variable" style="color:hsl(207, 82%, 66%)">-g</span><span class="token variable" style="color:hsl(207, 82%, 66%)">)</span><span class="token plain"> </span><span class="token environment constant" style="color:hsl(29, 54%, 61%)">$HOME</span><span class="token plain">/.kube/config</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Alternatively, </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">if</span><span class="token plain"> you are the root user, you can run:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">export</span><span class="token plain"> </span><span class="token assign-left variable" style="color:hsl(207, 82%, 66%)">KUBECONFIG</span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain">/etc/kubernetes/admin.conf</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">You should now deploy a pod network to the cluster.</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Run </span><span class="token string" style="color:hsl(95, 38%, 62%)">"kubectl apply -f [podnetwork].yaml"</span><span class="token plain"> with one of the options listed at:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  https://kubernetes.io/docs/concepts/cluster-administration/addons/</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">You can now </span><span class="token function" style="color:hsl(207, 82%, 66%)">join</span><span class="token plain"> any number of the control-plane </span><span class="token function" style="color:hsl(207, 82%, 66%)">node</span><span class="token plain"> running the following </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">command</span><span class="token plain"> on each as root:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  kubeadm </span><span class="token function" style="color:hsl(207, 82%, 66%)">join</span><span class="token plain"> k8s-cluster:6443 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--token</span><span class="token plain"> v6rrcg.vnl78b94fo740xv9 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        --discovery-token-ca-cert-hash sha256:5dd2edf3d668bd0608897a0097455c6a514a91fd0b1ad4d1044789d9f56123ce </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        --control-plane --certificate-key 4c9b2b3a2d19db4b4f58681e6cf264a0b7bb1eacf443ae90a50bd0e8c11e0c31</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Please note that the certificate-key gives access to cluster sensitive data, keep it secret</span><span class="token operator" style="color:hsl(207, 82%, 66%)">!</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">As a safeguard, uploaded-certs will be deleted </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">in</span><span class="token plain"> two hours</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"> If necessary, you can use</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token string" style="color:hsl(95, 38%, 62%)">"kubeadm init phase upload-certs --upload-certs"</span><span class="token plain"> to reload certs afterward.</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Then you can </span><span class="token function" style="color:hsl(207, 82%, 66%)">join</span><span class="token plain"> any number of worker nodes by running the following on each as root:</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubeadm </span><span class="token function" style="color:hsl(207, 82%, 66%)">join</span><span class="token plain"> k8s-cluster:6443 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--token</span><span class="token plain"> v6rrcg.vnl78b94fo740xv9 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        --discovery-token-ca-cert-hash sha256:5dd2edf3d668bd0608897a0097455c6a514a91fd0b1ad4d1044789d9f56123ce </span><br></div></code></pre></div></div>
<p>以上の出力情報から、対応する node ノードをクラスタに参加させるためのコマンド情報が分かります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ノードのクラスタ参加">ノードのクラスタ参加<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E5%8F%82%E5%8A%A0" class="hash-link" aria-label="ノードのクラスタ参加 への直接リンク" title="ノードのクラスタ参加 への直接リンク" translate="no">​</a></h2>
<p>kubeadm join コマンドに token 情報を付ければ、他の master ノードや worker ノードを参加させられます。token はデフォルトで 6 時間有効、再生成した token は 24 時間有効です。token の期限が切れた場合は、コマンドで再生成できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># master コントロールプレーンノードの参加時に実行:(証明書の暗号化情報を取得)コントロールプレーンノードの参加には token + certs 情報が必要</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubeadm init phase upload-certs --upload-certs</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># worker ノード参加用の token 情報を生成</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubeadm token create --print-join-command</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 現在有効な token の一覧を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubeadm token list</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ネットワーク-cni-コンポーネントのインストール">ネットワーク CNI コンポーネントのインストール<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%83%8D%E3%83%83%E3%83%88%E3%83%AF%E3%83%BC%E3%82%AF-cni-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="ネットワーク CNI コンポーネントのインストール への直接リンク" title="ネットワーク CNI コンポーネントのインストール への直接リンク" translate="no">​</a></h2>
<p>自社のビジネスシナリオと規模に応じて、セキュリティとパフォーマンスの観点から適切な CNI コンポーネントを選択できます。注意:クラスタに存在できる CNI コンポーネントは一種類だけです。</p>
<p><strong>詳細は公式ドキュメントも参照してください:</strong><a href="https://kubernetes.io/zh-cn/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/" target="_blank" rel="noopener noreferrer" class="">kubeadm を使ったクラスタの作成 | Kubernetes</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-47-e74df237f2373cb24825389fd1ff8792.png" width="1336" height="679" class="img_ev3q"></p>
<p>ここでは定番の CNI コンポーネントである Calico を採用します。</p>
<p>Calico 公式サイト:<a href="https://projectcalico.docs.tigera.io/getting-started/kubernetes/self-managed-onprem/onpremises" target="_blank" rel="noopener noreferrer" class="">オンプレミス環境への Calico ネットワークとネットワークポリシーのインストール (tigera.io)</a></p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># まず、クラスタに operator をインストールする。</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl create </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-f</span><span class="token plain"> https://raw.githubusercontent.com/projectcalico/calico/v3.24.0/manifests/tigera-operator.yaml</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Calico の設定に必要なカスタムリソースをダウンロードする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">curl</span><span class="token plain"> https://raw.githubusercontent.com/projectcalico/calico/v3.24.0/manifests/custom-resources.yaml </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-O</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># custom-resources.yaml ファイルをカスタマイズする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># その中の pod ネットワークセグメントの IP 情報を修正する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#  calicoNetwork:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    # Note: The ipPools section cannot be modified post-install.</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    ipPools:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#    - blockSize: 26</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#      cidr: 172.24.0.0/16 (ここを kubeadm 実行時と同じ pod ネットワークセグメントに修正する)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#      encapsulation: VXLANCrossSubnet</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#      natOutgoing: Enabled</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">#      nodeSelector: all()</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># マニフェストを作成して Calico をインストールする。</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl create </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-f</span><span class="token plain"> custom-resources.yaml</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Calico 関連の pod の稼働状況を確認する。4 分ほど待つ必要がある</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get pod -all-namespace</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コントロールプレーンのデプロイ">コントロールプレーンのデプロイ<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%97%E3%83%AC%E3%83%BC%E3%83%B3%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4" class="hash-link" aria-label="コントロールプレーンのデプロイ への直接リンク" title="コントロールプレーンのデプロイ への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="kuboard">kuboard<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#kuboard" class="hash-link" aria-label="kuboard への直接リンク" title="kuboard への直接リンク" translate="no">​</a></h3>
<p><strong>公式ドキュメント:</strong><a href="https://www.kuboard.cn/" target="_blank" rel="noopener noreferrer" class="">https://www.kuboard.cn/</a></p>
<p>docker イメージでワンコマンドでコンテナを起動し、その後クラスタのインポートウィザードを進めます。</p>
<p>クラスタ情報の監視に成功:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-48-a39e9ce841bd338e83876a2d26160fd8.webp" width="1600" height="783" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/20/k8s#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ここまでで、クラスタはほぼ使える状態になりました。kubeadm がコントロールプレーンの初期化を担い、ノードは token でクラスタに参加し、Calico が pod ネットワークを提供し、kuboard が可視化監視を担当します。この一連の流れで最もハマりやすいポイントは 2 か所に集中しています。Containerd の libseccomp のバージョンが古いこと、そして pause イメージがネットワークの都合で取得に失敗することです。どちらもエラーメッセージが分かりにくく、コンテナランタイムのログから手がかりを探す必要があります。今後はクラスタ内部のファイルストレージのために NAS ファイルサービスを追加すること、さらに Istio の導入やネットワーク入口 Ingress の設定を検討し、企業の実情に応じて適切に構成していく必要があります。</p>
<p>次回:<a href="https://tanzhuo.xyz/kubernetes/" target="_blank" rel="noopener noreferrer" class="">Kubernetes に Istio をデプロイする (tanzhuo.xyz)</a></p>]]></content:encoded>
            <category>K8S</category>
        </item>
        <item>
            <title><![CDATA[HBaseの紹介とMongoDBとの比較]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb</guid>
            <pubDate>Sat, 13 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[HBase はカラム指向のスケーラブルな分散ストレージシステムで、安価な PC クラスタ上で PB 級のデータを支えられます。本記事ではそのアーキテクチャ、読み書きの流れ、LSM ツリー構造とメリット・デメリットを整理し、MongoDB とユースケースを比較します。]]></description>
            <content:encoded><![CDATA[<p>HBase は正式名称を Hadoop Database といい、高信頼性・高性能・カラム指向でスケーラブルな分散ストレージシステムです。安価な PC サーバー上に大規模な構造化ストレージクラスタを構築できます。</p>
<!-- -->
<p>HBase の目標は大量データの保存と処理です。標準的なハードウェア構成だけで、数千の行と列を含む膨大なデータを扱うことができ、PB 級のデータストレージと億単位の QPS のクエリを支えられます。</p>
<p>リアルタイム性の要求が高くない業務シナリオに適しています。HBase が保存するのは Byte 配列であり、データ型を意識しないため、動的で柔軟なデータモデルが可能になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アーキテクチャの解説">アーキテクチャの解説<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E8%A7%A3%E8%AA%AC" class="hash-link" aria-label="アーキテクチャの解説 への直接リンク" title="アーキテクチャの解説 への直接リンク" translate="no">​</a></h2>
<p>HBase は HMaster と HRegionServer で構成され、マスター・スレーブ型のサーバーアーキテクチャに従います。HBase は論理テーブルを複数のデータブロック HRegion に分割し、それらを HRegionServer に格納します。</p>
<p>HMaster はすべての HRegionServer の管理を担当します。HMaster 自身はデータを一切保存せず、データから HRegionServer へのマッピング（メタデータ）だけを保持します。</p>
<p>クラスタ内のすべてのノードは Zookeeper によって調整され、HBase の稼働中に発生しうるさまざまな問題への対処に使われます。HBase の基本アーキテクチャは次のとおりです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-ad2553610635ee0adc362fe27c2a7537-a4b0200d1bfb0f00802e6f2aaf28ab93.webp" width="640" height="370" class="img_ev3q"></p>
<p><strong>クライアント:</strong> HBase の RPC メカニズムを使って HMaster および HRegionServer と通信し、リクエストを送信して結果を受け取ります。管理系の操作では、クライアントは HMaster を通じて RPC を実行し、データの読み書き操作では HRegionServer を通じて RPC を実行します。</p>
<p><strong>Zookeeper:</strong> クラスタ内の各ノードの状態情報が Zookeeper に登録されるため、HMaster は各 HRegionServer の健全性を常に把握でき、同時に HMaster の単一障害点も回避できます。</p>
<p><strong>HMaster:</strong> すべての HRegionServer を管理し、どの HRegion を維持すべきかを指示し、すべての HRegionServer の稼働状況を監視します。新しい HRegionServer が HMaster に登録されると、HMaster はデータ割り当てを待つよう指示します。ある HRegion が失効した場合、HMaster はその HRegion が担当していたすべての HRegion を未割り当てとしてマークし、他の HRegionServer に再割り当てします。HMaster には単一障害点の問題がありません。HBase は複数の HMaster を起動でき、Zookeeper の選出メカニズムによってクラスタ内で常に 1 つの HMaster が稼働していることが保証され、クラスタの可用性が高まります。</p>
<p><strong>HRegion:</strong> テーブルのサイズが事前設定された値を超えると、HBase は自動的にテーブルを複数のリージョンに分割し、各リージョンにはテーブル内の全行のサブセットが含まれます。ユーザーから見れば、各テーブルは主キー（RowKey）で区別される 1 つのデータ集合です。物理的には、1 つのテーブルは複数のブロックに分割され、各ブロックが 1 つの HRegion であり、「テーブル名 + 開始/終了主キー」で区別されます。1 つの HRegion はテーブル内の連続した一区間のデータを保持し、テーブル全体のデータは複数の HRegion に格納されます。</p>
<p><strong>HRegionServer:</strong> HBase のすべてのデータは通常、下層の HDFS に保存され、ユーザーは HRegionServer を通じてこれらのデータにアクセスします。一般に、クラスタの 1 ノード上では 1 つの HRegionServer だけが稼働し、各 HRegion も 1 つの HRegionServer だけによって維持されます。HRegionServer は主にユーザーの I/O リクエストに応答し、HDFS ファイルシステムへのデータ読み書きを担う、HBase の中核モジュールです。HRegionServer は内部で一連の HRegion オブジェクトを管理し、各 HRegion は論理テーブル内の連続した一区間のデータに対応します。HRegion は複数の HStore で構成され、各 HStore は論理テーブル内の 1 つのカラムファミリーのストレージに対応します。つまり、各カラムファミリーは 1 つの集中したストレージ単位であり、操作効率を高めるためには、同じ I/O 特性を持つ列を同じカラムファミリーにまとめるのが望ましいということです。</p>
<p><strong>HStore:</strong> HBase ストレージの中核であり、MemStore と StoreFile で構成されます。MemStore はメモリバッファで、ユーザーが書き込んだデータはまず MemStore に入ります。MemStore が満杯になると、Flush されて 1 つの StoreFile（下層の実装は HFile）になります。StoreFile のファイル数がある閾値まで増えると、Compact マージ操作がトリガーされ、複数の StoreFile が 1 つにマージされ、その過程でバージョンのマージとデータ削除が実行されます。このことから分かるように、HBase はデータを追記し続けるだけであり、すべての更新と削除は後続の Compact プロセスで行われます。ユーザーの書き込み操作はメモリに入った時点ですぐに返せるため、HBase の書き込み性能が保証されます。StoreFile は Compact を繰り返すうちに次第に大きなファイルになっていきます。単一の StoreFile のサイズがある閾値を超えると Split 操作がトリガーされ、現在の HRegion が 2 つの HRegion に分割されます。親 HRegion はオフラインになり、HMaster が 2 つの子 HRegion を対応する HRegionServer に割り当てることで、元の HRegion の負荷がこの 2 つの HRegion に分散されます。</p>
<p><strong>HLog:</strong> 各 HRegionServer は 1 つの HLog オブジェクトを持ちます。これは先行書き込みログ（Write-Ahead Log）を実装するクラスです。ユーザーが MemStore にデータを書き込むたびに、データのコピーが HLog ファイルにも書き込まれます。HLog ファイルは定期的にロールされ、古いファイル（その中のデータはすでに StoreFile に永続化済み）は削除されます。HMaster が Zookeeper を通じてある HRegionServer の異常終了を検知すると、まず残された HLog ファイルを処理し、その中のログデータを HRegion ごとに分割して対応する HRegion のディレクトリに配置し、その後これらの失効した HRegion を再割り当てします。これらの HRegion を引き継いだ HRegionServer は、ロードの過程で処理すべき過去の HLog があることを発見し、HLog 内のデータを MemStore に Replay してから StoreFile にフラッシュし、データ復旧を完了します。</p>
<p>HBase は BigTable モデルに基づいており、スパースで、長期保存（HDFS 上）され、多次元でソートされたマップテーブルです。このテーブルのインデックスは行キー、列キー、タイムスタンプです。HBase のデータはすべて文字列であり、型を持ちません。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-bdc5394355d37057a381069b139db6c7-fffe9699725f0292d1ef21fd0831af44.webp" width="1200" height="234" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hbase-の読み書きプロセス">HBase の読み書きプロセス<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#hbase-%E3%81%AE%E8%AA%AD%E3%81%BF%E6%9B%B8%E3%81%8D%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9" class="hash-link" aria-label="HBase の読み書きプロセス への直接リンク" title="HBase の読み書きプロセス への直接リンク" translate="no">​</a></h2>
<p>下図は HRegionServer のデータストレージ関係図です。前述のとおり、HBase は MemStore と StoreFile を使ってテーブルへの更新を保存します。データは更新時にまず HLog と MemStore に書き込まれ、MemStore 内のデータはソートされています。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-4f79359da1203e9a7f16754087434d53-70190020f2acab2fae91ebe0a5b1484c.webp" width="640" height="394" class="img_ev3q"></p>
<p>MemStore がある閾値まで蓄積されると、新しい MemStore が作成され、古い MemStore は Flush キューに追加され、専用スレッドによってディスクにフラッシュされて StoreFile になります。同時に、システムは Zookeeper に CheckPoint を記録し、その時点以前のデータ変更が永続化済みであることを示します。システムに異常が発生した場合、MemStore 内のデータは失われる可能性がありますが、その場合は HLog を使って CheckPoint 以降のデータを復旧します。</p>
<p>StoreFile は読み取り専用で、一度作成されると変更できません。そのため HBase の更新は実際には追記操作です。HStore 内の StoreFile の数がある閾値に達するとマージ操作が実行され、同じ key に対する変更が 1 つの大きな StoreFile にマージされます。StoreFile のサイズがある閾値に達すると、今度は 2 つの StoreFile に分割されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="書き込み操作の流れ">書き込み操作の流れ<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF%E6%93%8D%E4%BD%9C%E3%81%AE%E6%B5%81%E3%82%8C" class="hash-link" aria-label="書き込み操作の流れ への直接リンク" title="書き込み操作の流れ への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">ステップ 1：クライアントは Zookeeper のスケジューリングを通じて HRegionServer に書き込みリクエストを送信し、データを HRegion に書き込みます。</li>
<li class="">ステップ 2：データは HRegion の MemStore に書き込まれ、MemStore が事前設定の閾値に達するまで続きます。</li>
<li class="">ステップ 3：MemStore 内のデータが Flush されて StoreFile になります。</li>
<li class="">ステップ 4：StoreFile のファイル数が特定の閾値まで増えると、Compact マージ操作がトリガーされ、複数の StoreFile が 1 つにマージされ、同時にバージョンのマージとデータ削除が行われます。</li>
<li class="">ステップ 5：StoreFile は継続的な Compact 操作を経て、次第に大きな StoreFile になっていきます。</li>
<li class="">ステップ 6：単一の StoreFile のサイズが閾値を超えると Split 操作がトリガーされ、現在の HRegion が 2 つの新しい HRegion に分割されます。親 HRegion はオフラインになり、新たに分割された 2 つの子 HRegion は HMaster によって対応する HRegionServer に割り当てられ、元の HRegion の負荷が分散されます。</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="読み取り操作の流れ">読み取り操作の流れ<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#%E8%AA%AD%E3%81%BF%E5%8F%96%E3%82%8A%E6%93%8D%E4%BD%9C%E3%81%AE%E6%B5%81%E3%82%8C" class="hash-link" aria-label="読み取り操作の流れ への直接リンク" title="読み取り操作の流れ への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">ステップ 1：クライアントは Zookeeper にアクセスして -ROOT- テーブルを見つけ、そこから .META. テーブルの情報を取得します。</li>
<li class="">ステップ 2：.META. テーブルから対象データが存在する HRegion の情報を照会し、対応する HRegionServer を見つけます。</li>
<li class="">ステップ 3：HRegionServer を通じて目的のデータを取得します。</li>
<li class="">ステップ 4：HRegionServer のメモリは MemStore と BlockCache の 2 つの部分に分かれています。MemStore は主に書き込み用、BlockCache は主に読み取り用です。読み取りリクエストはまず MemStore でデータを探し、見つからなければ BlockCache を探し、それでも見つからなければ StoreFile にアクセスし、読み取った結果を BlockCache に格納します。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hbase-のユースケース">HBase のユースケース<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#hbase-%E3%81%AE%E3%83%A6%E3%83%BC%E3%82%B9%E3%82%B1%E3%83%BC%E3%82%B9" class="hash-link" aria-label="HBase のユースケース への直接リンク" title="HBase のユースケース への直接リンク" translate="no">​</a></h2>
<p>半構造化・非構造化データ：フィールドを事前に定義できない、あるいは雑多なデータ構造は、固定スキーマでモデリングするのが難しく、HBase に任せるのに向いています。ビジネスの成長に伴ってより多くのフィールドを保存する必要が出た場合、RDBMS ではテーブル構造の変更にダウンタイムを伴うメンテナンスが必要ですが、HBase は動的な列の追加をサポートしています。</p>
<p>レコードが非常にスパース：RDBMS のテーブルは列数が固定で、値が空の列も無駄にストレージを消費します。HBase では空の列はストレージを占有しないため、容量を節約できるうえに読み取り性能も向上します。</p>
<p>マルチバージョンデータ：RowKey と列識別子で特定される値は、（タイムスタンプの異なる）任意の数のバージョンを持てるため、変更履歴を保存する必要のあるデータには HBase が非常に便利です。</p>
<p>大量データ：データ量がどんどん増えていくと、RDBMS は次第に耐えられなくなります。そこでまず読み書き分離を行い、マスター 1 台が書き込み、複数のスレーブが読み取りを担当するようにしますが、サーバーコストは倍増します。負荷がさらに増えてマスターが耐えられなくなると、データベースの分割（シャーディング）を始め、ほとんど関連のないデータを分けてデプロイします。すると一部の join クエリが使えなくなり、中間層の導入が必要になります。データ量がさらに増えて単一テーブルのレコード数が増え続けると、クエリが非常に遅くなり、単一テーブルのレコード数を減らすためにテーブル分割（例えば ID の剰余で複数テーブルに分割）を余儀なくされます。このプロセスを経験した人なら、その煩雑さをよく知っているはずです。</p>
<p>HBase ならずっとシンプルです。クラスタに新しいノードを追加するだけで、HBase は自動的に水平分割を行い、さらに Hadoop とのシームレスな統合により、データの信頼性（HDFS）と高性能な大規模データ分析能力（MapReduce）が保証されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hbase-と-mapreduce">HBase と MapReduce<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#hbase-%E3%81%A8-mapreduce" class="hash-link" aria-label="HBase と MapReduce への直接リンク" title="HBase と MapReduce への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-ed27cafbd33b8be27e8810b45de839e5--1--ed4338a1786827b76930ee4962011de8.webp" width="554" height="277" class="img_ev3q"></p>
<p>HBase における Table と Region の関係は、HDFS における File と Block の関係にやや似ています。HBase は TableInputFormat や TableOutputFormat など、MapReduce と連携する API を提供しているため、HBase のデータテーブルを Hadoop MapReduce の入力・出力として直接使うことができ、MapReduce アプリケーションの開発が容易になり、HBase システム自体の詳細を意識する必要もありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データ構造の分析">データ構造の分析<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#%E3%83%87%E3%83%BC%E3%82%BF%E6%A7%8B%E9%80%A0%E3%81%AE%E5%88%86%E6%9E%90" class="hash-link" aria-label="データ構造の分析 への直接リンク" title="データ構造の分析 への直接リンク" translate="no">​</a></h2>
<p>HBase は LSM（Log-Structured Merge）ツリーを使用し、従来のデータベースの InnoDB は B+ ツリーを使用します。</p>
<p><strong>LSM ツリー</strong>の特徴：</p>
<ol>
<li class="">書き込み重視</li>
<li class="">読み取りは複数のツリーへのアクセスが必要で、IO 回数が B+ ツリーより多く、ばらつきが大きい</li>
<li class="">書き込みはすべてシーケンシャル IO。ランダム読み取りはランダム IO、シーケンシャル読み取りはシーケンシャル IO</li>
</ol>
<p>メリット：</p>
<ol>
<li class="">書き込み性能が大幅に向上する</li>
<li class="">SSD のランダム書き込み増幅の影響を受けない</li>
<li class="">空間増幅の影響を受けない</li>
</ol>
<p>デメリット：</p>
<ol>
<li class="">読み取り性能が犠牲になり、IO 回数が B+ ツリーより多い</li>
<li class="">定期的な Compaction が必要で、ネットワーク/ディスク IO 全体に対する増幅が存在する</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hbase-のメリット">HBase のメリット<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#hbase-%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88" class="hash-link" aria-label="HBase のメリット への直接リンク" title="HBase のメリット への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">データがカラム単位で保存されるため、クエリ時には関係する列にだけアクセスすればよく、システムの I/O を大幅に削減できます。同じ列のデータは型が一致し、NULL 値は保存されないため、効率的な圧縮保存が可能です。</li>
<li class="">Key/Value 型のストレージ方式のため、データが膨大に増え続けても、クエリ性能の低下はほとんど起こりません。</li>
<li class="">カラム型データベース（従来の行指向データベースに対して）として、1 つのテーブルのフィールドが非常に多い場合、異なる列を（Region 単位で）別々のサーバーインスタンスに分散させ、負荷を分散できます。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="hbase-のデメリット">HBase のデメリット<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#hbase-%E3%81%AE%E3%83%87%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88" class="hash-link" aria-label="HBase のデメリット への直接リンク" title="HBase のデメリット への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">ネイティブではセカンダリインデックスをサポートせず、主キーでしかアクセスできません。コミュニティ実装のセカンダリインデックスはデータ更新との間に遅延があり、悩ましい整合性の問題を引き起こします</li>
<li class="">ワイドテーブルモデルの概念は分かりにくく理解が難しいうえ、事前のモデリングが必要で柔軟性に欠けます</li>
<li class="">サポートするプログラミング言語の種類が少なく（Java、Thrift、RESTful API）、SQL がなく API しか使えません</li>
<li class="">クラスタ構造が複雑で、8 種類もの異なるタイプのノードがあります</li>
<li class="">データ型が低レベルで、バイトストリームしかサポートせず、開発者にやさしくありません</li>
<li class="">一貫性スナップショット機能がありません</li>
<li class="">定期的な Compact が必要で、継続的な読み書きのシナリオへの影響が大きいです</li>
<li class="">テーブルの結合操作をサポートしないため、データ分析は HBase の弱点であり、よくある group by や order by は MapReduce を書くことでしか実現できません</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/13/hbasehe-mongodb#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>HBase は特に大量データの書き込みシナリオに適しています。データを書き込んだ後、非同期でデータを分析・処理するという使い方です。対応する業務シナリオは、リアルタイム性の要求は高くないが書き込み量が特に大きいもの、例えばハードウェアや車載システムのデータモニタリング収集、ユーザープロファイリングなどです。主に大規模ストレージの問題を解決し、その後データ分析コンポーネントを統合することで、ビッグデータ分析、ユーザー行動分析、リアルタイムレコメンド、リスク管理などの機能を実現できます。</p>
<p>MongoDB については本記事では詳しく紹介していません。同等のマシンクラスタ構成では、MongoDB の書き込み能力は HBase に及びません。しかし MongoDB はデータ書き込み時に対応するインデックスを作成するため、クエリ速度とクエリ可能な次元は HBase を上回ります。HBase はデータをクエリできる次元が限られており、クエリの柔軟性やさまざまな条件下でのクエリ効率という点では MongoDB がやや優勢で、リアルタイムのクエリ分析が必要なシナリオに向いています。MongoDB の書き込み能力を高めるには、より多くの高性能なクラスタノードを追加する必要があります。実際の技術選定では、やはり自分の業務シナリオに合わせて分析しマッチングすべきです。</p>]]></content:encoded>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[Redis キャッシュのペネトレーション、ブレイクダウン、アバランチ]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng</guid>
            <pubDate>Thu, 11 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[キャッシュペネトレーション、ブレイクダウン、アバランチは Redis キャッシュで最もよくある三種類の問題です。それぞれの原因と違いを明確にし、対応策——ブルームフィルター、空オブジェクトのキャッシュ、ミューテックスロック、有効期限の分散、二層キャッシュ——を整理します。]]></description>
            <content:encoded><![CDATA[<p>ペネトレーション（穿透）、ブレイクダウン（撃穿）、アバランチ（雪崩）は、Redis をキャッシュとして使うなら避けて通れない三種類の問題です。名前は似ていますが、原因も対策もそれぞれ異なります。この記事で一つずつ明確にしていきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュペネトレーション">キャッシュペネトレーション<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%83%9A%E3%83%8D%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3" class="hash-link" aria-label="キャッシュペネトレーション への直接リンク" title="キャッシュペネトレーション への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-33-93577c026a68c565599c164e0eb90088.webp" width="862" height="597" class="img_ev3q"></p>
<p>キャッシュペネトレーションとは、ユーザーがリクエストしたデータがキャッシュに存在せず（つまりヒットせず）、同時にデータベースにも存在しないため、ユーザーがそのデータをリクエストするたびにデータベースへ問い合わせに行き、空を返すことになる現象を指します。</p>
<p>悪意のある攻撃者がシステムに存在しないデータをリクエストし続けると、短時間に大量のリクエストがデータベースに落ち、データベースの負荷が過大になり、最悪の場合データベースシステムを打ち倒してしまいます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="解決策">解決策<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E8%A7%A3%E6%B1%BA%E7%AD%96" class="hash-link" aria-label="解決策 への直接リンク" title="解決策 への直接リンク" translate="no">​</a></h3>
<p><strong>1）ブルームフィルター</strong></p>
<p>ブルームフィルターは、実際には非常に長いバイナリベクトルと一連のランダムなマッピング関数であり、ある要素が集合に含まれるかどうかの検索に使えます。長所は空間効率とクエリ時間が一般的なアルゴリズムよりはるかに優れていること、短所は一定の誤判定率があり、削除が難しいことです。</p>
<p>ある要素が集合の中にあるかどうかを判定したい場合、一般的な考え方はすべての要素を保存しておき、比較によって確認するというものです。連結リストや木といったデータ構造はこの考え方です。しかし集合の要素が増えるにつれて、必要なストレージはどんどん大きくなり、検索速度もどんどん遅くなります（O(n) または O(log n)）。一方、ハッシュテーブルは Hash 関数によって要素をビット配列（Bit array）の一点にマッピングできます。こうすれば、その点が 1 かどうかを見るだけで、集合にその要素があるかどうかがわかります——これがブルームフィルターの基本的な考え方です。</p>
<p><strong>2）空オブジェクトを返す</strong></p>
<p>キャッシュがミスし、データベースを照会しても空だった場合、返される空オブジェクトをキャッシュに書き込んでおきます。こうすると次に同じ key がリクエストされたとき、キャッシュから直接空オブジェクトを返すため、リクエストはデータベースまで落ちません。空オブジェクトを溜め込みすぎないように、通常は空オブジェクトに有効期限を設定します。</p>
<p>この方法には二つの問題があります。</p>
<ol>
<li class="">大量の key でペネトレーションが起きた場合、空オブジェクトのキャッシュがメモリを占有します。</li>
<li class="">key が期限切れになるまでの間、キャッシュとデータベースのデータが一致しない状況が発生し得ます。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュブレイクダウン">キャッシュブレイクダウン<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%83%96%E3%83%AC%E3%82%A4%E3%82%AF%E3%83%80%E3%82%A6%E3%83%B3" class="hash-link" aria-label="キャッシュブレイクダウン への直接リンク" title="キャッシュブレイクダウン への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-34-518f21065331aa6695c8f45bcbc2c799.webp" width="840" height="522" class="img_ev3q"></p>
<p>あるホットな key が期限切れになった瞬間、大量のリクエストが同時にその key のデータにアクセスすることがあります。キャッシュがちょうど失効しているため、これらのリクエストは同時に永続化データベースへ問い合わせに行き、データをキャッシュに書き戻そうとします。その結果、データベースの負荷が瞬間的に過大になります。これがブレイクダウンです。</p>
<p>ここで <strong>ブレイクダウン</strong> と <strong>ペネトレーション</strong> の違いを理解しておきましょう。</p>
<p>ブレイクダウンは、ある一つの key が非常にホットで、大量のアクセスがその key に集中しているとき、key が失効した瞬間にすべてのリクエストがデータベースに殺到し、穴を一つ撃ち抜いてしまう、というものです。一方ペネトレーションは、アクセスされるデータが存在しないケースが中心で、大量のリクエストがどれも存在しないデータにアクセスしている状況です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="解決策-1">解決策<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E8%A7%A3%E6%B1%BA%E7%AD%96-1" class="hash-link" aria-label="解決策 への直接リンク" title="解決策 への直接リンク" translate="no">​</a></h3>
<p><strong>1）ミューテックスロックを使う</strong></p>
<p>最初にアクセスしたスレッドだけにデータベース照会とキャッシュへの書き戻しを行わせ、他のスレッドは書き戻しの完了を待ってからキャッシュを読み直すようにします。</p>
<p><strong>2）ホットデータを無期限に設定する</strong></p>
<p>アクセスが極めて多いホットデータには有効期限を設定せず、代わりにビジネス側で非同期にキャッシュ内容を更新します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュアバランチ">キャッシュアバランチ<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%A2%E3%83%90%E3%83%A9%E3%83%B3%E3%83%81" class="hash-link" aria-label="キャッシュアバランチ への直接リンク" title="キャッシュアバランチ への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-35-fdaac8501a626f8c372ae833bbdf7e6e.webp" width="800" height="520" class="img_ev3q"></p>
<p>キャッシュアバランチとは、キャッシュ内の大量のホットな key が同時に有効期限を迎え、かつ照会されるデータ量が膨大なため、リクエストが直接データベースに落ち、データベースの負荷が過大になり、ダウンにまで至ることを指します。キャッシュブレイクダウンとの違いは、ブレイクダウンが同一データへの並行アクセスを指すのに対し、アバランチは異なるデータが軒並み期限切れになり、多くのデータが見つからないためにすべてがデータベースへ問い合わせに行く、という点です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="解決策-2">解決策<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E8%A7%A3%E6%B1%BA%E7%AD%96-2" class="hash-link" aria-label="解決策 への直接リンク" title="解決策 への直接リンク" translate="no">​</a></h3>
<p><strong>1）有効期限を分散させる</strong></p>
<p>異なる有効期限を設定して、キャッシュの失効タイミングを均等に分布させます。通常は有効期限にランダム値を加えるか、有効期限を統一的に設計します。</p>
<p><strong>2）ミューテックスロックをかける</strong></p>
<p>キャッシュブレイクダウンの解決策と同じ考え方で、同時刻にキャッシュを構築するスレッドを一つだけにし、他のスレッドはブロックして順番を待ちます。</p>
<p><strong>3）キャッシュを無期限にする</strong></p>
<p>キャッシュブレイクダウンの解決策と同じ考え方で、キャッシュを物理的には永久に失効させず、非同期スレッドでキャッシュを更新します。</p>
<p><strong>4）二層キャッシュ戦略</strong></p>
<p>メインとバックアップの二層キャッシュを使います。</p>
<ul>
<li class="">メインキャッシュ：有効期限は経験則に基づいて設定し、主たる読み取り先のキャッシュとします。メインキャッシュが失効したらデータベースから最新値をロードします。</li>
<li class="">バックアップキャッシュ：有効期限を長く設定し、ロックの取得に失敗したときに読むキャッシュとします。メインキャッシュを更新する際にはバックアップキャッシュも同期して更新する必要があります。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-chuan-tou-ji-chuan-xue-beng#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>三つの問題の本質はいずれも、リクエストがキャッシュを迂回して直接データベースを叩くことです。ペネトレーションは存在しないデータの照会、ブレイクダウンは単一のホット key の失効、アバランチは大量の key の同時失効です。対応の考え方にも共通点があります。キャッシュ層で無効なリクエストを食い止めるか（ブルームフィルター、空オブジェクト）、オリジンへ戻る並行数を制御するか（ミューテックスロック）、失効タイミングを制御可能にするか（期限の分散、無期限化、二層キャッシュ）です。実際に使うときは、データのホット度合いと整合性要求に応じて組み合わせて選べば十分です。</p>]]></content:encoded>
            <category>Redis</category>
        </item>
        <item>
            <title><![CDATA[Redis キャッシュ整合性]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti</guid>
            <pubDate>Thu, 11 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Redis キャッシュとデータベースの不整合が起きる原因を整理し、キャッシュ直接書き込み、単純削除、遅延二重削除といった方式の高並行環境下での挙動とトレードオフを比較します。]]></description>
            <content:encoded><![CDATA[<p>データベースの前にキャッシュを 1 層挟んだ時点で、「キャッシュのデータと DB のデータが食い違う」という問題は避けて通れなくなります。この記事では、キャッシュ整合性について自分の理解と、よく使われるいくつかの方式それぞれの落とし穴を整理します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュ整合性">キャッシュ整合性<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E6%95%B4%E5%90%88%E6%80%A7" class="hash-link" aria-label="キャッシュ整合性 への直接リンク" title="キャッシュ整合性 への直接リンク" translate="no">​</a></h2>
<p>まず、キャッシュ整合性問題とは何か、なぜ解決する必要があるのか、そしてどんな方式があるのかを説明します。</p>
<p>キャッシュ整合性問題とは、キャッシュを使用する際に、キャッシュのデータとデータベースのデータが一致しないことで、データの誤りやデータ損失などの問題が発生しうることを指します。この問題は高並行(高トラフィック)シナリオで特によく起こります。複数のスレッドが同じデータを同時に読み書きする場合、データの整合性を保証するのは非常に難しいからです。Redis のメモリ上のデータと DB のデータをどう一致させ、(高並行シナリオで)不整合を発生させないようにするか。不整合が起きると、ケースによっては大きなトラブルにつながりかねないため、キャッシュ整合性の解決策はとても重要です。</p>
<p>問題の根本原因はこうです。キャッシュとデータベースは 2 つの独立したストレージであり、両者への書き込みはアトミックな操作ではありません。どちらを先に書いても、2 つのステップの間には時間的な窓が存在し、その窓の間に他のスレッドが読み取ると、「半分新しく半分古い」状態を目にする可能性があります。そこにスレッドスケジューリングの不確定性、データベーストランザクションのコミットタイミング、ネットワーク遅延が重なると、あらゆる交錯パターンが発生します。つまり整合性の方式を議論することは、本質的には「この窓を消せるのか、消せないなら汚れたデータはどれくらい生き残るのか、それをビジネスは許容できるのか」を議論することなのです。</p>
<p>以下に、よく使われる方式と、それぞれが高並行シナリオで抱える問題を挙げていきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="キャッシュへの直接書き込み非推奨">キャッシュへの直接書き込み(非推奨)<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti#%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%81%B8%E3%81%AE%E7%9B%B4%E6%8E%A5%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF%E9%9D%9E%E6%8E%A8%E5%A5%A8" class="hash-link" aria-label="キャッシュへの直接書き込み(非推奨) への直接リンク" title="キャッシュへの直接書き込み(非推奨) への直接リンク" translate="no">​</a></h2>
<p>発想は最も直感的です。データベースを更新した直後に最新値を Redis に書き込み、キャッシュに常に値がある状態を保てば、読み取りリクエストがデータベースまで突き抜けることもありません。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-40-749de52be98887e346de853674fc613e.webp" width="942" height="615" class="img_ev3q"></p>
<p>ステップ 3、4 の実行時、高並行シナリオでは Redis にデータを書き込む Java スレッドが順序どおりに実行される保証がありません(CPU のタイムスライスの問題に加え、分散・マイクロサービス環境ではさらに顕著になります)。その結果、最新データが他のスレッドの古いデータで上書きされる可能性があり、その後に他のスレッドの実行がなければ、キャッシュのデータはずっと古いままになってしまうかもしれません。</p>
<p>言い換えると、スレッド A が先に DB に書き、スレッド B が後に DB に書いたとしても、Redis への書き込み順序は逆転して B が先、A が後になりうるということです。最終的にキャッシュに残るのは A の古い値です。しかもこの上書きには自己修復の仕組みが一切ありません。後続の新しい書き込みがこの Key を「洗い流して」くれない限り、汚れたデータはキャッシュに残り続け、繰り返し読まれてしまいます。</p>
<p><strong>ここで分散ロック(単一アプリケーションなら JVM ロックで十分)をそのまま使う人もいるでしょう。ロックをかける場合は DB 書き込みから Redis 書き込みまでのフロー全体を制御する必要があり、各 Key を操作する関数の範囲も自分で管理しなければならず、効率は間違いなく大きく低下します。ただしロックの粒度は自分で調整できます。分散ロックを使う方式は強整合性が求められるシナリオに向いており、整合性のために性能を犠牲にする形です(強整合性シナリオでは推奨)。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データベース書き込み後にキャッシュを削除単純削除戦略---非推奨">データベース書き込み後にキャッシュを削除(単純削除戦略 - 非推奨)<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E6%9B%B8%E3%81%8D%E8%BE%BC%E3%81%BF%E5%BE%8C%E3%81%AB%E3%82%AD%E3%83%A3%E3%83%83%E3%82%B7%E3%83%A5%E3%82%92%E5%89%8A%E9%99%A4%E5%8D%98%E7%B4%94%E5%89%8A%E9%99%A4%E6%88%A6%E7%95%A5---%E9%9D%9E%E6%8E%A8%E5%A5%A8" class="hash-link" aria-label="データベース書き込み後にキャッシュを削除(単純削除戦略 - 非推奨) への直接リンク" title="データベース書き込み後にキャッシュを削除(単純削除戦略 - 非推奨) への直接リンク" translate="no">​</a></h2>
<p>「キャッシュへの書き込み」に上書き問題があるなら、発想を変えましょう。データベース更新後はキャッシュを削除するだけにして、次の読み取りリクエストに自らデータベースへ戻って新しい値をロードし直させるのです。削除は冪等なので、新しい値が古い値に上書きされる問題はありません。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-41-579fa302b29cd904d2a261b4c31e3bf1.webp" width="1431" height="779" class="img_ev3q"></p>
<p>単純削除戦略は書き込みの上書き問題をうまく回避できるように見えますが、データを照会する他のスレッドがロジックに加わると問題が発生します。照会スレッドがデータベースからデータを取得するとき、ちょうど他のスレッドがデータベーストランザクションをコミット中かもしれません。このとき照会スレッドはトランザクションコミット前の古いデータを取得してキャッシュに保存してしまい、データ不整合の問題を引き起こします。</p>
<p>具体的な交錯の順序はこうです。読み取りスレッドがキャッシュミスに気づき、データベースを照会します。このとき取得できるのは書き込みトランザクションのコミット前の古い値です。その後、書き込みスレッドがトランザクションをコミットしてキャッシュを削除します。最後に読み取りスレッドが手元の古い値をキャッシュに書き戻します。削除が古い値の書き戻しの前に発生しているため、削除は無駄になり、キャッシュには再び汚れたデータが残ります。しかもこちらも自己修復の手段がありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="遅延二重削除戦略推奨">遅延二重削除戦略(推奨)<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti#%E9%81%85%E5%BB%B6%E4%BA%8C%E9%87%8D%E5%89%8A%E9%99%A4%E6%88%A6%E7%95%A5%E6%8E%A8%E5%A5%A8" class="hash-link" aria-label="遅延二重削除戦略(推奨) への直接リンク" title="遅延二重削除戦略(推奨) への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-44-8c4f7a424001dd9bbbe338d1daab955f.webp" width="1550" height="825" class="img_ev3q"></p>
<p>遅延キューを導入し、一定時間待ってからその Key をもう一度削除する戦略です。単純削除が残した問題を解決し、ロックも使わないため、このキャッシュ二重削除戦略は、一部のデータが短時間不整合になることを許容できるアプリケーションシナリオに、個人的にはより適していると考えています。</p>
<p>2 回目の削除の意義はセーフティネットにあります。先ほどの「古い値が削除後に書き戻される」交錯パターンは、遅延時間経過後の 2 回目の削除によって掃除されます。遅延時間は「読み取りスレッドが DB を照会してからキャッシュに書き戻すまで」の一連の所要時間をカバーするように選ぶ必要があり、実務ではインターフェースの実際の所要時間をもとに、やや大きめの値を見積もるのが一般的です。代償も明確で、遅延の窓の間は依然として古いデータを読む可能性があります。つまりこの方式が保証するのは結果整合性であって、強整合性ではありません。</p>
<p>以上の方式のほかにも、選択肢はたくさんあります。</p>
<ol>
<li class="">読み書きロック。書き込み操作時に書き込みロックを取得し、書き込みが完了するまで他のスレッドはキャッシュの読み書きができないようにします。この方式はデータの整合性を保証できますが、読み取りの遅延やロック競合の問題が増える可能性があります。</li>
<li class="">バージョン番号による制御。キャッシュにデータのバージョン番号を保存し、データ更新のたびにバージョン番号も更新します。読み取り時にバージョン番号が一致するかを比較し、一致しなければデータを再ロードします。この方式はデータの整合性を保証できますが、データの保存領域と読み取りのオーバーヘッドが増えます。</li>
<li class="">有効期限付きキャッシュ。キャッシュにデータを保存する際に有効期限を設定し、期限を過ぎるとキャッシュは自動的に無効になり、データの再ロードが必要になります。この方式はキャッシュとデータベースの不整合の可能性を減らせますが、データの保存領域と読み取りのオーバーヘッドが増える可能性があります。</li>
<li class="">データベースの非同期更新。データベースへの書き込み時にキャッシュを直接更新せず、更新操作を非同期でメッセージキューに送信し、コンシューマーがキャッシュの更新を担当します。この方式はデータベースの負荷を減らせますが、メッセージキューの複雑さと遅延が増える可能性があります。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">
<p>どの削除系の方式を選ぶにせよ、キャッシュにはセーフティネットとして有効期限を設定することをおすすめします。ある交錯パターンで汚れたデータが生まれても、期限切れ後には自動的に DB へ戻って修正され、不整合の生存時間を制御可能な範囲に抑えられます。</p>
</li>
<li class="">
<p>遅延二重削除の 2 回目の削除をメモリ内で行う(スレッドを sleep させてから削除するなど)と、アプリケーションの再起動で消えてしまいます。信頼性が求められるなら、メッセージキューや遅延キューで削除タスクを投げるようにすれば、失敗してもリトライできます。</p>
</li>
<li class="">
<p>キャッシュの削除自体も失敗する可能性があります(ネットワークの揺らぎ、Redis の一時的な不通など)。削除失敗にはリトライの仕組みを用意しないと、汚れたデータが同じように長期間残り続けます。</p>
</li>
<li class="">
<p>通常の業務で「キャッシュとデータベースが任意の瞬間に完全一致する」ことを追い求めないでください。キャッシュとデータベースは別々のストレージであり、ロックやトランザクションレベルの協調を導入しない限り、強整合性は実現できません。まずビジネスがどれくらいの不整合を許容できるかを明確にしてから、方式を選びましょう。</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/11/redis-huan-cun-yi-zhi-wen-ti#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>キャッシュ整合性に銀の弾丸はなく、本質は整合性・性能・実装の複雑さの間のトレードオフです。キャッシュへの直接書き込みは並行処理で上書きされ、自己修復もできません。単純削除は上書き問題を塞ぎましたが、古い値の書き戻しは防げません。遅延二重削除は 1 回の遅延削除をセーフティネットにして結果整合性を手に入れる方式で、短時間のダーティリードを許容できる大多数のビジネスに向いています。本当に強整合性が必要なシナリオなら、素直にロックをかけて、性能と引き換えに正しさを取りましょう。方式を選ぶ前に、まずこの問いに答えてください——このデータが数百ミリ秒汚れていたとき、ビジネスは本当に困るのか?</p>]]></content:encoded>
            <category>Redis</category>
        </item>
        <item>
            <title><![CDATA[CentOS7 で NIC を有効にする]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia</guid>
            <pubDate>Wed, 10 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[CentOS 7 の最小インストール後、NIC はデフォルトで無効になっています。NIC の設定ファイルを見つけて ONBOOT を yes に変更し、ネットワークサービスを再起動して、インストール直後からネットに繋がるようにする手順を記録します。]]></description>
            <content:encoded><![CDATA[<p>CentOS 7 をインストールしたばかりのマシンで、外部への ping が通らず、ip addr にも IP が見当たらない——これはネットワークが壊れているのではなく、システムがデフォルトで NIC を有効にしていないだけです。このノートでは、NIC を有効にする一連の手順と、その背後にある理由を記録します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>CentOS 7 を最小インストール（Minimal Install）する場合、インストールウィザードのネットワークスイッチはデフォルトでオフになっています。インストール中に手動でオンにしなかった場合、システムのインストール完了後、NIC はカーネルに認識されてはいるものの、起動時に自動的にアクティブにならず、IP アドレスの取得も行いません。現象としては、マシンのローカルはすべて正常なのに、ゲートウェイに ping が通らず、yum でソフトウェアをインストールできず、ssh でも接続できない、という状態になります。</p>
<p>解決方法は簡単です。対応する NIC の設定ファイルを見つけて、「起動時に有効化」のスイッチをオンにするだけです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="原理の概要">原理の概要<a href="https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia#%E5%8E%9F%E7%90%86%E3%81%AE%E6%A6%82%E8%A6%81" class="hash-link" aria-label="原理の概要 への直接リンク" title="原理の概要 への直接リンク" translate="no">​</a></h2>
<p>CentOS 7 の NIC 設定ファイルは <code>/etc/sysconfig/network-scripts/</code> ディレクトリにあり、NIC 1 枚ごとに <code>ifcfg-</code> で始まるファイルが 1 つ対応します。サフィックスは NIC 名で、たとえば <code>ifcfg-ens33</code>、<code>ifcfg-eth0</code> などです。</p>
<p>NIC を起動時に有効化するかどうかを決めるのは、ファイル内の <code>ONBOOT</code> パラメータです。</p>
<ul>
<li class=""><code>ONBOOT=no</code>：起動時にこの NIC をアクティブにしない。インストール時にネットワークスイッチをオンにしなかった場合のデフォルト値です；</li>
<li class=""><code>ONBOOT=yes</code>：起動時に自動的にアクティブにする。<code>BOOTPROTO=dhcp</code> と組み合わせれば、自動的にルーターへ IP を要求します。</li>
</ul>
<p>つまりこの件の本質は、<code>ONBOOT</code> を <code>no</code> から <code>yes</code> に変更し、ネットワークサービスに設定を読み直させることに尽きます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実際の手順">実際の手順<a href="https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia#%E5%AE%9F%E9%9A%9B%E3%81%AE%E6%89%8B%E9%A0%86" class="hash-link" aria-label="実際の手順 への直接リンク" title="実際の手順 への直接リンク" translate="no">​</a></h2>
<p>1）NIC のリソースフォルダに移動する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">cd</span><span class="token plain"> /etc/sysconfig/network-scripts/</span><br></div></code></pre></div></div>
<p>2）有効化したい NIC の名前を確認する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ip コマンドはプレフィックスの省略形に対応しており、addres / addr はいずれも完全な ip address と等価</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">ip</span><span class="token plain"> addres</span><br></div></code></pre></div></div>
<p>出力の中で <code>lo</code>（ローカルループバック）以外のインターフェースが物理 NIC です。よくある名前は <code>ens33</code>、<code>ens160</code>、<code>eth0</code> などです。この時点では状態は通常 <code>DOWN</code> で、inet アドレスもありません。</p>
<p>3）対応する設定ファイルを編集する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイル名は ifcfg- に前の手順で調べた NIC 名を付けたもの。たとえば NIC 名が ens33 なら：</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">vi</span><span class="token plain"> ifcfg-ens33</span><br></div></code></pre></div></div>
<p>4）<code>ONBOOT=no</code> の行を見つけて <code>no</code> を <code>yes</code> に変更し、ファイルを保存する</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-21-721dec15f4564323c825f9777afbf564.webp" width="652" height="278" class="img_ev3q"></p>
<p>DHCP のあるルーターに接続している場合は、ついでに <code>BOOTPROTO=dhcp</code> になっていることも確認しておきましょう。こうすればネットワーク再起動後に自動で IP を取得できます。</p>
<p>5）ネットワークサービスを再起動して設定を反映させる</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">service</span><span class="token plain"> network restart</span><br></div></code></pre></div></div>
<p>もう一度 <code>ip addr</code> を実行し、NIC の状態が <code>UP</code> になって inet アドレスが付いていれば成功です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）設定ファイル名は記憶違いしやすいです。ディレクトリ内のファイルは <code>ifcfg-NIC名</code>（間に余計なハイフンはありません）なので、vi で開く前に <code>ls</code> で一度確認しましょう。手が滑って空ファイルを新規作成してしまい、いくら変更しても反映されない、という事態を避けられます。</p>
<p>2）変更しているのは「設定」であって「状態」ではありません。<code>ONBOOT=yes</code> は起動時の挙動を決めるだけなので、変更後は必ずネットワークサービスを再起動（またはマシンを再起動）しないと反映されません。一時的に NIC を立ち上げたいだけなら <code>ifup ens33</code> も使えますが、設定ファイルを変更しなければ、次回の再起動でまたネットのない状態に戻ってしまいます。</p>
<p>3）仮想マシン環境で IP が取れない場合は、まず仮想ネットワークモードを確認しましょう。VMware/VirtualBox でブリッジや NAT の設定が正しくない場合、<code>ONBOOT</code> を変更しても外部に ping は通りません。これはホストマシン側の問題であり、本記事のスイッチとは無関係です。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>インストール時にウィザードの「ネットワークとホスト名」ページで NIC のスイッチを直接オンにしておけば、インストール完了と同時にネットワークが使えるようになり、上記のすべての手順を省略できます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2022/08/10/contos7-kai-qi-wang-qia#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>CentOS 7 のインストール後にネットが繋がらないのは、多くの場合ドライバやハードウェアの問題ではなく、単に <code>ONBOOT</code> がデフォルトで <code>no</code> になっているだけです。<code>ip addr</code> で NIC 名を確認し、<code>/etc/sysconfig/network-scripts/</code> 配下の対応する <code>ifcfg-</code> ファイルの <code>ONBOOT=yes</code> に変更して、<code>service network restart</code> を実行すれば、ネットワークは繋がります。これは新しく CentOS 7 を入れたマシンでほぼ毎回行うことになる定番の作業なので、記録しておけば毎回調べ直す手間が省けます。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Redis レプリケーション・Sentinel・Cluster の構築]]></title>
            <link>https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi</link>
            <guid>https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi</guid>
            <pubDate>Tue, 09 Aug 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Redis のレプリケーション(主従)・Sentinel・Cluster という 3 つのモードの構築手順と設定ポイントを記録し、各モードのメリット・デメリット、およびデータ偏り(データスキュー)問題の原因と対策を分析します。]]></description>
            <content:encoded><![CDATA[<p>Redis のレプリケーション(主従)・Sentinel・Cluster という 3 つのモードの構築手順を自分用に記録しておきます。Redis のデプロイモードはどれも複雑ではなく、ポイントは設定ファイルの書き方と、各モードのメリット・デメリットの分析にあります。</p>
<!-- -->
<p>公式サイト：<a href="https://redis.io/" target="_blank" rel="noopener noreferrer" class="">Redis</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイ環境">デプロイ環境<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E7%92%B0%E5%A2%83" class="hash-link" aria-label="デプロイ環境 への直接リンク" title="デプロイ環境 への直接リンク" translate="no">​</a></h2>
<p>OS：CentOS7</p>
<p>Redis バージョン：Redis7.0.5</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="レプリケーション主従モード">レプリケーション(主従)モード<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%83%AC%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E4%B8%BB%E5%BE%93%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="レプリケーション(主従)モード への直接リンク" title="レプリケーション(主従)モード への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="515" height="326" class="img_ev3q"></p>
<p><strong>メリット：</strong> 設定がシンプルで素早く構築でき、読み書き分離によってマスターノードの読み取り負荷を完全に分担できます。さらにスレーブノードの下に「入れ子」のように多段のスレーブノードを構成することも可能です。</p>
<p><strong>デメリット：</strong> 高可用性ではなく、マスターノードがダウンすると書き込み能力を失います。また、データの冗長量が大きく、各スレーブノードがマスターノードのデータを 100% 複製します。非高可用アーキテクチャに分類されるため、本番環境で単独で使われることは一般的にありません。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="設定手順">設定手順<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E8%A8%AD%E5%AE%9A%E6%89%8B%E9%A0%86" class="hash-link" aria-label="設定手順 への直接リンク" title="設定手順 への直接リンク" translate="no">​</a></h3>
<p>1）スレーブノードの redis.conf ファイルを設定します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-2-cfff0cf45887fffb34a8f4514cf67b8c.png" width="800" height="396" class="img_ev3q"></p>
<p>マスターとスレーブのパスワードは揃えておくのが望ましいです。</p>
<p>2）マスターノードの redis.conf ファイルを設定します。通常どおり認証パスワードとリモート接続情報を設定すれば十分です。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-5-019e0afdc2f974fde073d6b3c8d367bd.png" width="951" height="260" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAz0AAACDCAYAAACnQHXtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAgAElEQVR4nO2dTahcyXXHe++Nl1lZGBGyGbIJZBHwQAYCA5MQaDIJOBBihDdBgxfavOxk08jxgBLBI4FJYoNk2mCZQRMIImMkIkh4YzDS4CBBcISIMeI5b6F4wGA7GCrcj+pbt+rU1723u29X/35Q8F73/ahb3/8+p85dvPbaa8pOB8fTe+ryyam6vH5mfPhM3ag+O7mnnvjOu3ikrpycqhtP1cw4V+tbC7V63P77eKUWt9bqfOxV7y/V4vpKnXm/W6r1y+6zszsL6/gmX11e2v+v28cl5aY790575su1WtbXMp7dpCoHK48Sdb5j5dXeS7xPVv6N567zF8h/6J6PV2p5/zxaJ2n1NKY9nKnV9UUvL831Y+UulIeAndemrpZqmVCv4Wv6z5+yzFLrKa3MumtWbca+bha67+i+tMnDsOd0+5Dd96V8G33aOi5eZtW5cls0n6nrY925m+c07jlsPJOxr1X/X7fZIeOH/Uzt/1b+xTEsNH4kfNcrR89z2ue7ZXamVp58DW+/RhmIeRTagXBPsZ5C43GIjDLrX18YB6W6aduxL19TzGE6b/16OVMr8ZnafIeeN7FMUvM/Znwa3DeDZZbStlPnfXcejfWTYJlNMUcE5v2zgWNjat/sXTNQ33lrDeu4xyuhXt17LooQPabwMdPNR+oidM5sRU/TkHQFNh15u6Kn+14PRL4Gb0xQumHXHXLgwkqcMIzGbAghJzmLHPm4rvP1895PQwZe63pVfpxBNf2e7jOEBGqsnqYTPXLe5Pt2E5mRepOkKZTb+9RlNlz02NeUJoepymxUPQXuO0n+jMlXl0Gsz/uI16Oc76rdS5N3Wpm5fcWZOPV4IIgvO3/xMk2d2K3yqO/TH6OzsMa06hr2GJ8menLGM/lYWeRE2qEwJg//8ah/X/913PxLx/byX+d9RD3Fyky3O089bepAnMPc8W4rc5g1JsiLXTn5f3iUr5eWf+FaCSJqXN/MKLOktp0y78tla18rq8wS21GM86R5KX1sTO2bqccOXmt425F1z2JEDwAATELSL80AEzHY8noIjPYqKJSItQu2w/nAH8JKAdEDAAA9ED2wO2TrcjEgekQYY/bDOaIHSw8AAHSwIIEdLcEy3GgOFESPg7TnDnbDOaIH0QMAAB2IHtgucgCHIkH0CPswEDz74hzRg+gBAAAAAIByYU8PAAAAAAAUDaIHAAAAAACKBtEDAAAAAABFg+gBAAAAAICiQfQAAAAAAEDRIHoAAAAAAKBoED0wGt7pAQAAAABzBtEDo0H0AAAAAMCcKUj0XKi7N0/V5RMj3XykLswjHt7uf+85bk5vrF49bv99vJrtm6sRPQAAAAAwZwoRPc/UjUq8rJ+5nxmCphE999QTdQhUomep1i+b/87vLxE9AAAAAABHK3qe3lOXT26ru5a5xhY5ByV6Xq7V8vpKnZnWlDv6v1TO1frWQi3vnzfnX2/+rq1G16v/O1HV3bM5rknd/XtXrQRY77iFIMiae3fHWPcCAAAAANgRBYmeU3Xjaf/jJ+sDtPQ4wsNNtXDJET23lvU5jbVo2Yqpxn1OX0sLmY07nRZallhpPuuLIde9rbm2KdKa6yN8AAAAAGD3lCF6tMA5OVVXHl4Y//etP+Kenlnu57H38HQWmzy0taURKX1hYwqTvgBSPvHSCjJTGEmip7mPbSVq85JtrQIAAAAAGEcxose0+DQpxaLT7vuZofWnv4fHCmqQfpWe0OhbWwxB07q7Sdc3BY0sZmzR4xc3BDwAAAAAgH1QnKWncXHrIrlpy0+ua9y+qQXGRjjsQvTIrmemO1ua6Gmv60szjUAHAAAAAOVShOjRbmvinh4hwEH/5EfqSoo42hE64IA/ycEFtip6JrL0AAAAAADsgyJEj0/c+MSQe0xEGO0caw9PLUpyxE6m6Ins6ennwxPxzbDgZLux6WhyWIEAAAAAYAsUIXo2e3mk9/SE9uu0583FymOLlfEvJk0VPXJ0NTdSmx2VrRVFt6x3CGkhlGjt6axbRHcDAAAAgOkpQ/Q4QQzkyGx630+X5mbhUeIenuEvJk0XPd33kf03vZDazbXk/Ml7e8R9SVh6AAAAAGCLlCN6AAAAAAAABBA9AAAAAABQNIgeAAAAAAAoGkQPAAAAAAAUDaIHAAAAAACKBtEDAAAAAABFg+gBAAAAAICiQfQAAAAAAEDRIHoAAAAAAKBoED0AAAAAAFA0iB4AAAAAACgaRA8AAAAAABQNogcAAAAAAIqmINFzoe7ePFWXT4x085G6EA99pK6kHLdXztTq+kKtHrf/Pl6pxa21Ot9zrgAAAAAADo1CRM8zdaMSL+tn7meWoHmyroTObXV3fipHED1LtX7Z/Hd+f4noAQAAAAA4WtHz9J4oZC4e3laXT+6pJ5HjZsnLtVpeX6mz9t+zOwu1uKP/AwAAAACAIxQ9p+rG0/7HtVVnY+lp3d961qC5ip2FWgTS8j5ObgAAAAAAxyV6Nm5rp+rKwwuPG1vj7nbl4TNn748tlmZBbw/PuVrfGih2TBEVsBTVlqT6uM6lzrfPqD4usL+odsVrj9vsSRp6z8T8p90zLf+UGWVGO6NvMrYwHjOHpc+bzPv58yZlNmB9NpJiRI9p8WmS4dbWC17Qd29rXODmJ3z6e3isoAbpVzHEUvO3KRw2LnOVwNKudPXfhgipRUfzv+liV/9tNMxNfo3jtXjo8m08R9I9E/OfeM+k/FNmlBntjL6ZOZ4xtjAeM4cx77NWOtvu+lKNpzhLTyNeukhu2vKjRc/m/w3zdHurK3nTgAaKnpDoaP+uBEWogTW/RFQNtju+PUoUJN3xLZ4Gn3TPxPyn3TMt/5QZZUY7o2/mjWeMLYzHzGHM+6yVVlteX05BEaLHZ63pu7hp97Z5i57O5cuXjAl4CLi7maWNu5sBLoL5/YQyo8w8IzljC/1k1BzM2EKZ+WBrwJGLHl8Y6r4Y8okbnxjaJ9YeHvPXRQAAAAAAOD7Rs9nLI72nxwlZ3bcINYLJ2v8zE9HDi0kBAAAAAMZThuhxghi0yXoxqXicdMze6e/h4cWkAAAAAADDKUf0AAAAAAAACCB6AAAAAACgaBA9AAAAAABQNIgeAIDSeHGq1LWrSr33XP7uratKPRC+O2beu9qUi1RmQ8s/tYzre7+Ree/nSl17o8lzKvXxbyj1IOM2AACFgOgBgCPlebsAfB5YVF6Np/c+9N/iQbv4nSqJeX2u1Avr8wdXlfrMp5W6JuTt2qeb76ZY3Ieo8lSlBx82ST9DVeaXrir1InSycW4lCKrF+qU3wufU9zPuVd1Hen7f/d5qy2UKQaDLP7WM33tjQJ20ea7KMhVd94geADhCED0AcJzohWm9CJQWmx8qdan9PpTeOo0vZm3xUi3I63MTP78WWBTrezxIET3tM+UslCsh4RVip3J5pqQHnucIpfr5P2zKSYuUUEp9zsoyk1suIRA9AACzA9EDAMeLudB2fv1uBUIlarTVopeM70PXlywUelH8IPFzvSgfK3o2IuxDzzMZyb53TFSYomcj2k5bS4++rr+oNnl7y7CgVec65xlitBY/bxiWsMA5+lgpmc8XOs6X7Hrxip42H6ZYNJ8dSw8AwNZA9MyMV69ekQopg8ViUadt3+d/f/Tfe3/WQ04/e+f1dgH/uvr5x+Z376tfVovhN99Vn4jnxr5/pT659bpw3Vfq1QdfrO/5yw8SP//4XfWrS59Wv7r1RL6HfU57HfXO+25+Ey0xm+u1966u9cnHT9r0vvr5m1WZfVH9LHjP9CQ+x8By957zme0kp17asnDrS867fnapfv3piVsH0bZu1S2JMqAN0AZeHU8ZIHpmxr4bBOlwRM8nH/27+r8//2P10//6IfU2qiybxaNv4epfxCeKnm0urjNEzyYv77yrfvbB+9H0yceu6LHL7HBET1wI5AmOSJpA9Hzywbvq5++EyiQgej5+V/3yzdfVr6y6QPRsZywmUQa0gVcHUQaInpmhKwYOHy16JueTnyr15b9sFpj/+HfTXx8SN/ynuLe1+3RCbmT1Zv2Yu9mpP7pXinvbmD0r+tyeq5ywiT4UPCEF6TmGlnsqOs9T7eVJcW+T8m669kl7lXSZ6EANdaraRJX3KhiH8fkLX50RyAAAjhtEz8xA9JTDVkTPN7+h1GuXmoXQm5+b9trgLkKDi2Fjb0kvmlu70I3tmdFJ7/vZ7HuJpVzRY0Qm2+StzXuKQNm16HGCOFjlOpnoMeqvunZqfdl7nmyBa+4RqupVf/ZAyHtd/4LQqUNnVwEkLNGTEuzhQYroSWhXAACFgeiZoej5z3/4A7VYrNRZ4jlnX2kW18tvnU+bmY9WarFYqvWPp73ssTCp6Hny/UbkmIub6jPIRwojvYne1r77xBvcwMI81o4wtq09JLYQi4mejZgwREIlevRCOyYedi16fGlz3YlEjxYAQ5NtxYnl3xQ90Wc08mgGwpjK0pPSrgAACgPRMzMQPeUwiei5+B+lrl5xFyh/+HtKffRvTXr+w6myfBxIi9ONBUS7gWW8KNIb9Wwmlh5fBDnTAhR6B06u6KmF1ICoZ9582lYpIXpbKEkizGwDPsuSL9lWsxT3tlpkCu2ujm7XtgHpmrbokepPEisx0WNaoDYJ0QMAZVOQ6PmJ+vYXvqQ+9VkjfeG76if66/Pvqs+b39npqz9Qhyp6tsYoS8+ZWrWL/iaNsxhpa5ZOY6xa599a9q61+MqIkv7xWi17z9nV2yjR84tfKPW3f9O5sqWm6vg/+f0m/dkfKfXXX+2SFklVOmrM/TTC3gv9a/mUTCF6fGS9p8fGsGzpPEwheoZYS4aInpRkl4Gdz9wXdaaEl3balZVn356eXYkeXk4KAEdIIaLnB+prjnBpPzOFj0Qrhr42E0+hIkSPFgKGmGhEyxDhc67Wb/fFRJOvYcLHyYeQ12ScfPTzOlj0/OsDpX73t8e53uSkymqkhVIVIEGLpH96vxNJP/6RKhJR9AxYvKfu/RmaQpYYvZdkkOhJCCIwxL3NCdagy9n6zs5Djuhx3NsS3N6kut2J6InkEdEDALB1yhA93/+6+tRnb6hvW2vgn3znhvrUZ7+uvhc49Xtf/VL0mP2Jnr61xF7k29aP1UfCBetFfbPI7x3/9lqdR60Ww4RKfR/n+q0gyBUXHuHVWGsyhWH7fE45DRJ3vudp6qyqq2zRUwmLyjqzK7EzNP3Ob3YiqXK90yKpiiSnRdKh7DfyiR5zY3nQfSpD9KS8EDRkCcqxPg3ZX+OzJuWKHkkMhL7blegxBU+Vj2s7dG8bJHp0GWPpAQCYgoJEj2utqQVNyNLTWnk+/52gLWg/ouftZX+B3loV/MIm/F1fNLViylywS9cfJAa6RX+P9vq5QkUWUJ0YFJ/ZgyyUtHUm03LkKfON69zb63TRU7my/dWXlfr1X9u/oNlW0iKpSqbLXWXV0kKpCsW9a0KL8aBrkbVYDYoLY6FrbjhPTddSRM8AMZXqQrdr0eO4+U0gesw9PDoPUwcymFz0tMd6BTXubQAAxyd6NhabTsA0/7vWnzlbefqWHntB3S7OAxaaoOixLBJ9MeGxWgwRPUJeNmIj+3pCvjbX94irLAGlr3GWb4USnkVf/6x93iTRU1l3fus39i9KpHQpIW3r3pV7nxZJ1/6iE0nP/kNNSspC3Sto9KI0Jki27N42xQJeJ+lZdyl6UvLm268SEj36HPP+1+bo3tbmM1YnNYgeAICjFD2mxadJETEzQytPbE9P454mWEoSRI/9XU8ATOn21buWJVpGip6+pWak6OnlZYDrXe/8fl50PpMtPZXwkSK0lZSmElCVCJqS4GLciG72YMjLSzUZ78SRSHFv01ap9wwLUbXXR7IwvRU4dozoCYmBFNEjPYMOzWze3ytGI+5ttiVrlqLHCCVepWBENY/o0S+zJZABAEDZlp7Gxa2L5OYTNXO08sREj3cfyyxFjyBKRogev5UmX/ScOeU4QvR85LdsZe/pqawYh7CnJxYMQQqKoNO//HM/ilyVqrDc+yK2GN+EsPa8GyfpXTF6oVuFT87cP6L3kKSKHvOY0MI39Vjzu/p5jf1Mn/GIHklEJIseaZFfBUJo90OZ90kVFKH7zU70eKLnVeLHiaxniJ7KBVBH4av3jiF6AACKFD1NwALPnh7JxW2mVp4kS89A97adiZ7AfpshwQc2wRdsQRJ6Zg/mfhtpj9CQPT1S+eiyHRy9rRIClWjYlkAxw1r7wltLYa5LDHctvU+leneKKHCu9v9PDi6Q+V4ZO12akehxLHhX0/ZARUVPwFUr5VnHiJ6hKVX0aEFSl+0A0aOvZZ9TiyEhX4geAIByRY9P3ITF0PysPGHRIwQfmEr0iIt+vcF/B9HbdJADSdDlRm/biJEMi5hX3HVBDtyy3UL0NpvKKvLm5+QFV/W5LVC++Q1XoEy9B6Y0zMVp9bdv0WwvkLOiqe3QvW2boiclWINPtERFT6Jg8Vnecq6h2UX0NluMjBE9tZgxLDpmqp6h92JcLD0AAEWKns1eHuk9Pba4mbGVJyR6vPt5JhE97vWb76sIcsPf02OKqFD+u1DaAeFhCqJAJDvzxaPS904+hLzaz+F9gamTj35eR4sezd11EybaXORUkd726RZWCvbitN5TIi08LWtHloApwNJTu+Z9ODyKXUz0pLi/Re+zZ/c2LUjMdlKVc2U5fDEwj/b9zGAPosubzguiBwCgTNHjBDFokxCuOiWq275Fz6unf+++LycQttlN7qI+JnpMq8YmfLPxjp9snHf+BNzaQpYeIW/BUNUhS4/n/UZ+t7aQpcfKu1BPk4keTfUeHDPS2xf/dLprHyVGoILNr+XKXcja7+wxBYxv0VnSnp4YpvubT0DERE2SAIlFy9uz6EkSJLEw1LFgGlXQCU8d9V4Ga9Sn+fnmmTNClgMAFEI5oqcQatHz6tW+swETMLno0e/0qVzZqn051eKles8NDMRYUNovhayimdmCSC9ibSG0+TX/+W7d214kRmTLid5mHvsiU/CEni8oelKEgFFXsRDiWxU9hnCQLD0+QSI9R9WmnEh1Vp0kvfzWuu6YNLSNAgAcAIiemYHoKYetiB5N9SLP6oWm1d6eSgjBMF7oxb0gdGIuRNWv6dJCszr+mumiZlmKhri32efark7bSDFXs15Ut4jQCImelGhoScEjtiB6fMEOsvZ0ZQSGGHwvu80NTIgeACgYRM/MQPSUw1ZFj6ba11O95wem3d/T24cRwdxcrhfclYC6tsVUuycFrARTpBeJZZYiMkKip7acXU07P8X9bUrRUz+fIA6S3BojpNQfLmcAAJOB6JkZiJ5y2InogWlhkTnfsk05v96bknpB9rEAABwTiJ6Zih7S4ZeBFj37zgeJMqAN0AZoA7QB2gBt4NWRlwGiZ2bsu0GQJuxciB7aE32KNkAboA3QBmgDtAE1hzJA9AAAAAAAQNEgegAAAAAAoGgQPQAAAAAAUDSIHgAAAAAAKBpEDwAAAAAAFA2iBwAAAAAAiqYg0XOh7t48VZdPjHTzkbqwD3t6r3/Myam68XQ/OQYAAAAAgO1TiOh5pm5UAmb9zP3MED4XD2+ryye31V1DCTWfIXwAAAAAAEqlDNFTW2/6YqYTNPfUE9MS1BNGnTi68tCxCQEAAAAAQAEUJHpca82TtWnpaUWP7fJ28UhdwdIDAAAAAFAsZYgeLXAMi03zv2X9aQXOxvqj/3esPwAAAAAAUArFiB43SIF2awsHPMCtDQAAAACgbIqz9DQubp2w6YkabdnRLm4bkeQTSAAAAAAAcOgUIXp8Edj6Lm5uNLfmZFzcAAAAAABKpgjRI+7fscWQV9x4xBAAAAAAABRBEaJn46YmvafHDlnteU8Pe3sAAAAAAMqkDNHjBDFok2O96Qcx0Ml2iwMAAAAAgHIoR/QAAAAAAAAIIHoAAAAAAKBoED0AAAAAAFA0iB4AAAAAACgaRA8AAAAAABQNogcAAAAAAIoG0QMAAAAAAEWD6AEAAAAAgKJB9AAAAAAAQNEgegAAAAAAoGgQPQAAAAAAUDSIHtgNL9dqeX2hVo/HXORcrW8t1OJ6l5b3z6fLo/c+S7V+uYXbHDOPV03Z3lqrbdQgTEQx9XSmVpHx5+xOf2xZ3DnbZQYPhMyxsR33F9dXitKEec/7R8bLHfZNPY/MYGxF9MCBiB498O148qw7a2Bin0TM7X8xuPNJpJjFdOF1XkQ9tWNH4BkawcOPG5OOjTMWPef3l7PL0zao23UJffcI6mrndf5yH32zrc/Romf4uqUg0XOh7t48VZdPjHTzkbqwD3t6r3/M+pk6iF8mqwlmyOBlK+x9/VIyVhy05+9ncY7ogZLYk9DdE42gCU3sTXlg2dmC6JkpiJ4DYV/z/kTsw3o8b6F7juiZhmfqhiNg2s8M4XPx8La6fHJb3b2whJIkjvZONRF3E0o9SA8WPdbE1AqhnQ4kE4menVtUED1QHEckepLGjSMqjylB9MyeeS+AEzhYTwqPherxauvjzLzr/BzRMwm19cYUM6bIuaee1P88UldOTtWNp9K5wuez6Oxdh6kb8pBfCcSJqd/wml+9pIEl7haiYr+eWsm9h32c5xfZlMFvY671+ZvLi5vgr36eid35BSf6nBPiPKeVd09ZmYOhrnNfssvI+7xWm7SPcwb4JN/ezsrZz6dQR05ZjN2HldIe7WPkdhbNf5335txeuYn9LbGfBPZcZNV5hg92rM67H2z6zyBN/k4eR/4ymrYAiIgeYwzoP6tbB+H8N3VT3Udfp77npqxz26wxjlv9wH2W+F6czRxjXcs7loVEj90vg3WQsk9oin0d8pzkr89YP08hs86jc1hCO/OOib66iI0tOe1sYmLz/lz7Znte2jpggr6ZU+cvU/pmxhwslK2/XYwTPbnrFmmtVIZ7m0e4PFkbVhyPMNIWoSsPZ2LriTXe3IEmQfR43TuGmpal86TBSziu74qSM0mdqZXVeV0//elET/C5togkUJ3nTBA9Ob9yO+5BoqWwrStpcBUHuNDgZ9T75ntBgDvtZ6T/t57genk6Uyvzf+HZ3TrJy3//ekJfjPYT63q9Oj5X6zv5dZ5VT5E67yYqo40Ki4LmuH5fqz4b3rdCz+kuovvJzWu4PqW2Z5dPu7C6tazz1IjBZfvj1hBrk/kMobJ125RU1t2i0RbKnvEv0dITFJ6JbfbszrTeClH3tqR+nnSnjDpPmcPy+klU9CeNLantbCoy5v259s3k9pnbN7tn8PXNHEvPmffYxDlMai/BdePuLD3etVIRokcLHEO8NP8bIsdn6RFd42ZAbw9P9yvEoOskDJrSImqo37PYkYSFeNYifIi4cDrfoYseX0e3Bs5JRY/0vTv4+crQv2jKXEwL9xDvOXgiThmMfcfY5ZGWf58otOsptd7SJ7tpRE9qncuLxTH5n9r9KsXSExk3fPfqtcf+4qtfLkP2FflEfv955Hpy61UO5hAomwlEz/A6H+OFEJvbUvt5Rj6H1rlPlIxe1Ia+t+s8rZ3tz9Iz574ZthKN6ptZ8/xA0XMrbQ6z8+DvX7sSPf61UjGixw1S0Lq1OYEO+p9rsTQ30dPfwxMPt5obyMC5lrNYHNo4U0WLr1F67jtIXAi/pB2y6Aks6HuD15SiR/zVxq6jwGLAWz5x0ROrJ9HaMXSfQYb7pN8tq2+hjLazpHpK7Sc5C5ApRE96ncv9y83DxiI0kfBJ/9EmTfTEw137XSGba0tuxbqtjlhYpbiOCddNE9eBfjpa9IxbNI8RycG2kdzPk+40ss7H9ZNwGaWOLeME5i5Ez/z6Zj9/spfOyL65A9GzjMxh3v7gHRt2JHoC7aIY0aPFS2PJ6SK59d3W3AhvN57OzL3NbEzWgma46PH4DQs+rX1rwRAf5kTRE3PjGyB6fP6eZYkeOS+9gX1H7m1JAtm7gBgverzubVMF/Mioa2mSnET0pPaTrHY4oehJqPNU0TP1+xx2LnpCv5jWz7EP0RNxFdqn6Mlps75+sEXRE+/nSXfKqvP4HJbXT5JcC6NzcAGiZ+d9M2D5sT0ChvbNGYies+De5n2LHrmfFiF6moAFnj094j4e82Sf29t+iG2Qz3Y381S+zy+4Z94dEcBguKVn2OCX5kJTuOjZhqXHs/ehf+34Angblh7Rijl0Ys6w9PgXQ7u09NjsydKTUOdZokcaC0dtep2H6JmjpSftGWZg6Ul0A92d6NmOpSfVDTSnnwyz9HieoVDRs1VLj0W/jkf2zRmInvOR/WE4Ry56fOLGJ4acY2YXstraw1NX4JjN2T7RIy9EVo9H7CHyNGo9MA92nwsOfr5BOUX0RDa/Ryf29AVkLyrMFvb0bD4Xy0r20Y3WQ+LCJry/I9e3N2XAnWrw7N9z1J6eiKvZMNGT+pw5C5Ocshu6pyc2Mab1m1H7fCbe0xMUxMF9A/rzfYie9DLMWVi5zzbk/mn5n2LDdl7eBwh9L6l1njqHyfjKYtjeJc9xqWU95UsvJxA9++mbMraw3UbfzBEiZyNFT747+VTzduw6pe/p0Xt5pPf0OHt7wnt85kFToaNfTJrl3uaPEpKde2kjcx0JxeqgORF4IoOfL4JZ//q+qC5jRE9qxLCEeyWQFoTCHqQNM7rQjoJvo0+2ZKVFHZt8T8+Evz7qCcmJSidEbwtHHZtS9GT0EzH6nBS9LVLnuQEnInWeJnqq+3hCBg+eICeykiUFx5AWhnb+9yN6wlEUO9z+FLn+FNHbUtqs0/4lV6FMYmWSGF0wTnqdp89hGf0kNnYkjS2ZosewwI8OcjCF6NlD35Sj6Qk/PA7um4GyyVhXnY0VPdkRU6f7sTI2h3nXSkWIHieIQZssC84maEGb5raPp6O/h2e4q1lGIAP7+JGNsuemV1/Lsy9J9CsWGnJ08W27YflCTfb9aOvvbEtayNc5Ft4xVL6buimWzr0AAAGESURBVBj3C5jj9x3wlzfz429HrgubuHE2Whbxcgi5b8ZcH90B1+8TPdjd0Okvvk2aoWMmFj0D+knYrzpe52n11D1rqOzTLT3utcYumqZ7T09KmwqX6d5ET6CvuKHv0/ZWxsYD75gRcH0OtVn7elW+R//oEevHKWNBlJw6HzaHxfpJdL6Iji257m3dc8xD9Oyjb8r9SS6PxL6ZYYUN1fl5Ut/M2woQGzvS55McYnUql0M5ogemYWux98HvYjZfvO+mSPyFah9lmW7FgOIZ+q6xI2TykOFwtAx7pxHA9kH0QA8mvu1xiIvxWLjPvYmeaFjZwypn2B6H2O/2AWM/TMLefxAD8IPogQ0sFrfERG5tc/rFzg1MMRNLj/iGdzhucv3OjxNED4xjQrc2gC2B6AHD35JfQ8FF9gGewwJS9oVmwgVfW8HdJtCbcG8DgMJB9AAAAAAAQNEgegAAAAAAoGgQPQAAAAAAUDSIHgAAAAAAKBpEDwAAAAAAFA2iBwAAAAAAVMn8P+DBDmivkhXNAAAAAElFTkSuQmCC" width="829" height="131" class="img_ev3q"></p>
<p>設定の結果：</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="516" height="186" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="542" height="179" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="552" height="173" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sentinel-モード">Sentinel モード<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#sentinel-%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="Sentinel モード への直接リンク" title="Sentinel モード への直接リンク" translate="no">​</a></h2>
<p>公式ドキュメント：<a href="https://redis.io/docs/manual/sentinel/" target="_blank" rel="noopener noreferrer" class="">Redis Sentinel による高可用性の実現</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-12-52dc39f41e38d8ff5835cf731c4b255c.webp" width="762" height="526" class="img_ev3q"></p>
<p><strong>メリット：</strong> Sentinel モードは、主従クラスタでマスターノードがダウンすると書き込めなくなる問題を解決します。マスターノードがダウンすると、sentinel がマスターノードの利用不可を検知し、残りの利用可能なスレーブノードの中から新しいマスターノードを選出することで、書き込み不能の問題を解消します。元のマスターノードは再起動後、スレーブノードとしてクラスタに参加します。</p>
<p><strong>デメリット：</strong> 依然として大量のデータ冗長が存在します。また Sentinel ノードはデータストレージに参加しないため計算リソースの無駄があり、<strong>ストレージ能力は向上せず</strong>、<strong>書き込み能力も向上せず</strong>、中心化された構成のままです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="デプロイ時の注意事項">デプロイ時の注意事項<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E6%99%82%E3%81%AE%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85" class="hash-link" aria-label="デプロイ時の注意事項 への直接リンク" title="デプロイ時の注意事項 への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">Sentinel ノード自身はデータを保存しません。</li>
<li class="">Sentinel ノードは最低 3 つ、かつ必ず奇数個にします（Raft アルゴリズムに基づいて選出を行うため）。</li>
<li class="">Sentinel ノードは独立してデプロイし、Sentinel プロセスのみを動かします。他のアプリケーションを同居させると Sentinel が TILT モードに入る恐れがあり、またハードウェアリソースの奪い合いによって Sentinel の監視信頼性が低下します。</li>
</ul>
<p><strong>Sentinel モードはレプリケーションモードを基盤としています。</strong> マスター 1 台 + スレーブ 2 台に Sentinel 3 台を加えると、すでに 6 つの計算インスタンスが必要です。本番環境では、クラスタ全体の安定性を保つために Sentinel は必ず単独でデプロイし、マスターノードやスレーブノードに同居させてはいけません。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="1023" height="72" class="img_ev3q"></p>
<p>このモードは本番環境で利用可能な高可用アーキテクチャであり、Redis のすべての機能が保持されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sentinel-ノードの設定手順">Sentinel ノードの設定手順<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#sentinel-%E3%83%8E%E3%83%BC%E3%83%89%E3%81%AE%E8%A8%AD%E5%AE%9A%E6%89%8B%E9%A0%86" class="hash-link" aria-label="Sentinel ノードの設定手順 への直接リンク" title="Sentinel ノードの設定手順 への直接リンク" translate="no">​</a></h3>
<p>前提：レプリケーションモードがすでに構成済みであること。</p>
<p>1）sentinel.conf ファイルを設定し、監視対象の master アドレスを指定します。Sentinel はマスターノードの info 情報から対応するスレーブノードの情報を自動的に読み取ります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-13-6b1ea6129087baad8494b2a35524cd70.png" width="1115" height="296" class="img_ev3q"></p>
<p>2）すべてのマスター・スレーブノードのパスワードを揃えます。</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="1012" height="162" class="img_ev3q"></p>
<p>3）redis-sentinel コマンドで Sentinel を起動します。</p>
<p>4）spring-boot で Sentinel 接続を設定します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-17-3bdd041211172d2edb77c33a0d6bfd06.webp" width="1087" height="487" class="img_ev3q"></p>
<p>Sentinel ノードの数が多い場合は、行を分けて設定できます：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-20-b041c7b3f51196de178e10d5c3f7adf1.webp" width="689" height="497" class="img_ev3q"></p>
<p>Sentinel によるノードダウンの判定には主観的ダウンと客観的ダウンがあり、十分な数の Sentinel がマスターノードを利用不可と判断したときにのみ選出投票が行われます。<strong>選出の間、クラスタ全体は外部にサービスを提供できません</strong>。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="主観的ダウンと客観的ダウン">主観的ダウンと客観的ダウン<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E4%B8%BB%E8%A6%B3%E7%9A%84%E3%83%80%E3%82%A6%E3%83%B3%E3%81%A8%E5%AE%A2%E8%A6%B3%E7%9A%84%E3%83%80%E3%82%A6%E3%83%B3" class="hash-link" aria-label="主観的ダウンと客観的ダウン への直接リンク" title="主観的ダウンと客観的ダウン への直接リンク" translate="no">​</a></h3>
<p>SDOWN（subjectively down）は直訳すると「主観的」障害で、現在の sentinel インスタンスがある redis サービスを「利用不可」状態と判断していることを意味します。</p>
<p>ODOWN（objectively down）は直訳すると「客観的」障害で、複数の sentinel インスタンスがいずれも master を SDOWN 状態と判断した場合、master は ODOWN になります。ODOWN は、master がクラスタによって「利用不可」と確定されたと簡単に理解でき、フェイルオーバーの仕組みが開始されます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-18-597476c5a1db840af5dcda6d7bbf4db9.png" width="1107" height="910" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cluster-モード">Cluster モード<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#cluster-%E3%83%A2%E3%83%BC%E3%83%89" class="hash-link" aria-label="Cluster モード への直接リンク" title="Cluster モード への直接リンク" translate="no">​</a></h2>
<p>公式ドキュメント：<a href="https://redis.io/docs/latest/operate/oss_and_stack/management/scaling/" target="_blank" rel="noopener noreferrer" class="">Redis Cluster によるスケーリング</a></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-22-e4af1f77ba6ebb2ec4dbd5b2942abc63.webp" width="1563" height="587" class="img_ev3q"></p>
<p>よく使われるクラスタソリューションは 3 つあります：</p>
<p>1）Twemproxy（Twitter がオープンソース化した Redis プロキシ）</p>
<ul>
<li class="">使いやすい監視・管理用のバックエンド画面がなく、運用監視に不向きです。</li>
<li class="">最大の課題は、スムーズなスケールアウト/スケールインができないことです。ビジネス上の必要から Redis インスタンスを追加する際、運用の作業量が非常に大きくなります。</li>
</ul>
<p>2）Codis（豌豆荚(Wandoujia)による独自開発）</p>
<ul>
<li class="">UI 管理画面があります。</li>
<li class="">ノードの動的な追加をうまく扱えます。</li>
</ul>
<p>3）公式ソリューションの Redis Cluster</p>
<p><strong>メリット：</strong> Cluster モードは Sentinel モードと比べて中心化の考え方を取り除き、ロードバランシングを自動的に実現します。データストレージの上限はノード数の増加に伴って拡大します。ノード間は軽量なプロトコルを使用して帯域の消費を抑え、ノードの動的な拡張をサポートします。クラスタはハッシュスロット（hash slot）を使ってキーとインスタンスのマッピングを扱います。クラスタ全体で 16384 個のハッシュスロットがあり、デフォルトでは 16384 個のハッシュスロットが全ノードに割り当てられ、各インスタンスノードが一区間のハッシュスロットを担当します。これはデータパーティションに似ています。各キーは CRC16 アルゴリズムでハッシュ値を求め、それを 16384 で割った剰余、すなわち <strong>CRC16(key) mod 16384</strong> の結果によって、キーが属するハッシュスロットの位置が決まります。</p>
<p>まとめると 3 点です：書き込み能力の向上、ストレージ能力の向上、非中心化。</p>
<p><strong>デメリット：</strong> データ保存の偏りとデータアクセスの偏り（データスキュー）の問題が存在します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="データスキューの問題">データスキューの問題<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B9%E3%82%AD%E3%83%A5%E3%83%BC%E3%81%AE%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="データスキューの問題 への直接リンク" title="データスキューの問題 への直接リンク" translate="no">​</a></h3>
<p>データがシャーディングクラスタの複数インスタンスに不均等に分布し、大量のデータが 1 つまたは少数のインスタンスに集中すると、ストレージ負荷が均等に分散されず、さらにホットデータへのアクセスが常に特定のインスタンスに集中するため、そのインスタンスの負荷が増大し、最終的にはダウンのリスクに直面します。データスキューが発生する主な原因は 3 つあります。</p>
<p>1）bigkey を保存している。</p>
<p>bigkey は value が非常に大きい（String 型）か、あるいは大量のコレクション要素を保持している（コレクション型）ため、そのインスタンスのデータ量が増加し、メモリリソースの消費もそれに応じて増えます。しかも bigkey の操作は一般にインスタンスの IO スレッドをブロックするため、bigkey へのアクセスが多いと、そのインスタンス上の他のリクエストの処理速度にも影響します。</p>
<p>bigkey によるデータスキューを避けるための根本的な対策は、ビジネス層でデータを生成する際に、過剰なデータを同じキーバリューに保存しないようにすることです。bigkey がコレクション型である場合は、bigkey を多数の小さなコレクション型データに分割し、異なるインスタンスに分散して保存する方法もあります。</p>
<p>2）Slot の割り当てが不均衡。</p>
<p>クラスタの運用担当者が Slot を均等に割り当てていないと、大量のデータが同じ Slot に割り当てられます。同じ Slot は 1 つのインスタンスにしか配置されないため、大量のデータが 1 つのインスタンスに集中し、データスキューを引き起こします。</p>
<p>運用規約によって、割り当て前に過剰な Slot が同じインスタンスに集中しないようにできます。すでに Slot が割り当て済みのクラスタでは、まず Slot とインスタンスの具体的な割り当て関係を確認し、過剰な Slot が同じインスタンスに集中していないかを判断します。集中している場合は、一部の Slot を他のインスタンスに移行することで、データスキューを回避できます。</p>
<p>Slot の割り当て状況を確認する方法はクラスタによって異なります。Redis Cluster であれば CLUSTER SLOTS コマンドを使用し、Codis であれば codis dashboard の UI コントロールパネルで確認できます。</p>
<p>3）Hash Tag を使用している。</p>
<p>Hash Tag とは、キーバリューの key に付ける一対の波括弧 {} のことです。この括弧で key の一部を囲むと、クライアントは key の CRC16 値を計算する際に、Hash Tag の波括弧内の key の内容だけを計算対象にします。</p>
<p>key が user:profile:3231 だとして、そのうちの 3231 を Hash Tag にすると、key は user:profile:{3231} になります。クライアントがこの key の CRC16 値を計算するときは 3231 の CRC16 値だけを計算します。そうでなければ、クライアントは "user:profile:3231" 全体の CRC16 値を計算します。</p>
<p>Hash Tag を使うメリットは、異なる key の Hash Tag の内容が同じであれば、それらの key に対応するデータが同じ Slot にマッピングされ、同時に同じインスタンスに割り当てられることです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-8eec74dc40b94404b7903c0c126bb753-1-3af83afc758a9b796c22bc133b63ec9b.webp" width="1600" height="389" class="img_ev3q"></p>
<p>Hash Tag は一般にどんな場面で使われるのでしょうか。実は主に Redis Cluster と Codis において、トランザクション操作と範囲クエリをサポートするために使われます。Redis Cluster と Codis 自体はインスタンスをまたぐトランザクション操作や範囲クエリをサポートしていないため、業務アプリケーションにこうした要件がある場合は、データをビジネス層に読み込んでトランザクション処理を行うか、各インスタンスを 1 つずつクエリして範囲クエリの結果を得るしかありません。</p>
<p>Hash Tag を使ってトランザクション操作や範囲クエリの対象データを同じインスタンスにマッピングすれば、トランザクションや範囲クエリを簡単に実現できます。</p>
<p>しかし Hash Tag を使う潜在的な問題として、大量のデータが 1 つのインスタンスに集中してデータスキューを引き起こし、クラスタ内の負荷が不均衡になる可能性があります。そのため、範囲クエリ・トランザクション実行の要件と、データスキューがもたらすアクセス負荷との間でトレードオフを考える必要があります。</p>
<p>私のおすすめは、Hash Tag でシャーディングしたデータが大きなアクセス負荷をもたらすようであれば、データスキューの回避を優先し、Hash Tag によるデータシャーディングは使わないことです。トランザクションも範囲クエリもクライアント側で実行できますが、データスキューはインスタンスの不安定化を招き、サービス停止につながるからです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="データアクセススキューの原因と対策">データアクセススキューの原因と対策<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%83%87%E3%83%BC%E3%82%BF%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%82%B9%E3%82%AD%E3%83%A5%E3%83%BC%E3%81%AE%E5%8E%9F%E5%9B%A0%E3%81%A8%E5%AF%BE%E7%AD%96" class="hash-link" aria-label="データアクセススキューの原因と対策 への直接リンク" title="データアクセススキューの原因と対策 への直接リンク" translate="no">​</a></h3>
<p><strong>データアクセススキューが発生する根本原因は、インスタンス上にホットデータが存在することです</strong>（例えばニュースアプリのホットなニュースコンテンツ、EC のセールにおける人気商品情報など）。<strong>ホットデータがあるインスタンスに保存されると、そのインスタンスへのリクエストアクセス量は他のインスタンスよりはるかに多くなり</strong>、巨大なアクセス負荷に直面します。</p>
<p>ホットデータは通常 1 つか数個のデータなので、Slot を再割り当てするだけではホットデータの問題は解決できません。一般的に、ホットデータは読み取り操作が中心なので、この場合はホットデータの多重レプリカ方式で対応できます。</p>
<p>具体的には、ホットデータを複数コピーし、各データレプリカの key にランダムなプレフィックスを追加して、他のレプリカデータと同じ Slot にマッピングされないようにします。こうすることで、ホットデータには複数のレプリカがあって同時にリクエストを処理でき、しかもこれらのレプリカデータの key は互いに異なるため、異なる Slot にマッピングされます。これらの Slot にインスタンスを割り当てる際も、異なるインスタンスに配置するよう注意すれば、ホットデータのアクセス負荷は異なるインスタンスに分散されます。</p>
<p>注意：ホットデータの多重レプリカ方式は読み取り専用のホットデータにしか適用できません。ホットデータに読み書き両方がある場合、多重レプリカ間のデータ整合性を保証するための追加コストがかかるため、多重レプリカ方式は適しません。読み書きのあるホットデータについては、インスタンス自体のリソースを増強する、例えばより高スペックのマシンを使うなどして、大量のアクセス負荷に対応する必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスタ使用時の注意事項">クラスタ使用時の注意事項<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E4%BD%BF%E7%94%A8%E6%99%82%E3%81%AE%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A0%85" class="hash-link" aria-label="クラスタ使用時の注意事項 への直接リンク" title="クラスタ使用時の注意事項 への直接リンク" translate="no">​</a></h3>
<p>Redis Cluster のノード間では、定期的に Gossip メッセージの交換とハートビートチェックが行われます。公式は Redis Cluster のノード数を 1000 以下にすることを推奨しています。クラスタ内のノード数が多すぎると、無視できない帯域消費が発生します。</p>
<ul>
<li class="">メッセージ送信頻度：あるノードが他ノードとの最終通信時刻が cluster-node-timeout/2 を超えたことを検知すると、直接 PING メッセージを送信します。</li>
<li class="">メッセージのデータ量：slots スロット配列（2kb の領域）と、クラスタ全体の 1/10 の状態データ（ノード 10 個分の状態データで約 1kb）。</li>
<li class="">ノードを配置するマシン規模：クラスタを分散させるマシンが多く、各マシンに割り当てるノード数が均等であるほど、クラスタ全体で利用可能な帯域は高くなります。</li>
</ul>
<p><strong>Redis Cluster</strong> クラスタの制限：</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">キーのバッチ操作のサポートは限定的：例えば mget、mset は同一 slot 内でなければならない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">キーのトランザクションと Lua のサポートは限定的：操作するキーは同一ノード上になければならない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">キーはデータパーティションの最小粒度：bigkey のパーティション分割はサポートされない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">複数データベースはサポートされない：クラスタモードでは db0 のみ</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">レプリケーションは 1 段のみ：ツリー型レプリケーション構造はサポートされない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Redis Cluster が満たす容量と性能の拡張性は、多くのビジネスでは「必要ない」</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">多くの場合クライアントの性能は「低下」する</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">ノードをまたぐコマンドは使用不可：mget、keys、scan、flush、sinter など</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Lua とトランザクションはノードをまたいで使用できない</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">クライアントの保守がより複雑になる：SDK とアプリケーション自体のコスト増（例えばより多くのコネクションプール）</span><br></div></code></pre></div></div>
<p>多くの場面では Redis Sentinel で十分です。</p>
<p>Codis と Redis Cluster クラスタの比較：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-23-492599c8b3e333924535a3320378b0d1.webp" width="1230" height="600" class="img_ev3q"></p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-cluster-のまとめ">Redis Cluster のまとめ<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#redis-cluster-%E3%81%AE%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="Redis Cluster のまとめ への直接リンク" title="Redis Cluster のまとめ への直接リンク" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">1. Redis Cluster のデータパーティションルールは仮想スロット方式(16384 スロット)を採用し、各ノードが一部のスロットと関連データを担当することで、データとリクエストのロードバランシングを実現する</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">2. Redis Cluster の構築は 4 つのステップに分かれる：ノードの準備、meet 操作、スロットの割り当て、データの複製。</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">3. Redis 公式は redis-trib.rb ツールによる Redis Cluster の迅速な構築を推奨している（redis5.0 より前は redis-trib を、それ以降は redis-cli コマンドを使用）</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">4. クラスタのスケーリングはノード間でスロットと関連データを移動することで実現する。スケールアウト時はスロット移行計画に従ってスロットをソースノードから新ノードへ移行し、スケールイン時はオフラインにするノードが担当するスロットがあれば他ノードへ移行し、その後 cluster forget コマンドでクラスタ内の全ノードにオフラインノードを忘れさせる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">5. smart クライアントを使ってクラスタを操作することで通信効率を最大化する。クライアント内部でキー、スロット、ノードのマッピングを計算・保持し、ターゲットノードへの高速な特定に用いる</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">6. クラスタの自動フェイルオーバープロセスは障害検出とノード復旧に分かれる。ノードのダウン判定は主観的ダウンと客観的ダウンに分かれ、過半数のノードが障害ノードを主観的ダウンと判断した場合、そのノードは客観的ダウン状態としてマークされる。スレーブノードが客観的ダウンとなったマスターノードに対する障害復旧フローを起動し、クラスタの可用性を保証する</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">7. 開発・運用でよくある問題：超大規模クラスタの帯域消費、pub/sub のブロードキャスト問題、クラスタの偏り問題、単一マシンとクラスタの比較など</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="redis-cli-による-redis-cluster-の構築構築バージョン-704">redis-cli による Redis Cluster の構築（構築バージョン 7.0.4）<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#redis-cli-%E3%81%AB%E3%82%88%E3%82%8B-redis-cluster-%E3%81%AE%E6%A7%8B%E7%AF%89%E6%A7%8B%E7%AF%89%E3%83%90%E3%83%BC%E3%82%B8%E3%83%A7%E3%83%B3-704" class="hash-link" aria-label="redis-cli による Redis Cluster の構築（構築バージョン 7.0.4） への直接リンク" title="redis-cli による Redis Cluster の構築（構築バージョン 7.0.4） への直接リンク" translate="no">​</a></h3>
<p>最低 6 ノード、(マスター 1 + スレーブ 1) × 3 = 6 ノードが必要です。redis 5.0 より前は redis-trib ツールを、それ以降のバージョンでは redis-cli コマンドを使用します。</p>
<p>1）redis.conf ファイルを設定します。すべてのノードのパスワードを同一にし、クラスタモードを有効にします（詳細な設定は公式ドキュメントを参照）：</p>
<div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># クラスタモードを有効にする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key attr-name" style="color:hsl(29, 54%, 61%)">cluster-enabled</span><span class="token plain"> </span><span class="token value attr-value" style="color:hsl(95, 38%, 62%)">yes</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-24-f3c1ba172fed2ea618311e8117e78173.png" width="907" height="233" class="img_ev3q"></p>
<p>2）クラスタ作成コマンドを実行します：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">redis-cli </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--cluster</span><span class="token plain"> create --cluster-replicas </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.121.145:6379 </span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.121.143:6379 </span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.121.142:6379 </span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.121.141:6379 </span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.121.144:6379 </span><span class="token number" style="color:hsl(29, 54%, 61%)">192.168</span><span class="token plain">.121.140:6379 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-a</span><span class="token plain"> 【パスワード】</span><br></div></code></pre></div></div>
<p>ここで redis-cli --cluster または ./redis-trib.rb はクラスタ操作コマンドを表し、create はクラスタの作成を表します。--replicas 1 または --cluster-replicas 1 は、クラスタ内の各 master のレプリカ数を 1 に指定するもので、このときノード総数 ÷ (replicas + 1) が master の数になります。したがってノードリストの先頭 n 個が master となり、残りのノードはすべて slave ノードとして、異なる master にランダムに割り当てられます。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>コマンドを入力するとクラスタの slot 割り当ての確認を求められます。必ず y ではなく完全な yes を入力してください。そうしないと slot の自動割り当てがスキップされ、クラスタが使用不能になります。未割り当ての場合は、<code>redis-cli --cluster fix 127.0.0.1:6379 -a 【パスワード】</code>（任意のノード）コマンドで slot を修復できます。</p></div></div>
<p>ログ出力から、クラスタが自動的に作成完了したことがわかります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-26-422b2f151de78e39201db4d10440f08f.webp" width="1473" height="430" class="img_ev3q"></p>
<p>3）現在のクラスタノード情報を確認します：</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">redis-cli </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-h</span><span class="token plain"> 【任意のノードIP】 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain"> 【ポート】 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-a</span><span class="token plain"> 【パスワード】 cluster nodes</span><br></div></code></pre></div></div>
<p>下図はノード情報です（この時点では slot 未割り当て）：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-25-961e8f1293208ec3c1845e699dff0863.webp" width="1365" height="196" class="img_ev3q"></p>
<p>slot の割り当てを実行します（各 master が担当する slot スロットが確認できます）：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-28-05b75092278370a56d2f2cc44a0858fe.webp" width="1600" height="379" class="img_ev3q"></p>
<p>値の挿入を試します：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-27-23c5475a76cba619e960379c596931d2.webp" width="932" height="289" class="img_ev3q"></p>
<p>UI ツールで値が保存されたかを確認します。クラスタの任意のノードに接続すれば OK です：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-29-5703e1fbebb16ffc84c54bbb1e248717.webp" width="555" height="553" class="img_ev3q"></p>
<p>Redis クラスタの構築に成功したことが確認できます。</p>
<p>spring-boot からクラスタに接続します：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-08-image-30-a295ea9fd270c78888bafa3e889a27dd.webp" width="712" height="561" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/08/09/redis-zhu-cong-shao-bing-ji-qun-mo-shi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>レプリケーションモードは設定が最もシンプルですが、マスターノードがダウンするとクラスタは書き込み能力を失うため、他のモードの基盤として使われることがほとんどです。Sentinel モードはレプリケーションの上に自動フェイルオーバーを補うもので、多くのビジネスシーンではこれで十分です。Cluster モードは書き込み能力とストレージ容量の水平スケーリングをもたらしますが、データスキューやノードをまたぐコマンドの制限といった新たな問題も持ち込みます。選定の際はまずデータ量と書き込み負荷を評価するべきで、最初からいきなり Cluster を導入する必要はありません。</p>]]></content:encoded>
            <category>Redis</category>
        </item>
        <item>
            <title><![CDATA[Serverlessアーキテクチャについて]]></title>
            <link>https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao</link>
            <guid>https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao</guid>
            <pubDate>Mon, 04 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[FaaS の実行モデルから出発し、Serverless の従量課金と自動スケーリングのメリットを整理。ベンダーロックインとコールドスタートという 2 つのよくある課題を分析し、適用シナリオを判断するための考え方を示します。]]></description>
            <content:encoded><![CDATA[<p>最近、チームのデプロイ方針を整理する中で、Serverless をあらためて見直す機会がありました。この記事では、このアーキテクチャに対する私の理解——どんな問題を解決するのか、裏側の実行メカニズムはどうなっているのか、どんなシナリオに向いていて、どんなシナリオでは慎重になるべきか——を記録しておきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>この記事を書くきっかけは単純です。私たちのチームには低頻度のタスクがたくさんあります——定期実行スクリプト、イベントのコールバック、たまに発生するファイル処理など。それぞれがサーバー 1 台、あるいは半分を占有し、ほとんどの時間 CPU は遊んでいるのに、マシン代と運用の手間は一切減りません。この「リソースは常駐、負荷はまばら」という矛盾こそ、Serverless が解決しようとしている問題です。</p>
<p>Serverless(サーバーレスアーキテクチャ)は、近年クラウドコンピューティング分野で徐々に注目を集めている方向性です。そのコアコンセプトは、<strong>開発者はサーバーのデプロイ、運用、スケーリングを気にする必要がなく、ビジネスロジックの開発だけに集中すればよい</strong>、というものです。</p>
<p>補足しておくと、「サーバーレス」とは本当にサーバーが存在しないという意味ではなく、サーバーが開発者から見えなくなるということです。サーバーは依然として存在しますが、クラウドベンダーが一括してスケジューリングと保守を行い、開発者が向き合う抽象レイヤーが「マシン」から「関数」に変わるのです。</p>
<p>従来のアーキテクチャでは、システムをリリースするには通常、サーバーの準備、環境のデプロイ、ネットワークの設定、リソースの監視、そしてスケーリングへの対応が必要で、これらの作業には追加の運用コストがかかります。一方 Serverless アーキテクチャでは、これらのインフラはすべてクラウドベンダーが管理を担い、開発者は関数のコードを書いてデプロイするだけで実行できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="serverlessをクラウドで使う">Serverlessをクラウドで使う<a href="https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao#serverless%E3%82%92%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%81%A7%E4%BD%BF%E3%81%86" class="hash-link" aria-label="Serverlessをクラウドで使う への直接リンク" title="Serverlessをクラウドで使う への直接リンク" translate="no">​</a></h2>
<p>Serverless は通常、<strong>クラウドファンクション(Function as a Service、FaaS)</strong> の形で提供されます。例えば Alibaba Cloud Function Compute(FC)、AWS Lambda、Tencent Cloud SCF などです。開発者はコードをアップロードするだけで、リクエストがトリガーされるとプラットフォームが自動的に関数を実行し、実際の呼び出し回数と実行時間に基づいて課金されます。</p>
<p>その実行モデルは、1 本のチェーンとしてシンプルにまとめられます:<strong>イベントソースがトリガー → プラットフォームがインスタンスをスケジューリング → 関数を実行 → インスタンスを回収</strong>。イベントソースは HTTP リクエスト、メッセージキュー、オブジェクトストレージのファイル変更のほか、タイマーでも構いません。プラットフォームはイベントを受け取ると、隔離された実行環境(通常はコンテナか軽量サンドボックス)に関数コードをロードして実行します。実行終了後、環境は再利用のためにしばらく保持され、長時間呼び出しがなければ回収されます。</p>
<p>このモデルがもたらす最大の変化は、<strong>アプリケーションがサーバーリソースを長期占有するのではなく、必要なときだけ実行できる</strong>ことです。</p>
<p>課金方式もそれに伴って変わります。従来のサーバーはトラフィックの有無にかかわらず「保有時間」に対して支払いますが、FaaS は「実際の実行」に対して支払い、通常は呼び出し回数と実行時間(割り当てたメモリスペックを掛けたもの)で計算されます。負荷がまばらであるほど、このモデルのコスト優位性は顕著になります。</p>
<p>例えば、次のような業務シナリオです:</p>
<ul>
<li class="">定期タスクの処理</li>
<li class="">Webhook イベントのトリガー</li>
<li class="">画像処理</li>
<li class="">データ変換</li>
</ul>
<p>これらのタスクには常時稼働のサーバーは必要なく、Serverless を使えばイベント発生時に関数を起動してタスクを実行できるため、リソースコストを大幅に節約できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="serverlessの弾力性">Serverlessの弾力性<a href="https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao#serverless%E3%81%AE%E5%BC%BE%E5%8A%9B%E6%80%A7" class="hash-link" aria-label="Serverlessの弾力性 への直接リンク" title="Serverlessの弾力性 への直接リンク" translate="no">​</a></h2>
<p>Serverless の重要な特徴のひとつが<strong>自動スケーリング機能</strong>です。システムへのリクエストが急増すると、プラットフォームは自動的により多くの関数インスタンスを起動してリクエストを処理し、トラフィックが減れば自動的にリソースを解放します。この一連のプロセスに人手の介入は不要で、分散性と耐障害性を最初から備えています。</p>
<p>この弾力性の裏側にある仕組みは、プラットフォームが「1 回の呼び出し」をスケジューリングの基本単位としていることです。各関数インスタンスは同時に処理するリクエスト数が通常限られており、リクエストが増えれば単一インスタンスに負荷を積み上げるのではなく、水平方向にインスタンスを追加起動します。インスタンス自体がステートレスで、いつでも作成・破棄できるため、スケールアウトや障害対応はプラットフォームレベルの日常的なオペレーションとなり、業務側が自前で設計する必要はなくなります。</p>
<p>小規模プロジェクトやスタートアップチームにとって、このモデルは非常に相性が良いものです。従来のモデルでは、アクセス量がごくわずかでもサーバーを長期的に維持する必要がありましたが、Serverless は呼び出し回数に応じた課金なので、初期のデプロイ・運用コストを大幅に下げられます。例えば Alibaba Cloud Function Compute(FC)は現在、毎月約 <strong>100 万回の無料呼び出し枠</strong>を提供しており、多くの小規模アプリケーションにはこれで十分です。</p>
<p>とはいえ、Serverless アーキテクチャも完璧ではなく、現時点ではトレードオフを考慮すべき問題がいくつか残っています。</p>
<p>最もよく挙げられるのが<strong>ベンダーロックインの問題</strong>です。Serverless モデルでは、関数は OSS、RDS、メッセージキュー、ログシステムなど、クラウドプラットフォームが提供するサービスに深く依存しがちです。これらの機能を大量に使ったシステムを他のクラウドベンダーに移行しようとすると、コードにある程度の改修が必要になることが少なくありません。このロックインはコードレベルだけの話ではありません——トリガーの設定、権限体系、ログや監視の連携方法はベンダーごとにやり方が異なり、移行コストは想像以上に分散して発生します。</p>
<p>もうひとつの典型的な問題が<strong>コールドスタート(Cold Start)</strong> です。Serverless の関数は通常コンテナやサンドボックス環境で動作するため、長時間呼び出されないとプラットフォームがリソースを解放します。新しいリクエストが来たときには実行環境を再起動する必要があり、このプロセスが一定のレイテンシをもたらします。</p>
<p>コールドスタートにかかる時間は、おおよそ次の要素の積み重ねです:プラットフォームによる実行環境のスケジューリングと起動、関数コードと依存関係のロード、ランタイムとフレームワークの初期化処理。ランタイムが重く、依存が多いほどこのプロセスは遅くなります——同じビジネスロジックでも、Java のように仮想マシンの起動が必要なランタイムは、スクリプト言語よりコールドスタートが顕著になりがちなのはこのためです。</p>
<p>HTTP リクエストやイベントトリガーのシナリオでは、関数の初回呼び出し時にレスポンスの遅さをはっきり感じることがあります。レイテンシに非常に敏感な業務であれば、<strong>予約インスタンス(ウォームアップリソース)</strong> を購入することでコールドスタートの影響を減らせますが、その分リソースコストは増えます——本質的には「一部を常駐させる」ことでレイテンシと引き換えにするわけで、従来モデルのコストカーブに逆戻りすることになるため、トラフィックの特性に応じてコストを試算する必要があります。</p>
<p>総じて、Serverless は次のようなシナリオに向いています:</p>
<ul>
<li class="">イベント駆動型アプリケーション</li>
<li class="">短時間で完了するタスク</li>
<li class="">トラフィックが不安定なサービス</li>
<li class="">初期規模が小さいプロジェクト</li>
</ul>
<p>一方、長期稼働、高パフォーマンス、あるいは高度にカスタマイズされた環境が必要なシステム——例えば大規模データベースサービスや継続実行される計算タスクなど——には、依然として従来のサーバーアーキテクチャの方が適しています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>これまでの分析を踏まえて、業務を Serverless に移行するつもりなら、事前に考えておくべきポイントがいくつかあります。</p>
<p>1)<strong>関数はステートレスに書くこと。</strong> インスタンスはいつ回収されてもおかしくないため、ローカルメモリやローカルディスクに書き込んだ状態は信頼できません。永続化が必要なデータは外部ストレージやキャッシュサービスに置くべきです。</p>
<p>2)<strong>実行時間の制限に注意すること。</strong> FaaS プラットフォームには通常、1 回の実行に対するタイムアウト上限があります。長時間かかるタスクは複数の関数に分割して連結するか、別のコンピューティング形態への切り替えを検討しましょう。</p>
<p>3)<strong>コールドスタートがリクエストチェーンに与える影響を評価すること。</strong> 外部向けの同期 HTTP インターフェースはレイテンシに敏感で、コールドスタートの影響が最も直接的に出ます。一方、非同期タスクや定期タスクはほぼ影響を感じないため、こちらを優先的に移行するとよいでしょう。</p>
<p>4)<strong>クラウドサービスへの依存の深さをコントロールすること。</strong> クラウドサービスにまったく依存しないのは現実的ではありませんが、OSS やメッセージキューといったサービスへの呼び出しをコード内で独立したアダプター層に集約しておけば、いざ移行するときの改修範囲をかなり小さくできます。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>現実的なやり方としては、まず非コアの非同期タスク(定期クリーンアップや画像圧縮など)を 1〜2 個選んで移行して試し、課金・ログ・アラートの一連のフローが問題なく回ることを確認してから、利用範囲を広げるかどうかを判断するのがおすすめです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/07/04/serverless-de-si-kao#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>まとめると、Serverless は従来のアーキテクチャを完全に置き換えるものではなく、新しいコンピューティングモデルです。そのコアバリューは、<strong>開発者がインフラではなく、ビジネスそのものにより集中できるようにする</strong>ことにあります。</p>
<p>「オンデマンド実行・自動スケーリング」をプラットフォームの機能として提供する代わりに、コールドスタート、実行時間の制限、そしてある程度のベンダーロックインを受け入れることになります。採用するかどうかは、アーキテクチャの新しさではなく、業務のトラフィック形態とレイテンシ要件によって決まります。</p>
<p>クラウドコンピューティングの発展に伴い、Serverless 機能を提供するプラットフォームはますます増えています。将来のシステムアーキテクチャは、次のような組み合わせへと徐々に進化していく可能性が高いでしょう:</p>
<p><strong>Serverless + マイクロサービス + コンテナ化</strong>の組み合わせモデルです。</p>]]></content:encoded>
            <category>Java</category>
            <category>K8S</category>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Linux ディストリビューションの比較]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi</guid>
            <pubDate>Fri, 24 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Debian、CentOS、Ubuntu、openSUSE といった代表的な Linux ディストリビューションの位置づけ、パッケージ管理、安定性の違いを比較し、私が本番環境で選んだ理由を記録します。]]></description>
            <content:encoded><![CDATA[<p>linux システムにはさまざまなディストリビューションがあり、それぞれ異なる組織が自分たちのディストリビューションを保守しています。今回のノートでは、私が理解している範囲でいくつかのディストリビューションの違いを記録します。</p>
<!-- -->
<p>なぜわざわざ一本の記事にする価値があるのか。それは、ディストリビューション選びというものが、インストールしたその瞬間に、その後数年間の運用体験を決めてしまうことが多いからです。どのパッケージマネージャーを使うのか、リポジトリのソフトウェアはどれくらい新しいのか、ローリングアップデートできるのかメジャーバージョン移行しかできないのか、問題が起きたときコミュニティのドキュメントは調べやすいのか。これらの違いは普段はあまり意識されませんが、本番環境でのトラブルシューティングやシステムアップグレードの際に一気に噴出します。だからこそ、主要なディストリビューションの特徴を先に整理しておくほうが、問題が起きてからシステムを乗り換えるよりずっと割に合うのです。</p>
<p>まず背景を少し補足します。Linux ディストリビューションは本質的にどれも「Linux カーネル + パッケージ管理システム + 一式のデフォルトソフトウェアと設定」の組み合わせです。カーネルは同じ上流を共有しており、本当に差がつくのはパッケージ管理エコシステム（Debian 系は apt/dpkg、Red Hat 系は yum/dnf/rpm、SUSE 系は zypper/rpm）とリリース戦略——安定性を追求してソフトウェアバージョンを数年間凍結するのか、それとも新機能を追求して頻繁に更新するのか——です。この二点を理解すれば、以下の各ディストリビューションのトレードオフは理解しやすくなります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="debian">Debian<a href="https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi#debian" class="hash-link" aria-label="Debian への直接リンク" title="Debian への直接リンク" translate="no">​</a></h2>
<p>1、Debian は現在、私が最も好んで使っているディストリビューションです。バージョン 10 は非常に安定しており、システムのリソース使用量もとても小さい。現在、私が本番環境で選んでいるものです。</p>
<p>Debian の安定性はそのリリースの仕組みに由来します。パッケージはまず unstable、testing という二つの段階で長期間磨き上げられてから、ようやく stable ブランチに入ります。stable は一度リリースされるとソフトウェアバージョンはほぼ凍結され、セキュリティパッチと重要な修正だけを受け取ります。代償としてリポジトリのバージョンは古めですが、その引き換えに、サーバー上で「更新したらシステムが壊れた」という事態はめったに起きません。加えてデフォルトインストールが非常にミニマルで、余計なサービスをプリインストールしないため、メモリもディスクも使用量が控えめです。これが私が本番環境に Debian を据えている主な理由です。</p>
<p><a href="https://www.debian.org/" target="_blank" rel="noopener noreferrer" class="">Debian -- The Universal Operating System</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="centos">CentOS<a href="https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi#centos" class="hash-link" aria-label="CentOS への直接リンク" title="CentOS への直接リンク" translate="no">​</a></h2>
<p>2、CentOS は Red Hat の商用有償オペレーティングシステムから分岐したバージョンで、商用 OS を無料にしたものです。機能も安定性も現在なかなか優れていますが、どこかしらで機能が削られているのは間違いないでしょう。何しろ向こうには有償のシステムがあるのですから。さらに CentOS はすでに保守を終了しており、元の作者である Gregory Kurtzer は新プロジェクトとして Rocky Linux オペレーティングシステムを立ち上げています。</p>
<p>この歴史を少し詳しく説明します。CentOS はもともと RHEL（Red Hat Enterprise Linux）のオープンソースコードを再コンパイルし、商標を取り除いて無料で公開したものでした。そのため RHEL と高い互換性を実現でき、多くの企業が無料の RHEL として使っていました。その後 Red Hat は戦略を転換し、CentOS を CentOS Stream へと方向転換させました。「RHEL の下流の安定した複製」から「RHEL の上流のローリングプレビュー」への変化であり、安定性を求める本番ユーザーにとっては別物になったも同然です。そこでコミュニティには Rocky Linux、AlmaLinux といった後継プロジェクトが登場し、RHEL の下流互換という従来路線を引き継いでいます。既存のビジネスが CentOS エコシステムに強く依存している場合、これらの代替品への移行は Debian 系への乗り換えより通常はコストが低くなります。</p>
<p><a href="https://www.centos.org/" target="_blank" rel="noopener noreferrer" class="">The CentOS Project</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ubuntu">Ubuntu<a href="https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi#ubuntu" class="hash-link" aria-label="Ubuntu への直接リンク" title="Ubuntu への直接リンク" translate="no">​</a></h2>
<p>3、Ubuntu は Debian をベースに開発されたオペレーティングシステムで、機能はより豊富で強力ですが、安定性とシステムリソースの使用量では Debian にはるかに及びません。</p>
<p>Ubuntu は Canonical 社が主導しており、Debian をベースにより積極的な選択をしています。ソフトウェアバージョンはより新しく、ドライバーサポートはより充実し、デスクトップ体験はすぐに使える状態で提供され、さらに固定周期の LTS（長期サポート）版もあります。得意とする場面はデスクトップと開発マシンです。新しいハードウェアへの対応が速く、コミュニティのチュートリアルが多く、問題に遭遇してもほぼ必ず既存の答えが検索で見つかります。しかしプリインストールされるコンポーネントやデフォルトのサービスが多いぶん、同じスペックのマシンでは Debian よりも多くのリソースを消費します。いくつかのサービスを動かすだけのサーバーにとっては、この「豊富さ」はむしろ負担です。</p>
<p><a href="https://ubuntu.com/" target="_blank" rel="noopener noreferrer" class="">Enterprise Open Source and Linux | Ubuntu</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="opensuse">openSUSE<a href="https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi#opensuse" class="hash-link" aria-label="openSUSE への直接リンク" title="openSUSE への直接リンク" translate="no">​</a></h2>
<p>4、openSUSE はドイツで最も人気のあるオペレーティングシステムだと言われており、その安定性はかなり優れているはずです。機会があれば Debian との違いを試してみるつもりです。</p>
<p>openSUSE の背後には SUSE 社がいて、Red Hat と似た「商用版 + コミュニティ版」路線を歩んでいます。特筆すべき特徴が二つあります。一つは YaST というグラフィカルなシステム管理ツールで、ネットワーク、パーティション、サービス設定を一箇所に集約しており、設定ファイルを手書きしたくない人にとても優しい作りです。もう一つは Leap（固定バージョン、商用版 SLE に対応）と Tumbleweed（ローリングアップデート）の二本立てで、安定を取るか最新を試すかをニーズに応じて選べます。中国で使っている人は比較的少なく、中国語の資料は Debian/Ubuntu ほど見つけやすくないので、この点は選定時に考慮に入れるべきです。</p>
<p><a href="https://www.suse.com/" target="_blank" rel="noopener noreferrer" class="">SUSE - Open Source Solutions for Enterprise Servers &amp; Cloud</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）保守が終了したシステムを本番環境で使わないこと。セキュリティアップデートのないディストリビューションでは、新たに公開される脆弱性の一つ一つに無防備でさらされるしかありません。CentOS の教訓はまさに目の前にあります。選定前にバージョンのサポート期間を必ず確認しましょう。</p>
<p>2）系統をまたぐディストリビューション移行のコストは高いです。apt と yum/dnf の違いはコマンド名だけではなく、パッケージの命名、分割方法、設定ファイルのパスまで異なり、スクリプトも運用の習慣もそれに合わせて変える必要があります。同じ系統内での移行（CentOS から Rocky など）はずっとスムーズです。</p>
<p>3）「ソフトウェアが新しい」と「システムが安定している」は多くの場合、相反します。Debian stable で新しいバージョンのソフトウェアを使いたいなら、まず公式の backports リポジトリかコンテナ方式を検討しましょう。サードパーティのリポジトリを混ぜて強引にアップグレードするのは、依存関係を壊しやすいのでできるだけ避けるべきです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/24/linux-xi-tong-bu-tong-fa-xing-ban-dui-bi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>これらのディストリビューションに絶対的な優劣はなく、あるのは場面との適合だけです。安定性と低リソース使用を求めるサーバーには、私は Debian を選びます。RHEL エコシステムに強く依存しているなら、Rocky Linux のような CentOS の後継を検討するとよいでしょう。デスクトップと開発マシンには、Ubuntu のすぐ使える体験のほうが楽です。openSUSE は今後実際に触って比較するのを楽しみにしています。一つの系統を選び、そのパッケージ管理とリリースのリズムを熟知するほうが、頻繁にシステムを乗り換えるよりずっと価値があります。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Bloodborne（ブラッドボーン）]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou</guid>
            <pubDate>Fri, 17 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[『Bloodborne（ブラッドボーン）』は FromSoftware が制作したダークな作風のアクション RPG です。その物語の背景は濃厚なクトゥルフ神話体系の上に築かれており、世界観全体が神秘と狂気、未知に満ちています。]]></description>
            <content:encoded><![CDATA[<p>『Bloodborne（ブラッドボーン）』は FromSoftware が制作したダークな作風のアクション RPG です。その物語の背景は濃厚な <strong>クトゥルフ神話体系</strong>の上に築かれており、世界観全体が神秘と狂気、未知に満ちています。</p>
<!-- -->
<p>本作の主な舞台は、遥か東方の山岳地帯にある古い都市——**ヤーナム（Yharnam）<strong>です。ヤーナムは極度に閉鎖的で排他的な都市でありながら、</strong>「血の医療（Blood Healing）」**と呼ばれる神秘的な医療技術によって世界にその名を知られています。この技術はほとんどあらゆる病を治せると伝えられ、無数の病人や旅人、研究者たちが血の医療によって病苦から逃れることを願い、遠路はるばるこの地を訪れました。</p>
<p>しかし、血の医療が広く使われるにつれ、恐るべき疫病がヤーナムに蔓延し始めます——**獣の病（Beast Scourge）**です。感染者は次第に理性を失い、最後には血に飢えた狂乱の獣と化してしまいます。都市全体はこうして混乱と恐怖に陥り、プレイヤーが演じる「狩人」は、まさにこのような状況の中でヤーナムへとやって来るのです。</p>
<p>物語が進むにつれ、プレイヤーは次第に気づいていきます。<strong>獣化は単なる病気ではなく、より高次の存在と関わりがあるのだ</strong>、と。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="上位者と宇宙の真相">上位者と宇宙の真相<a href="https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou#%E4%B8%8A%E4%BD%8D%E8%80%85%E3%81%A8%E5%AE%87%E5%AE%99%E3%81%AE%E7%9C%9F%E7%9B%B8" class="hash-link" aria-label="上位者と宇宙の真相 への直接リンク" title="上位者と宇宙の真相 への直接リンク" translate="no">​</a></h2>
<p>『Bloodborne』の世界では、太古の時代に **「上位者（Great Ones）」**と呼ばれる神秘的な生命が存在していました。これらの存在は伝統的な意味での神ではなく、宇宙の深淵から来た高次元の生命です。人類の理解を遥かに超えた知恵と力を持ち、その姿はしばしば名状しがたいものです。</p>
<p>クトゥルフ神話体系では、類似した存在は通常こう呼ばれます。</p>
<ul>
<li class="">旧支配者（Old Ones）</li>
<li class="">古の神々（Elder Gods）</li>
<li class="">外なる神（Outer Gods）</li>
</ul>
<p>これらの上位者は太古の昔からこの世界に存在していましたが、時の流れとともにその肉体は次第に消滅し、強大な <strong>精神と意識</strong>だけが今なお宇宙をさまよっています。</p>
<p>上位者は<strong>子孫を残すこと</strong>を渇望していますが、その形態と生命構造があまりに特殊であるため、通常の方法では生殖できないことが多く、人類や他の生命を媒介として新たな「神の子」を宿そうと試みます。これこそが、物語全体の最も重要な起点となるのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-src-http___img2.ali213.net_picfile_News_2015_09_15_2015091553403282.jpg-refer-http___img2.ali213-7d8f63a869338c1cfc657c0e23b4b560.webp" width="1200" height="1696" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="トゥメル文明の発見">トゥメル文明の発見<a href="https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou#%E3%83%88%E3%82%A5%E3%83%A1%E3%83%AB%E6%96%87%E6%98%8E%E3%81%AE%E7%99%BA%E8%A6%8B" class="hash-link" aria-label="トゥメル文明の発見 への直接リンク" title="トゥメル文明の発見 への直接リンク" translate="no">​</a></h2>
<p>人類文明の初期、**トゥメル（Pthumeru）**と呼ばれる古代文明が、地下に高度に発達した都市を築いていました。トゥメル人は長きにわたり地下遺跡で暮らし、古い遺物と神秘の力について膨大な研究を重ねました。ある探索の過程で、彼らは <strong>上位者の遺骸</strong>を発見し、そこから特殊な物質を抽出します。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="古い血old-blood">古い血（Old Blood）<a href="https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou#%E5%8F%A4%E3%81%84%E8%A1%80old-blood" class="hash-link" aria-label="古い血（Old Blood） への直接リンク" title="古い血（Old Blood） への直接リンク" translate="no">​</a></h3>
<p>この血液は信じがたい力を持っています。</p>
<ul>
<li class="">病をたちどころに癒すことができる</li>
<li class="">人体を強化できる</li>
<li class="">さらには人の精神的知覚さえ高められる</li>
</ul>
<p>しかし同時に、深刻な副作用ももたらします。古い血に長く触れた人間は次第に理性を失い、最終的に <strong>獣化</strong>してしまうのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="教会と血の医療の興隆">教会と血の医療の興隆<a href="https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou#%E6%95%99%E4%BC%9A%E3%81%A8%E8%A1%80%E3%81%AE%E5%8C%BB%E7%99%82%E3%81%AE%E8%88%88%E9%9A%86" class="hash-link" aria-label="教会と血の医療の興隆 への直接リンク" title="教会と血の医療の興隆 への直接リンク" translate="no">​</a></h2>
<p>トゥメル遺跡が再発見された後、ヤーナムには強大な組織が設立されました。**医療教会（Healing Church）**です。教会は古い血を利用して有名な <strong>血の医療技術</strong>を発展させ、ヤーナムを瞬く間に世界に名だたる医療の聖地へと押し上げました。大勢の病人がここで治療を受けに訪れ、都市もそれによって繁栄します。</p>
<p>しかし、血の医療の使用が頻繁になるにつれ、獣の病も都市の中で猛威を振るい始めます。この問題を解決するため、教会は密かに新たな組織を設立しました。**狩人組織（Hunters）**です。狩人の使命はただ一つ。</p>
<blockquote>
<p>夜に、獣と化した者たちを狩ること。</p>
</blockquote>
<p>これがいわゆる **「狩りの夜（The Hunt）」**です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="夢と狩人の宿命">夢と狩人の宿命<a href="https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou#%E5%A4%A2%E3%81%A8%E7%8B%A9%E4%BA%BA%E3%81%AE%E5%AE%BF%E5%91%BD" class="hash-link" aria-label="夢と狩人の宿命 への直接リンク" title="夢と狩人の宿命 への直接リンク" translate="no">​</a></h2>
<p>プレイヤーが演じる狩人は、血の医療を受けた後、神秘的な空間へと引き込まれます。**狩人の夢（Hunter's Dream）**です。ここは上位者の力によって創られた特殊な領域であり、狩人が繰り返し蘇る場所でもあります。狩人は死ぬたびに夢の中で再び目覚め、ふたたびヤーナムへと赴いて狩りを続けるのです。</p>
<p>しかし物語が進むにつれ、狩人は次第に気づいていきます。ヤーナムの災厄、血の医療の秘密、上位者の存在、そして夢そのものにも、より深い真実が隠されていることに。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bloodborne-の真のテーマ">Bloodborne の真のテーマ<a href="https://tanzhuo.xyz/ja/2022/06/17/xie-yuan-zu-zhou#bloodborne-%E3%81%AE%E7%9C%9F%E3%81%AE%E3%83%86%E3%83%BC%E3%83%9E" class="hash-link" aria-label="Bloodborne の真のテーマ への直接リンク" title="Bloodborne の真のテーマ への直接リンク" translate="no">​</a></h2>
<p>表面的には、『Bloodborne』は狩人と獣の戦いを描いた物語です。しかしより深い層で本当に問いかけているのは、次のようなことです。</p>
<ul>
<li class="">未知の力に対する人類の <strong>貪欲</strong></li>
<li class="">神と宇宙の真相への <strong>探究</strong></li>
<li class="">そして <strong>理性と狂気の境界</strong></li>
</ul>
<p>プレイヤーが世界の秘密へと深く踏み込んでいくにつれ、人類はある残酷な事実に気づいていきます。<strong>宇宙は人類の想像よりも遥かに広大で、そして遥かに恐ろしい</strong>、と。</p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[Vue UI ライブラリ]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku</guid>
            <pubDate>Tue, 14 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Vue エコシステムでよく使われる UI コンポーネントライブラリを整理:View Design、Element Plus、Vuetify、Ant Design Vue、Vuesax。それぞれの位置づけと適したユースケースを添えます。]]></description>
            <content:encoded><![CDATA[<p>Vue プロジェクトを作るとき、UI コンポーネントライブラリの選定はたいてい着手前の最初の決断になります。うまく選べばボタン、フォーム、モーダルの繰り返し作業を大幅に省けますし、選び間違えると後からライブラリを乗り換えるコストはかなり高くつきます。ここでは、普段よく目にする Vue UI ライブラリをいくつか整理し、それぞれの位置づけと印象を記録しておきます。今後の選定時に見返せるように。</p>
<!-- -->
<p>まず、選定時に私が普段注目するポイントを挙げておきます。コンポーネントのカバレッジが十分か(テーブル、フォーム、日付選択といった高頻度コンポーネントの完成度)、ドキュメントとコミュニティが活発か、デザインスタイルがプロダクトの方向性に合っているか、そしてプロジェクトが継続的にメンテナンスされているか。以下、この視点でひとつずつ見ていきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="view-design">View Design<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#view-design" class="hash-link" aria-label="View Design への直接リンク" title="View Design への直接リンク" translate="no">​</a></h2>
<p>Vue.js 3 ベースのエンタープライズ向け UI コンポーネントライブラリ兼フロントエンドソリューションで、数万人の開発者に利用されています。</p>
<p>その前身は iView で、Vue 2 の時代からすでに多くのユーザーを獲得しており、スタイルはシンプルな管理画面(バックオフィス)路線です。コンポーネントライブラリ本体のほかに、公式が付属のテンプレートとツールも提供しており、バラバラのコンポーネントではなく管理画面のソリューション一式が欲しいユースケースに向いています。</p>
<p><a href="https://www.iviewui.com/" target="_blank" rel="noopener noreferrer" class="">iView / View Design 一套企业级 UI 组件库和前端解决方案</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="element-ui">Element UI<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#element-ui" class="hash-link" aria-label="Element UI への直接リンク" title="Element UI への直接リンク" translate="no">​</a></h2>
<p>Vue 3 ベースの、デザイナーと開発者向けのコンポーネントライブラリです。</p>
<p>Element シリーズはおそらく中国国内の Vue プロジェクトで最も登場頻度の高いコンポーネントライブラリで、Vue 3 対応バージョンは Element Plus と呼ばれます。強みは中国語ドキュメントの充実、コンポーネントの網羅性、コミュニティの大きさで、問題に遭遇してもたいてい既存の回答が検索で見つかります。中規模・大規模の管理システムを作るなら、ほぼデフォルトの選択肢のひとつです。</p>
<p><a href="https://element-plus.org/" target="_blank" rel="noopener noreferrer" class="">A Vue 3 UI Framework | Element Plus</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vuetify">Vuetify<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#vuetify" class="hash-link" aria-label="Vuetify への直接リンク" title="Vuetify への直接リンク" translate="no">​</a></h2>
<p>Vuetify は Vue UI ライブラリで、美しく手作りされたマテリアルコンポーネントを備えています。デザインスキルは一切不要——優れたアプリケーションを作るために必要なものがすべて手の届くところにあります。</p>
<p>Google の Material Design 仕様に厳格に準拠しており、ビジュアルスタイルは統一感があり、はっきりとした Material の特徴を持っています。プロダクト自体が Material スタイルで行くつもりなら、あるいは海外ユーザー向けなら、Vuetify は中国国内のライブラリよりも適しています。逆に、独自のビジュアル体系へ大幅にカスタマイズしたい場合は、変更にかなり手間がかかります。</p>
<p><a href="https://vuetifyjs.com/" target="_blank" rel="noopener noreferrer" class="">Vuetify — A Material Design Framework for Vue.js</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ant-design-vue">Ant Design Vue<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#ant-design-vue" class="hash-link" aria-label="Ant Design Vue への直接リンク" title="Ant Design Vue への直接リンク" translate="no">​</a></h2>
<p>Ant Design 仕様に準拠して開発された Vue UI ライブラリで、リッチでインタラクティブなユーザーインターフェースを構築するための高品質なコンポーネントとデモが揃っています。</p>
<p>Ant Design 自体は Ant(螞蟻)チームが React エコシステム向けに策定したデザインシステムで、Ant Design Vue はその Vue 側の実装です。利点は、デザイン仕様が大量の中規模・大規模バックオフィスプロダクトで検証済みであることと、テーブルやフォームといった複雑なコンポーネントの機能が非常に充実していることです。チーム内に React と Vue のプロジェクトが併存しているなら、同じデザイン言語を使うことでコミュニケーションコストも下げられます。</p>
<p><a href="https://www.antdv.com/" target="_blank" rel="noopener noreferrer" class="">Ant Design Vue — An enterprise-class UI components based on Ant Design and Vue.js</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vuesax">Vuesax<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#vuesax" class="hash-link" aria-label="Vuesax への直接リンク" title="Vuesax への直接リンク" translate="no">​</a></h2>
<p>スタイルとアニメーションは素晴らしいのですが、残念ながらすでにメンテナンスが停止しています。</p>
<p>Vuesax のコンポーネントは、配色とインタラクションアニメーションにおいて確かに上記のどのライブラリよりも遊び心があり、ビジュアルに若々しさを求めるプロジェクトに向いています。しかしメンテナンス停止は、新しいバージョンの Vue への互換性もバグ修正も保証されないことを意味します。新規プロジェクトでの採用はもうおすすめしません。ここに挙げたのは主に参考としてです。</p>
<p><a href="https://vuesax.com/" target="_blank" rel="noopener noreferrer" class="">Vuesax - Framework for Vuejs</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1)メンテナンス状況を先に確認すること。Vuesax のようにメンテナンスが停止したライブラリを新規プロジェクトに入れるのは、未来に地雷を埋めるようなものです。選定前にリポジトリのコミット履歴と issue への反応状況を見ておきましょう。</p>
<p>2)Vue 2 と Vue 3 のバージョンを見分けること。これらのライブラリの多くは Vue 2 から Vue 3 への移行を経ており、パッケージ名とドキュメントが 2 系統に分かれていることがよくあります(たとえば Element UI と Element Plus)。バージョンを間違えてインストールすると、そもそも動きません。</p>
<p>3)オンデマンドインポートを使うこと。UI ライブラリはどれもフルパッケージだとサイズが小さくありません。本番プロジェクトではオンデマンドロードを設定し、使用するコンポーネントだけをバンドルすることをおすすめします。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>デモや社内ツールを書くだけなら、ドキュメントが一番しっくりくるものを選べば十分で、悩む必要はありません。選定にコストをかけるべきなのは、長期的にメンテナンスしていく本番プロジェクトのほうです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/14/vue-chang-jian-uiku#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>中規模・大規模の管理システムなら迷わず Element Plus か Ant Design Vue。どちらもコミュニティは十分大きいです。Material スタイルが欲しい、あるいは海外向けなら Vuetify を検討。View Design は管理画面のソリューション一式が欲しいチームに向いています。Vuesax はメンテナンス停止のため参考程度に。選定に標準的な正解はなく、チームの習慣とプロダクトのスタイルに合っているものこそが良い選択です。</p>]]></content:encoded>
            <category>フロントエンド</category>
        </item>
        <item>
            <title><![CDATA[Linux ディレクトリ構造の分析]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi</guid>
            <pubDate>Mon, 13 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[FHS 規格を出発点に、Linux のルート直下にある各ディレクトリの役割を整理します。/bin、/etc、/var、/usr、/opt などにそれぞれ何を置くのか、なぜこう分かれているのか、そして混同しやすいディレクトリの見分け方まで。]]></description>
            <content:encoded><![CDATA[<p>初めて Linux サーバーにログインし、<code>ls /</code> で見慣れない短い名前のディレクトリがずらりと並んでいるのを見ると、たいていは少し面食らうものです。このノートでは、ルートディレクトリ配下のよく見かけるディレクトリの役割を一通り整理し、「何をどこに置くべきか」をはっきりさせます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜディレクトリはこういう形なのか">なぜディレクトリはこういう形なのか<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#%E3%81%AA%E3%81%9C%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AF%E3%81%93%E3%81%86%E3%81%84%E3%81%86%E5%BD%A2%E3%81%AA%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜディレクトリはこういう形なのか への直接リンク" title="なぜディレクトリはこういう形なのか への直接リンク" translate="no">​</a></h2>
<p>Linux のディレクトリレイアウトは適当な取り決めではなく、背後には FHS（Filesystem Hierarchy Standard、ファイルシステム階層標準）という規格があります。実行プログラム、設定、ログ、一時ファイルなど、各種類のファイルをどのディレクトリに置くべきかを定めたものです。こうすることの利点は直接的です——</p>
<ul>
<li class="">パッケージマネージャーはファイルをどこにインストールすべきかを把握でき、アンインストール時もきれいに削除できます。</li>
<li class="">運用担当が問題を調査するとき、設定やログの場所を推測する必要がなく、<code>/etc</code> と <code>/var/log</code> を見ればまず外しません。</li>
<li class="">ディストリビューション間でおおむね一貫性が保たれ、経験を持ち運べます。</li>
</ul>
<p>自分のマシンのルートディレクトリのレイアウトを見るには、コマンド 1 つで十分です。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ルートディレクトリ直下の第 1 階層のディレクトリを表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">ls</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-l</span><span class="token plain"> /</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="主要ディレクトリを順に見る">主要ディレクトリを順に見る<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#%E4%B8%BB%E8%A6%81%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%82%92%E9%A0%86%E3%81%AB%E8%A6%8B%E3%82%8B" class="hash-link" aria-label="主要ディレクトリを順に見る への直接リンク" title="主要ディレクトリを順に見る への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1プログラムとコマンド">1）プログラムとコマンド<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#1%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%A8%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89" class="hash-link" aria-label="1）プログラムとコマンド への直接リンク" title="1）プログラムとコマンド への直接リンク" translate="no">​</a></h3>
<p>/bin：よく使うコマンドとバイナリ実行プログラムが格納されています。<code>ls</code>、<code>cp</code>、<code>cat</code> のような、すべてのユーザーが使う基礎コマンドです。</p>
<p>/sbin：管理者ユーザーだけが実行できるコマンドとプログラムのフォルダで、典型的にはネットワーク設定、ディスクパーティション、システムのシャットダウン・再起動関連のツールです。一般ユーザーの <code>PATH</code> には通常含まれていません。</p>
<p>/usr：ユーザーアプリケーションとファイルを配置するフォルダディレクトリです。名前から "user" と誤解されがちですが、実際は Unix System Resources の意味です。システムにインストールされるソフトウェアの大部分はここにあり、<code>/usr/bin</code>、<code>/usr/lib</code>、<code>/usr/share</code> にそれぞれプログラム、ライブラリ、共有データが置かれます。多くのモダンなディストリビューションでは usr マージが行われており、<code>/bin</code> は実は <code>/usr/bin</code> を指すシンボリックリンクです。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># usr マージ済みのディストリビューションではシンボリックリンクの関係が確認できる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">ls</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-ld</span><span class="token plain"> /bin /sbin</span><br></div></code></pre></div></div>
<p>/opt：optional、つまり「選択可能」の意味で、サードパーティソフトウェアをインストールする場所です。パッケージマネージャーが管理する <code>/usr</code> と違い、<code>/opt</code> は完全なディレクトリ構造を持つ商用ソフトウェアや、手動で展開してインストールする大型ソフトウェアに向いています。ソフトウェアごとにサブディレクトリを 1 つ使うので、削除も手間いらずです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2設定とデバイス">2）設定とデバイス<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#2%E8%A8%AD%E5%AE%9A%E3%81%A8%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9" class="hash-link" aria-label="2）設定とデバイス への直接リンク" title="2）設定とデバイス への直接リンク" translate="no">​</a></h3>
<p>/etc：システムの設定ファイルとサブディレクトリ群を格納する専用の場所です。システムの挙動を変えるときは基本的にここをいじります。<code>/etc/fstab</code>、<code>/etc/hosts</code>、<code>/etc/ssh/</code> などです。システム設定をバックアップするなら、このディレクトリが要となります。</p>
<p>/dev：Device（デバイス）の略で、Linux システムに現在接続されている外部デバイスです。Linux は「すべてはファイルである」という設計に従っており、外部デバイスへのアクセスもファイルアクセスと同じ方式です。ディスクは <code>/dev/sda</code>、端末は <code>/dev/tty</code>、さらに <code>/dev/null</code> のような特殊デバイスもあります。</p>
<p>/boot：Linux システムを起動するためのカーネルファイルを格納しており、リンクファイルとイメージファイルが含まれます。ブートローダー（GRUB など）の設定もここにあります。このディレクトリは軽々しく触ってはいけません。誤ってファイルを削除するとシステムが起動しなくなる可能性があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3ユーザーデータ">3）ユーザーデータ<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#3%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%83%87%E3%83%BC%E3%82%BF" class="hash-link" aria-label="3）ユーザーデータ への直接リンク" title="3）ユーザーデータ への直接リンク" translate="no">​</a></h3>
<p>/home：ユーザーフォルダです。システムアカウントごとに異なる名前のフォルダが作成されます。たとえばユーザー alice のホームディレクトリは <code>/home/alice</code> です。</p>
<p>/root：管理者ユーザーのホームディレクトリです。<code>/home</code> の下にないことに注意してください。こうしておけば、<code>/home</code> をマウントしているパーティションに問題が起きても、root ユーザーは正常にログインして障害対応ができます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4ランタイムと可変データ">4）ランタイムと可変データ<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#4%E3%83%A9%E3%83%B3%E3%82%BF%E3%82%A4%E3%83%A0%E3%81%A8%E5%8F%AF%E5%A4%89%E3%83%87%E3%83%BC%E3%82%BF" class="hash-link" aria-label="4）ランタイムと可変データ への直接リンク" title="4）ランタイムと可変データ への直接リンク" translate="no">​</a></h3>
<p>/var：頻繁に変動するファイル、たとえばシステムログなどのファイルが格納されています。<code>/var/log</code> にはログ、<code>/var/lib</code> にはサービスの永続データ（データベースファイルはよくここにあります）、<code>/var/cache</code> にはキャッシュが置かれます。ディスクが満杯になったとき、最初に確認すべきなのはたいていここです。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># /var 配下で最も容量を消費しているサブディレクトリを特定する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">du</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-sh</span><span class="token plain"> /var/* </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-rh</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">head</span><br></div></code></pre></div></div>
<p>/tmp：一時ファイルディレクトリです。プログラムの実行中に生成される一時ファイルを格納し、どのユーザーでも書き込めます。多くのディストリビューションは、長期間アクセス・変更・修正されていないファイル（たとえば 10 日）を定期的に掃除するので、残しておきたいものをここに置いてはいけません。</p>
<p>/run：一時ファイルディレクトリで、システム起動以降の一時的な情報、たとえばサービスの PID ファイルや socket ファイルを保存します。システムを再起動すると、このディレクトリ配下のファイルは掃除されます。通常はメモリ上にマウントされており、本質的に揮発性です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="混同しやすいディレクトリの組み合わせ">混同しやすいディレクトリの組み合わせ<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#%E6%B7%B7%E5%90%8C%E3%81%97%E3%82%84%E3%81%99%E3%81%84%E3%83%87%E3%82%A3%E3%83%AC%E3%82%AF%E3%83%88%E3%83%AA%E3%81%AE%E7%B5%84%E3%81%BF%E5%90%88%E3%82%8F%E3%81%9B" class="hash-link" aria-label="混同しやすいディレクトリの組み合わせ への直接リンク" title="混同しやすいディレクトリの組み合わせ への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>/tmp と /run</strong>：どちらも一時ディレクトリですが、<code>/tmp</code> はアプリケーションとユーザー向け、<code>/run</code> はシステムサービスのランタイム状態向けです。<code>/run</code> は再起動で必ず空になり、<code>/tmp</code> はディストリビューションのポリシー次第です。</li>
<li class=""><strong>/opt と /usr/local</strong>：どちらもパッケージマネージャーの外でソフトウェアをインストールする場所です。手動でコンパイル・インストールするソフトウェアは慣習的に <code>/usr/local</code>（<code>/usr</code> と並行した構造）に、ひとまとまりで展開するサードパーティソフトウェアは <code>/opt</code> に置きます。</li>
<li class=""><strong>/root と /</strong>：前者は root ユーザーのホームディレクトリ、後者はファイルシステム全体のルートです。名前は似ていますが、まったくの別物です。</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>「ディスクが満杯」といった問題を調査するときは、まず <code>/var/log</code> と <code>/tmp</code> を見ましょう。あるソフトウェアの設定を探すときは、まず <code>/etc</code> 配下で同名のディレクトリを検索しましょう。具体的なパスを覚えるよりも、ディレクトリの役割を覚えるほうが役に立ちます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/13/linux-mu-lu-jie-gou-fen-xi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Linux のディレクトリ構造は本質的に「ファイルの性質」による分類です。プログラムは <code>/bin</code> と <code>/usr</code> に、設定は <code>/etc</code> に、可変データは <code>/var</code> に、一時ファイルは <code>/tmp</code> と <code>/run</code> に、ユーザーデータは <code>/home</code> に置かれます。この分類ロジックを理解すれば、見知らぬマシンを前にしても、探しものをすばやく見つけられるようになります。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[CentOS に Docker をインストールする]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker</guid>
            <pubDate>Wed, 08 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[CentOS 上で yum を使って Docker をインストールする完全な手順を記録：国内ミラーソースの設定、バージョン指定インストール、サービスの起動、そしてイメージ保存パスの変更、レジストリミラーの設定、自動起動の設定まで。]]></description>
            <content:encoded><![CDATA[<p>昨今のコンテナ技術の流行に伴い、Docker のインストール方法を学ぶことも極めて重要になっています。</p>
<!-- -->
<p>Docker 公式ドキュメントのインストール手順自体は複雑ではありませんが、中国国内のネットワーク環境でそのまま実行すると、ダウンロード速度がボトルネックになりがちです。また、本番環境では通常 Docker のバージョンを固定したり、イメージの保存場所を調整したりする必要がありますが、こうした細かい点は公式ドキュメントでは分散して書かれています。この記事では、私が CentOS に Docker をインストールした一連の流れを、インストール後のよく使う設定も含めて整理しておきます。今後の再インストール時にそのまま打ち込めるようにするためです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="インストール手順">インストール手順<a href="https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%89%8B%E9%A0%86" class="hash-link" aria-label="インストール手順 への直接リンク" title="インストール手順 への直接リンク" translate="no">​</a></h2>
<p>1）対応する yum ツールパッケージをインストールする</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># yum-utils は yum-config-manager ツールを提供し、後でリポジトリを追加するのに使う</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># device-mapper-persistent-data と lvm2 は devicemapper ストレージドライバの依存パッケージ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> yum </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-y</span><span class="token plain"> yum-utils </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  device-mapper-persistent-data </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  lvm2</span><br></div></code></pre></div></div>
<p>このステップでインストールするのはすべて基本ツールです。<code>yum-config-manager</code> は次のステップでリポジトリを追加するのに使うコマンドで、残りの 2 つのパッケージはストレージドライバ関連の依存です。まとめて入れておけば後でエラーになる心配がありません。</p>
<p>2）国内ミラーソースを設定し、Docker のダウンロードを高速化する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># アリババクラウドの docker-ce リポジトリを追加し、公式ソースの代わりにする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> yum-config-manager </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  --add-repo </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo</span><br></div></code></pre></div></div>
<p>Docker の公式ソースは中国国内からのダウンロードが非常に遅く、タイムアウトすることさえあります。アリババクラウドのミラーリポジトリに切り替えると、パッケージのダウンロード速度がかなり正常になります。このコマンドは実際には repo ファイルを <code>/etc/yum.repos.d/</code> ディレクトリに書き込むもので、以降 yum はこのリポジトリから docker 関連のパッケージを検索できるようになります。</p>
<p>3）利用可能なバージョンのパッケージを一覧表示する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># リポジトリ内の利用可能な docker-ce の全バージョンを、バージョン番号の降順で一覧表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">yum list docker-ce </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--showduplicates</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-r</span><br></div></code></pre></div></div>
<p><code>--showduplicates</code> パラメータを付けると、最新版だけでなくリポジトリ内のすべての過去バージョンが一覧表示されます。出力の 2 列目がバージョン文字列で、次のステップでバージョン指定インストールをするときに使います。</p>
<p>4）指定バージョンの Docker をインストールする</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># &lt;VERSION_STRING&gt; を前のステップで調べたバージョン文字列に置き換える</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> yum </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> docker-ce-</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">VERSION_STRING</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> docker-ce-cli-</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&lt;</span><span class="token plain">VERSION_STRING</span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> containerd.io</span><br></div></code></pre></div></div>
<p>例：対応する Docker バージョンを指定する場合。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 20.10.9 バージョンを指定してインストール。docker-ce と docker-ce-cli のバージョンは揃える</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> yum </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> docker-ce-20.10.9-3.el7 docker-ce-cli-20.10.9-3.el7 containerd.io</span><br></div></code></pre></div></div>
<p>ここでは一度に 3 つのパッケージをインストールしており、それぞれ役割が異なります。<code>docker-ce</code> はデーモン（dockerd）、<code>docker-ce-cli</code> はコマンドラインクライアント、<code>containerd.io</code> は下層のコンテナランタイムで、コンテナの作成とライフサイクル管理を実際に担います。バージョンを指定せずに <code>yum install docker-ce</code> を実行すると、リポジトリの最新版がインストールされます。本番環境ではバージョンを固定し、マシンごとにバージョンが揃わないことによる差異を避けることをおすすめします。</p>
<p>5）Docker サービスを起動する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> systemctl start </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><br></div></code></pre></div></div>
<p>起動後は <code>docker info</code> または <code>docker version</code> を実行してデーモンが正常に動いていることを確認できます。ここまででインストールは完了です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="発展的な知識">発展的な知識<a href="https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker#%E7%99%BA%E5%B1%95%E7%9A%84%E3%81%AA%E7%9F%A5%E8%AD%98" class="hash-link" aria-label="発展的な知識 への直接リンク" title="発展的な知識 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1docker-のデフォルトイメージ保存先の変更イメージダウンロード元の変更">1）Docker のデフォルトイメージ保存先の変更、イメージダウンロード元の変更<a href="https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker#1docker-%E3%81%AE%E3%83%87%E3%83%95%E3%82%A9%E3%83%AB%E3%83%88%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E4%BF%9D%E5%AD%98%E5%85%88%E3%81%AE%E5%A4%89%E6%9B%B4%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%83%80%E3%82%A6%E3%83%B3%E3%83%AD%E3%83%BC%E3%83%89%E5%85%83%E3%81%AE%E5%A4%89%E6%9B%B4" class="hash-link" aria-label="1）Docker のデフォルトイメージ保存先の変更、イメージダウンロード元の変更 への直接リンク" title="1）Docker のデフォルトイメージ保存先の変更、イメージダウンロード元の変更 への直接リンク" translate="no">​</a></h3>
<p>Docker はデフォルトでイメージやコンテナのデータを <code>/var/lib/docker</code> ディレクトリに保存します。多くのサーバーではルートパーティションの容量が限られており、イメージをたくさん pull するとルートパーティションを埋め尽くしがちなので、通常は保存ディレクトリを容量の大きいデータディスクに移します。同時に、Docker Hub からのイメージ取得も中国国内では遅めなので、国内のレジストリミラーのアドレスを設定すると明らかに改善します。この 2 つはどちらも daemon.json で設定します。</p>
<p>1、vim で /etc/docker/daemon.json ファイルを編集する</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># このファイルはデフォルトでは存在せず、vim で開いて保存すれば作成される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">vim</span><span class="token plain"> /etc/docker/daemon.json</span><br></div></code></pre></div></div>
<p>2、data-root に対応する値を入力してイメージ保存先を変更し、registry-mirrors に対応する値を入力してイメージダウンロード元を変更します。配列なので複数のアドレスを設定でき、カンマで区切ります。</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token property" style="color:hsl(355, 65%, 65%)">"data-root"</span><span class="token operator" style="color:hsl(207, 82%, 66%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"/home/docker-data"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token property" style="color:hsl(355, 65%, 65%)">"registry-mirrors"</span><span class="token operator" style="color:hsl(207, 82%, 66%)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">[</span><span class="token string" style="color:hsl(95, 38%, 62%)">"https://docker.mirrors.ustc.edu.cn/"</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><br></div></code></pre></div></div>
<p>ここのミラーアドレスには<a href="https://docker.mirrors.ustc.edu.cn/" target="_blank" rel="noopener noreferrer" class="">中国科学技術大学のミラーサイト</a>を使っています。<code>registry-mirrors</code> は配列で複数のアドレスを設定でき、Docker はイメージ取得時に順番に試行します。</p>
<p>daemon.json ファイルの編集が完了したら、Docker の再起動コマンドを実行します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">service</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> restart</span><br></div></code></pre></div></div>
<p>その後 docker info コマンドを実行して、変更した設定が反映されているか確認します。反映されていれば、<code>docker info</code> の出力の <code>Docker Root Dir</code> に新しい保存パスが表示され、<code>Registry Mirrors</code> の欄に設定したミラーアドレスが列挙されます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2docker-サービスを自動起動に登録する">2）Docker サービスを自動起動に登録する<a href="https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker#2docker-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%92%E8%87%AA%E5%8B%95%E8%B5%B7%E5%8B%95%E3%81%AB%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B" class="hash-link" aria-label="2）Docker サービスを自動起動に登録する への直接リンク" title="2）Docker サービスを自動起動に登録する への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># システムサービスとして登録し、サーバー再起動後に docker が自動起動するようにする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">systemctl </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">enable</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><br></div></code></pre></div></div>
<p>サーバーの再起動は避けられないものです。このステップを忘れると、再起動後にコンテナが全部立ち上がらず、手動で Docker サービスを起動しに行く羽目になります。インストールが終わったらついでに自動起動を設定しておきましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">daemon.json は厳格な JSON フォーマットです。カンマが 1 つ多い、引用符が 1 つ足りないだけで Docker の起動が失敗します。設定変更後の再起動でエラーが出たら、まずこのファイルの構文をチェックしてください。</li>
<li class=""><code>data-root</code> の変更は新しいディレクトリに切り替えるだけで、元の <code>/var/lib/docker</code> 配下にある既存のイメージやコンテナのデータは自動では移行されません。必要な場合は自分でコピーしてから再起動してください。</li>
<li class="">バージョン指定インストールでは、<code>docker-ce</code> と <code>docker-ce-cli</code> のバージョン文字列を揃える必要があります。混在させるとクライアントとデーモンの非互換の警告が出ることがあります。</li>
<li class="">インストール前にマシンに旧バージョンの Docker（パッケージ名が <code>docker</code> や <code>docker-engine</code> の古いパッケージなど）がある場合は、競合を避けるため、先にきれいにアンインストールしてから docker-ce をインストールすることをおすすめします。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/08/centos-an-zhuang-docker#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>インストールの流れ全体は、ツールパッケージのインストール、国内ソースへの切り替え、バージョンの確認、指定バージョンのインストール、サービスの起動、という 5 ステップにまとめられます。インストールが終わってもすぐに使い始めず、まずイメージ保存パス、レジストリミラー、自動起動の 3 項目を設定しておきましょう。どれも一度設定すれば長く恩恵を受けられるものです。今後新しいマシンを初期化するときは、このノートに沿って上から順に実行すれば OK です。</p>]]></content:encoded>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[Docker Compose の基本的な使い方]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong</guid>
            <pubDate>Wed, 08 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[docker-compose は 1 枚の YAML ファイルでマルチコンテナアプリケーションのサービス、ネットワーク、データボリュームを記述し、1 コマンドでオーケストレーションと起動を完了させます。本記事ではそのインストール方法、動作の仕組み、日常でよく使うコマンドを記録します。]]></description>
            <content:encoded><![CDATA[<p>docker をインストールすると、多くのコンテナサービスを起動することになります。しかし、これらのコンテナをどう統一的にオーケストレーションするか、ネットワークグループの管理、起動順序、データボリュームのマウントなどが課題になってきます。そこで docker チームは、コンテナのオーケストレーションを容易にする docker-compose コンポーネントを開発しました。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜオーケストレーションが必要なのか">なぜオーケストレーションが必要なのか<a href="https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong#%E3%81%AA%E3%81%9C%E3%82%AA%E3%83%BC%E3%82%B1%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%8C%E5%BF%85%E8%A6%81%E3%81%AA%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜオーケストレーションが必要なのか への直接リンク" title="なぜオーケストレーションが必要なのか への直接リンク" translate="no">​</a></h2>
<p>単一のコンテナなら <code>docker run</code> で起動できますが、実際のアプリケーションがコンテナ 1 つだけで済むことはめったにありません。Web サービスにはたいていデータベース、キャッシュ、メッセージキューが付いてきます。これらのコンテナ間には依存関係があり、同じネットワーク内で互いにアクセスできる必要があり、それぞれデータボリュームのマウントも必要です。すべてを手打ちの <code>docker run</code> に頼っていると、パラメータが長くなれば誰も覚えていられませんし、別のマシンに再デプロイするのはもはや苦行です。</p>
<p>docker-compose が解決するのはまさにこの問題です。すべてのコンテナの起動パラメータを 1 枚の YAML ファイルに固定化してバージョン管理に組み込めば、誰でもファイルさえあれば 1 コマンドで環境一式を再現できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="インストール">インストール<a href="https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB" class="hash-link" aria-label="インストール への直接リンク" title="インストール への直接リンク" translate="no">​</a></h2>
<p>オープンソースプロジェクトのアドレス:</p>
<p><a href="https://github.com/docker/compose" target="_blank" rel="noopener noreferrer" class="">https://github.com/docker/compose</a></p>
<p>プロジェクトの Releases ページから、システムアーキテクチャに対応するバイナリファイルをダウンロードするだけです。インストールは 2 ステップです。</p>
<p>1、ファイルをダウンロードしてサーバーの /usr/local/bin フォルダにアップロードします。</p>
<p>/usr/local/bin に置くのは、このディレクトリがデフォルトで PATH に含まれているからです。ここに置けば、どのパスからでも直接 <code>docker-compose</code> コマンドを打てます。</p>
<p>2、ファイルに実行権限を付与します</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 実行権限を付与する。付与しないと shell が Permission denied と表示する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sudo</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">chmod</span><span class="token plain"> +x /usr/local/bin/docker-compose</span><br></div></code></pre></div></div>
<p>インストール後は <code>docker-compose version</code> を実行して確認できます。バージョン情報が表示されればインストール完了です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="動作の仕組み">動作の仕組み<a href="https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong#%E5%8B%95%E4%BD%9C%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="動作の仕組み への直接リンク" title="動作の仕組み への直接リンク" translate="no">​</a></h2>
<p>docker-compose は単なるバイナリファイルで、linux システム上で直接実行できます。docker-compose を使うと、yaml ファイルでアプリケーションに必要なすべてのコンテナを設定できます。そして docker-compose up -d という 1 つのコマンドで、yaml ファイルの設定からすべてのサービスを作成・起動できます。</p>
<p>docker-compose 自体は別のコンテナエンジンではなく、低レイヤーでやっていることは手動の <code>docker run</code> とまったく同じです。YAML を解析した後、docker の API を呼び出してネットワーク、ボリューム、コンテナを作成します。いくつかの重要な仕組みを挙げます。</p>
<p>1)<strong>プロジェクト(project)</strong>:compose はデフォルトで yaml ファイルのあるディレクトリ名をプロジェクト名とし、同じプロジェクト配下に作成されたコンテナ、ネットワーク、ボリュームにはこのプレフィックスが付き、互いに分離されます。</p>
<p>2)<strong>デフォルトネットワーク</strong>:<code>up</code> の際にブリッジネットワークが自動作成され、ファイルに定義されたすべてのサービスがそこに入ります。同じネットワーク内のコンテナはサービス名をホスト名として互いにアクセスでき、コンテナの IP を意識する必要はありません。</p>
<p>3)<strong>宣言的管理</strong>:再度 <code>up</code> を実行すると、compose は yaml と現在のコンテナの状態を比較し、変更のあったサービスだけを再作成し、変わっていないものはそのまま維持します。</p>
<p>典型的な docker-compose.yml はだいたい次のような形です。</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token key atrule" style="color:hsl(29, 54%, 61%)">version</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"3"</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">services</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">web</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">image</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> nginx            </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 使用するイメージ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">ports</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">"80:80"</span><span class="token plain">             </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ホスト側ポート:コンテナ側ポート</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">volumes</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> ./html</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain">/usr/share/nginx/html   </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># データボリュームのマウント</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">depends_on</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">      </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">-</span><span class="token plain"> app                 </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 起動順序の宣言:先に app を起動してから web を起動</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">app</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">build</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> ./app            </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># Dockerfile からその場でビルドすることも可能</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token key atrule" style="color:hsl(29, 54%, 61%)">restart</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">:</span><span class="token plain"> always         </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># コンテナが異常終了したら自動で再起動</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="よく使うコマンド">よく使うコマンド<a href="https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong#%E3%82%88%E3%81%8F%E4%BD%BF%E3%81%86%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89" class="hash-link" aria-label="よく使うコマンド への直接リンク" title="よく使うコマンド への直接リンク" translate="no">​</a></h2>
<p>以下のコマンドはすべて docker-compose.yml のあるディレクトリで実行するか、<code>-f</code> でファイルパスを指定する必要があります。</p>
<p>よく使うコマンド:</p>
<p>docker-compose up [コンテナを起動 -d 付きでバックグラウンド起動]</p>
<p>docker-compose stop [コンテナを停止]</p>
<p>docker-compose ps [現在のすべてのオーケストレーション済みサービスを確認]</p>
<p>docker-compose logs -f --tail=500 &lt;コンテナName&gt; [コンテナのリアルタイムログを確認]</p>
<p>docker-compose restart [コンテナを再起動]</p>
<p>docker-compose rm  [コンテナを削除]</p>
<p>docker-compose build  [イメージをビルド]</p>
<p>いくつか補足します。</p>
<ul>
<li class=""><code>up</code> を <code>-d</code> なしで実行するとフォアグラウンドで動作し、すべてのコンテナのログが現在のターミナルに流れます。デバッグ時にはとても便利で、Ctrl+C を押せば停止します。日常のデプロイでは基本的に <code>up -d</code> です。</li>
<li class=""><code>logs</code> の <code>--tail=500</code> は、最後の 500 行だけ表示してから追跡を始めるという意味です。付けないと過去のログが全部流れてきて、ログ量が多いとターミナルが長時間固まります。</li>
<li class=""><code>stop</code> はコンテナを停止するだけで削除はしません。<code>rm</code> が削除するのは停止済みのコンテナです。コンテナとネットワークを一括で停止・削除したい場合は <code>docker-compose down</code> を使います。</li>
<li class="">Dockerfile を変更した後にそのまま <code>up -d</code> してもイメージは再ビルドされません。先に <code>build</code> してから <code>up</code> するか、<code>up -d --build</code> を直接実行します。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1)<strong>yaml を変更したら忘れずに再度 up する</strong>:設定を変更した後に <code>restart</code> だけしても反映されません。<code>restart</code> は古いコンテナを再起動するだけです。新しい設定を反映させるには、もう一度 <code>up -d</code> を実行して、compose に変更のあったコンテナを再作成させる必要があります。</p>
<p>2)<strong>depends_on が保証するのは起動順序だけ</strong>:コンテナが順番どおりに起動することは保証しますが、依存先のサービスが本当に準備完了していることは保証しません。たとえばデータベースコンテナは起動したもののプロセスがまだ接続を受け付けていない場合、アプリケーションがこのタイミングで接続しにいくとエラーになります。アプリケーション側に再接続のロジックが必要です。</p>
<p>3)<strong>データボリュームで相対パスを使うときは実行ディレクトリに注意</strong>:yaml 内の相対パスは yaml ファイルの位置を基準に解決されます。別のディレクトリから実行するときは <code>-f</code> でファイルを明示的に指定し、マウント先を間違えないようにするのがよいでしょう。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p><code>docker-compose down</code> に <code>-v</code> パラメータを付けるとデータボリュームも一緒に削除されます。ステートフルなサービス(データベースなど)に対して実行する前に、ボリューム内のデータを破棄してよいか必ず確認してください。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/08/docker-compose-ji-ben-shi-yong#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>docker-compose は本質的に、大量の <code>docker run</code> パラメータをバージョン管理可能な 1 枚の YAML に固定化するものです。<code>up -d</code> 1 発でサービス一式が起動し、<code>logs</code>、<code>ps</code>、<code>restart</code> で日常運用をカバーできます。単一マシン環境ならこれで十分快適です。サービスの規模が大きくなってマルチマシンのスケジューリングが必要になったら、そのとき Kubernetes のようなオーケストレーションシステムを検討しても遅くはありません。</p>]]></content:encoded>
            <category>Docker</category>
        </item>
        <item>
            <title><![CDATA[Linux よく使うコマンド]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling</guid>
            <pubDate>Wed, 08 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Linux 日常運用コマンドのクイックリファレンス:CPU、メモリ、ファイル操作、システム情報の確認。よく使うコマンド一覧も付けて、サッと参照できるようにしました。]]></description>
            <content:encoded><![CDATA[<p>日常業務でこれらの Linux コマンドを何度も繰り返し使っているので、毎回検索するくらいなら、クイックリファレンスのノートとして整理してしまうことにしました。CPU、メモリ、ファイル、システムのいくつかの観点で分類し、最後によく使うコマンドの一覧を付けています。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cpu-関連コマンド">CPU 関連コマンド<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling#cpu-%E9%96%A2%E9%80%A3%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89" class="hash-link" aria-label="CPU 関連コマンド への直接リンク" title="CPU 関連コマンド への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># CPU の物理個数を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'physical id'</span><span class="token plain"> /proc/cpuinfo </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-u</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">wc</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-l</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># CPU のコア数を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'core id'</span><span class="token plain"> /proc/cpuinfo </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-u</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">wc</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-l</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># CPU のスレッド数を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'processor'</span><span class="token plain"> /proc/cpuinfo </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-u</span><span class="token plain"> </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">wc</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-l</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># CPU の型番を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">dmidecode </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-s</span><span class="token plain"> processor-version</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># CPU の詳細情報を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain"> /proc/cpuinfo</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="メモリ関連コマンド">メモリ関連コマンド<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling#%E3%83%A1%E3%83%A2%E3%83%AA%E9%96%A2%E9%80%A3%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89" class="hash-link" aria-label="メモリ関連コマンド への直接リンク" title="メモリ関連コマンド への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 現在のメモリ使用状況を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">free</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-h</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># メモリ使用率が高い上位 10 プロセスを確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">ps</span><span class="token plain"> aux </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">head</span><span class="token plain"> -1</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token function" style="color:hsl(207, 82%, 66%)">ps</span><span class="token plain"> aux </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-v</span><span class="token plain"> PID </span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token function" style="color:hsl(207, 82%, 66%)">sort</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-rn</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-k</span><span class="token plain"> +4</span><span class="token operator" style="color:hsl(207, 82%, 66%)">|</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">head</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-10</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ファイルコマンド">ファイルコマンド<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling#%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89" class="hash-link" aria-label="ファイルコマンド への直接リンク" title="ファイルコマンド への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">ls</span><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイルまたはディレクトリを表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">ls</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-l</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイルの詳細情報を一覧表示(list)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">ls</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-a</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># カレントディレクトリの全ファイル・ディレクトリを表示、隠しファイルも含む(all)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">mkdir</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ディレクトリを作成</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">mkdir</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain">  </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ディレクトリを作成、親ディレクトリがなければまとめて作成(parent)</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">cd</span><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ディレクトリを移動</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">touch</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 空ファイルを作成</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">echo</span><span class="token plain">      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 内容付きのファイルを作成</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cat</span><span class="token plain">       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイルの内容を表示</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">cp</span><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># コピー</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">mv</span><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 移動またはリネーム</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rm</span><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイルを削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-r</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 再帰的に削除、サブディレクトリとファイルも削除可能</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rm</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-f</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 強制削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">find</span><span class="token plain">      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイルシステム内でファイルを検索</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">wc</span><span class="token plain">        </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># テキストの行数・単語数・文字数をカウント</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">grep</span><span class="token plain">      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># テキストファイル内で文字列を検索</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">rmdir</span><span class="token plain">     </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 空ディレクトリを削除</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">tree      </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ディレクトリをツリー構造で表示、tree パッケージのインストールが必要</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">pwd</span><span class="token plain">       </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># カレントディレクトリを表示</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="システム関連コマンド">システム関連コマンド<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E9%96%A2%E9%80%A3%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89" class="hash-link" aria-label="システム関連コマンド への直接リンク" title="システム関連コマンド への直接リンク" translate="no">​</a></h2>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># システム情報を確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">lsb_release </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-a</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># カーネルバージョンを確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">uname</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-r</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="よく使うコマンド一覧">よく使うコマンド一覧<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling#%E3%82%88%E3%81%8F%E4%BD%BF%E3%81%86%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E4%B8%80%E8%A6%A7" class="hash-link" aria-label="よく使うコマンド一覧 への直接リンク" title="よく使うコマンド一覧 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">cd:カレントワーキングディレクトリを変更する</li>
<li class="">ls:カレントワーキングディレクトリのファイルとサブディレクトリを一覧表示する</li>
<li class="">mkdir:新しいディレクトリを作成する</li>
<li class="">rm:ファイルまたはディレクトリを削除する</li>
<li class="">cp:ファイルまたはディレクトリをコピーする</li>
<li class="">mv:ファイルまたはディレクトリを移動する</li>
<li class="">touch:新しいファイルを作成する、または既存ファイルのアクセス・更新時刻を変更する</li>
<li class="">cat:ファイルの内容を表示する</li>
<li class="">grep:ファイル内で特定の文字列を検索する</li>
<li class="">find:ファイルシステム内でファイルを探す</li>
<li class="">chmod:ファイルまたはディレクトリの権限を変更する</li>
<li class="">chown:ファイルまたはディレクトリの所有者を変更する</li>
<li class="">top:システムで現在実行中のプロセスを表示する</li>
<li class="">ps:現在実行中のプロセスを一覧表示する</li>
<li class="">ssh:セキュアシェルでリモートホストに接続する</li>
<li class="">scp:セキュアシェルであるホストから別のホストへファイルをコピーする</li>
<li class="">tar:tar アーカイブを作成または展開する</li>
<li class="">gzip:ファイルを圧縮する</li>
<li class="">unzip:ファイルを解凍する</li>
<li class="">ping:別のホストとのネットワーク接続をテストする</li>
<li class="">ifconfig:ネットワークインターフェースを表示・管理する</li>
<li class="">route:ネットワークルーティングテーブルを表示・管理する</li>
<li class="">netstat:ネットワークの状態情報を表示する</li>
<li class="">iptables:ファイアウォールのルールを管理する</li>
<li class="">systemctl:システムサービスを管理する</li>
<li class="">service:システムサービスを管理する</li>
<li class="">du:ディレクトリとファイルのディスク使用状況を表示する</li>
<li class="">df:ファイルシステムのディスク使用状況を表示する</li>
<li class="">uname:システム情報を表示する</li>
<li class="">date:システムの日付と時刻を表示または変更する</li>
<li class="">history:過去に実行したコマンドの履歴を表示する</li>
<li class="">man:コマンドのマニュアルページを表示する</li>
<li class="">which:指定したコマンドのパスを探す</li>
<li class="">whereis:指定したコマンドのパスとマニュアルページを探す</li>
<li class="">whoami:現在のユーザーのユーザー名を表示する</li>
<li class="">su:他のユーザーまたはスーパーユーザーに切り替える</li>
<li class="">sudo:スーパーユーザー権限でコマンドを実行する</li>
<li class="">ssh-keygen:SSH 鍵ペアを生成する</li>
<li class="">ssh-copy-id:SSH 公開鍵をリモートホストにコピーする</li>
<li class="">scp:SSH 経由で安全にファイルをコピーする</li>
<li class="">curl:サーバーへリクエストを送信しレスポンスを取得する</li>
<li class="">wget:Web からファイルをダウンロードする</li>
<li class="">vim:強力なテキストエディタ</li>
<li class="">nano:使いやすいテキストエディタ</li>
<li class="">tar:tar アーカイブを作成または展開する</li>
<li class="">gzip:ファイルを圧縮する</li>
<li class="">bzip2:ファイルを圧縮する</li>
<li class="">unzip:ファイルを解凍する</li>
<li class="">zip:zip アーカイブを作成または展開する</li>
<li class="">sshfs:SSH 経由でリモートファイルシステムをマウントする</li>
<li class="">rsync:リモートのファイルとディレクトリを同期する</li>
<li class="">awk:テキストの処理と分析に使う</li>
<li class="">sed:テキストの処理と変換に使う</li>
<li class="">sort:テキストの行をソートする</li>
<li class="">uniq:テキスト内の重複行を取り除く</li>
<li class="">cut:テキストから列を抽出する</li>
<li class="">paste:テキストの列を 1 つのファイルに結合する</li>
<li class="">tee:標準出力をファイルとターミナルの両方に複製する</li>
<li class="">diff:2 つのファイル間の差分を比較する</li>
<li class="">patch:diff ファイルからパッチを適用する</li>
<li class="">head:ファイルの先頭数行を表示する</li>
<li class="">tail:ファイルの末尾数行を表示する</li>
<li class="">tar:tar アーカイブを作成または展開する</li>
<li class="">gzip:ファイルを圧縮する</li>
<li class="">bzip2:ファイルを圧縮する</li>
<li class="">unzip:ファイルを解凍する</li>
<li class="">zip:zip アーカイブを作成または展開する</li>
<li class="">rpm:RPM パッケージのインストール・削除・照会を行う</li>
<li class="">dpkg:Debian パッケージのインストール・削除・照会を行う</li>
<li class="">ps:実行中のプロセスを一覧表示する</li>
<li class="">kill:実行中のプロセスを停止する</li>
<li class="">top:システムで現在実行中のプロセスを表示する</li>
<li class="">uptime:システムの稼働時間と負荷を表示する</li>
<li class="">free:システムのメモリ使用状況を表示する</li>
<li class="">df:ファイルシステムのディスク使用状況を表示する</li>
<li class="">du:ディレクトリとファイルのディスク使用状況を表示する</li>
<li class="">mount:ファイルシステムをマウントする</li>
<li class="">umount:ファイルシステムをアンマウントする</li>
<li class="">chroot:ルートディレクトリを変更する</li>
<li class="">ln:リンクまたはハードリンクを作成する</li>
<li class="">echo:テキストや変数を出力する</li>
<li class="">export:環境変数を設定する</li>
<li class="">source:スクリプトを実行して現在のシェルに取り込む</li>
<li class="">alias:エイリアスを作成する</li>
<li class="">unalias:エイリアスを削除する</li>
<li class="">jobs:現在実行中のジョブを一覧表示する</li>
<li class="">bg:ジョブをバックグラウンドで実行する</li>
<li class="">fg:ジョブをフォアグラウンドに戻して実行する</li>
<li class="">nohup:バックグラウンドでジョブを実行し、ターミナルを閉じても停止しない</li>
<li class="">crontab:定期実行タスクを設定する</li>
<li class="">at:指定時刻にコマンドを実行する</li>
<li class="">screen:複数のターミナルセッションを作成する</li>
<li class="">tmux:複数のターミナルセッションを作成する</li>
<li class="">curlftpfs:FTP 経由でリモートファイルシステムをマウントする</li>
<li class="">dig:DNS サーバーに問い合わせる</li>
<li class="">nslookup:DNS サーバーに問い合わせる</li>
<li class="">ping:別のホストとのネットワーク接続をテストする</li>
<li class="">traceroute:パケットがネットワーク内を通る経路を表示する</li>
<li class="">tcpdump:ネットワークパケットをキャプチャする</li>
<li class="">iftop:ネットワークトラフィック情報を表示する</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-chang-yong-ming-ling#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>コマンドを全部覚えきれないのは普通のことで、大事なのはどこで調べればいいかを知っていることです。このノートでは CPU、メモリ、ファイル、システムの分類で日常最もよく使うコマンドを整理しました。man マニュアルと組み合わせれば、ほとんどのトラブルシューティングのシーンをカバーできるはずです。今後、記録する価値のある新しいコマンドに出会ったら、引き続き追記していきます。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[Linux で新しいディスクをマウントする]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan</guid>
            <pubDate>Wed, 08 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Linux サーバーに新しいディスクをマウントする一連の流れの記録:fdisk でのパーティション作成、mkfs でのフォーマット、mount でのマウント、そして /etc/fstab による起動時の自動マウント。よくあるハマりどころ付き。]]></description>
            <content:encoded><![CDATA[<p>サーバーに新しいハードディスクを追加して接続したのに、システム上では使える容量が見えない——初めてストレージを増設するときによく遭遇する場面です。ディスクが「マザーボードにつながる」から「ファイルを保存できる」までの間には、認識・パーティション作成・フォーマット・マウントという 4 つのステップがあります。このノートでは全体の流れを一通りたどりながら、各ステップが何をしているのかを説明します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景ディスクを接続しただけでは使えない理由">背景:ディスクを接続しただけでは使えない理由<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#%E8%83%8C%E6%99%AF%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%82%92%E6%8E%A5%E7%B6%9A%E3%81%97%E3%81%9F%E3%81%A0%E3%81%91%E3%81%A7%E3%81%AF%E4%BD%BF%E3%81%88%E3%81%AA%E3%81%84%E7%90%86%E7%94%B1" class="hash-link" aria-label="背景:ディスクを接続しただけでは使えない理由 への直接リンク" title="背景:ディスクを接続しただけでは使えない理由 への直接リンク" translate="no">​</a></h2>
<p>Linux では「すべてはファイル」であり、ディスクも例外ではありません。新しいディスクを接続すると、カーネルはそれを <code>/dev/sdb</code> や <code>/dev/vdb</code> のようなブロックデバイスとして認識しますが、この時点ではまだ「生のディスク」にすぎません。</p>
<ul>
<li class="">パーティションテーブルがなく、システムは領域をどう区切るか分からない;</li>
<li class="">ファイルシステムがなく、カーネルはデータをどんな構造で編成するか分からない;</li>
<li class="">マウントポイントがなく、ユーザー空間のプログラムはパス経由でアクセスできない。</li>
</ul>
<p>したがって完全な流れは、認識 → パーティション作成 → フォーマット → マウント、最後に起動時の自動マウント設定、となります。どのステップも省略できません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実際の手順">実際の手順<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#%E5%AE%9F%E9%9A%9B%E3%81%AE%E6%89%8B%E9%A0%86" class="hash-link" aria-label="実際の手順 への直接リンク" title="実際の手順 への直接リンク" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-ディスクをマザーボードに接続する重要">1) ディスクをマザーボードに接続する(重要)<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#1-%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%82%92%E3%83%9E%E3%82%B6%E3%83%BC%E3%83%9C%E3%83%BC%E3%83%89%E3%81%AB%E6%8E%A5%E7%B6%9A%E3%81%99%E3%82%8B%E9%87%8D%E8%A6%81" class="hash-link" aria-label="1) ディスクをマザーボードに接続する(重要) への直接リンク" title="1) ディスクをマザーボードに接続する(重要) への直接リンク" translate="no">​</a></h3>
<p>物理マシンなら SATA ケーブルと電源ケーブルをしっかり接続します。クラウドサーバーなら、コンソールでクラウドディスクをインスタンスにアタッチします。当たり前に見えるステップですが、「システムがディスクを認識しない」問題を調査すると、ハードウェアの接続不良が実際に最も多い原因です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-システムを再起動する">2) システムを再起動する<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#2-%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%92%E5%86%8D%E8%B5%B7%E5%8B%95%E3%81%99%E3%82%8B" class="hash-link" aria-label="2) システムを再起動する への直接リンク" title="2) システムを再起動する への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">reboot</span><br></div></code></pre></div></div>
<p>古めのシステムや、ホットプラグが効かない場合は、一度再起動してカーネルにハードウェアを再スキャンさせるのが一番手っ取り早い方法です。ホットプラグ対応の環境なら再起動なしでも認識されますが、再起動は最も確実なフォールバックです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-ディスクが検出されているか確認する">3) ディスクが検出されているか確認する<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#3-%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%81%8C%E6%A4%9C%E5%87%BA%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%8B%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B" class="hash-link" aria-label="3) ディスクが検出されているか確認する への直接リンク" title="3) ディスクが検出されているか確認する への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">fdisk</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-l</span><br></div></code></pre></div></div>
<p><code>fdisk -l</code> はすべてのブロックデバイスとそのパーティションを一覧表示します。新しいディスクの典型的な特徴は、容量はあるのにパーティションがない(下に <code>/dev/sdb1</code> のようなエントリがない)ことです。このデバイス名をメモしておいてください。以降のステップすべてで使います。<code>lsblk</code> で照らし合わせて確認するのもよいでしょう。ディスクとパーティションの関係をツリー表示してくれるので、より直感的です。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-ディスクに入ってパーティション作成を始める">4) ディスクに入ってパーティション作成を始める<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#4-%E3%83%87%E3%82%A3%E3%82%B9%E3%82%AF%E3%81%AB%E5%85%A5%E3%81%A3%E3%81%A6%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E4%BD%9C%E6%88%90%E3%82%92%E5%A7%8B%E3%82%81%E3%82%8B" class="hash-link" aria-label="4) ディスクに入ってパーティション作成を始める への直接リンク" title="4) ディスクに入ってパーティション作成を始める への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">fdisk</span><span class="token plain"> /dev/</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain">ディスク名</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><br></div></code></pre></div></div>
<p><code>{ディスク名}</code> は前のステップで確認したデバイス名、たとえば <code>sdb</code> に置き換えます。<code>fdisk</code> は対話型ツールで、中に入ってからよく使うコマンドは次のとおりです。</p>
<ul>
<li class=""><code>n</code>:パーティションの新規作成</li>
<li class=""><code>p</code>:現在のパーティションテーブルの表示</li>
<li class=""><code>d</code>:パーティションの削除</li>
<li class=""><code>w</code>:変更をディスクに書き込んで終了</li>
<li class=""><code>q</code>:保存せずに終了</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-パーティションを設定する1-つだけでもよい">5) パーティションを設定する(1 つだけでもよい)<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#5-%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B1-%E3%81%A4%E3%81%A0%E3%81%91%E3%81%A7%E3%82%82%E3%82%88%E3%81%84" class="hash-link" aria-label="5) パーティションを設定する(1 つだけでもよい) への直接リンク" title="5) パーティションを設定する(1 つだけでもよい) への直接リンク" translate="no">​</a></h3>
<p>ディスク全体をデータ保存用に使うなら、<code>n</code> でプライマリパーティションを 1 つ新規作成し、開始・終了セクタはすべて Enter でデフォルト値を採用して、ディスク全体を 1 つのパーティションにすれば十分です。その後 <code>w</code> で保存して終了します。複数パーティションに分けることは今日のユースケースではあまり意味がなく、むしろ管理コストが増えます。</p>
<p>一つ注意点:従来の MBR パーティションテーブルは最大 2TB までしかサポートしていません。2TB を超えるディスクには GPT パーティションテーブルを使う必要があり、その場合は <code>parted</code> や <code>gdisk</code> でパーティションを作成してください。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="6-パーティションをフォーマットする">6) パーティションをフォーマットする<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#6-%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88%E3%81%99%E3%82%8B" class="hash-link" aria-label="6) パーティションをフォーマットする への直接リンク" title="6) パーティションをフォーマットする への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">mkfs.ext4 /dev/</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain">パーティション名</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><br></div></code></pre></div></div>
<p>ここで操作する対象はディスク全体ではなく<strong>パーティション</strong>(たとえば <code>/dev/sdb1</code>)であることに注意してください。<code>mkfs.ext4</code> はパーティション上に ext4 ファイルシステムを作成し、スーパーブロックや inode テーブルなどのメタデータ構造を書き込みます。ext4 は現在の Linux ディストリビューションで最も汎用的な選択肢です。ディストリビューションのデフォルトが XFS の場合(CentOS 系など)は、<code>mkfs.xfs</code> に置き換えても同じです。</p>
<p>フォーマットするとパーティション上のすべてのデータが消去されます。実行前にデバイス名を打ち間違えていないか必ず確認してください。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="7-パーティションをマウントする">7) パーティションをマウントする<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#7-%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%81%99%E3%82%8B" class="hash-link" aria-label="7) パーティションをマウントする への直接リンク" title="7) パーティションをマウントする への直接リンク" translate="no">​</a></h3>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">mount</span><span class="token plain"> /dev/</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain">パーティション名</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><span class="token plain"> /</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain">任意のディレクトリ</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><br></div></code></pre></div></div>
<p>マウントポイントはただの普通のディレクトリです。存在しなければ先に <code>mkdir</code> で作成します(例:<code>mkdir /data</code>)。マウント後、<code>/data</code> に書き込んだファイルは実際には新しいディスクに保存されます。<code>df -h</code> で検証でき、パーティション・容量・マウントポイントの対応関係が確認できます。</p>
<p>マウントポイントのディレクトリにもともとファイルがあった場合、マウント後は「覆い隠されて」見えなくなり、アンマウント(<code>umount</code>)すると元に戻ります。データが消えたわけではありませんが混乱のもとになるので、マウントポイントには空のディレクトリを使うことをおすすめします。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="8-起動時の自動マウントを設定する">8) 起動時の自動マウントを設定する<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#8-%E8%B5%B7%E5%8B%95%E6%99%82%E3%81%AE%E8%87%AA%E5%8B%95%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B" class="hash-link" aria-label="8) 起動時の自動マウントを設定する への直接リンク" title="8) 起動時の自動マウントを設定する への直接リンク" translate="no">​</a></h3>
<p><code>mount</code> コマンドの効果は今回の起動中のみ有効で、再起動するとマウントは消えてしまいます。永続化するには <code>/etc/fstab</code> を編集します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">vim</span><span class="token plain"> /etc/fstab</span><br></div></code></pre></div></div>
<p>ファイルの末尾に 1 行追加します。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">/dev/(ディスクパーティション) /（マウントディレクトリ） ext4（ファイル形式）defaults 0 0</span><br></div></code></pre></div></div>
<p>この設定行は 6 つのフィールドからなり、順に次のとおりです。</p>
<ol>
<li class=""><strong>デバイス</strong>:マウントするパーティション。例 <code>/dev/sdb1</code>;</li>
<li class=""><strong>マウントポイント</strong>:ディレクトリパス。例 <code>/data</code>;</li>
<li class=""><strong>ファイルシステムタイプ</strong>:フォーマット時と一致させます。ここでは <code>ext4</code>;</li>
<li class=""><strong>マウントオプション</strong>:<code>defaults</code> は読み書き可・実行許可などのデフォルトオプション一式を意味します;</li>
<li class=""><strong>dump バックアップフラグ</strong>:<code>0</code> は dump ツールでバックアップしないことを意味します;</li>
<li class=""><strong>fsck チェック順序</strong>:<code>0</code> は起動時にファイルシステムチェックを行わないことを意味します。ルートパーティションは通常 <code>1</code>、データディスクは <code>2</code> または <code>0</code> に設定します。</li>
</ol>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p><code>/etc/fstab</code> の記述を誤ると、システムが起動に失敗して緊急モードに入ることがあります。保存後はまず <code>mount -a</code> で検証してください。fstab に従って未マウントのエントリをすべてマウントしてくれるので、エラーが出ないことを確認してから再起動しましょう。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><strong>デバイス名より UUID を使う方が安定します。</strong> <code>/dev/sdb</code> のような名前はカーネルの認識順に依存し、ディスクの増減で変わる可能性があり、fstab が別のディスクをマウントしてしまう原因になります。<code>blkid</code> でパーティションの UUID を調べ、fstab の第 1 フィールドを <code>UUID=xxxx</code> の形式で書けば、デバイス名の変動に影響されません。</li>
<li class=""><strong>フォーマット前にデバイス名を確認する。</strong> <code>mkfs</code> の対象を打ち間違えると取り返しがつきません。操作前に <code>lsblk</code> や <code>fdisk -l</code> でもう一度確認し、容量が想定どおりであることを確かめてください。</li>
<li class=""><strong>ディスクとパーティションを区別する。</strong> パーティション作成・フォーマット・マウントはそれぞれ操作対象が異なります。<code>fdisk</code> はディスク全体(<code>/dev/sdb</code>)を、<code>mkfs</code> と <code>mount</code> はパーティション(<code>/dev/sdb1</code>)を対象とします。これらを混同するのが初心者の最もよくあるエラーの原因です。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/08/linux-gua-zai-ci-pan#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>新しいディスクをマウントする流れの本筋は 4 ステップです。<code>fdisk -l</code> で認識を確認し、<code>fdisk</code> でパーティションを作成し、<code>mkfs.ext4</code> でフォーマットし、<code>mount</code> でマウントする。最後に <code>/etc/fstab</code> に書いて、設定が再起動後も有効になるようにします。「生のディスク → パーティション → ファイルシステム → マウントポイント」というつながりを理解しておけば、今後ストレージ増設やディスク交換などの作業に出会っても、この流れに沿って一つずつ確認していけば十分です。</p>]]></content:encoded>
            <category>Linux</category>
        </item>
        <item>
            <title><![CDATA[K3S クラスター構築]]></title>
            <link>https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian</link>
            <guid>https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian</guid>
            <pubDate>Thu, 02 Jun 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[k3sup を使って Alibaba Cloud のサーバー 6 台に HA etcd 高可用 K3S クラスターを構築した記録:環境準備、master/worker ノードのデプロイコマンド、k3sup のよく使うパラメータの解説。]]></description>
            <content:encoded><![CDATA[<p>最近 K8S クラスターのデプロイを学び、その過程で多くのデプロイ方法を使ってみました。例えば公式が提供する <a href="https://kubernetes.io/zh/docs/setup/production-environment/tools/kubeadm/" target="_blank" rel="noopener noreferrer" class=""><strong>kubeadm</strong></a> ツール、QingCloud の KK ツールによるワンクリックデプロイ、さらに GitHub 上のオープンソースの無料・有料ワンクリックデプロイツールなど、いずれも試しましたが、最終的にクラスターのデプロイには <a href="https://github.com/alexellis/k3sup" target="_blank" rel="noopener noreferrer" class="">K3S-up</a> を選びました。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-k3s-を選んだのか">なぜ K3S を選んだのか<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#%E3%81%AA%E3%81%9C-k3s-%E3%82%92%E9%81%B8%E3%82%93%E3%81%A0%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ K3S を選んだのか への直接リンク" title="なぜ K3S を選んだのか への直接リンク" translate="no">​</a></h2>
<p>K8S と比べて、K3S はシステムコンテナの数が少なく軽量で、デフォルトでコンテナランタイムに containerd を採用しています。内部の ingress には Go 言語で開発された traefik を使用し、Etcd の代わりに SQLite を統合しています。ただし、複数の master ノードを持つシナリオでは、データの一貫性を保証して HA を実現するために、Etcd コンポーネントを使うのがベストです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="環境設計">環境設計<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#%E7%92%B0%E5%A2%83%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="環境設計 への直接リンク" title="環境設計 への直接リンク" translate="no">​</a></h2>
<p>今回の構築では Alibaba Cloud の共有型サーバー 6 台を使い、HA-etcd 高可用 k3s クラスターを構築します:</p>
<ul>
<li class="">master 3 台(2 コア 4GB)</li>
<li class="">worker 3 台(2 コア 4GB)</li>
</ul>
<p>OS は Debian 10.10 を使用します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="前提条件">前提条件<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6" class="hash-link" aria-label="前提条件 への直接リンク" title="前提条件 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">まずファイアウォールを無効化し、パッケージソースを更新します:</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">apt-get</span><span class="token plain"> update</span><br></div></code></pre></div></div>
<ol start="2">
<li class="">cgroupfs-mount をインストールし、マシンを再起動します:</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token function" style="color:hsl(207, 82%, 66%)">apt-get</span><span class="token plain"> </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> cgroupfs-mount</span><br></div></code></pre></div></div>
<ol start="3">
<li class="">
<p>K3s のデプロイ用ソフトウェアをダウンロードします。<a href="https://github.com/k3s-io/k3s" target="_blank" rel="noopener noreferrer" class="">k3s</a> のリソースは GitHub 上にあり、中国国内のサーバーからはダウンロードが非常に遅いため、先に GitHub から k3s のインストール用バイナリをダウンロードしてサーバーに配置しておくと、ダウンロードの待ち時間を省けます。</p>
</li>
<li class="">
<p>環境に問題がないか確認します:</p>
</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">k3s </span><span class="token builtin class-name" style="color:hsl(29, 54%, 61%)">help</span><span class="token plain">          </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 基本コマンドを確認</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">k3s check-config  </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 環境が実行条件を満たしているか検査</span><br></div></code></pre></div></div>
<p>すべてのサーバーで上記 1〜4 の手順を繰り返す必要があります。面倒であれば、1 台のサーバーでこれらの手順を済ませてからシステムディスクのイメージを複製し、以降のクラウドサーバー作成時にそのイメージを選択すれば OK です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスターのデプロイ">クラスターのデプロイ<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4" class="hash-link" aria-label="クラスターのデプロイ への直接リンク" title="クラスターのデプロイ への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">環境チェックに問題がなければ、k3sup を使って最初のコントロールプレーン master ノードを初期化します:</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># --cluster は組み込み etcd のクラスターモードで起動することを意味する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">k3sup </span><span class="token function" style="color:hsl(207, 82%, 66%)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--ip</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">#{your_service_ip} --user root --cluster --k3s-version v1.23.4+k3s1 --print-command</span><br></div></code></pre></div></div>
<ol start="2">
<li class="">残りの master ノードをクラスターに参加させます:</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># --server は master としての参加を意味し、--server-ip は 1 台目の master を指す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">k3sup </span><span class="token function" style="color:hsl(207, 82%, 66%)">join</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--ip</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">#{next_service_ip} --user root --server-user root --server-ip #{first_service_ip} --server --k3s-version v1.23.4+k3s1 --print-command</span><br></div></code></pre></div></div>
<ol start="3">
<li class="">残りの worker ノードをクラスターに参加させます:</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">k3sup </span><span class="token function" style="color:hsl(207, 82%, 66%)">join</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--user</span><span class="token plain"> root --server-ip </span><span class="token variable" style="color:hsl(207, 82%, 66%)">$SERVER1</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--ip</span><span class="token plain"> </span><span class="token variable" style="color:hsl(207, 82%, 66%)">$AGENT1</span><span class="token plain"> --print-command</span><br></div></code></pre></div></div>
<ol start="4">
<li class="">ノードのステータスを確認します:</li>
</ol>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">kubectl get nodes</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="k3sup-のよく使うパラメータ">k3sup のよく使うパラメータ<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#k3sup-%E3%81%AE%E3%82%88%E3%81%8F%E4%BD%BF%E3%81%86%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF" class="hash-link" aria-label="k3sup のよく使うパラメータ への直接リンク" title="k3sup のよく使うパラメータ への直接リンク" translate="no">​</a></h2>
<ul>
<li class=""><code>--cluster</code> — 組み込み etcd(組み込み HA)を使って、このサーバーをクラスターモードで起動する</li>
<li class=""><code>--skip-install</code> — すでに k3s がインストールされている場合、このコマンドで kubeconfig を取得できる</li>
<li class=""><code>--ssh-key</code> — リモートログイン用の SSH 鍵のパスを指定する</li>
<li class=""><code>--local-path</code> — kubeconfig の保存パスを設定する。デフォルト値は <code>./kubeconfig</code> で、デフォルトではこのファイルは上書きされる</li>
<li class=""><code>--merge</code> — 上書きせずに既存ファイルへ設定をマージする。例えばデフォルトの kubectl 設定に追加する場合:<code>--local-path ~/.kube/config --merge</code></li>
<li class=""><code>--context</code> — kubeconfig のコンテキスト名を設定する。デフォルト値は <code>default</code></li>
<li class=""><code>--ssh-port</code> — SSH ポートを指定する。デフォルトは 22</li>
<li class=""><code>--k3s-extra-args</code> — k3s インストーラーに渡す任意の追加引数。引用符で囲む必要がある。例:<code>--k3s-extra-args '--no-deploy traefik'</code> や <code>--k3s-extra-args '--docker'</code>。複数の引数はシングルクォート内にまとめて書く:<code>--k3s-extra-args '--no-deploy traefik --docker'</code></li>
<li class=""><code>--k3s-version</code> — k3s の特定バージョンを設定する。例:<code>v1.21.1</code></li>
<li class=""><code>--k3s-channel</code> — チャンネルで k3s のバージョンを設定する。例:<code>stable</code></li>
<li class=""><code>--ipsec</code> — k3s に <code>--flannel-backend ipsec</code> の使用を強制する</li>
<li class=""><code>--print-command</code> — SSH 経由でリモートマシンに送信されるコマンドを表示する</li>
<li class=""><code>--datastore</code> — SQL 接続文字列を k3s の <code>--datastore-endpoint</code> に渡す。フォーマットは<a href="https://rancher.com/docs/k3s/latest/en/installation/ha/" target="_blank" rel="noopener noreferrer" class="">Rancher ドキュメントの k3s が要求する形式</a>に従って記述する必要がある</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="クラスターのコントロールパネル">クラスターのコントロールパネル<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%83%BC%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%AB%E3%83%91%E3%83%8D%E3%83%AB" class="hash-link" aria-label="クラスターのコントロールパネル への直接リンク" title="クラスターのコントロールパネル への直接リンク" translate="no">​</a></h2>
<p>k3s は kuboard と組み合わせて使えます。kuboard コントロールパネルのドキュメント:<a href="https://kuboard.cn/overview/share-coder.html" target="_blank" rel="noopener noreferrer" class="">Kuboard介绍 | Kuboard</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2022/06/02/k3s-ji-qun-da-jian#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>これで k3s クラスターが手に入り、ビジネス規模を水平方向にスケールできるようになりました。一連の流れは k3sup が SSH 経由でリモートで完結してくれるため、kubeadm を手作業で実行するよりずっと手間が省け、中国国内のネットワーク環境で何度も再構築するのにも向いています。K3S 自体が軽量で、組み込み etcd と組み合わせれば master の高可用性を実現できるので、小規模クラスターには良い選択肢です。今後はさらに各種モニタリングや service mesh 一式を統合していく予定です。</p>]]></content:encoded>
            <category>K8S</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[ディヴィニティ：オリジナル・シン2]]></title>
            <link>https://tanzhuo.xyz/ja/2022/01/13/shen-jie-yuan-zui-2</link>
            <guid>https://tanzhuo.xyz/ja/2022/01/13/shen-jie-yuan-zui-2</guid>
            <pubDate>Thu, 13 Jan 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[リヴェロン大陸では、リヴェロンの誕生を語るほぼすべての書物が七神とリヴェロンの起源に触れています。本記事では七神の誕生、そして神託者と覚醒者の登場を整理します。]]></description>
            <content:encoded><![CDATA[<p>リヴェロン大陸では、リヴェロンの誕生について語るほぼすべての書物が、七神とリヴェロンの起源に触れています。その真偽を確かめられる者は誰もいませんが、この物語はあらゆる種族の間で古くから語り継がれてきました。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="七神の誕生">七神の誕生<a href="https://tanzhuo.xyz/ja/2022/01/13/shen-jie-yuan-zui-2#%E4%B8%83%E7%A5%9E%E3%81%AE%E8%AA%95%E7%94%9F" class="hash-link" aria-label="七神の誕生 への直接リンク" title="七神の誕生 への直接リンク" translate="no">​</a></h2>
<p>伝説によれば、次元界も世界も、あらゆる内なるものが生まれる前、そこには純粋で手つかずのエネルギーしか存在しませんでした——生命を創造する原材料、ソース（源力）と呼ばれるものです。その対極にあるのがヴォイド（虚空）——破壊と虚無でした。この正反対の二つの存在は、互いに均衡を保ちながら在り続けていました。均衡と摩擦のあるところに生命は根を下ろし、宇宙が誕生します。そして、大地の植物が陽の光を求めるように、ソースは自らが創造をなしうる次元界への道を見出しました。同じように、ヴォイドも宇宙の次元界へと移ろい、大きな影響を及ぼし始めました。多くの次元界で、ソースとヴォイドはともに優位を占めようとしました。ソースが勝利した場所では、生命が花開きました。リヴェロンはまさにそのような世界のひとつです。</p>
<p>その強大なソースの力によって、この世界は力を宿した原初の生命——元素の神々を自然発生的に生み出しました。これが現在知られている神々の最初の姿です。時が経つにつれ、これらの神々はあるいは成長して強くなり、あるいは萎んで消えていきました。その力の変化は、彼らの創造物たちの行いに左右されました。たとえば、ほどなくして現れた原始信仰の神々——戦争の神や愛の神は、定命の者たちが戦争と愛を知るにつれて力を増していきました。</p>
<p>神々の成長とともに、六つの新興種族が、のちに「崇拝」と呼ばれる行為の中で、このソースの集中を正式に形づくりました。こうして七柱の主神が現れました——それぞれの主神が対応する種族を持ちます（そのうちの一柱は魔導士に対応します）。七神は自らの信徒の崇拝を通じてソースを獲得し、それを用いて力を得ました——神の力が増すにつれ、その種族もまた強大になっていきました。そして競争に敗れた神々や力の弱い神々は、この過程で姿を消していったのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-src-http___img.kxdw.com_2018_0108_20180108110747256.jpg-refer-http___img.kxdw-13ed6fe85d129658cdc922a7dc7f55d5.webp" width="1920" height="1080" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="神託者と覚醒者の登場">神託者と覚醒者の登場<a href="https://tanzhuo.xyz/ja/2022/01/13/shen-jie-yuan-zui-2#%E7%A5%9E%E8%A8%97%E8%80%85%E3%81%A8%E8%A6%9A%E9%86%92%E8%80%85%E3%81%AE%E7%99%BB%E5%A0%B4" class="hash-link" aria-label="神託者と覚醒者の登場 への直接リンク" title="神託者と覚醒者の登場 への直接リンク" translate="no">​</a></h2>
<p>七神の力が増し続けるにつれ、彼らの間の摩擦もますます激しくなっていきました。誰も譲歩しようとせず、彼らの争いはそれぞれの種族によって証明されることになりました。戦火は乱れ飛び、長い間、彼らを再び団結させるものは何もありませんでした……ヴォイドが訪れるまでは。</p>
<p>現実世界の帳（とばり）は堅固であるとはいえ、完璧ではありません。ヴォイドは周期的に裂け目を見つけて侵入し、種族と神々そのものを脅かしました。七神はヴォイドを阻むために手を組まねばならないことを悟り、神託者（ゴッドウォーケン）——孤独なる定命の勇士を創り出しました。彼らは自らの神聖な力をその者に授け、死を超越して神の化身となる英雄へと変えたのです。この神託者はヴォイドを撃退し、現実の帳の外、ヴォイド自身の領域へと追い返しました。こうしたことが幾度も繰り返されました——一人の神託者が現れてはヴォイドを撃退するのです。最も新しく世界を救った神託者がルシアンで、彼はヴォイドと幾度も戦い、勝利を収めました。しかし今やルシアンは亡く、新たな神託者はまだ現れていません。</p>
<p>第一次カオス戦争でヴォイドの撃退に貢献した定命の英雄たちの末裔から、「覚醒者（ゴッドウォーケン候補）」と呼ばれる者たちが次々と現れるようになりました。その血統ゆえに、覚醒者は定命の者にはまれな鋭敏な技でソースを観察し、操ることができます。この能力によって、覚醒者は神々の贈り物——七神のすべての力を宿すもの——を受け取れる唯一の定命の存在となりました。神性の覚醒はあらゆる種族に現れ、一人ひとり異なるかたちで発現します。彼らは皆、生まれながらにソースを観察する能力を持ち、適切な訓練を積めばソースを吸収し、それを用いて信じがたい創造と破壊を成し遂げることができるのです。</p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[Jenkins による自動リリース]]></title>
            <link>https://tanzhuo.xyz/ja/2021/11/19/jenkins</link>
            <guid>https://tanzhuo.xyz/ja/2021/11/19/jenkins</guid>
            <pubDate>Fri, 19 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Jenkins で自動リリースフローを構築した記録：コードの取得、maven ビルド、docker-compose によるイメージのビルドとプライベートレジストリへのプッシュ、そしてリモートでの本番デプロイの起動まで。ハマりどころとクリーンアップの助言つき。]]></description>
            <content:encoded><![CDATA[<p>手作業でのリリースを重ねていると、毎回のパッケージング・転送・再起動という一連の流れは時間がかかるうえにミスも起きやすいものです。この記事では、Jenkins を使ってリリースプロセス全体をパイプラインとしてつなげた際の設定手順を記録します。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>パイプラインを導入する前のリリースはだいたいこんな流れでした。ローカルでパッケージングし、scp でサーバーに転送し、ログインしてサービスを停止し、jar ファイルを差し替えて再起動する。手順自体は複雑ではありませんが、すべて人力での実行に頼っており、環境設定の変更を 1 か所見落としたり、パッケージを別のマシンに転送してしまったりというのは、本番障害のよくある原因です。これらの作業を Jenkins に任せることで、手間が省けるのはもちろん、より重要なのはリリースプロセスが再現可能かつ追跡可能になることです。ビルドごとに記録が残り、問題が起きたときは指定バージョンのイメージにロールバックできます。</p>
<p>最近設定した Jenkins のリリースフローの手順の一部を記録しておきます。</p>
<p>全体の流れ：Jenkins で本番リリースを実行し、docker イメージをビルドしてプライベート docker イメージレジストリにアップロードし、docker login を設定して本番側でイメージのプッシュ/プルとデプロイ稼働を行います。</p>
<p>簡単に分解すると、このパイプラインは 4 つのことをやっています。git からコードを取得して環境設定を置換し、maven で jar パッケージをビルドし、docker-compose でイメージ化してプライベートレジストリにプッシュし、最後にリモートで対象サーバーに新しいイメージの取得と起動を通知します。以下、この順番で見ていきます。</p>
<p>docker のインストールや Jenkins イメージの取得については、既存の記事が多数あるためここでは省略します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プラグインの統合">プラグインの統合<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E7%B5%B1%E5%90%88" class="hash-link" aria-label="プラグインの統合 への直接リンク" title="プラグインの統合 への直接リンク" translate="no">​</a></h2>
<p>Jenkins 本体は単なるスケジューラーにすぎず、ビルド能力は基本的にグローバルツール設定とプラグインで補われます。そこで最初のステップは「グローバルツール設定」でビルドに必要なコンポーネントを設定することです。Jenkins は自動ダウンロード・インストールに対応しており、ホストマシン上の既存のインストールパスを指定することもできます。</p>
<p>maven、jdk、git、nodeJs、docker などの基本コンポーネントを設定・インストールします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-11-image-1-e2d082c744c33296646996dab9f2ca3a.webp" width="1587" height="886" class="img_ev3q"></p>
<p>ツールの設定が済んだら、次はプラグインセンターで外部システムと連携するプラグインをインストールします。ここでの原則は必要に応じてインストールすることです。コードを gitlab でホスティングしているなら gitlab プラグインを入れます。これにより gitlab の webhook プッシュを受信し、コードのコミット後に自動でビルドを起動できます。</p>
<p>各種サポートプラグイン（gitlab など）をインストールします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-11-image-15888d64c81502e58d0621da66a56419.webp" width="1600" height="165" class="img_ev3q"></p>
<p>nodejs プラグインはフロントエンドのリリースに使用します。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="1597" height="112" class="img_ev3q"></p>
<p>フロントエンドとバックエンドは同じ Jenkins で運用できます。ビルドツールが異なるだけで、バックエンドは maven を使い、フロントエンドのタスクでは nodeJs のバージョンを指定して npm build を実行すれば済みます。</p>
<p>最後に、リモートサーバーに接続してコマンドを実行できるプラグインも必要です。Jenkins が動いているマシンと本番稼働しているマシンは通常別物なので、ビルド成果物をイメージレジストリにプッシュした後、対象サーバーに取得と再起動を通知する手段が要ります。このステップは、このプラグインが SSH 経由でリモートでスクリプトを実行することで完了します。</p>
<p>リモートサーバーに接続してコマンドを実行するプラグインです。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="1600" height="165" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="パイプラインタスクの作成">パイプラインタスクの作成<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3%E3%82%BF%E3%82%B9%E3%82%AF%E3%81%AE%E4%BD%9C%E6%88%90" class="hash-link" aria-label="パイプラインタスクの作成 への直接リンク" title="パイプラインタスクの作成 への直接リンク" translate="no">​</a></h2>
<p>プラグインの準備ができたらタスクを作成します。タスクの本質は、一連のビルドステップを順番にオーケストレーションすることです。各ステップでどのコンポーネントを使い、どのコマンドを実行するかを指定し、前のステップの成果物が次のステップの入力になります。</p>
<p>タスクを作成し、各段階の実行コマンドと使用コンポーネントを設定します。</p>
<p>まず取得する git リポジトリのアドレスとプロジェクトコードを設定します。</p>
<p>その後、ソースコード内の環境値を置換します。たとえば dev 環境を pro の nacos 接続アドレスやネームスペースなどに置き換えます。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-11-image-4-d082c74127085b930fc6786f03f16aff.png" width="1269" height="136" class="img_ev3q"></p>
<p>環境値の置換というステップは、もう少し補足する価値があります。開発環境と本番環境では設定センターのアドレスやネームスペースが異なることが多く、コードリポジトリにデフォルトで dev 設定が書かれている場合、本番パッケージのビルド前にそれを置き換えなければなりません。さもないと、サービスが本番稼働後にテスト環境の nacos に接続してしまいます。</p>
<p>私が使ったのは最もシンプルな sed -i コマンドです。このコマンドは正規表現にマッチさせてテキスト文字列を置換できます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># -i はファイル自体を直接変更する指定。s/旧値/新値/g は正規表現置換で、g はマッチしたすべてを置換</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">sed</span><span class="token plain"> </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-i</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'s/dev-nacos-addr/pro-nacos-addr/g'</span><span class="token plain"> src/main/resources/bootstrap.yml</span><br></div></code></pre></div></div>
<p>sed の利点は依存ゼロで、スクリプト 1 行で解決できることです。欠点は置換の対応関係が Jenkins のタスク設定に散在してしまい、設定項目が増えるとメンテナンスしづらくなることです。プロジェクトの規模が大きくなってきたら、maven profile や設定センターのマルチ環境機能を使い、環境差分を一箇所に集約することを検討するとよいでしょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リソースのパッケージングとプッシュ">リソースのパッケージングとプッシュ<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E3%81%A8%E3%83%97%E3%83%83%E3%82%B7%E3%83%A5" class="hash-link" aria-label="リソースのパッケージングとプッシュ への直接リンク" title="リソースのパッケージングとプッシュ への直接リンク" translate="no">​</a></h2>
<p>maven で jdk build を実行し、プロジェクトの jar パッケージをビルドします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-11-image-5-7fe705d054ce0e93b99727213b30f198.webp" width="1475" height="788" class="img_ev3q"></p>
<p>パッケージングが完了したら、shell コマンドで target ディレクトリ配下の jar パッケージを対応する docker-compose ファイルの場所に cp します。ここでは docker-compose ファイルと、対応するサービスの docker-file ファイルを事前に書いておく必要があります。</p>
<p>この 2 つのファイルを事前に用意しておく理由は次のとおりです。Dockerfile は単一のサービスをどうイメージ化するか（ベースイメージ、jar のコピー、起動コマンド）を記述し、docker-compose は複数サービスのビルド宣言を 1 つにまとめます。こうすることで 1 コマンドでイメージを一括生成でき、サービスごとに個別に docker build する必要がなくなります。</p>
<p>docker-compose を実行してイメージを一括ビルドします。</p>
<p>ビルド完了後、docker tag を実行してイメージにバージョンタグを付けます。</p>
<p>タグ付けの目的は、リリースごとのイメージに一意のバージョン番号を持たせることであり、すべてを latest と呼ばないことです。Jenkins 組み込みのビルド番号をタグに使うのは手軽な方法で、ロールバック時には過去のバージョン番号を直接指定するだけで済みます。</p>
<p>イメージのタグ付けが完了したら docker push でイメージレジストリにアップロードします。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ビルド番号でイメージにバージョンタグを付け、プライベートレジストリにプッシュ</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> tag myapp:latest registry.example.com/myapp:</span><span class="token variable" style="color:hsl(207, 82%, 66%)">${BUILD_NUMBER}</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> push registry.example.com/myapp:</span><span class="token variable" style="color:hsl(207, 82%, 66%)">${BUILD_NUMBER}</span><br></div></code></pre></div></div>
<p>プッシュ前に、Jenkins が動いているマシンと対象サーバーの両方でプライベートレジストリに docker login しておくことを忘れないでください。さもないと push/pull が認証失敗で中断します。</p>
<p>リモートで対象サーバーまたは k8s master ノードに接続し、job コマンドを実行してイメージのダウンロードアドレスを渡します。</p>
<p>ここまでで、ビルドマシンの仕事は終わりです。リモートコマンドでは対象サーバーに新しいイメージの完全なアドレスを伝えるだけでよく、あとはサーバー自身がイメージを pull してコンテナを再起動します。ビルドと稼働が完全に分離されるため、対象サーバーにはビルドツールを一切インストールする必要がありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="過去リソースの削除">過去リソースの削除<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E9%81%8E%E5%8E%BB%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%AE%E5%89%8A%E9%99%A4" class="hash-link" aria-label="過去リソースの削除 への直接リンク" title="過去リソースの削除 への直接リンク" translate="no">​</a></h2>
<p>パッケージングしてプライベートレジストリにアップロードした後は、ローカルワークスペース内の不要なリソースや docker イメージなどのリソースをクリアすることを忘れないでください。複数回のリリース後にリソース使用量が高くなりすぎる問題を回避できます。これでクローズドループの運用が形成され、プロジェクトのバージョンを何度でもリリースできます。</p>
<p>クリーンアップはパイプラインの最後のステップとしてそのまま組み込めます。ワークスペース内のビルド成果物を削除し、今回ビルドしたローカルイメージも消しておきます。docker のイメージはレイヤー構造で保存されるため、旧バージョンのイメージを掃除しないと積み上がり続け、数十回のリリース後にはディスクがいっぱいになり、ビルドが原因不明のまま失敗するようになります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">
<p>Jenkins をコンテナで動かしている場合、タスク内で docker コマンドを実行するには、ホストマシンの docker.sock をコンテナにマウントする必要があります。さもないとコンテナ内から docker デーモンが見つかりません。</p>
</li>
<li class="">
<p>プライベートレジストリが https ではなく http の場合、docker はデフォルトで接続を拒否します。プッシュ側・プル側両方の docker 設定で、レジストリのアドレスを insecure-registries に追加する必要があります。</p>
</li>
<li class="">
<p>sed で設定を置換する際は特殊文字のエスケープに注意してください。置換内容に <code>/</code> が含まれる場合は <code>#</code> などのデリミタに切り替えると、sed 自身のデリミタとの衝突を避けられます。</p>
</li>
<li class="">
<p>リモートコマンド実行の認証情報（SSH 秘密鍵、レジストリのアカウントとパスワード）は Jenkins の認証情報管理に保存し、タスクの shell スクリプトに平文で書かないようにしましょう。</p>
</li>
</ol>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>イメージのタグはできるだけ git のコミットやビルド番号と関連付けておきましょう。本番の問題を調査するとき、特定のコード変更にすばやく対応づけられます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2021/11/19/jenkins#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>パイプライン全体が通れば、リリースはビルドボタンを 1 回押す（あるいはコードのコミットで自動起動する）だけになります。コードの取得、設定の置換、maven でのパッケージング、docker-compose でのイメージ生成、タグ付けとプッシュ、リモートでの新バージョン起動、後片付けまで、全工程が人手を介しません。手作業のリリースと比べた利点は速さだけではなく、すべてのステップがタスク設定に固定化されることで、リリース結果が安定して予測可能になり、ロールバックもイメージのタグを差し替えるだけで済む点にあります。</p>]]></content:encoded>
            <category>技術</category>
            <category>CI/CD</category>
        </item>
        <item>
            <title><![CDATA[ウィッチャー3 ワイルドハント]]></title>
            <link>https://tanzhuo.xyz/ja/2021/08/10/wu-shi-3</link>
            <guid>https://tanzhuo.xyz/ja/2021/08/10/wu-shi-3</guid>
            <pubDate>Tue, 10 Aug 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[『ウィッチャー3 ワイルドハント』は小説『魔法剣士ゲラルト（ウィッチャー）』シリーズの世界観を受け継いでいます。本記事では「天体の交わり」という背景設定、ウィッチャーの由来、そしてゲーム本編の幕開けを簡潔に紹介します。]]></description>
            <content:encoded><![CDATA[<p>本作は小説『<a href="https://baike.baidu.com/item/%E7%8C%8E%E9%AD%94%E4%BA%BA/7252589?fromModule=lemma_inlink&amp;ref=tanzhuo.xyz" target="_blank" rel="noopener noreferrer" class="">魔法剣士ゲラルト（ウィッチャー）</a>』シリーズの世界観を受け継いでいます。小説の物語が始まる 1500 年前、ある大災厄が起こりました。異なる次元の世界同士が衝突し、世界は直接つながり、互いに浸透し合ったのです。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="天体の交わり">天体の交わり<a href="https://tanzhuo.xyz/ja/2021/08/10/wu-shi-3#%E5%A4%A9%E4%BD%93%E3%81%AE%E4%BA%A4%E3%82%8F%E3%82%8A" class="hash-link" aria-label="天体の交わり への直接リンク" title="天体の交わり への直接リンク" translate="no">​</a></h2>
<p>この災厄は「天体の交わり（The Conjunction of the Spheres）」と呼ばれ、さまざまな異世界から来た多くの生物がこの世界に閉じ込められることになりました。たとえばグールやネッカー、上位吸血鬼などは、天体の交わりが残していった残滓です。これらの生物は独自の生態を持つ、過ぎ去った時代の遺物といえる存在です。エルフの伝承によれば、人類は自分たちの元の世界が滅んだためにこの世界へやって来たとされています。人類の祖先は混沌の力を御する術を学び、そこから最初の人類の魔法使いが誕生し、異世界から来た他の生物たちと戦いました。しかし怪物は各地に散らばり、討伐は容易ではありません。普通の人間には怪物と渡り合う力がないという状況の中で、ウィッチャーが生まれました。彼らは怪物を狩るために普通の人々に雇われる変異した人間であり、それを生業として暮らしを立て、人々を怪物の脅威から守っているのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-src-http___img-baofun.zhhainiao.com_fs_9a80f29dd057d4656d3e1132ad4bc3e3.jpg-refer-http___img-baofun.zhhainiao-794c46ab1ff3d30afb6e83e764945a42.webp" width="1920" height="1080" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ゲームのストーリー">ゲームのストーリー<a href="https://tanzhuo.xyz/ja/2021/08/10/wu-shi-3#%E3%82%B2%E3%83%BC%E3%83%A0%E3%81%AE%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AA%E3%83%BC" class="hash-link" aria-label="ゲームのストーリー への直接リンク" title="ゲームのストーリー への直接リンク" translate="no">​</a></h2>
<p>記憶を取り戻したゲラルトは、イェネファーの足取りを探して各地を巡り始めます。やがて、数年間音信のなかったイェネファーからゲラルトに連絡が入り、ウィロビーの村で落ち合いたいと告げられます。しかしゲラルトが約束の場所に駆けつけたとき、村はすでに軍勢によって焦土と化しており、イェネファーの姿もありませんでした。道中でゲラルトはヴェセミルと出会い、彼の助けを借りてともにイェネファーの行方を追います。旅の途中、ゲラルトはケィア・モルヘンでの出来事を夢に見ます。その夢は、養女であるシリが何らかの危機に見舞われていることをゲラルトに警告するものでした。ゲラルト一行はホワイト・オーチャードに滞在し、そこの旅館にイェネファーが現れます。傍らにはニルフガード兵の護衛がついていました。旧知の恋人同士が積もる話をする間もなく、イェネファーは一刻も早く自分とともにヴィジマへ赴き、ニルフガード皇帝に謁見するようゲラルトに強く求めます。一行は道中でワイルドハントの襲撃に遭いますが、どうにか目的地にたどり着きました。ニルフガード皇帝との謁見で、ゲラルトはシリが遥か遠くの国から帰還したものの、命の危険にさらされ、ワイルドハントに追われていることを知ります。ここからゲームの本編が正式に幕を開けるのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-src-http___img.3dmgame.com_uploads_images_news_20211007_1633573821_869066.jpg-refer-http___img.3dmgame-00debfd584cc583ff322591dbed76ddb.webp" width="1920" height="1200" class="img_ev3q"></p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[Java ロジックエンジンの構想]]></title>
            <link>https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si</link>
            <guid>https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si</guid>
            <pubDate>Wed, 16 Jun 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[ファクトリ、プロキシ、ストラテジー、デコレーターパターンと SPI の考え方をビジュアルなロジックエンジンに封じ込める構想: Kettle のようにコンポーネントをドラッグ＆ドロップしてビジネスロジックを編成し、バックエンド開発の重複コードを減らします。]]></description>
            <content:encoded><![CDATA[<p>何年も仕事をしてきて、デザインパターンの重要性を痛感しています。デザインパターンを適切に使えば、複雑なビジネスロジックをより柔軟に実装でき、ポリモーフィズム、拡張性、保守性をより良くサポートできます。ならば一歩進んで——デザインパターンをはじめから内蔵したロジックエンジンを作れないだろうか？このノートでは、その構想を記録しておきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="解決したい問題">解決したい問題<a href="https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si#%E8%A7%A3%E6%B1%BA%E3%81%97%E3%81%9F%E3%81%84%E5%95%8F%E9%A1%8C" class="hash-link" aria-label="解決したい問題 への直接リンク" title="解決したい問題 への直接リンク" translate="no">​</a></h2>
<p>バックエンドのビジネス開発には気まずい現実があります。大量のコードは実のところ同じ骨組みの繰り返しなのです——パラメータを受け取り、データを検索し、条件によって分岐し、結果を組み立て、永続化する。毎回この流れを手書きしなければならず、デザインパターンをうまく使える人はそれをきれいに抽象化しますが、そうでない人は巨大な if-else の塊を書いてしまいます。</p>
<p>問題は開発者がデザインパターンを理解していないことではなく、プロジェクトごとにファクトリやストラテジーといったパターンを改めて実装し直さなければならず、その実装品質もまちまちだという点にあります。もしこの抽象化の層をエンジンに沈殿させることができれば、ビジネス開発は本当のビジネスロジックそのものにだけ集中すればよくなります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コアとなる構想">コアとなる構想<a href="https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si#%E3%82%B3%E3%82%A2%E3%81%A8%E3%81%AA%E3%82%8B%E6%A7%8B%E6%83%B3" class="hash-link" aria-label="コアとなる構想 への直接リンク" title="コアとなる構想 への直接リンク" translate="no">​</a></h2>
<p>私の構想はこうです。バックエンドの Java 開発者のためにあつらえた、デザインパターンをはじめから備えた UI ロジックエンジンを設計する。開発者はロジックコンポーネントを組み合わせ、コンポーネントをダブルクリックして SQL や Java コードを書き、コンポーネントのメンバー変数をコンポーネント間で受け渡し、最終的に永続化するだけです。</p>
<p>原理は現在の Kettle ツールに似ています——Kettle がやっているのはデータ抽出・変換のビジュアルな編成ですが、このエンジンがやるのはビジネスロジックのビジュアルな編成です。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-06-image-4c7820a68dee74f2dae3cf60411a78e5.webp" width="892" height="517" class="img_ev3q"></p>
<p>コンポーネントは最小のロジック単位で、各コンポーネントは 1 つのことだけを行います。1 回のクエリ、1 回のバリデーション、1 回の計算、あるいは 1 回の書き込みです。複雑なビジネスとは、これらの単位を順序や条件に従って組み合わせた 1 枚のフローチャートにほかなりません。こうしたロジックエンジンがあれば、Java 開発は間違いなく重複作業を大幅に減らせるはずです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デザインパターンがどう支えるか">デザインパターンがどう支えるか<a href="https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si#%E3%83%87%E3%82%B6%E3%82%A4%E3%83%B3%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%8C%E3%81%A9%E3%81%86%E6%94%AF%E3%81%88%E3%82%8B%E3%81%8B" class="hash-link" aria-label="デザインパターンがどう支えるか への直接リンク" title="デザインパターンがどう支えるか への直接リンク" translate="no">​</a></h2>
<p>この構想のインスピレーションは、私が仕事で実際に使っているいくつかのパターンの組み合わせから来ています。ファクトリパターン、プロキシパターン、ストラテジーパターン、デコレーターパターン、そして SPI の考え方です。エンジンの中でそれぞれに役割分担があります。</p>
<ol>
<li class="">
<p>ファクトリパターン: 設定に基づいて具体的なロジックコンポーネントのインスタンスを生成する役割を担います。UI 上でドラッグして配置した各ノードは、実行時にファクトリが対応するオブジェクトを産出します。</p>
</li>
<li class="">
<p>ストラテジーパターン: 各ロジックコンポーネントは 1 つのストラテジー実装であり、同じフローノードは全体の編成に影響を与えることなく別の実装に差し替えられます。</p>
</li>
<li class="">
<p>プロキシパターン: 実行の入口をプロキシストラテジークラスに集約し、パラメータの受け渡しや例外処理といった横断的なロジックを一手に引き受けてから、本当のコンポーネントへ転送します。</p>
</li>
<li class="">
<p>デコレーターパターン: コンポーネント自体に手を加えることなく能力を積み重ねます。たとえばあるコンポーネントにログやキャッシュの外殻を被せる、といった具合です。</p>
</li>
<li class="">
<p>SPI の考え方: コンポーネントは取り決められたインターフェースで外部に公開され、新しいコンポーネントはインターフェースに従って実装・登録するだけでエンジンに発見されます。エンジン自体のコードを変更する必要はありません。</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実行の仕組み">実行の仕組み<a href="https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si#%E5%AE%9F%E8%A1%8C%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="実行の仕組み への直接リンク" title="実行の仕組み への直接リンク" translate="no">​</a></h2>
<p>全体の流れは設定駆動です。UI コンポーネントから設定ファイルを生成し、設定ファイルがプロキシストラテジークラスに、その中の対応するコンポーネントロジック単位の .class ファイルを呼び出すよう指示します。システム起動時にすべてのロジックストラテジーをロードし、これらの Spring Bean が正常にメモリへロードされることを保証します——クラスロードと Bean 初期化のコストを起動時に済ませておき、実行時にはテーブル参照と転送だけを行うのです。</p>
<p>以降の実行はすべてプロキシストラテジークラスを通じてロジック処理を行い、個々の Bean を組み立てることで、1 つの複雑なビジネスロジックを完成させます。</p>
<p>このような実装は単一責任の原則とオープン・クローズドの原則にも適っています。各コンポーネントは責務が単一で、ビジネスの追加はコンポーネントの追加と設定の変更によって行われ、既存コードの修正によっては行われません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ローコードプラットフォームとの関係">ローコードプラットフォームとの関係<a href="https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si#%E3%83%AD%E3%83%BC%E3%82%B3%E3%83%BC%E3%83%89%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82" class="hash-link" aria-label="ローコードプラットフォームとの関係 への直接リンク" title="ローコードプラットフォームとの関係 への直接リンク" translate="no">​</a></h2>
<p>現在の市場でこの構想に対応するプロダクトといえば、ローコードプラットフォームやノーコードプラットフォームになるのでしょう。ただし位置づけには違いがあります。ローコードプラットフォームの多くはビジネス担当者やフルスタックのシーンに向いていて、ページまで含めて生成しようとします。一方この構想はバックエンドの Java 開発者だけに向いており、SQL や Java コードを書くことを避けず、編成とデザインパターンという骨組みの層だけをエンジンに委ねます。</p>
<p>これこそが、私がこの構想は実現可能だと考える理由です——「コードを書かない」ことを追求するのではなく、「重複する構造コードを書かない」ことだけを追求するのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2021/06/16/javaluo-ji-yin-qing-de-gou-si#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>このノートは本質的に、あるアイデアのアーカイブです。ファクトリ、プロキシ、ストラテジー、デコレーターと SPI を組み合わせて設定駆動のロジックエンジンを作り、手書きのフローの骨組みをビジュアルな編成で置き換える。必ずしも大きくて何でも揃ったプラットフォームに仕立てる必要はありません。まずは 1 つのプロジェクトで「コンポーネント + 設定 + プロキシストラテジー」というこの仕組みを通しで動かすだけでも、この方向性の価値を検証することができるでしょう。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Vuetify UI ライブラリ]]></title>
            <link>https://tanzhuo.xyz/ja/2021/02/22/vuetify</link>
            <guid>https://tanzhuo.xyz/ja/2021/02/22/vuetify</guid>
            <pubDate>Mon, 22 Feb 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Vuetify は Vue.js ベースの Material Design コンポーネントフレームワークです。主な特徴、ドキュメントの品質、適用シーン、そして選定時に注意すべき点を紹介します。]]></description>
            <content:encoded><![CDATA[<p>Vuetify は Vue.js をベースとした Material Design コンポーネントフレームワークで、豊富な UI コンポーネントとスタイルを提供し、美しくレスポンシブな Web アプリケーションをより速く構築する手助けをしてくれます。Vuetify は Material Design の仕様に準拠しており、ボタン、カード、テーブル、フォーム、メニュー、ナビゲーション、ダイアログなど、標準化された大量のコンポーネントを提供しているため、一貫性と美しさを備えたユーザーインターフェースを素早く構築できます。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2023-05-image-5-c0296426bf2bb007f952c88106fb31f3.webp" width="1353" height="319" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜ-ui-コンポーネントライブラリに注目するのか">なぜ UI コンポーネントライブラリに注目するのか<a href="https://tanzhuo.xyz/ja/2021/02/22/vuetify#%E3%81%AA%E3%81%9C-ui-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%81%AB%E6%B3%A8%E7%9B%AE%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜ UI コンポーネントライブラリに注目するのか への直接リンク" title="なぜ UI コンポーネントライブラリに注目するのか への直接リンク" translate="no">​</a></h2>
<p>フロントエンドのプロジェクトでは、画面まわりの作業の多くは実は繰り返しです。ボタン、フォームバリデーション、モーダル、テーブルのページネーション——どのプロジェクトでも一通りやることになります。ゼロから自分で書くと時間がかかるだけでなく、ビジュアルとインタラクションの一貫性を保つのも難しくなります。成熟したコンポーネントライブラリはこうした共通部分を蓄積してくれて、人間はビジネスロジックに集中できます。これが、私が各社の UI ライブラリを継続的にウォッチしている理由です。</p>
<p>Vue エコシステムには選べるコンポーネントライブラリが少なくありません。Element や Ant Design Vue は管理画面寄りのデザイン言語を採用していますが、Vuetify は別の道を選びました。Google の Material Design 仕様を完全に実装するという道です。プロジェクト自体が Material 風のスタイルに寄せたい場合や、Android 側とビジュアルの統一を保ちたい場合、Vuetify はとても自然な選択肢になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="主な特徴">主な特徴<a href="https://tanzhuo.xyz/ja/2021/02/22/vuetify#%E4%B8%BB%E3%81%AA%E7%89%B9%E5%BE%B4" class="hash-link" aria-label="主な特徴 への直接リンク" title="主な特徴 への直接リンク" translate="no">​</a></h2>
<p>Vuetify の主な特徴は次の通りです。</p>
<ol>
<li class="">Vue.js ベース：Vuetify は Vue.js をベースとしたコンポーネントフレームワークで、Vue.js アプリケーションとうまく統合でき、豊富なコンポーネントとスタイルを提供します。コンポーネントは props で設定し、イベントとスロットで拡張します。書き方は普通の Vue コンポーネントと変わらず、学習コストは主に各コンポーネントの属性に慣れることにあります。</li>
<li class="">Material Design スタイル：Vuetify は Material Design 仕様に準拠し、標準化された大量のコンポーネントとスタイルを提供して、一貫性と美しさを備えたユーザーインターフェースの構築を助けてくれます。仕様が定めるのは配色や角丸だけでなく、影の階層、モーションカーブ、スペーシング体系といった細部も含まれます。仕様に沿って進めれば、ページが雑然としにくくなります。</li>
<li class="">レスポンシブデザイン：Vuetify はレスポンシブデザインを提供し、画面サイズとデバイスタイプに応じてレイアウトとスタイルを自動調整できます。グリッドシステムとブレークポイント（xs から xl）を内蔵しており、レイアウトコンポーネントはビューポート幅に応じて自動的に切り替わるため、モバイル対応で追加のメディアクエリを書く必要はほぼありません。</li>
<li class="">プラグイン化設計：Vuetify はプラグイン化された設計を採用しており、コンポーネントの拡張やカスタマイズが簡単にでき、さまざまなビジネス要件に対応できます。テーマカラー、フォント、コンポーネントのデフォルト挙動は初期化時に一括設定でき、コンポーネントのオンデマンドインポートによるバンドルサイズの制御もサポートしています。</li>
<li class="">豊富なコンポーネントライブラリ：Vuetify はボタン、カード、テーブル、フォーム、メニュー、ナビゲーション、ダイアログなど、豊富な UI コンポーネントとスタイルを提供しており、美しくレスポンシブな Web アプリケーションを素早く構築できます。基礎的なコンポーネントに加えて、日付ピッカーやデータテーブルといった自前実装が面倒な部類のコンポーネントもカバーされています。</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ドキュメントとエコシステム">ドキュメントとエコシステム<a href="https://tanzhuo.xyz/ja/2021/02/22/vuetify#%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%E3%81%A8%E3%82%A8%E3%82%B3%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" class="hash-link" aria-label="ドキュメントとエコシステム への直接リンク" title="ドキュメントとエコシステム への直接リンク" translate="no">​</a></h2>
<p>Vuetify は充実した中国語ドキュメントも提供しています。今のところ、彼らのドキュメントはかなり丁寧に書かれていると感じており、コンポーネントのすべての属性と状態が相当明確に記述されています。各コンポーネントのページにはインタラクティブなサンプルが付属し、属性、イベント、スロットがタブで整理されていて、とても調べやすいです。ドキュメントの品質は選定時に過小評価されがちですが、コンポーネントライブラリを使い込んでいくと、時間の大半は実はドキュメントを調べることに費やされるのです。</p>
<p>現時点で、Alibaba のオープンソースプロジェクトでの採用が確認できます。</p>
<p><a href="https://vuetifyjs.com/en/" target="_blank" rel="noopener noreferrer" class="">Vuetify — A Vue Component Framework</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2021/02/22/vuetify#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>選定前に考えておくべき点がいくつかあります。</p>
<p>1）デザインスタイルへの結びつきが深いこと。Material Design のビジュアル特性はかなり明確なので、デザインカンプが別のスタイルで作られている場合、Vuetify を無理にカスタマイズするコストは得られるメリットを上回るかもしれません。そういう場合は、よりニュートラルなスタイルのライブラリを選ぶほうが適切です。</p>
<p>2）バンドルサイズに注意。コンポーネントとスタイルを全量インポートするとサイズは小さくありません。本番プロジェクトではオンデマンドインポートで、使うコンポーネントだけをバンドルすることをおすすめします。</p>
<p>3）Vue とのバージョン対応関係。Vuetify のメジャーバージョンは Vue のメジャーバージョンと結びついています。アップグレード前に、ターゲットバージョンが現在の Vue バージョンをサポートしているか確認しましょう。アップグレードの途中でコンポーネントの API が変わっていたと気づく事態は避けたいものです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2021/02/22/vuetify#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Vuetify の位置づけは明確です。Vue の中で Material Design を完全に実現すること。コンポーネントのカバー範囲が広く、ドキュメントの品質が高く、レスポンシブ対応もすぐに使えるため、Material スタイルを受け入れ、素早く画面を組み上げたいプロジェクトに適しています。逆に、ビジュアルに強いカスタマイズ要求があるなら、テーマ変更のコストをよく見積もる必要があります。</p>
<p>今後、自分のフロントエンドプロジェクトで使ってみるつもりです。</p>]]></content:encoded>
            <category>フロントエンド</category>
        </item>
        <item>
            <title><![CDATA[Kettle ツールの使い方]]></title>
            <link>https://tanzhuo.xyz/ja/2021/02/04/kettlegong-ju-shi-yong</link>
            <guid>https://tanzhuo.xyz/ja/2021/02/04/kettlegong-ju-shi-yong</guid>
            <pubDate>Thu, 04 Feb 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Kettle を使って複数データソース間の同期・移行・変換を行った経験の記録。コアとなる概念、よく使うコンポーネントの用途、そしてクエリベースの CDC 方式が適する場面と限界について。]]></description>
            <content:encoded><![CDATA[<p>最近、ETL ツールの Kettle を使って、複数データソース間のデータ同期、移行、変換、修正といった機能を調べながら実際に使ってみました。</p>
<!-- -->
<p>業務システムを作っていると、こんな要件によく出会います。旧システムのデータを新システムに移したいがテーブル構造が両者で異なる、あるいは複数のデータベースのデータをレポート用データベースに集約したいがフィールド名、ステータスコード、データ型がまるで一致しない、といったケースです。この種の作業をすべて手書きのスクリプトでこなすと、SQL とインポート・エクスポートのプログラムを大量に書くことになり、要件が変わるたびにコードを直す羽目になって保守が大変です。ETL ツールが解決するのはまさにこの問題で、抽出（Extract）、変換（Transform）、ロード（Load）の三つのステップをフロー化・可視化します。Kettle はこの種のツールの中でも代表的なオープンソース実装の一つです。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,UklGRroDAABXRUJQVlA4WAoAAAAQAAAAMQAAMQAAQUxQSGgAAAABV6CojRQ4uhc/cIMKPBARAffxizsE20iSnFx9DP8+KWASBiYZQCLS1RABf2Ei52yciP4rSNuA2bEQEZLWUb+7l16S6TxZ4fLzbGFQ2mzBqQ6cPuLs+gvWgIKBkZnDM3KqDLgZOOSAClZQOCAsAwAA0BEAnQEqMgAyAD5tMJVHJCKiISgYC1CADYlsAHfR9a/E/mSOgiIn+X6QG2A/Yz1Afsh1E/QA/UDrRPQA6TP9m/Rru6Sjd+QGTrfGZnveCMVWoKc7/j/FH9EewN+rnWN/Y72QDEolNNzahDO8qPxFbXAxj2MefhSQMCLmVU1CgXm8PRqgsSqhLagaO2MNxKPHEPVkQW5VwAD++lqX5r3UgAx08fl1ave1YE+zozZrmXs4+e+tNT2P+cN5v/mPJr///4si/huEJ6MKP//+FNcKa3pxU4083zi9/Qc/a21EofhRbdK3xpjdhrHpVx+8fEDP9aSI9BfA/+FXwqRpZQRdXEgAn6IRBvbe/h5mip64VKe8aDWCLQl/7vYH/To9rd8CEc57uZHjgX7jZpUVTOSlDOvNDjvPCsrcB6Y1HwivOqEruHjYDlH0uOn/FuypEc9nrjeWBlWLd5WX47r/rpzCnR0uoYuYHf8ZNUM0ZZrahBmoHQ3P7W7vmvx59ia3C+XXMpXVmHoozxzuJ2SQ7WHvRT+161QPjDffDLnepHz/5O05TBaM1/5a3MdMFP+MASCYF3pnbeEzJC8kuW70FyXzSJFqnuvO8QcTF1eXpoiz963ncn+GEa9sUs+YBLV9SvacjOS9JGeV7jhyjWH/6oYLZuju5asH4w0vntxE0vdk1PPo/WcDrj8X2X1wX5bBFKEiA93LUFgcuEwv+DelK46YH7mZgp1rQ/8vNnn9eClf1biG9phuNZXQbzGL9cEBD4N19sWzJSI0xym1O0/8kp2sn2vNGD90ZsddoGAaLs4RapLYPZSgNH8FwvHWw+3Y0zGAz2gxxL6X8WOafTavHjU0Valf7LqAav+G+ijxdYGptc8f4P/7nA6QRetS8pendUF01TwvEu0gmdD0mJsuYFwHydzcbv6SOhAtGAAzR59mbpkQRY/YY8o6M+Pgt3OJoZO08e6YGsedIEhL17v54+4i1oZ/nEdPWdYMEHemmSlTgu8krYLRGNcvfkIoNlpPkArKoqyUoSGZVvsB5/MsLeiPMyZNidQb/lLpbiKzL6pSbsJRqQOjIPPdu3gAAAA=" width="50" height="50" class="img_ev3q"></p>
<p>Kettle は「やかん」という意味で、その名の通り、さまざまなデータソースのテーブルデータを水の流れに見立て、複数の流れを合流・分流・解析するツールです。オープンソースのデータ統合ツールであり、データの抽出、変換、ロード（ETL）など豊富なデータ処理機能を提供します。Kettle の中核は GUI ベースのデザインツールで、ユーザーは簡単なドラッグ&amp;ドロップと接続操作だけでデータ処理フローを構築できます。また、強力なデータ処理エンジンを備えており、マルチスレッドと分散処理をサポートし、大規模なデータを効率よく処理できます。同時に、リレーショナルデータベース、ファイル、Web サービスなど多様なデータソースとターゲットをサポートしており、各種データソースと手軽に統合できます。さらに豊富なプラグイン機構も用意されており、ユーザーが独自のプラグインを開発して Kettle の機能を拡張することも可能です。要するに、Kettle は機能が強力で使いやすく拡張性にも優れたデータ統合ツールであり、データウェアハウス、BI、データ分析などの分野で広く使われています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="動作の仕組み">動作の仕組み<a href="https://tanzhuo.xyz/ja/2021/02/04/kettlegong-ju-shi-yong#%E5%8B%95%E4%BD%9C%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="動作の仕組み への直接リンク" title="動作の仕組み への直接リンク" translate="no">​</a></h2>
<p>Kettle には二つの基本概念があります。変換（Transformation）と作業（Job）です。変換は具体的なデータフロー処理を担当し、一つ一つのステップ（Step）をホップ（Hop）と呼ばれる接続線でつないだもので、データは行単位でステップ間を流れていきます。作業はより上位のスケジューリング単位で、複数の変換を順序や条件に従って組織して実行できます。GUI デザイナーで描いたフローは、保存すると一つの XML 記述ファイルになり、デザイナー上で直接実行することも、コマンドラインツールに渡してサーバー上で定期実行することもできます。</p>
<p>各ステップは実行時に独立したスレッドとして動き、上流のステップが一行のデータを生成するたびに下流へ一行ずつ押し出していきます。フロー全体はパイプライン方式で、前のステップの処理がすべて終わるのを待つ必要はありません。これが比較的大きなデータ量を処理できる理由の一つで、データを一度にすべてメモリに載せることがないのです。</p>
<p>プログラミングは一切不要で、コンポーネントを手動でドラッグして設定するだけで、複雑なデータ処理機能を実現できます。CDC の観点で言うと、Kettle はクエリベースの方式でデータの読み取りと変換を行うため、一回限りのデータ移行・変換に適しています。リアルタイム性の要求が高い場面には使えません。</p>
<p>ここで少し補足します。CDC（Change Data Capture、変更データキャプチャ）の実装には大きく二つの考え方があります。一つはログベースで、例えば MySQL の binlog を解析する方式です。データベースで変更が発生するたびに、ほぼリアルタイムでキャプチャできます。もう一つはクエリベースで、定期的に SQL を実行し、タイムスタンプや自動採番主キーを比較して変化したデータを見つけ出す方式です。Kettle は後者に属し、取得できるのはクエリを実行した瞬間のスナップショットです。二回のクエリの間にあった中間状態は感知できず、削除操作も発見しにくいという特性があります。そのため、一回限りの移行や定期的なバッチ同期といった場面に適しており、リアルタイム同期をしたいならログベースの方式に切り替える必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データ移行の小さな例">データ移行の小さな例<a href="https://tanzhuo.xyz/ja/2021/02/04/kettlegong-ju-shi-yong#%E3%83%87%E3%83%BC%E3%82%BF%E7%A7%BB%E8%A1%8C%E3%81%AE%E5%B0%8F%E3%81%95%E3%81%AA%E4%BE%8B" class="hash-link" aria-label="データ移行の小さな例 への直接リンク" title="データ移行の小さな例 への直接リンク" translate="no">​</a></h2>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-02-image-fb2bb7146b43fcc0dff8c1e94bf83070.webp" width="1600" height="879" class="img_ev3q"></p>
<p>Kettle にはかなり多くのコンポーネントが用意されており、さまざまな場面でのデータ転送、インポート、エクスポート、値マッピングなどの機能に対応できます。データを Excel ファイルとしてエクスポートすることも可能です。上図は典型的な移行フローです。ソースデータベースからデータを取得し、途中のいくつかのステップでクレンジング・変換を経て、最後にターゲットデータベースへ書き込みます。以下、フローに登場する順にこれらのよく使うコンポーネントを説明します。</p>
<p>テーブル入力：データベースで SQL を実行して、インポートするデータを取得します。これはフロー全体の起点です。SELECT 文を一つ書けば、クエリ結果の各行がデータフローの一行として下流に渡されていきます。SQL 内では変数によるパラメータ化ができるため、同じ変換を異なる環境で再利用しやすくなります。</p>
<p>テーブル出力：Kettle の実行で得られた最終結果セットをテーブルに出力します。フローの終点で、流れ込んできた各行のデータをターゲットテーブルに INSERT します。バッチコミットの件数を設定でき、一件ずつコミットするよりバッチ書き込みのほうがずっと高速です。</p>
<p>フィールド名の整備：データ列の絞り込みや、列のエイリアス設定などができます。ソーステーブルとターゲットテーブルでフィールド名が一致しないことはよくあります。例えば旧データベースでは user_name、新データベースでは username という場合、このステップで名前を統一しておけば、後続のステップはソーステーブルの命名を気にしなくて済みます。不要な列もここで直接破棄でき、後続ステップの処理量を減らせます。</p>
<p>ソート：データのフィールドに基づいてソートできます。単独ではあまり用途がありませんが、多くの場合は次のステップのためのものです。Kettle のマージ系コンポーネントは通常、二系統の入力が結合キーでソート済みであることを要求するため、マージの前には一般にそれぞれソートしておく必要があります。</p>
<p>データマージ：異なるソースからの二つのデータを結合するもので、MySQL の join 機能に似ています。二つのテーブル入力からのデータフローを指定フィールドで関連付けられるため、別々のデータベースにあるテーブル同士でも「join」できます。これは純粋な SQL ではできないことです。前提は先に述べた通り、両系統のデータをあらかじめ結合キーでソートしておくことです。</p>
<p>値マッピング：多くのデータベースでステータス値 1、2、3 だったステータスコードが、新しいデータベースでは 4、5、6 になっている場合、値マッピングで値の置換ができます。本質的にはコンポーネント内に設定する対照表で、ソース値とターゲット値が一対一に対応し、マッチしなかった場合のデフォルト値も設定できます。</p>
<p>フィールド修正：データソースのフィールド名とデータ型を修正し、新しいデータソースへの移行をしやすくします。典型的なのは、旧データベースでは日付を文字列で保存していたが新データベースでは datetime 型である、あるいは数値の精度を調整する必要がある、といった場面です。すべてこのステップで変換しておけば、ターゲットデータベースへの書き込み時に型エラーが出るのを避けられます。</p>
<p>新規追加・更新：ターゲットデータソースに対して新規追加を実行し、対応する id がすでに存在する場合は更新を行います。いわゆる upsert です。指定したキーフィールドでターゲットテーブルを検索し、見つからなければ挿入、見つかれば更新します。増分同期ではテーブル出力の代わりにこれを使えば、変換を繰り返し実行しても重複データが発生しません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2021/02/04/kettlegong-ju-shi-yong#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）中国語の文字化け：データベース接続の文字セットはデータベース自体と一致させる必要があります。MySQL の接続パラメータでは明示的にエンコーディングを指定するのが望ましく、そうしないと移行後に中国語がすべて「?」になっていて、やり直しになります。</p>
<p>2）マージ前のソート忘れ：データマージ系のコンポーネントは入力がソート済みであることに依存します。ソートステップが抜けていても必ずしもエラーにはなりませんが、結合結果が正しくなくなります。この種の問題はエラーより発見しにくいので、必ず結果の行数を照合してください。</p>
<p>3）バッチコミットとトランザクション：テーブル出力のデフォルトのコミット件数は大きくすれば高速化できますが、失敗したときにどうするかを考えておく必要があります。途中でエラーが起きても、すでにコミットされたデータはロールバックされません。再実行する前にターゲットテーブルをクリアするか、「新規追加・更新」コンポーネントに切り替えて冪等性を保証しましょう。</p>
<p>4）大きなテーブルの移行：条件なしの SELECT による全件取得はソースデータベースへの負荷が小さくありません。できるだけ業務のオフピーク時間帯に実行するか、主キーや時刻でバッチに分けて実行しましょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2021/02/04/kettlegong-ju-shi-yong#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Kettle をうまく使えば、データベースのデータ管理を簡素化できます。プロジェクトの大きなバージョン変更で、データベース構造や新旧データの互換処理が必要になるとき、Kettle は良い選択肢の一つです。その位置づけは明確で、バッチ処理型のデータ搬送とクレンジングです。GUI によるフローが保守コストを下げ、この種の一回限り・あるいは周期的なデータタスクのために大量の使い捨てスクリプトを書かずに済むようにしてくれます。リアルタイム同期の要件については、ログベースの CDC ソリューションに任せましょう。ツールにはそれぞれの持ち場があります。</p>]]></content:encoded>
            <category>技術</category>
            <category>ビッグデータ</category>
        </item>
        <item>
            <title><![CDATA[SEKIRO: SHADOWS DIE TWICE(隻狼)]]></title>
            <link>https://tanzhuo.xyz/ja/2020/11/12/zhi-lang</link>
            <guid>https://tanzhuo.xyz/ja/2020/11/12/zhi-lang</guid>
            <pubDate>Thu, 12 Nov 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[『SEKIRO: SHADOWS DIE TWICE』は From Software が制作した三人称視点のアクションアドベンチャーゲームです。本記事では、ゲーム背景の二つの軸——葦名一心の国盗り、そして桜竜と不死の力——を整理します。]]></description>
            <content:encoded><![CDATA[<p>『SEKIRO: SHADOWS DIE TWICE(隻狼)』は、From Software が制作した三人称視点のアクションアドベンチャー型オープンフィールドゲームです。プレイヤーは一人の忍びを操作し、日本の高貴な血筋を引く主君の御子を救い出し、その宿敵に復讐を果たすことになります。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2022-10-src-http___img.3dmgame.com_uploads_images_news_20190201_1548990325_614408.jpg-refer-http___img.3dmgame-1338da6632be831416ebb589f3a13634.webp" width="1920" height="1080" class="img_ev3q"></p>
<p>ゲームの背景には二つの軸があります。一つは葦名一心による国盗り、もう一つは桜竜と不死の力です。まず葦名一心の国盗りから見ていきましょう。葦名の地はもともと葦名一族の本拠地でしたが、後に内府勢力に併呑されてしまいます。戦国末期、剣聖・葦名一心は奪われた土地を取り戻すため、葦名衆と、葦名の分家——つまり同じ血筋で姓の異なる平田家——を率いて内府の兵を打ち破りました。一心の孫である葦名弦一郎との初対面時の会話から、平田家の当主が弦一郎の叔父であることが分かります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="葦名一心の国盗り">葦名一心の国盗り<a href="https://tanzhuo.xyz/ja/2020/11/12/zhi-lang#%E8%91%A6%E5%90%8D%E4%B8%80%E5%BF%83%E3%81%AE%E5%9B%BD%E7%9B%97%E3%82%8A" class="hash-link" aria-label="葦名一心の国盗り への直接リンク" title="葦名一心の国盗り への直接リンク" translate="no">​</a></h2>
<p>平田家に仕える忍びこそが、戦場で主人公の狼を拾い育てた大柄な忍び・梟です。その特徴は肩まで垂れた蓬髪で、繁体字版では「猫頭鷹(フクロウ)」と訳されています。一方、同じ頃、主を持たないもう一人の忍び・猿——後に荒れ寺の仏師となる人物——は、戦場で当時まだ幼い少女だったエマに自分の握り飯を分け与えました。しかし猿はエマを自ら育てることはせず、親友である薬師・道玄に託します。酒を飲んだ猿が「忍びに付いていくより、薬師に付いていく方がましだ」と語ったのがその理由です。やがて医術に長けたエマは葦名一心お抱えの薬師となり、同時に一心の剣術も学びました。この点からも、同じ忍びでありながら梟と猿の違いが見て取れます。猿にはより善意があり、梟は自らの目的を果たすために、手駒を育てることを重んじたのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="桜竜と不死の力">桜竜と不死の力<a href="https://tanzhuo.xyz/ja/2020/11/12/zhi-lang#%E6%A1%9C%E7%AB%9C%E3%81%A8%E4%B8%8D%E6%AD%BB%E3%81%AE%E5%8A%9B" class="hash-link" aria-label="桜竜と不死の力 への直接リンク" title="桜竜と不死の力 への直接リンク" translate="no">​</a></h2>
<p>もう一つの軸が桜竜です。桜竜は下半身が桜の木、上半身が隻腕の竜という姿で、西方の竜の故郷に由来し、流れ着いて葦名に根を下ろしました。ゲーム中の不死の力はすべて、直接または間接的に桜竜に由来します。竜胤、蟲憑き、変若の水がそれで、この三つの力の中では竜胤が最も正統であり、桜竜そのものに最も近い力です。桜竜の不死には力の源が必要です。アイテム「神喰み」の説明文から、桜竜の力は葦名の土地に宿る草木の小さな神々の力を吸い上げていることが分かります。そして古い葦名の大地には、こうした吸い上げられる力の源となる小さな神々が数多く育まれていました。これこそが、桜竜が葦名に留まることを選んだ理由です。竜胤の力も同様で、使い手自身には影響がないものの、周囲の人々の生命力を必要とします。それが竜咳です。</p>
<p>また、桜竜の力は水にも影響を及ぼしました。桜竜に最も近い山頂に住む淤加美一族は、不死の力を宿した水を飲み、桜竜を崇拝して源の宮を築き、これを「仙郷」と美称しました。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-02-src-http___img.yxbao.com_article_image_201903_26_8620b32096.jpg-refer-http___img.yxbao-1-9750066dc196b2540a549f3da64fba4b.webp" width="1600" height="900" class="img_ev3q"></p>
<p>初代の竜胤の御子は淤加美一族の出身、すなわち丈様です。丈もその従者である巴様も、おそらくどちらも女性です。淤加美一族の主戦力は女性で、舞による攻撃力強化(三つ集めると鬼仏でスキルポイントを攻め力に変換できる、あのお面です)と雷を呼ぶ術を主な攻撃手段とし、男性はいずれも蛇身の奇怪な姿をしています。淤加美一族と源の宮については後の記事で詳しく述べます。丈様と巴様は竜胤を断つ方法を求めて故郷を離れ、葦名一心と葦名弦一郎と交わり、彼らに雷を呼ぶ術を伝授しました。同時に多くの書き付けを残し、それらは後に九郎とその忍び・狼が不死を断つための重要な手がかりとなるのです。</p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[IntelliJ IDEA 2020 JRebel クラック]]></title>
            <link>https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie</link>
            <guid>https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie</guid>
            <pubDate>Tue, 03 Nov 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[JRebel を IDEA でアクティベートする手順を記録し、あわせて JVM ホットデプロイの能力の境界と、いくつかの合法的な代替案を整理します。]]></description>
            <content:encoded><![CDATA[<p>IDEA に最新版の JRebel をインストールしたあとは、残りはアクティベーションのステップだけです。ついでにその手順を記録しつつ、ホットデプロイそのものの原理と境界もあわせて整理しておきます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜこれを使うのか">なぜこれを使うのか<a href="https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie#%E3%81%AA%E3%81%9C%E3%81%93%E3%82%8C%E3%82%92%E4%BD%BF%E3%81%86%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜこれを使うのか への直接リンク" title="なぜこれを使うのか への直接リンク" translate="no">​</a></h2>
<p>Java 開発で最も忍耐を消耗する工程のひとつが、コードを一行変えるたびにサービス全体を再起動しなければならないことです。ある程度の規模を持つ Spring アプリケーションでは、起動時にクラススキャン、Bean のインスタンス化、コンテキストのリフレッシュ、コネクションプールの初期化といった工程を通す必要があり、プロジェクトが大きいほど遅くなります。文言を変える、if の分岐を一つ調整する、ヌルポインタを一つ直す——そのどれもが、まるまる一回分の起動時間を代償として要求してきます。</p>
<p>JRebel がやることは、この待ち時間を丸ごと削り落とすことです。クラスファイルを変更すればそのまま反映され、プロセスは再起動せず、メモリ上のセッション・キャッシュ・コネクションもそのまま残ります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ホットデプロイの境界はどこにあるのか">ホットデプロイの境界はどこにあるのか<a href="https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie#%E3%83%9B%E3%83%83%E3%83%88%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%AE%E5%A2%83%E7%95%8C%E3%81%AF%E3%81%A9%E3%81%93%E3%81%AB%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="ホットデプロイの境界はどこにあるのか への直接リンク" title="ホットデプロイの境界はどこにあるのか への直接リンク" translate="no">​</a></h2>
<p>JVM 自体はホットスワップ能力を備えています。デバッガは JPDA を通じて JVMTI の <code>RedefineClasses</code> を呼び出し、すでにロード済みのクラスのバイトコードを差し替えられます——ただし差し替えられるのはメソッドボディだけです。</p>
<p>いったんクラスの構造に手を触れると、失敗します。</p>
<ul>
<li class="">メソッドの追加・削除</li>
<li class="">フィールドの追加・削除</li>
<li class="">継承関係や実装インターフェースの変更</li>
<li class="">アノテーションの変更、メソッドシグネチャの変更</li>
</ul>
<p>これらの操作は直ちに <code>schema change not implemented</code> を報告し、再起動するしかありません。</p>
<p>JRebel のやり方は標準の HotSwap を通すのではなく、クラスロードの段階で介入してバイトコードを書き換え、独自のバージョンマッピングを維持することです。だからこそ構造的な変更も反映できます。さらに Spring、Hibernate、MyBatis といったフレームワークへの適合も行っており——<code>@Service</code> を一つ追加する、XML マッピングを一段変える、Controller メソッドを一つ足す、といったときに、フレームワーク側のメタデータも一緒にリフレッシュされます。この適合作業は決して小さくなく、JRebel が有償である理由でもあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アクティベーションの手順">アクティベーションの手順<a href="https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie#%E3%82%A2%E3%82%AF%E3%83%86%E3%82%A3%E3%83%99%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E6%89%8B%E9%A0%86" class="hash-link" aria-label="アクティベーションの手順 への直接リンク" title="アクティベーションの手順 への直接リンク" translate="no">​</a></h2>
<p>アクティベーションページをクリックします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2020-11-image-3c1090b040f1384f1eb9111c5a5c4f67.webp" width="1600" height="991" class="img_ev3q"></p>
<p>オンラインサーバーによるライセンスアクティベーションを選択します。</p>
<p>サーバーのライセンス URL アドレスを入力します。</p>
<p>URL アドレスは：<a href="https://jrebel.qekang.com/5d45985a-26e1-4172-95ad-e75e551605a2" target="_blank" rel="noopener noreferrer" class="">https://jrebel.qekang.com/</a>{GUID}</p>
<p>このうち GUID は生成する必要があります（生成先）：</p>
<p><a href="https://www.guidgen.com/" target="_blank" rel="noopener noreferrer" class="">Generate GUIDs online</a></p>
<p>サイトにアクセスして GUID を生成したら、それを URL アドレスに書き込みます。</p>
<p>規約に同意するにチェックを入れ、アクティベートをクリックします。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2020-11-image-1-092fcb5e72c89913544c4338f2d3f8f2.webp" width="1600" height="991" class="img_ev3q"></p>
<p>これで JRebel のクラックは成功です。</p>
<p>ここでの URL は「サーバーアドレス + 一つの GUID」で組み立てた完全な文字列であり、ドメインだけを入れるのではありません。GUID はクライアント識別子のようなもので、自分で新しく一つ生成すれば十分です。後ろのメールアドレスは適当に入れて構いません。ライセンスサーバーは検証を行いません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）<strong>アクティベーション成功は反映されることと同義ではありません</strong>。JRebel は自身の Run / Debug ボタンでアプリケーションを起動する必要があります。通常の Run で起動すると agent がマウントされず、コードを変更してもやはり再起動が必要です。</p>
<p>2）<strong>rebel.xml が、どこに class を探しに行くかを決めます</strong>。JRebel はこのファイルを頼りに、ランタイムのクラスパスをプロジェクトのコンパイル出力ディレクトリへとマッピングし直します。Maven プロジェクトでは <code>jrebel-maven-plugin</code> で生成します。</p>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">&lt;!-- pom.xml の build/plugins に追加し、mvn jrebel:generate を実行して rebel.xml を生成する --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">plugin</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">groupId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">org.zeroturnaround</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">groupId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">artifactId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">jrebel-maven-plugin</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">artifactId</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">executions</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">execution</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">id</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">generate-rebel-xml</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">id</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">phase</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">process-resources</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">phase</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goals</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">                </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goal</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain">generate</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goal</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">            </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">goals</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">        </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">execution</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">executions</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&lt;/</span><span class="token tag" style="color:hsl(355, 65%, 65%)">plugin</span><span class="token tag punctuation" style="color:hsl(220, 14%, 71%)">&gt;</span><br></div></code></pre></div></div>
<p>IDEA のプラグインは通常このステップを自動で処理してくれますが、マルチモジュールプロジェクトでは各モジュールで生成されていることを確認してください。</p>
<p>3）<strong>外部ネットワークに依存します</strong>。このオンラインライセンス方式は起動のたびにライセンスサーバーへ接続する必要があるため、社内ネットワークの開発機やオフライン環境では検証の段階で直接止まってしまいます。</p>
<p>4）<strong>アップグレードで無効になりやすい</strong>。IDEA のメジャーバージョンが更新されるとプラグインが非互換になる可能性があり、ライセンスサーバー自体もいつ停止するかわかりません。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>サードパーティのライセンスサーバーを使うことは、本質的に JRebel の商用ライセンスを回避する行為です。個人でいじる分には問題ありませんが、商用プロジェクトでこれをやると法的リスクがあります。チーム環境では正規の license を通すことをおすすめします。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="より手間のかからない代替案">より手間のかからない代替案<a href="https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie#%E3%82%88%E3%82%8A%E6%89%8B%E9%96%93%E3%81%AE%E3%81%8B%E3%81%8B%E3%82%89%E3%81%AA%E3%81%84%E4%BB%A3%E6%9B%BF%E6%A1%88" class="hash-link" aria-label="より手間のかからない代替案 への直接リンク" title="より手間のかからない代替案 への直接リンク" translate="no">​</a></h2>
<p>再起動を数回減らしたいだけなら、必ずしも JRebel でなくても構いません。</p>
<ul>
<li class=""><strong>DCEVM + HotSwapAgent</strong>：オープンソースの方案です。DCEVM は改造された JVM で、HotSwap の制限を緩めてメソッドとフィールドの追加・削除までサポートします。HotSwapAgent はその上でフレームワーク適合を補います。能力は JRebel に近いですが、代償として JVM を差し替える必要があります。</li>
<li class=""><strong>Spring Boot DevTools</strong>：二つの ClassLoader でサードパーティ依存とビジネスコードを分離し、変更後はビジネス側の層だけを再起動します。真のホットデプロイではありませんが、再起動速度はコールドスタートよりずっと速く、コストもリスクもゼロです。</li>
<li class=""><strong>IDEA ネイティブの HotSwap</strong>：Debug モードで単一クラスを再コンパイルすれば反映されます。メソッドボディしか変更できませんが、日常的なパラメータ調整やログの修正には十分です。</li>
<li class="">JRebel 公式も無料トライアルを提供しているので、評価段階では公式のものをそのまま使うほうが気楽です。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2020/11/03/idea-2020-jrebelpo-jie#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>ホットデプロイツールが解決するのは「フィードバックループが長すぎる」という一点の問題です。JVM がメソッドボディの差し替えしか許さないというこの硬い境界を理解すれば、JRebel の高さがどこにあるのか、DevTools がなぜ折衷案にとどまるのかがわかります。どれを選ぶかは、プロジェクトの規模と、受け入れられる代償次第です。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[MySQL トランザクションデッドロック解析]]></title>
            <link>https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo</link>
            <guid>https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo</guid>
            <pubDate>Thu, 15 Oct 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[並行トランザクションはどのようにして互いに待ち合う膠着状態に陥るのか。ロック取得順序からデッドロックの成因を推理し、InnoDB の undo log に基づくロールバックの取捨と wait-for graph 検出機構を解説し、最後にデッドロック確率を下げる実践的ノウハウを紹介します。]]></description>
            <content:encoded><![CDATA[<p>MySQL データベースを使っていると、ビジネスや機能モジュールの増加に伴い、データベースのトランザクション数もそれにつれて増えていきます。ビジネス機能を開発するシーンでは、トランザクションを開始してデータを操作したり、分散グローバルトランザクションを開始してデータを操作したりすることが頻繁にあります。トランザクションが多くなると、並行実行のシーンでは一定の確率でトランザクションのデッドロック問題が発生します。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,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" width="255" height="255" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜこの問題を取り上げるのか">なぜこの問題を取り上げるのか<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#%E3%81%AA%E3%81%9C%E3%81%93%E3%81%AE%E5%95%8F%E9%A1%8C%E3%82%92%E5%8F%96%E3%82%8A%E4%B8%8A%E3%81%92%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜこの問題を取り上げるのか への直接リンク" title="なぜこの問題を取り上げるのか への直接リンク" translate="no">​</a></h2>
<p>デッドロックの厄介なところは、安定して再現できるバグではないことです。テスト環境でシングルスレッドで百回実行しても何も起きないのに、本番の並行ピーク時になると突然現れ、ログには <code>Deadlock found when trying to get lock</code> という一文だけが残ります。その成因を理解していないと、偶発的な例外として見過ごしてしまいがちで、ある日ビジネスのピーク時に集中して爆発することになります。この記事では、デッドロックの発生から検出、回避までを一通り整理します。</p>
<p>まず前提となる背景を少し補足します。InnoDB の行ロックはインデックスレコードに対してかけられます。あるトランザクションがある行のデータを更新するとき、その行の排他ロック（X ロック）を取得し、トランザクションがコミットまたはロールバックされるまで保持し続けます——これは二相ロッキングプロトコルの要求です。ロックの保持時間はトランザクションの生存時間に等しく、この点が以降のすべての分析の基礎になります。つまり、トランザクションが長いほど、そのロックに他者がぶつかるウィンドウは大きくなるのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デッドロックはどのように発生するのか">デッドロックはどのように発生するのか<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#%E3%83%87%E3%83%83%E3%83%89%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AF%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E7%99%BA%E7%94%9F%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="デッドロックはどのように発生するのか への直接リンク" title="デッドロックはどのように発生するのか への直接リンク" translate="no">​</a></h2>
<p>それでは、トランザクションデッドロックが発生する原因を分析してみましょう。</p>
<p>2 つの session 接続があるとします。一方の session はトランザクション T1 を、もう一方の session はトランザクション T2 を保持しています。</p>
<ol>
<li class="">
<p>T1 トランザクションが rows1 を更新する</p>
</li>
<li class="">
<p>T2 トランザクションが rows2 を更新する</p>
</li>
<li class="">
<p>T2 トランザクションが rows1 を更新しようとする</p>
</li>
<li class="">
<p>T2 は T1 が rows1 の X ロックを解放するのを待つ</p>
</li>
<li class="">
<p>T1 トランザクションが rows2 を更新しようとする</p>
</li>
<li class="">
<p>T1 は T2 が rows2 の X ロックを解放するのを待つ</p>
</li>
<li class="">
<p>相互に待ち合ってデッドロック</p>
</li>
</ol>
<p>鍵となるのはステップ 3 とステップ 5 です。2 つのトランザクションが逆の順序で、相手がすでにロックしている行に触れようとしています。T1 は rows1 を握ったまま rows2 を待ち、T2 は rows2 を握ったまま rows1 を待ち、どちらも先に手放そうとしません——手放すことはロールバックを意味するからです。待機関係が環を形成する、これがデッドロックの本質です。<strong>リソースの循環待機</strong> なのです。</p>
<p>デッドロックは外部からの干渉がない限り、プログラム自身にはこの問題を解決する力がありません。人手、あるいは別のデーモンスレッドの助けを借りて、このデッドロック問題を解決する必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="mysql-はどのように犠牲者を選ぶのか">MySQL はどのように犠牲者を選ぶのか<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#mysql-%E3%81%AF%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E7%8A%A0%E7%89%B2%E8%80%85%E3%82%92%E9%81%B8%E3%81%B6%E3%81%AE%E3%81%8B" class="hash-link" aria-label="MySQL はどのように犠牲者を選ぶのか への直接リンク" title="MySQL はどのように犠牲者を選ぶのか への直接リンク" translate="no">​</a></h2>
<p>デッドロックを解決する方法自体はシンプルです。どちらか一方のトランザクションを解放し、ロールバックさせればよいのです。</p>
<p>しかしロールバックには問題があります。どちらのトランザクションをロールバックするのがより合理的か、という問題です。MySQL は <strong>undo log</strong> の中でどちらのレコード数が多いか、つまりどちらの重みが大きいかに基づき、重みの小さいほうのトランザクションを切り捨ててロールバックします。これによりトランザクションデッドロックをより適切に解決できます。ロールバックするデータ量をできるだけ減らし、サービスのパフォーマンスの犠牲を最小化するためです。</p>
<p>この選択ロジックは難しくありません。undo log に記録されているのはトランザクションがすでに行った変更であり、レコード数が多いほどそのトランザクションはより多くの仕事をしており、それをロールバックするコストは大きくなります。そのため InnoDB は「仕事量の少ない」ほうのトランザクションをロールバックし、やり直しのコストを最小限に抑える傾向があります。選ばれたトランザクションはデッドロックエラーを受け取り、アプリケーション層はこのエラーを受けてトランザクション全体をリトライすることを選択できます——コードを書くときにトランザクションをリトライ可能に設計すべき理由がここにあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="デッドロック確率の見積もり">デッドロック確率の見積もり<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#%E3%83%87%E3%83%83%E3%83%89%E3%83%AD%E3%83%83%E3%82%AF%E7%A2%BA%E7%8E%87%E3%81%AE%E8%A6%8B%E7%A9%8D%E3%82%82%E3%82%8A" class="hash-link" aria-label="デッドロック確率の見積もり への直接リンク" title="デッドロック確率の見積もり への直接リンク" translate="no">​</a></h2>
<p>システム内の任意のトランザクションがデッドロックを起こす確率 ≈ n2r4/4R2</p>
<p>n: トランザクション内のトランザクション数 n。数が多いほどデッドロック発生の確率が高くなります（トランザクションが内包するトランザクションの数）</p>
<p>r: 各トランザクションの操作数 r。各トランザクションの操作数が多いほど、デッドロック発生の確率が高くなります（トランザクションが操作する行数）</p>
<p>R: 操作対象データの集合 R。小さいほどデッドロック発生の確率が高くなります（異なるトランザクションが同じ集合のデータに触れる——データ集合。更新するデータが分散しているほど、つまりデータ集合が多いほど、X ロックが同じレコード上で衝突する確率は小さくなります）</p>
<p>この式から最適化の方向性を読み取ることができます。r の指数は 4 で、影響が最も激しい——<strong>大きなトランザクションを小さく分割し、単一トランザクションが操作する行数を減らすことが、デッドロック確率を下げるうえで最も効果の高い手段です</strong>。そして R は分母にあり、これはホットスポットデータがデッドロックの温床であることを意味します。すべてのトランザクションが同じ一握りの行（たとえばカウンター行や在庫行）に殺到して更新すると、デッドロック確率は急激に増幅されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="能動的検出-wait-for-graph">能動的検出: wait-for graph<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#%E8%83%BD%E5%8B%95%E7%9A%84%E6%A4%9C%E5%87%BA-wait-for-graph" class="hash-link" aria-label="能動的検出: wait-for graph への直接リンク" title="能動的検出: wait-for graph への直接リンク" translate="no">​</a></h2>
<p>MySQL の能動的なトランザクション検出: wait-for graph</p>
<p>各 session のトランザクション開始前に、アルゴリズムによってトランザクションの閉路（2 つのトランザクションが互いに相手の X ロック対象データに影響し合って閉路を形成する状態）が存在するかを事前に検知し、undo log の重みが小さいほうのトランザクションを先に解放できます。</p>
<p>wait-for graph の原理は、待機関係を有向グラフとしてモデル化することです。各トランザクションをノードとし、「T2 が T1 のロックを待っている」なら T2 から T1 へ向かう辺を 1 本引きます。あるトランザクションがロックを取得できずに待機状態に入るたびに、InnoDB はグラフに辺を追加し、環が発生していないかチェックします——環があればデッドロックであり、直ちに重みの小さいトランザクションを選んでロールバックするため、無駄に待ち続けることはありません。</p>
<p>能動的検出のほかに、InnoDB にはもう一つの安全網があります。ロック待機タイムアウトです。行ロックの待機が <code>innodb_lock_wait_timeout</code> に設定された時間を超えると、待機していたステートメントはエラーを返します。これは本物のデッドロックか単なるロック競合かを区別しませんが、トランザクションが無期限にハングし続けないことを保証します。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 直近のデッドロックの詳細情報を確認する（LATEST DETECTED DEADLOCK セクション）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 2 つのトランザクションそれぞれが保持するロック、待機中のロック、ロールバックされた側が含まれる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">SHOW</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">ENGINE</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">INNODB</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">STATUS</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- ロック待機タイムアウト時間を確認する（単位: 秒）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">SHOW</span><span class="token plain"> VARIABLES </span><span class="token operator" style="color:hsl(207, 82%, 66%)">LIKE</span><span class="token plain"> </span><span class="token string" style="color:hsl(95, 38%, 62%)">'innodb_lock_wait_timeout'</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><br></div></code></pre></div></div>
<p>しかし複雑なビジネスシーンでは、MySQL の能動的なトランザクション検出 wait-for graph が効かないうちに、デッドロックがすでに発生してしまうこともよくあることです。</p>
<p>だからこそ、ビジネスフローとビジネスが影響するデータ範囲をきちんと整理し、デッドロック発生の確率を小さくしておく必要があります。さもないと、人手でデッドロックに対処する事態に直面しかねません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>前述の原理を踏まえると、実際の開発で留意すべき経験則がいくつかあります。</p>
<p>1）<strong>ロック取得順序を統一する</strong>。デッドロックの発生過程を振り返ると、根本原因は 2 つのトランザクションが逆順で同じ行群にアクセスすることでした。すべてのビジネスコードが同じ順序（たとえば主キーの昇順）でデータを更新するよう取り決めれば、循環待機の環はそもそも描けなくなります。</p>
<p>2）<strong>トランザクションは短く</strong>。トランザクションの中で RPC 呼び出し、メッセージ送信、ユーザー入力待ちといった時間のかかる操作をしてはいけません。ロックの保持時間が引き延ばされることは、デッドロックに窓を開け放つのと同じです。</p>
<p>3）<strong>ギャップロックに注意</strong>。REPEATABLE READ 分離レベルでは、範囲条件の更新や削除はギャップロックを取得します。2 つのトランザクションが隣接するギャップをロックし合ってから互いに挿入しようとすると、同様にデッドロックになります——この種のデッドロックは行データだけを見ても原因が分からないことが多く、<code>SHOW ENGINE INNODB STATUS</code> のロック情報を読む必要があります。</p>
<p>4）<strong>アプリケーション層でリトライを用意する</strong>。デッドロックが検出されると必ずどちらか一方がロールバックされます。ビジネスコードはデッドロックエラーを捕捉してトランザクション全体をリトライできるようにすべきで、最後の 1 文だけをリトライしてはいけません。</p>
<div class="theme-admonition theme-admonition-warning admonition_xJq3 alert alert--warning"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"></path></svg></span>警告</div><div class="admonitionContent_BuS1"><p>デッドロック検出自体にもコストがあります。ホットスポット行に大量のトランザクションが並ぶと、新しく待機に入るたびに待機グラフを走査する必要があり、並行度が高いほど検出のオーバーヘッドは大きくなります。ホットスポット更新のシーンでは、データベースに力ずくで耐えさせるのではなく、ビジネス側でホットスポットを分散させる方法を考えるべきです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2020/10/15/mysqlshi-wu-si-suo#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>デッドロックの本質は、ロック取得順序が交錯して形成される循環待機です。InnoDB は wait-for graph で能動的に閉路を発見し、undo log の重みに基づいてコストの小さいトランザクションを選んでロールバックするという安全網を備えています。しかし検出もロールバックもあくまで事後の補救にすぎず、本当に効くのは事前の工夫です。トランザクションを小さく分割し、ロック取得順序を統一し、ホットスポットデータを分散させることで、デッドロックの確率を無視できる水準まで抑え込むことができるのです。</p>]]></content:encoded>
            <category>技術</category>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[IDEA プラグイン開発の記録]]></title>
            <link>https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu</link>
            <guid>https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu</guid>
            <pubDate>Tue, 25 Aug 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[MyBatis の SQL ログ解析プラグインを自作した記録です。なぜ作ったのか、パラメータ補完の原理、IDEA プラグイン開発の学習曲線、そしてプラグインマーケットへの公開前後で感じたことをまとめます。]]></description>
            <content:encoded><![CDATA[<p>以前からずっと mybatis log plugin というプラグインを使っていて、その中の sql log パラメータ補完という機能がかなり気に入っていました。以前の低いバージョンでは有料化されていなかったようなのですが、高いバージョンから有料になってしまいました。そこで自分で似た機能のプラグインを開発することにし、名前を mybatis log analysis としました。1.0.0 バージョンでは、まず sql log の解析と自動パラメータ補完、sql の整形といったシンプルな機能を完成させます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜこのプラグインを作るのか">なぜこのプラグインを作るのか<a href="https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu#%E3%81%AA%E3%81%9C%E3%81%93%E3%81%AE%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%82%92%E4%BD%9C%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜこのプラグインを作るのか への直接リンク" title="なぜこのプラグインを作るのか への直接リンク" translate="no">​</a></h2>
<p>MyBatis で業務コードを書いている人なら実感があるはずです。コンソールに出力される SQL は <code>?</code> プレースホルダー付きのプリペアドステートメントで、パラメータは別の行に単独で出力されます。問題を調査するとき、この SQL をデータベースクライアントに持っていって直接実行しようとすると、パラメータを 1 つずつ手作業で埋め戻さなければなりません——パラメータが多くなると、この作業は面倒なうえに位置を間違えやすいのです。パラメータ補完系のプラグインが解決するのは、まさにこの小さいけれど高頻度な作業です。だから、いつも使っていたあのプラグインが有料化したとき、私の最初の反応は代替品を探すことではなく、この機能自体は別に神秘的なものではないのだから自分で書いてしまおう、ついでに IDEA プラグイン開発という道を一通り体験してみよう、というものでした。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プラグインの機能計画">プラグインの機能計画<a href="https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%AE%E6%A9%9F%E8%83%BD%E8%A8%88%E7%94%BB" class="hash-link" aria-label="プラグインの機能計画 への直接リンク" title="プラグインの機能計画 への直接リンク" translate="no">​</a></h2>
<p>1.0.0 バージョンでは、まず最も核心的でよく使われる部分を作ります。</p>
<ol>
<li class="">
<p>sql log の解析と自動パラメータ補完；</p>
</li>
<li class="">
<p>sql の整形（フォーマットして出力し、長い SQL を読みやすくする）。</p>
</li>
</ol>
<p>その後、時間に余裕ができたら他の便利機能も開発していく予定です。たとえば xml と java 間のリンクジャンプ、controller、service、entity、mapper、xml のワンクリック生成といった機能です。</p>
<p>現在すでに 1.0.0 バージョンの開発を完了し、IDEA PLUGIN リポジトリに提出済みです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="パラメータ補完の原理">パラメータ補完の原理<a href="https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu#%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E8%A3%9C%E5%AE%8C%E3%81%AE%E5%8E%9F%E7%90%86" class="hash-link" aria-label="パラメータ補完の原理 への直接リンク" title="パラメータ補完の原理 への直接リンク" translate="no">​</a></h2>
<p>この機能の原理は複雑ではありません。MyBatis はログレベルを DEBUG にすると、1 回のクエリを 2 行に分けて出力します。</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">==&gt;  Preparing: select * from user where id = ? and status = ?</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">==&gt; Parameters: 1024(Long), 1(Integer)</span><br></div></code></pre></div></div>
<p><code>Preparing</code> 行はプレースホルダー付きのプリペアド SQL で、<code>Parameters</code> 行は順番に並んだパラメータ値、括弧内にはパラメータの Java 型が付記されています。プラグインがやるべきことは、この 2 行をペアリングすることです。</p>
<ol>
<li class="">
<p>ログテキストから隣接する <code>Preparing</code> 行と <code>Parameters</code> 行を特定する；</p>
</li>
<li class="">
<p>カンマでパラメータリストを分割し、括弧内の型に応じて連結方法を決める——文字列や日付型は引用符を付ける必要があり、数値型はそのまま置換する；</p>
</li>
<li class="">
<p>処理後のパラメータを順番に SQL 内の <code>?</code> へ埋め戻し、そのまま実行できる完全な文を得る；</p>
</li>
<li class="">
<p>最後に SQL フォーマットを一度かけて、整形された結果を出力する。</p>
</li>
</ol>
<p>コアロジックはテキスト解析と文字列置換だけです。むしろ本当に時間がかかったのは、プラグインと IDE の統合部分でした。ツールウィンドウをどう登録するか、コンソール出力をどう監視するか、アクションをどうメニューにぶら下げるか——これらはすべて IntelliJ プラットフォームの API と付き合う必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="開発過程で感じたこと">開発過程で感じたこと<a href="https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu#%E9%96%8B%E7%99%BA%E9%81%8E%E7%A8%8B%E3%81%A7%E6%84%9F%E3%81%98%E3%81%9F%E3%81%93%E3%81%A8" class="hash-link" aria-label="開発過程で感じたこと への直接リンク" title="開発過程で感じたこと への直接リンク" translate="no">​</a></h2>
<p>プラグインを開発していると、シンプルな機能の実装は実に簡単なのですが、少し複雑な機能を実装しようとすると、IDEA が提供する API などのメソッドをかなり深く理解していなければなりません。ところが公式のプラグインドキュメントには、十分に整備された API ドキュメントが見当たりませんでした。あるのは公式が提供するいくつかの demo コードだけです。そうなると、時間をかけて demo コードを読み込んで理解するしかありません。あまり優しくない学習曲線になりそうだと感じました。</p>
<p>この点は普段の業務開発とは大きく異なります。業務フレームワークのドキュメントは通常「API を調べて、サンプルを見て、そのまま使う」という流れですが、IntelliJ プラットフォームでは主に 3 つの方法で手探りすることになります。公式の demo リポジトリをあさる、プラットフォームのソースコードを直接読む、そして他のオープンソースプラグインが類似機能をどう実装しているかを見る、です。どの拡張ポイントを使えばいいか分からないときは、機能が近いオープンソースプラグインを見つけて、その <code>plugin.xml</code> とエントリクラスを照らし合わせるほうが、ドキュメントをめくるより早いことが多いです。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,UklGRjwDAABXRUJQVlA4WAoAAAAQAAAAJwAAJwAAQUxQSE0BAAABkEPbtmlJp1yV2RXZto12d2Tbtm3btm3btm3bO7huxRExAfR3D0/7Xb6uMufiwDzE0HqVXIyUQirjNXbx0DuoYaI6lhA5D189f24Ny3ebTNA2nJq4fuuIZQ9w/Oc0xrXpsp27ltewVfJac6ahBRFRHWAhkdNCCNurSAYB1ykdsnvU8qoztBXfpj+Xwhal1rgztawV+X2H8jCVEgD4ejh7rtpPX4GFFUO3wf76pobmAuqzs3OtYoeFHK2TRe0A4OdHfZejHa05Xt9g+HETFw4NNTWNhPb3zMx1sRNQgpnaJFvJSJIUxb/Wd5kUt+qbLOd7A/pTpBrD4BmSTDgHk5VFCQdg9DhxfUbfg+ESnsdRGJ5J4ulmrlhIUDsT73xJOuSatg8BpNryp57LbqRu3+2ehr6kuXDqE6l7vZxJv2V09V6TF61cOmNAnSj67wYAVlA4IMgBAACwCgCdASooACgAPm0sj0WkIqEY6zYAQAbEtABCLq5vdJOb6gM4B/AP4h+p/v/+gDeAOe59kPypSZf3ehZERTjxOnMBk2ziN6XdwD6BbT97F+QvdMMMODQe9l9pVoQAAP7vsu/9CY9D/H/6LSdGqPYbmyfP+JREX5o3u2hV03pH/0d5f//WbfJkFm4N/8zb5MgrljB/VfLZwLK7fP0wIWH//5Ld0Rv8pUUGarqm0kYVjkGf7PLA5g2Ms/PfVM0W/SN+0Calu+YMZMkJoXQdFkI/7Iqku+nGuBN4WQ50URDcAFjxgB3HN6p0/0RQTuLBv/27q52VmPeH7xNLYQHXiwJVTFMO/FniMlqO+tr987/6/SCHufmvug2cJRj78+QxMyTj71VX+EYUOl/4vMtpFVOSVf6qqfTl00O14W+O22m2LsxX9TM3jblp+Z+jnkDSd8WbtTMw3krC5D9Q2yDaFjiHt6/M2t/RrjGi2N8O+6EQY0j0rI4Ba/47twysFmcA/VkC6A18f9JWCbPNb22IB9RIOiE4O8b8znyUVhGmNG/qeJsb5siXxrr7RHqEkHyUqCVE54fxQbOtD9oCkPhWfWZSO0/2XOlUAAA=" width="40" height="40" class="img_ev3q"></p>
<p>開発済みのプラグインのアドレスを貼っておきます：<a href="https://plugins.jetbrains.com/plugin/14958-mybatis-log-analysis" target="_blank" rel="noopener noreferrer" class="">https://plugins.jetbrains.com/plugin/14958-mybatis-log-analysis</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">パラメータ置換を単純なグローバル文字列置換でやってはいけません。パラメータ値自体にカンマや疑問符が含まれる可能性があるため、<code>Parameters</code> 行をカンマで分割して順番に 1 つずつ <code>?</code> を置換するほうが、一括の正規表現置換よりずっと堅実です。</li>
<li class="">型情報を活用しましょう。<code>Parameters</code> 行の括弧内の型が引用符を付けるかどうかを決めます。これを無視して組み立てた SQL は、データベースでまず実行できません。</li>
<li class="">JetBrains プラグインマーケットへの提出には人手による審査が必要です。プラグインの説明、アイコン、互換性のある IDE のバージョン範囲を設定にきちんと書いておく必要があり、これらを事前に準備しておけば、やり直しの往復を何回か減らせます。</li>
</ul>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>プラグイン開発ドキュメントで足りないときは、<a href="https://plugins.jetbrains.com/docs/intellij/welcome.html" target="_blank" rel="noopener noreferrer" class="">IntelliJ Platform SDK ドキュメント</a>とオープンソースプラグインのソースコードを照らし合わせて読むのが、現時点で最も体験の良い学習ルートです。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2020/08/25/kai-fa-ideacha-jian-ji-lu#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>このプラグイン自体のロジックはとても小さく、その価値は高頻度の手作業を自動化した点にあります。私にとってより大きな収穫は、IDEA プラグインの開発から公開までのプロセスを一通り完走したことでした。機能の実装はほんの一部分にすぎず、プラットフォームの拡張機構を理解し、ソースコードと demo を読んで学ぶスタイルに慣れることこそが、この道における本当のハードルです。今後のバージョンでは、xml と java 間のジャンプや、コードのワンクリック生成といった機能を引き続き追加していきます。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[この数年間の仕事で得た気づき]]></title>
            <link>https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de</link>
            <guid>https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de</guid>
            <pubDate>Tue, 26 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[はじめに 気がつけば、もうインターネット業界で数年働いています。 技術を学び始めたばかりの頃を思い返すと、毎日いろいろな Demo プロジェクトを書いていました。いちばん簡単な Hello World から、ミニゲーム、クローラー、管理システムまで、新しい技術を身につけるたびにはっきりとした達成感がありました。]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="はじめに">はじめに<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB" class="hash-link" aria-label="はじめに への直接リンク" title="はじめに への直接リンク" translate="no">​</a></h2>
<p>気がつけば、もうインターネット業界で数年働いています。</p>
<!-- -->
<p>技術を学び始めたばかりの頃を思い返すと、毎日いろいろな Demo プロジェクトを書いていました。いちばん簡単な <strong>Hello World</strong> から、ミニゲーム、クローラー、管理システムまで、新しい技術を身につけるたびにはっきりとした達成感がありました。あの頃は学習スピードも速く、情熱にあふれていました。</p>
<p>時間が経つにつれて、インターネット業界のとても分かりやすい特徴にも気づくようになりました。</p>
<p><strong>参入のハードルはそれほど高くないが、技術の天井は非常に高い。</strong></p>
<p>技術の世界の知識量は途方もなく膨大で、学べば学ぶほど、むしろ自分の知っていることが実はとても限られていると気づかされます。多くの場合、私たちは巨人の肩の上に立って、さらに高い巨人を見上げているにすぎないのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術的成長の初期段階">技術的成長の初期段階<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E6%8A%80%E8%A1%93%E7%9A%84%E6%88%90%E9%95%B7%E3%81%AE%E5%88%9D%E6%9C%9F%E6%AE%B5%E9%9A%8E" class="hash-link" aria-label="技術的成長の初期段階 への直接リンク" title="技術的成長の初期段階 への直接リンク" translate="no">​</a></h2>
<p>技術的成長の初期段階では、ほとんどの人がいくつかの方法で自分を高めていきます。例えば、新しい技術フレームワークを学ぶ、優れたプロジェクトを真似る、技術記事を読む、そしてさまざまな Demo プロジェクトを作り続ける、といったことです。</p>
<p>特にオープンソースコミュニティでは、優れた設計思想、アーキテクチャパターン、高品質なプロジェクトコードを数多く目にすることができます。これらのプロジェクトを読むことで、多くのエンジニアリングプラクティスを素早く理解できます。</p>
<p>ただし、本当に技術力を高めたいなら、とても重要なプロセスがあります。</p>
<p><strong>完全なシステムプロジェクトを独力で完成させることです。</strong></p>
<p>システム設計、コーディングによる実装から、デプロイ・リリース、その後の保守まで、この一連のプロセスを通じて、ソフトウェア開発への理解は格段に深まります。初めて独力でシステムアーキテクチャを設計し、システムを安定稼働させることに成功したときの達成感は、非常に強烈なものです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術的な壁の出現">技術的な壁の出現<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E6%8A%80%E8%A1%93%E7%9A%84%E3%81%AA%E5%A3%81%E3%81%AE%E5%87%BA%E7%8F%BE" class="hash-link" aria-label="技術的な壁の出現 への直接リンク" title="技術的な壁の出現 への直接リンク" translate="no">​</a></h2>
<p>実務経験が積み重なるにつれて、多くのエンジニアが次第にあるフェーズに突き当たります。<strong>技術的成長のスピードが明らかに鈍り始める</strong>のです。</p>
<p>このフェーズでは、システムはもはや単純な機能開発だけではなく、より複雑な問題を考慮する必要が出てきます。例えば:</p>
<ul>
<li class="">システムセキュリティ</li>
<li class="">システムの信頼性</li>
<li class="">高可用アーキテクチャ</li>
<li class="">パフォーマンスチューニング</li>
<li class="">モニタリングと運用</li>
<li class="">システムの拡張性</li>
<li class="">デプロイとリソース管理</li>
</ul>
<p>システムの規模が次第に大きくなると、アーキテクチャもどんどん複雑になり、使うべき技術もますます増えていきます。この段階で多くの人が焦りを感じ、自分の学習スピードが落ちたと思い込み、さらには若いエンジニアに取って代わられるのではと不安になることさえあります。</p>
<p>しかし実際には、この段階のブレイクスルーは、もはや単純な学習スピードに依存するものではなく、<strong>長期的な経験の蓄積と大量のプロジェクト実践</strong>を必要とするのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術的な深さの重要性">技術的な深さの重要性<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E6%8A%80%E8%A1%93%E7%9A%84%E3%81%AA%E6%B7%B1%E3%81%95%E3%81%AE%E9%87%8D%E8%A6%81%E6%80%A7" class="hash-link" aria-label="技術的な深さの重要性 への直接リンク" title="技術的な深さの重要性 への直接リンク" translate="no">​</a></h2>
<p>技術力が一定の段階に達すると、本当に重要なのはもはや「どれだけ多くの技術を知っているか」ではなく、<strong>自分ならではの技術的コア能力</strong>を形成できているかどうかになります。</p>
<p>多くの若いエンジニアはコーディング能力では非常に優秀かもしれませんが、<strong>システム全体の設計、アーキテクチャ的な思考、複雑な問題の分析</strong>という面では、まだ十分な経験が不足していることが少なくありません。この差は、本質的に長期にわたる技術の蓄積から生まれるものです。</p>
<p>技術がある段階まで成長したとき、さらに能力を伸ばしたいのであれば、単一の領域にとどまるのではなく、自分の知識の境界を広げ続ける必要があります。</p>
<p>例えばデータベースの領域では、完全な技術体系には次のようなものが含まれます:</p>
<ul>
<li class="">リレーショナルデータベース</li>
<li class="">非リレーショナルデータベース</li>
<li class="">グラフデータベース</li>
<li class="">データストレージ構造</li>
<li class="">データシャーディング戦略</li>
<li class="">ストレージエンジンの原理</li>
</ul>
<p>データベースはシステムの最も中核的な部分のひとつであることが多く、データベースの内部原理を理解することは、システムアーキテクチャ設計にとって非常に重要です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ソフトウェアアーキテクチャの複雑さ">ソフトウェアアーキテクチャの複雑さ<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E8%A4%87%E9%9B%91%E3%81%95" class="hash-link" aria-label="ソフトウェアアーキテクチャの複雑さ への直接リンク" title="ソフトウェアアーキテクチャの複雑さ への直接リンク" translate="no">​</a></h2>
<p>システムの規模が次第に拡大すると、ソフトウェアアーキテクチャは多くの異なる技術領域に関わってきます。例えば:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="システムのデプロイとリソース管理">システムのデプロイとリソース管理<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%81%A8%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E7%AE%A1%E7%90%86" class="hash-link" aria-label="システムのデプロイとリソース管理 への直接リンク" title="システムのデプロイとリソース管理 への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">Docker コンテナ</li>
<li class="">CPU / メモリ / 帯域幅の制限</li>
<li class="">ストレージ性能</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="システムアーキテクチャ設計">システムアーキテクチャ設計<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="システムアーキテクチャ設計 への直接リンク" title="システムアーキテクチャ設計 への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">マイクロサービスアーキテクチャ</li>
<li class="">インターフェース設計</li>
<li class="">システムの拡張性</li>
<li class="">パフォーマンスチューニング</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="分散システム">分散システム<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E5%88%86%E6%95%A3%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0" class="hash-link" aria-label="分散システム への直接リンク" title="分散システム への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">データベース・テーブル分割(シャーディング)</li>
<li class="">地理的マルチアクティブ構成</li>
<li class="">ディザスタリカバリ設計</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="セキュリティとデータ">セキュリティとデータ<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%81%A8%E3%83%87%E3%83%BC%E3%82%BF" class="hash-link" aria-label="セキュリティとデータ への直接リンク" title="セキュリティとデータ への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">通信の暗号化</li>
<li class="">セキュリティポリシー</li>
<li class="">データバリデーション</li>
</ul>
<p>さらに、一見とても細かい問題——データベースのカラム長、データ保存のバイト数、API パラメータの設計など——でさえ、システムの将来の拡張性に影響を与える可能性があります。</p>
<p>したがって、優れたアーキテクトには技術力の高さだけでなく、<strong>幅広い知識の蓄積と豊富な実践経験</strong>がより一層求められます。アーキテクチャ設計は、システムの将来の発展の余地を大きく左右するのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ソフトウェア業界におけるさまざまな役割">ソフトウェア業界におけるさまざまな役割<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E6%A5%AD%E7%95%8C%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%81%95%E3%81%BE%E3%81%96%E3%81%BE%E3%81%AA%E5%BD%B9%E5%89%B2" class="hash-link" aria-label="ソフトウェア業界におけるさまざまな役割 への直接リンク" title="ソフトウェア業界におけるさまざまな役割 への直接リンク" translate="no">​</a></h2>
<p>ソフトウェア業界では、異なる役割がそれぞれ異なる責務を担っています。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="プロダクトマネージャー">プロダクトマネージャー<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%83%97%E3%83%AD%E3%83%80%E3%82%AF%E3%83%88%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC" class="hash-link" aria-label="プロダクトマネージャー への直接リンク" title="プロダクトマネージャー への直接リンク" translate="no">​</a></h3>
<p>ソフトウェアがどんな問題を解決するのか、そしてプロダクトの将来の方向性を主に決める役割です。優れたプロダクトマネージャーには、市場のニーズを理解するだけでなく、技術的な実装コストと制約への理解も必要です。そうでないと、実現困難な機能を設計してしまいがちです。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="プロジェクトマネージャー">プロジェクトマネージャー<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%83%9E%E3%83%8D%E3%83%BC%E3%82%B8%E3%83%A3%E3%83%BC" class="hash-link" aria-label="プロジェクトマネージャー への直接リンク" title="プロジェクトマネージャー への直接リンク" translate="no">​</a></h3>
<p>よりプロジェクト管理に寄った役割で、進捗のコントロール、タスクの割り当て、チームコラボレーションを含み、プロジェクト全体の状況を明確に把握している必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="エンジニア--アーキテクト">エンジニア / アーキテクト<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2--%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88" class="hash-link" aria-label="エンジニア / アーキテクト への直接リンク" title="エンジニア / アーキテクト への直接リンク" translate="no">​</a></h3>
<p>技術方案の設計とシステムの実装を主に担当し、複雑な技術体系の中から適切なソリューションを見つけ出し、システムの長期的な安定稼働を保証する必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術的成長の転換">技術的成長の転換<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E6%8A%80%E8%A1%93%E7%9A%84%E6%88%90%E9%95%B7%E3%81%AE%E8%BB%A2%E6%8F%9B" class="hash-link" aria-label="技術的成長の転換 への直接リンク" title="技術的成長の転換 への直接リンク" translate="no">​</a></h2>
<p>技術がある段階まで成長すると、多くのエンジニアは次第に、自分がもはや単なる技術の利用者ではなく、より深いレベルの問題を理解しようとし始めていることに気づきます。</p>
<p>例えば:</p>
<ul>
<li class="">なぜシステムはこのように設計されるのか</li>
<li class="">なぜあるアーキテクチャが現在のビジネスにより適しているのか</li>
<li class="">複雑なシステムのボトルネック問題をどう解決するか</li>
</ul>
<p>最初はオープンソースソフトウェアを使う側だったのが、やがてオープンソースプロジェクトに参加するようになり、さらには自分の技術フレームワークやミドルウェアを設計するまでになる。</p>
<p>これは実は、多くのエンジニアの成長過程における重要な転換なのです。</p>
<p><strong>技術の利用者から、少しずつ技術の創造者へ。</strong></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="最後に思うこと">最後に思うこと<a href="https://tanzhuo.xyz/ja/2020/05/26/jin-ji-nian-de-gong-zuo-xin-de#%E6%9C%80%E5%BE%8C%E3%81%AB%E6%80%9D%E3%81%86%E3%81%93%E3%81%A8" class="hash-link" aria-label="最後に思うこと への直接リンク" title="最後に思うこと への直接リンク" translate="no">​</a></h2>
<p>自分のキャリアを振り返るとき、こんな問いを考えることがあるかもしれません。</p>
<p>自分をこの業界で歩み続けさせているものは何なのか?</p>
<p>技術的知識への渇望なのか。<br>
若い頃のほとばしる情熱なのか。<br>
それとも技術がすでに生活の一部になっているからなのか。</p>
<p>答えはそれほど重要ではないのかもしれません。</p>
<p>大切なのは、この絶えず変化する技術の世界の中で、いつまでも持ち続けることです。</p>
<p><strong>好奇心、学ぶ力、そして探求し続ける情熱を。</strong></p>]]></content:encoded>
            <category>ライフ</category>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[分散ロックと Spring トランザクション管理の順序問題]]></title>
            <link>https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti</link>
            <guid>https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti</guid>
            <pubDate>Tue, 31 Mar 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[あるネットユーザーが zookeeper の分散ロックでチケット販売の在庫減算を実装したところ、高並行時に枚数が合わなくなりました。根本原因はトランザクションのコミット前にロックが解放され、次のスレッドがダーティデータを読んでしまうこと。推理の過程と修正の考え方を記録します。]]></description>
            <content:encoded><![CDATA[<p>これはネット上であるユーザーから寄せられた質問で、私が推理して問題を解決したものです。そのため、ここに記録しておきます。</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2021-02-src-http___img2.sycdn.imooc.com_5b3b66960001738707500375.jpg-refer-http___img2.sycdn.imooc-3ae12e651fe4d27c6fb9f127d3888435.webp" width="750" height="375" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="問題の背景">問題の背景<a href="https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti#%E5%95%8F%E9%A1%8C%E3%81%AE%E8%83%8C%E6%99%AF" class="hash-link" aria-label="問題の背景 への直接リンク" title="問題の背景 への直接リンク" translate="no">​</a></h2>
<p>「分散ロックを入れたのに、データがまだ合わない」というのは、典型的な並行性の問題の一種です。ロック自体にバグはなく、トランザクションのロールバックもコミットも正常に動いている。しかし両者を組み合わせたとき、ロックの解放とトランザクションのコミットの順序がわずかに狂うだけで、直列化による保護は有名無実になってしまいます。この種の問題は負荷試験の前にはなかなか表面化せず、調査の際もロックの実装ばかりに注意が向いて、トランザクション境界を見落としがちです。このユーザーが遭遇したのは、まさにこのケースでした。</p>
<p>彼は zookeeper の一時順序ノードによる分散ロックを使って MySQL のデータ値を変更していました。この機能はチケット販売の仕組みに近く、高並行下でも販売枚数が一致することが求められます。多くても少なくてもいけません。</p>
<p>zookeeper の一時順序ノードによる分散ロックを使うにあたり、彼は service 層をロックしていました。service メソッド全体の実行が完了した時点でロックは解放されますが、このときメソッドに付与された Spring 管理の宣言的トランザクションはまだコミットされていません。このような状況は、並行時にデータの問題を引き起こします。</p>
<p>当然のことながら、彼のデータベースのデータには異常が発生しました。100 スレッドが同時に 1 回ずつ実行し、1 回につき 1 枚取得するなら、100 枚のチケットの残りは 0 になるはずです。しかし彼の結果は 0 ではなく、期待値に達しませんでした。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="原因分析">原因分析<a href="https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti#%E5%8E%9F%E5%9B%A0%E5%88%86%E6%9E%90" class="hash-link" aria-label="原因分析 への直接リンク" title="原因分析 への直接リンク" translate="no">​</a></h2>
<p>原理はこうです。最初のスレッドが update メソッドの実行を終えた後、トランザクションのコミットはまだ完了していないのに、分散ロックが一足先に解放されてしまいます。次のスレッドがこのメソッドを実行するとき、select で取得するのは update 前のダーティリードのデータであり、これがデータの問題を引き起こします。Spring の宣言的トランザクション @Transactional(rollbackFor = Exception.class) は AOP に基づいており、メソッドの実行完了後に実行されるアラウンドアドバイスです。しかしアラウンドアドバイスがトランザクションをコミットする時点では、zookeeper のロックはすでに解放されています。この順序のずれによって、次のスレッドがダーティデータを読み、データ処理に誤りが生じるのです。</p>
<p>この実行順序を掘り下げてみましょう。宣言的トランザクションの本質は、Spring が対象クラスに対してプロキシオブジェクトを生成することです。トランザクションの開始とコミットはいずれもプロキシ層で行われ、ビジネスメソッドの外側を包んでいます。一方、彼のロック取得・解放のコードはビジネスメソッドの内部に書かれていました。その結果、一回の呼び出しの実際のタイムラインは次のようになります。</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">// プロキシオブジェクトの呼び出しタイムライン（模式図）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 1. トランザクションインターセプタがトランザクションを開始</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 2. ビジネスメソッドに入り、zookeeper のロックを取得</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 3. select で残りチケットを照会し、update で減算</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 4. ビジネスメソッド内でロックを解放          &lt;-- ロックはここでもう消えている</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">// 5. ビジネスメソッドが return し、トランザクションインターセプタがコミット  &lt;-- コミットはここ</span><br></div></code></pre></div></div>
<p>ステップ 4 とステップ 5 の間にはウィンドウが存在します。ロックはすでに解放されているのに、トランザクションはまだコミットされていない。ロック待ちの次のスレッドがこのウィンドウ内でロックを取得して select を実行すると、前のトランザクションが未コミットのため、デフォルトの READ COMMITTED や REPEATABLE READ の分離レベルでは、減算前の古い値を読んでしまいます。つまり二つのスレッドが同じ残数を基に、それぞれ一回ずつ減算したことになります。ロックの排他性が破られたのではなく、「ロック内の読み取りは、直前のロック保持者の書き込みを必ず見られる」という暗黙の前提が破られたのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="解決方法">解決方法<a href="https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti#%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95" class="hash-link" aria-label="解決方法 への直接リンク" title="解決方法 への直接リンク" translate="no">​</a></h2>
<p>したがって、Spring トランザクションと分散ロック解放の順序を修正する必要があります。先にトランザクションをコミットしてから分散ロックを解放するか、あるいは controller 層でロックしてしまえば、この問題は回避できます。</p>
<p>二つのやり方は本質的に同じで、どちらもロックの保持範囲がトランザクションのライフサイクルを完全に覆うようにするものです。</p>
<p>1）service の外側（たとえば controller や、トランザクションを持たない外側のメソッド）でロックの取得・解放を行い、トランザクションメソッドをロックに包まれた内側の呼び出しにする。メソッドが return した時点でトランザクションはコミット済みなので、その後にロックを解放します。</p>
<p>2）宣言的トランザクションをやめてプログラム的トランザクション（TransactionTemplate または手動 commit）に切り替え、コードの中で明示的に先にコミットし、それからロックを解放する。順序を自分の手で制御します。</p>
<p>注意すべき点として、ロック処理を同じクラスの別メソッドに移し、this の自己呼び出しでトランザクションメソッドに入ると、トランザクションアノテーションは効きません——自己呼び出しはプロキシオブジェクトを経由しないからです。二つのクラスに分けるか、コンテナから自分自身のプロキシを取得してから呼び出す必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="分散ロックはデータ整合性と同義ではない">分散ロックはデータ整合性と同義ではない<a href="https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti#%E5%88%86%E6%95%A3%E3%83%AD%E3%83%83%E3%82%AF%E3%81%AF%E3%83%87%E3%83%BC%E3%82%BF%E6%95%B4%E5%90%88%E6%80%A7%E3%81%A8%E5%90%8C%E7%BE%A9%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84" class="hash-link" aria-label="分散ロックはデータ整合性と同義ではない への直接リンク" title="分散ロックはデータ整合性と同義ではない への直接リンク" translate="no">​</a></h2>
<p>ここでもう一言付け加えておくと、分散ロックはデータの整合性を保証するものではなく、当該ビジネス層の service メソッドの呼び出しが一意であることを保証するだけです。</p>
<p>もし他の service メソッドの中に、この同じデータを処理する mapper があれば、データはやはり問題を起こします。</p>
<p>ですから、テーブルに楽観ロックのフィールドを追加してデータの整合性を保証するのが筋です。楽観ロックはデータの整合性をうまく維持でき、並行性能も彼が使っていた service ロックより高くなります。</p>
<p>楽観ロックのやり方は、テーブルに version フィールドを追加し、更新時に読み取ったバージョン番号を条件に含めることです。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 読み取り時に version も一緒に取得しておく</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 更新時は旧バージョン番号を条件にし、同時にバージョン番号をインクリメントする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">update</span><span class="token plain"> ticket</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">set</span><span class="token plain"> stock </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> stock </span><span class="token operator" style="color:hsl(207, 82%, 66%)">-</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token plain"> version </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> version </span><span class="token operator" style="color:hsl(207, 82%, 66%)">+</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">where</span><span class="token plain"> id </span><span class="token operator" style="color:hsl(207, 82%, 66%)">=</span><span class="token plain"> </span><span class="token comment" style="color:hsl(220, 10%, 40%)">#{id} and version = #{version};</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 影響行数が 0 なら、データはすでに他者に変更されている。リトライするかエラーにする</span><br></div></code></pre></div></div>
<p>この防御線はデータそのものに掛かっているので、どれだけ多くの入口がこの行を変更しようとも、最終的な整合性はデータベースが保証します。分散ロックが解決するのは「同じ瞬間に働いているのは一人だけ」という問題で、楽観ロックが解決するのは「たとえ複数人が働いても、データは間違わない」という問題です。両者は対象とするレイヤーが異なり、互いに代替するものではありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）分散ロックを入れるときは、ロックの境界とトランザクションの境界のどちらがどちらを包むのかを先に見極めましょう。ロックはトランザクションを完全に覆わなければなりません。「メソッド内でロック + メソッドに @Transactional」は、最も書きやすく、最も間違えやすい組み合わせです。</p>
<p>2）この種の問題は、ユニットテストや低並行のシーンではほぼ検出できません。ロック解放とトランザクションコミットの間の時間ウィンドウに並行負荷が届いたときにだけ再現します。リリース前に、減算系の API に対して並行整合性の検証を行うことは必須です。</p>
<p>3）データ不整合を調査するときは、ロックの実装が正しいかだけを見つめないでください。トランザクションの開始・コミットのタイミングもタイムラインに描き込むと、たいてい問題は両者の境界にあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2020/03/31/fen-bu-shi-suo-yu-springbootshi-wu-guan-li-shun-xu-wen-ti#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>このケースではロックもトランザクションもそれぞれ正常に動いていました。問題だったのは両者の順序です。ロックがトランザクションのコミット前に解放され、次のスレッドがダーティデータを読むウィンドウを与えてしまいました。修正の考え方は、ロックがトランザクションを完全に包むようにするか、いっそプログラム的トランザクションでコミットのタイミングを明示的に制御することです。さらに一歩進めるなら、減算系のデータの正しさを入口での排他だけに頼るべきではありません。テーブルに楽観ロックのフィールドを追加し、最後の防御線をデータベース層に置くことが、より堅実なやり方です。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[Java バックエンドアーキテクチャの理解]]></title>
            <link>https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou</link>
            <guid>https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou</guid>
            <pubDate>Fri, 20 Mar 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Java バックエンドアーキテクチャに対する理解を整理します。コード規約、モジュール分割、ミドルウェア、データベースガバナンスから、セキュリティ、チームコラボレーション、マイクロサービスへの進化まで、全体的な考察をまとめます。]]></description>
            <content:encoded><![CDATA[<p>最近、自分でいくつかのシステムアーキテクチャを構築したので、バックエンドアーキテクチャに対する理解と考察を整理してみました。</p>
<!-- -->
<p>実際の開発の中で、私たちはよくこう考えます。<strong>どのようなプロジェクト構造が合理的なアーキテクチャと言えるのか？</strong></p>
<p>良いアーキテクチャとは次のようなものだ、と考える人もいます。</p>
<ul>
<li class="">開発者の重複作業を減らせる</li>
<li class="">構造が明快で理解しやすい</li>
<li class="">良好な安定性を備えている</li>
<li class="">拡張と保守がしやすい</li>
</ul>
<p>しかし実際のところ、これは <strong>プロジェクトレベルのアーキテクチャ</strong> にすぎません。</p>
<p>ビジネスの発展に伴い、システムは往々にして <strong>単一マシンのプロジェクトからクラスタ構成へと徐々に進化し、最終的には分散システムへと発展していきます</strong>。この過程で、さまざまなコンポーネントが次々と導入されます。たとえば：</p>
<ul>
<li class="">サービスレジストリ</li>
<li class="">コンフィグセンター</li>
<li class="">API ゲートウェイ</li>
<li class="">サーキットブレーカーとサービスデグレード</li>
<li class="">データベース・テーブルのシャーディング</li>
<li class="">ディザスタリカバリの仕組み</li>
<li class="">各種ミドルウェア</li>
</ul>
<p>同時にシステムのデプロイも <strong>Docker + K8S のコンテナ化体系</strong> へと徐々に進化し、サーバーの台数も当初の 1〜2 台から、数十台、さらには百台以上へと増えていく可能性があります。この時点でシステムはもはや単純なプロジェクトではなく、完全な <strong>システムアーキテクチャ体系</strong> になっています。</p>
<p>アーキテクチャ設計には、マクロレベルのシステム設計と、ミクロレベルのコード設計の両方が含まれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コードレベルのアーキテクチャ設計">コードレベルのアーキテクチャ設計<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AC%E3%83%99%E3%83%AB%E3%81%AE%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="コードレベルのアーキテクチャ設計 への直接リンク" title="コードレベルのアーキテクチャ設計 への直接リンク" translate="no">​</a></h2>
<p>コードレベルでは、アーキテクチャ設計によって開発規約を統一し、開発効率を高める必要があります。たとえば：</p>
<ul>
<li class="">DTO の自動パラメータバリデーション（AOP）</li>
<li class="">Result 統一レスポンスオブジェクト</li>
<li class="">コードの自動生成</li>
<li class="">Service / Mapper / Controller の標準化された構造</li>
<li class="">オンライン API ドキュメントの自動生成</li>
</ul>
<p>同時に、コードの組織構造も標準化する必要があります。パッケージ構造を厳格に管理し、クラスの命名規約を統一し、モジュールを合理的に分割することです。</p>
<p>良いコードとは単に効率が高いことではなく、<strong>他の人がコードの意味を素早く理解できること</strong> です。優れたコードアーキテクチャは、保守コストを大幅に下げてくれます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ビジネスモジュールの分割とシステムの疎結合化">ビジネスモジュールの分割とシステムの疎結合化<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%93%E3%82%B8%E3%83%8D%E3%82%B9%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E5%88%86%E5%89%B2%E3%81%A8%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%81%AE%E7%96%8E%E7%B5%90%E5%90%88%E5%8C%96" class="hash-link" aria-label="ビジネスモジュールの分割とシステムの疎結合化 への直接リンク" title="ビジネスモジュールの分割とシステムの疎結合化 への直接リンク" translate="no">​</a></h2>
<p>システム設計の過程では、ビジネス間の関係を十分に理解し、ビジネスの関係に基づいて <strong>モジュールの分割と疎結合化</strong> を行い、モジュールの独立性、コードの再利用率、システムの複雑さといった観点の間でバランスを取る必要があります。</p>
<p>合理的なアーキテクチャ設計は、次のことを実現すべきです。</p>
<ul>
<li class="">異なるビジネスモジュールが互いに干渉しない</li>
<li class="">機能モジュールを独立して保守できる</li>
<li class="">コードの結合度を可能な限り下げる</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ミドルウェアとシステムアーキテクチャの能力">ミドルウェアとシステムアーキテクチャの能力<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%9F%E3%83%89%E3%83%AB%E3%82%A6%E3%82%A7%E3%82%A2%E3%81%A8%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="ミドルウェアとシステムアーキテクチャの能力 への直接リンク" title="ミドルウェアとシステムアーキテクチャの能力 への直接リンク" translate="no">​</a></h2>
<p>システム規模の拡大に伴い、システムには通常、さまざまなインフラコンポーネントが導入されます。たとえば：</p>
<ul>
<li class="">MQ（メッセージキュー）</li>
<li class="">Redis</li>
<li class="">NoSQL</li>
<li class="">ELK ログシステム</li>
<li class="">分散ロック</li>
<li class="">分散トランザクション</li>
</ul>
<p>アーキテクチャ設計では、それぞれのミドルウェアのユースケースに精通し、どのような状況でどのコンポーネントを導入するかを判断でき、サーバーリソースを合理的に配分できる必要があります。同時に、各ミドルウェアの責務、各モジュールの境界、そして各サービス間の呼び出し関係を明確に把握しておく必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="データベース設計と-sql-ガバナンス">データベース設計と SQL ガバナンス<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E8%A8%AD%E8%A8%88%E3%81%A8-sql-%E3%82%AC%E3%83%90%E3%83%8A%E3%83%B3%E3%82%B9" class="hash-link" aria-label="データベース設計と SQL ガバナンス への直接リンク" title="データベース設計と SQL ガバナンス への直接リンク" translate="no">​</a></h2>
<p>データベース設計はアーキテクチャの中で非常に重要な位置を占めており、以下の観点に重点的に注意を払う必要があります。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="データベース設計">データベース設計<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="データベース設計 への直接リンク" title="データベース設計 への直接リンク" translate="no">​</a></h3>
<ul>
<li class="">フィールドの型を合理的に設計する</li>
<li class="">フィールドの命名規約を統一する</li>
<li class="">インデックスを合理的に設計する</li>
<li class="">冗長フィールドを適度に追加する</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="sql-の品質管理">SQL の品質管理<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#sql-%E3%81%AE%E5%93%81%E8%B3%AA%E7%AE%A1%E7%90%86" class="hash-link" aria-label="SQL の品質管理 への直接リンク" title="SQL の品質管理 への直接リンク" translate="no">​</a></h3>
<p>システム内のすべての SQL を厳格にレビューする必要があります。</p>
<ul>
<li class="">インデックスが正しく使われているか</li>
<li class="">フルテーブルスキャンが存在しないか</li>
<li class="">複雑で保守しにくい SQL が存在しないか</li>
</ul>
<p>複雑な SQL はできるだけ分割し、同時に SQL の影響行数とデッドロックの発生確率をできるだけ減らす必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="システム性能と負荷テスト">システム性能と負荷テスト<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%80%A7%E8%83%BD%E3%81%A8%E8%B2%A0%E8%8D%B7%E3%83%86%E3%82%B9%E3%83%88" class="hash-link" aria-label="システム性能と負荷テスト への直接リンク" title="システム性能と負荷テスト への直接リンク" translate="no">​</a></h2>
<p>システムのリリース前には、<strong>負荷テストと性能テスト</strong> を行う必要があります。たとえば、高並行リクエストのシミュレーション、サーバーの限界性能のテスト、システムの TPS の評価などです。</p>
<p>同時に、以下を最適化する必要があります。</p>
<ul>
<li class="">サーバー設定</li>
<li class="">コンテナ設定</li>
<li class="">JVM パラメータ</li>
</ul>
<p>負荷テストを通じて、システムの性能ボトルネックをより明確に把握できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コード品質管理">コード品質管理<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%82%B3%E3%83%BC%E3%83%89%E5%93%81%E8%B3%AA%E7%AE%A1%E7%90%86" class="hash-link" aria-label="コード品質管理 への直接リンク" title="コード品質管理 への直接リンク" translate="no">​</a></h2>
<p>低品質なコードはシステムの安定性に深刻な影響を与えます。たとえば CPU の異常な上昇、メモリ使用量の過多、さらにはシステムのクラッシュにつながることさえあります。</p>
<p>そのため、コード品質検査ツールの導入が必要です。たとえば：</p>
<ul>
<li class="">FindBugs</li>
<li class="">SonarQube</li>
</ul>
<p>静的コード解析ツールを使うことで、潜在的な問題を事前に発見できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サーバーのセキュリティ設計">サーバーのセキュリティ設計<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E8%A8%AD%E8%A8%88" class="hash-link" aria-label="サーバーのセキュリティ設計 への直接リンク" title="サーバーのセキュリティ設計 への直接リンク" translate="no">​</a></h2>
<p>システムのセキュリティも同様に、アーキテクチャ設計の重要な部分です。一般的なセキュリティ対策には以下があります。</p>
<ul>
<li class="">デフォルトポートの変更</li>
<li class="">内部・外部ネットワークのアクセスホワイトリスト</li>
<li class="">システム脆弱性の定期チェック</li>
<li class="">API パラメータの暗号化</li>
</ul>
<p>API レベルでは、HTTPS による暗号化通信、共通鍵 / 公開鍵暗号、パラメータ署名の仕組みなどの手段を採用できます。同時に、開放するポートをできるだけ減らし、強度の高いパスワードを設定し、ブルートフォース攻撃対策を構成する必要があります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プロジェクト管理とチームコラボレーション">プロジェクト管理とチームコラボレーション<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%97%E3%83%AD%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E7%AE%A1%E7%90%86%E3%81%A8%E3%83%81%E3%83%BC%E3%83%A0%E3%82%B3%E3%83%A9%E3%83%9C%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3" class="hash-link" aria-label="プロジェクト管理とチームコラボレーション への直接リンク" title="プロジェクト管理とチームコラボレーション への直接リンク" translate="no">​</a></h2>
<p>アーキテクチャは単なる技術の問題ではなく、<strong>チーム管理と開発プロセス</strong> も含みます。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="git-ブランチ管理">Git ブランチ管理<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#git-%E3%83%96%E3%83%A9%E3%83%B3%E3%83%81%E7%AE%A1%E7%90%86" class="hash-link" aria-label="Git ブランチ管理 への直接リンク" title="Git ブランチ管理 への直接リンク" translate="no">​</a></h3>
<p>Git のブランチを合理的に管理します。</p>
<ul>
<li class="">新機能開発には feature ブランチを使う</li>
<li class="">緊急のバグ修正には hotfix ブランチを使う</li>
<li class="">テスト環境には test ブランチを使う</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="タスク管理">タスク管理<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%82%BF%E3%82%B9%E3%82%AF%E7%AE%A1%E7%90%86" class="hash-link" aria-label="タスク管理 への直接リンク" title="タスク管理 への直接リンク" translate="no">​</a></h3>
<p>開発タスクを合理的に配分し、開発者間のタスクの偏りが大きくなりすぎないようにします。禅道（ZenTao）や Teambition などのツールを使ってチームコラボレーションとタスク管理を行うことができます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="自動化テスト体系">自動化テスト体系<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E8%87%AA%E5%8B%95%E5%8C%96%E3%83%86%E3%82%B9%E3%83%88%E4%BD%93%E7%B3%BB" class="hash-link" aria-label="自動化テスト体系 への直接リンク" title="自動化テスト体系 への直接リンク" translate="no">​</a></h2>
<p>システム規模が拡大すると、手動テストだけに頼るのは非常に効率が悪いため、<strong>自動化テスト体系</strong> を段階的に構築していく必要があります。</p>
<p>自動化テストによって：</p>
<ul>
<li class="">機能を素早く検証できる</li>
<li class="">問題を事前に発見できる</li>
<li class="">システムの安定性を高められる</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="フロントエンドバックエンド分離アーキテクチャ">フロントエンド・バックエンド分離アーキテクチャ<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%83%90%E3%83%83%E3%82%AF%E3%82%A8%E3%83%B3%E3%83%89%E5%88%86%E9%9B%A2%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" class="hash-link" aria-label="フロントエンド・バックエンド分離アーキテクチャ への直接リンク" title="フロントエンド・バックエンド分離アーキテクチャ への直接リンク" translate="no">​</a></h2>
<p>現在、フロントエンドとバックエンドの分離はすでに主流のアーキテクチャパターンになっています。フロントエンドフレームワーク（たとえば Vue）はすでに完全なエコシステムを形成しており、ページレンダリング、パラメータバリデーション、インタラクションロジック、アニメーション効果までカバーしています。</p>
<p>フロントエンドとバックエンドを分離すると、バックエンドは API 開発に、フロントエンドはページのインタラクションに専念でき、同時にサーバーが静的リソースを処理する負荷も減らせます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="静的リソースとオブジェクトストレージ">静的リソースとオブジェクトストレージ<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E9%9D%99%E7%9A%84%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%A8%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8" class="hash-link" aria-label="静的リソースとオブジェクトストレージ への直接リンク" title="静的リソースとオブジェクトストレージ への直接リンク" translate="no">​</a></h2>
<p>画像、動画、ファイルなどの大量の静的リソースは、<strong>OSS</strong> などのオブジェクトストレージに統一的に保存できます。</p>
<p>アップロードのフローは、フロントエンドから OSS へ直接アップロードする方式に変更でき、バックエンドはリソースの URL の保存と権限制御だけを担当します。こうすることでバックエンドサーバーの負荷を大幅に減らせます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="マイクロサービスと将来のアーキテクチャ">マイクロサービスと将来のアーキテクチャ<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%83%9E%E3%82%A4%E3%82%AF%E3%83%AD%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%A8%E5%B0%86%E6%9D%A5%E3%81%AE%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3" class="hash-link" aria-label="マイクロサービスと将来のアーキテクチャ への直接リンク" title="マイクロサービスと将来のアーキテクチャ への直接リンク" translate="no">​</a></h2>
<p>システムは設計の初期段階から <strong>マイクロサービスアーキテクチャ</strong> を考慮すべきです。たとえばサービスレジストリ、コンフィグセンター、サービスガバナンスなどで、さらに <strong>Kubernetes</strong> にデプロイして水平スケーリングできるようにしておくべきです。</p>
<p>将来のアーキテクチャはさらに発展していく可能性があります。たとえば Service Mesh やクラウドネイティブアーキテクチャです。多くの大手企業はすでに実運用に落とし込んでいますが、中小企業にとっては、現時点では依然として <strong>従来型のマイクロサービスアーキテクチャ</strong> が主流です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="アーキテクトに求められる思考力">アーキテクトに求められる思考力<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%88%E3%81%AB%E6%B1%82%E3%82%81%E3%82%89%E3%82%8C%E3%82%8B%E6%80%9D%E8%80%83%E5%8A%9B" class="hash-link" aria-label="アーキテクトに求められる思考力 への直接リンク" title="アーキテクトに求められる思考力 への直接リンク" translate="no">​</a></h2>
<p>成熟したアーキテクトは単なる技術の実装者ではなく、システムの長期的な発展を見据えたプランナーでもあります。アーキテクチャを設計する際は、通常いくつかの観点から考える必要があります。</p>
<ul>
<li class="">ビジネスの将来の発展を支えられるか</li>
<li class="">システムは保守しやすいか</li>
<li class="">技術的な方式は安定していて信頼できるか</li>
<li class="">コストはコントロール可能か</li>
<li class="">チームが長期的に保守できるか</li>
</ul>
<p>良いアーキテクチャは往々にして一度の設計で完成するものではなく、<strong>ビジネスの発展、技術の進化、そして絶え間ない最適化の中で徐々に形成されていくもの</strong> です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2020/03/20/javahou-tai-jia-gou#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>アーキテクチャ設計は、コードからシステムへ、技術からチームへとつながる一つの完全な体系です。コード規約、モジュール分割、ミドルウェアの選定、データベースガバナンスが解決するのはシステム自体の問題であり、セキュリティ設計、チームコラボレーション、自動化テストが保障するのはシステムが長期的に健全に進化し続けられることです。あらゆる場面に通用するアーキテクチャは存在せず、あるのは現在のビジネス規模とチームの能力にマッチしたアーキテクチャだけです。一足飛びの完成を追い求めるよりも、進化の余地を残しておき、アーキテクチャをビジネスとともに成長させていくほうがよいのです。</p>]]></content:encoded>
            <category>技術</category>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[Java でリモートデスクトップを実装する]]></title>
            <link>https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian</link>
            <guid>https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian</guid>
            <pubDate>Tue, 28 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Java でリモートデスクトップを実装してみた記録: Robot によるスクリーンキャプチャの効率、画像転送の帯域幅から、h.264 ビデオストリームとランレングス符号化による差分転送のトレードオフまで。]]></description>
            <content:encoded><![CDATA[<p>ふと思いついたのですが、Java でリモートデスクトップ共有の機能を実装することはできるのでしょうか？</p>
<!-- -->
<p>リモートデスクトップの類のツールは普段からよく使います。たとえば Sunlogin や TeamViewer などですが、その裏側が実際どう動いているのかを考えたことのある人は少ないでしょう。分解してみると、実はやることは 3 つだけです。画面の映像をキャプチャする、データを小さく圧縮する、ネットワーク越しに相手側へ送って復元する。この 3 ステップにはそれぞれ効率の問題があり、Java は最初のステップの時点で先天的に不利です。この記事では、私が Java でリモートデスクトップをいじってみて、この 3 つの段階でそれぞれどんな落とし穴を踏み、最終的にどんな方式を選んだかを記録します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="スクリーンキャプチャの効率が最初の関門">スクリーンキャプチャの効率が最初の関門<a href="https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian#%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%AD%E3%83%A3%E3%83%97%E3%83%81%E3%83%A3%E3%81%AE%E5%8A%B9%E7%8E%87%E3%81%8C%E6%9C%80%E5%88%9D%E3%81%AE%E9%96%A2%E9%96%80" class="hash-link" aria-label="スクリーンキャプチャの効率が最初の関門 への直接リンク" title="スクリーンキャプチャの効率が最初の関門 への直接リンク" translate="no">​</a></h2>
<p>もちろん実装は可能です。どれもコードを走らせるだけの話ですから。ただ Java は効率が悪いのです。なぜかというと、Java のコード実行効率が低いからではなく、Java 標準のスクリーンキャプチャツールの効率が低すぎるからです。Java はデスクトップのキャプチャを実現する Robot クラスを提供していますが、2K 画面の JPG 1 枚のサイズは 200KB 程度で、私の RX 2700X CPU で 1 枚のキャプチャになんと 30 ミリ秒もかかりました。毎秒 30 フレームにはまだ距離があり、毎秒 60 フレームは言うまでもありません。仮に毎秒 60 フレーム出せたとしても、Java でどうやって各リモート端末へ効率よく転送するのでしょうか？</p>
<p>仕組みの面での原因を補足しておきます。<code>java.awt.Robot</code> の <code>createScreenCapture</code> は AWT のネイティブ呼び出しを経由し、下層では画面全体のピクセルを VRAM からメモリへコピーし、それを <code>BufferedImage</code> にラップします。この過程は同期的なフルコピーであり、解像度が高いほどコピーするデータ量は大きくなります。2K 画面の 1 フレームの生の RGB データは十数 MB にもなり、このメモリ搬送 1 回だけで速くなりようがないことが決まってしまいます。そもそもの設計意図は GUI 自動テストでたまに 1 枚キャプチャするためのものであり、継続的な高頻度キャプチャのために用意されたものではないのです。</p>
<p>マルチスレッドでキャプチャしてフレームレートを上げればいい、と言う人もいるでしょう。しかしそのやり方は、個人的にはあまり合理的でないと思います。なぜなら CPU リソースの消費が大きすぎるからです。効率が高く、かつ CPU リソースの消費が少ない方法を探すべきです。</p>
<p>マルチスレッドにはもう一つ隠れた問題があります。キャプチャのボトルネックはシステムコールとメモリコピーにあり、複数のスレッドが同時にこの経路を奪い合っても、スループットが線形に伸びるとは限らない一方で、CPU 使用率は確実に倍増します。リモートデスクトップは常駐型のバックグラウンドツールであり、ホストマシンの CPU を食い潰すこと自体が受け入れられません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="画像の直接転送は成り立たない">画像の直接転送は成り立たない<a href="https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian#%E7%94%BB%E5%83%8F%E3%81%AE%E7%9B%B4%E6%8E%A5%E8%BB%A2%E9%80%81%E3%81%AF%E6%88%90%E3%82%8A%E7%AB%8B%E3%81%9F%E3%81%AA%E3%81%84" class="hash-link" aria-label="画像の直接転送は成り立たない への直接リンク" title="画像の直接転送は成り立たない への直接リンク" translate="no">​</a></h2>
<p>さらに、画像の転送という方式は取れません。1 枚の画像が大きすぎるからです。200KB として、1 秒に 30 枚なら 6000KB となり、帯域幅を深刻に圧迫します。</p>
<p>計算してみればこの道が通らないことが分かります。毎秒 6000KB は帯域幅に換算すると 50Mbps 近くになり、家庭用の上り回線ではまったく耐えられません。ましてや 1 台のホストから複数のリモート端末へ同時に配信するなど論外です。しかも JPEG はフレームごとに独立して圧縮されるため、前後のフレームで 99% のピクセルが変わっていなくても、画像全体を丸ごとエンコードして送り直すことになり、大量の重複情報に無駄が費やされます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ビデオストリームが本道">ビデオストリームが本道<a href="https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian#%E3%83%93%E3%83%87%E3%82%AA%E3%82%B9%E3%83%88%E3%83%AA%E3%83%BC%E3%83%A0%E3%81%8C%E6%9C%AC%E9%81%93" class="hash-link" aria-label="ビデオストリームが本道 への直接リンク" title="ビデオストリームが本道 への直接リンク" translate="no">​</a></h2>
<p>最良の方式はビデオストリームを使うことです。オープンソースの h.264 ビデオフォーマットを使います。さらに優れた h.265 もありますが、対応するオープンソースの jar は見つけられませんでした。h.265 の技術はまだ有償の段階にあります。h.264 ビデオフォーマットは、各フレームにおけるピクセルの変化に基づいてデータを記録します。画像全体の大部分のピクセル情報を記録する必要はありません。そのため h.264 フォーマットの動画の容量は、画像転送のデータ総量よりはるかに小さく、ネットワーク転送により適しています。</p>
<p>これこそがビデオエンコーディングにおけるフレーム間圧縮の考え方です。エンコーダーは一定間隔で完全なキーフレームを送り、その間のフレームは前のフレームに対する変化量だけを記録します。デスクトップの画面はまさにこの圧縮の理想的なシーンです——大半の時間、動いているのはマウスと一部のウィンドウだけで、背景は微動だにせず、フレーム間の冗長性が極めて高いため、圧縮率は自然と高くなります。</p>
<p>デスクトップを録画する方式でクライアントへ効率よく配信すれば、画像がもたらす帯域幅の問題とキャプチャの効率問題を大幅に軽減できます。ただしビデオストリームを使う場合、リアルタイムのトランスコードとトラフィックはどちらも小さくない課題です。</p>
<p>リアルタイムトランスコードの難しさは、遅延と計算力のバランスにあります。圧縮率を上げればエンコード時間が延びます。リモート操作で最も嫌なのは、画面が手の動きより半テンポ遅れることです。圧縮率を下げれば今度は帯域幅が持ちません。成熟したリモートデスクトップソフトは一般にハードウェアエンコーディングでこの矛盾を解いており、純粋な Java でソフトウェアレベルだけで追いつくのは、なかなか難易度が高いでしょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="私が最終的に採った折衷案">私が最終的に採った折衷案<a href="https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian#%E7%A7%81%E3%81%8C%E6%9C%80%E7%B5%82%E7%9A%84%E3%81%AB%E6%8E%A1%E3%81%A3%E3%81%9F%E6%8A%98%E8%A1%B7%E6%A1%88" class="hash-link" aria-label="私が最終的に採った折衷案 への直接リンク" title="私が最終的に採った折衷案 への直接リンク" translate="no">​</a></h2>
<p>最終的に私が採用したのはランレングス符号化で、Robot のキャプチャによる RGB ビットマップのうち変化のあったデータだけを転送する方式です。しかしランレングス符号化は、画面のピクセル変化が大きすぎる状況に遭遇すると、データ量の体積が急激に膨れ上がります。もし本当に画像でデスクトップ情報を転送するなら、Java が画面のピクセルデータを取得する効率と、転送データの体積圧縮の最小化を突破しなければ、リモートデスクトップのフレームレートを最大化することはできない、というのが私の考えです。</p>
<p>この方式をもう少し詳しく説明します。やり方は、現在のフレームと前のフレームの RGB 配列をピクセル単位で比較し、変化したピクセルだけを位置情報とともにエンコードして送り出し、相手側は受け取った差分データをローカルにキャッシュした画面に上書きするというものです。ランレングス符号化（RLE）自体は非常にシンプルで、連続する同じ値を「値 + 繰り返し回数」として記録します。静止画面では差分がほぼゼロになり、効果は上々です。しかしその弱点も明白で、ひとたび全画面スクロールや動画再生が起きると、ほぼすべてのピクセルが変化し、差分が全量に退化します。さらに符号化自体のオーバーヘッドが上乗せされ、データ量はかえって元画像をそのまま送るより大きくなることさえあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）Robot キャプチャの所要時間は解像度に直結します。テスト時に低解像度で見るとフレームレートはまずまずに見えても、2K 画面に切り替えた途端に馬脚を現します。方式を評価するときはターゲット解像度で測るべきです。</p>
<p>2）差分転送では最初のフレームとパケットロスをきちんと処理しなければなりません。相手側に基準となる画面がなければ差分は何の意味も持たないため、まず一度全量フレームを送る必要があります。転送の途中で差分フレームを 1 つ失うと、それ以降の画面はずっとずれ続けます。信頼性のある転送を使うか、定期的に全量フレームを強制送信して保険をかけるかのどちらかです。</p>
<p>3）相手側の復元コストも軽視できません。差分をビットマップにマージして画面に描画する処理も同様に CPU を消費します。受信側が非力だと、やはりカクつきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2020/01/28/javayuan-cheng-zhuo-mian-shi-xian#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回の試みを通して、結論はかなり明確になりました。Java でリモートデスクトップを作る場合、ボトルネックは言語そのものではなく、Robot というスクリーンキャプチャ経路の効率と、純ソフトウェア圧縮のオーバーヘッドにあります。画像の直接転送は帯域幅が爆発し、ランレングス符号化の差分転送は静的な画面には向いていても激しい変化には耐えられず、ビデオストリームこそがエンジニアリング上の正解です。ただ純 Java のエコシステムには使い勝手のよい h.264 のオープンソース実装が欠けています。原理検証のおもちゃを作るだけなら差分転送で十分ですが、本当にプロダクトを作るなら、やはりネイティブのエンコードライブラリやハードウェアエンコーディングの力を借りる必要があるでしょう。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[MySQL 億単位のデータをExcelドキュメントにエクスポートする]]></title>
            <link>https://tanzhuo.xyz/ja/2020/01/16/mysqlbai-mo-ji-shu-ju-dao-chu</link>
            <guid>https://tanzhuo.xyz/ja/2020/01/16/mysqlbai-mo-ji-shu-ju-dao-chu</guid>
            <pubDate>Thu, 16 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[SaaS システムで億単位のリストデータを Excel にエクスポートするための実践的なソリューション。MQ によるピークカットと非同期コンシューム、リードレプリカでのクエリ負荷分散、id カーソルページングと EasyExcel によるディスク書き出し・OSS アップロードにより、7,550 万件のデータを約 20 分でエクスポート完了。]]></description>
            <content:encoded><![CDATA[<p>私たちの会社の SaaS システムでは、ユーザーにリストデータを Excel ドキュメントとしてエクスポートする機能を提供する必要があります。しかし、企業によっては財務明細などのリストデータがすでに数千万件、億単位に達しており、このようなデータのエクスポートには専用のソリューションが必要になります。</p>
<!-- -->
<p>そうしないと、月末に数百社が同時に Excel をエクスポートした場合、MySQL の QPS が急上昇し、サーバーの CPU と RAM が一瞬でアラート閾値に達してしまいます。大量エクスポートによってサーバーが利用不能になり、最悪の場合ダウンする恐れもあります。(注意:財務などの機密データについては、ファイルの暗号化や一時ファイルの認可付きダウンロードといった対策が必要です)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="具体的なソリューション">具体的なソリューション<a href="https://tanzhuo.xyz/ja/2020/01/16/mysqlbai-mo-ji-shu-ju-dao-chu#%E5%85%B7%E4%BD%93%E7%9A%84%E3%81%AA%E3%82%BD%E3%83%AA%E3%83%A5%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3" class="hash-link" aria-label="具体的なソリューション への直接リンク" title="具体的なソリューション への直接リンク" translate="no">​</a></h2>
<p>ソリューションの出発点は、「大量エクスポートが通常業務の稼働に影響を与えてはならない」ということです。</p>
<p>データベースのリードレプリカノードを別途デプロイしてクエリ負荷を分担し、さらに複数のサーバーグループを別途用意して Spring Boot プロジェクトを MQ のコンシューマークラスターとして稼働させます。すべてのエクスポートリクエストは MQ キューに投入され、Spring Boot はプル方式でキューから Excel エクスポートタスクのメッセージを能動的に取得して実行します。MQ のピークカット・平準化と非同期処理の能力を有効活用し、Spring Boot プロジェクト内ではスレッドプールのリソースを厳密に割り当てます。</p>
<p>実際のタスクコンシューマーは、億単位ページングの手法で MySQL のリストデータを分割クエリし、Alibaba の EasyExcel を使ってデータをハードディスクに書き出します。出力が完了したら OSS にアップロードし(OSS の内部ネットワーク経由でストリーム転送し、直接 OSS ファイルシステムに送ることも可能です)、その後ローカルディスクのファイルを削除します。</p>
<p>このような設計により、エクスポート機能はメイン業務から完全に切り離され、独立した機能モジュールとなり、デプロイも専用のサーバーノードで行われます。仮にエクスポートプロジェクトが突発的な大量エクスポートリクエストでダウンしたとしても(MQ がこの問題をうまく回避してくれるため、通常は起こりませんが)、メイン業務の正常な機能には影響しません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="リソース制限とページングクエリ">リソース制限とページングクエリ<a href="https://tanzhuo.xyz/ja/2020/01/16/mysqlbai-mo-ji-shu-ju-dao-chu#%E3%83%AA%E3%82%BD%E3%83%BC%E3%82%B9%E5%88%B6%E9%99%90%E3%81%A8%E3%83%9A%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0%E3%82%AF%E3%82%A8%E3%83%AA" class="hash-link" aria-label="リソース制限とページングクエリ への直接リンク" title="リソース制限とページングクエリ への直接リンク" translate="no">​</a></h2>
<p>スレッドプールによるリソース制限に加えて、1 顧客あたりの同時エクスポートファイル数の上限を設け、JVM が一度にプルするタスク数を厳密にコントロールすることで、サービスの堅牢性を確保します。</p>
<p>データベースのクエリには億単位ページングの手法を採用しています。まず条件に基づいて対象データの id を取得し、limit の大きなオフセットを使う方法をやめて id カーソル方式に切り替えることで、毎回の SQL クエリの実行効率をミリ秒レベルに保ちます。同時にクエリするカラムを適切に選択し(インデックスは必須です)、MySQL から毎回流れ出るデータ量が一定のサイズ範囲(100KB〜500KB)に収まるようにします。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 大きなオフセットの limit の代わりに id カーソルを使い、毎回前バッチの最大 id を起点にする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">select</span><span class="token plain"> id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">from</span><span class="token plain"> テーブル </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">where</span><span class="token plain"> 条件 </span><span class="token operator" style="color:hsl(207, 82%, 66%)">and</span><span class="token plain"> id </span><span class="token operator" style="color:hsl(207, 82%, 66%)">&gt;</span><span class="token plain"> 前バッチの最大id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">order</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">by</span><span class="token plain"> id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">limit</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">5000</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><br></div></code></pre></div></div>
<p>さらに抽象ファクトリーとインターフェースをラップして、開発者が各種機能リストのエクスポートを実装できるように提供します。開発者は、具体的にどうページング・バッチ分割でデータを取得するか、どう Excel を生成するか、どうアップロードするかを気にする必要はなく、エクスポート用の SQL クエリを書くだけで、データの取得に専念できます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="実際の効果">実際の効果<a href="https://tanzhuo.xyz/ja/2020/01/16/mysqlbai-mo-ji-shu-ju-dao-chu#%E5%AE%9F%E9%9A%9B%E3%81%AE%E5%8A%B9%E6%9E%9C" class="hash-link" aria-label="実際の効果 への直接リンク" title="実際の効果 への直接リンク" translate="no">​</a></h2>
<p>その後のテストでは、7,550 万件のデータのエクスポートに約 20 数分かかりました(適切にインデックスを追加し、マルチスレッドでタスクを分解してクエリ・エクスポートすれば、実際にはもっと速くできます)。現在の業務シナリオの要件は十分に満たせています。</p>
<p>1 回のクエリの SQL 通信 + 実行時間を 100ms と見積もると、おおよその時間の計算式は次のとおりです:(75500000 / 5000 × 100ms) / 1000 / 60 ≈ 25min</p>
<p>1 顧客のエクスポートによるメモリ増加は 20MB 程度の振れ幅しかありません。dataList は一度に 5,000 件しか取得せず、データがディスクに出力された後は list オブジェクトのメモリがすでに回収されているため、メモリの増加はごくわずかで、ディスク I/O は毎回 3〜4MB です。複数顧客の場合は、スレッドリソースの制限と総エクスポートタスク数の制限を行い、JVM の OOM を防ぐ必要があります。</p>
<p><strong>Excel の 1 つのワークシートには約 100 万行までしか書き込めないため、それを超える場合はワークブックを分割して処理する必要があります。ワークブックの分割はマシンのメモリを考慮して行います。合計 1 億件のレコードをエクスポートする場合、単一ファイルのサイズが大きくなりすぎるため、その後 Excel ファイルを分割保存して圧縮する必要があります。単一ファイルが大きすぎると、開く際のメモリ使用量が高くなりすぎて、顧客がダウンロード後にファイルを開けなくなってしまうからです。</strong></p>
<p>データベースが毎回吐き出すデータもパフォーマンスを消費しすぎないようにし、7,550 万件のデータのエクスポートは複数バッチに分けて完了させる必要があります。ディスクに出力された Excel ファイルが完成したら zip 圧縮してそのまま OSS にアップロードし、最後に OSS のリソースパスをフロントエンドのアプリや Web ブラウザに提供して、クライアントにダウンロードさせます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2020/01/16/mysqlbai-mo-ji-shu-ju-dao-chu#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>たかがデータエクスポート機能とはいえ、パフォーマンスを極限まで追求しようとすると、注目すべきポイントは非常に多くあります。MQ による非同期ピークカット、リードレプリカでのクエリ負荷分散から、カーソルページング、メモリ制御とファイル分割、さらにデプロイレベルでの分離、そして将来の業務成長に向けた水平スケールアウトの考慮まで、あらゆる面をきちんと設計しておく必要があるのです。</p>]]></content:encoded>
            <category>技術</category>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[MySQL 億単位データのページネーション]]></title>
            <link>https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo</link>
            <guid>https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo</guid>
            <pubDate>Wed, 15 Jan 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[単一テーブルのデータが数百万件になると、単純な limit でのページ送りはどんどん遅くなります。会計テーブルのページネーション最適化の記録：サブクエリで主キーを特定、遅延 JOIN、explain での検証によって、深いページネーションのクエリを許容範囲まで抑え込みました。]]></description>
            <content:encoded><![CDATA[<p>会社の事業が拡大するにつれて、データ量も急激に増えていきます。MySQL はコミュニティ版が無料のオープンソースデータベースですが、数百万件のデータのページネーションを limit だけに頼るのは現実的ではありません。もちろん MySQL を貶めるつもりはありません。MySQL はプラガブルなストレージエンジンを持つオープンソースデータベースとして、大部分のアプリケーションシーンの要求をすでに満たせますし、MySQL で 100TB を管理することも問題ではありません。しかし、その使い方こそが問題なのです。</p>
<!-- -->
<p>ページネーションはほぼすべての管理画面のリストページに標準装備される機能で、データ量が少ないうちはどう書いても動きます。問題が表面化するのは、たいていテーブルが数百万行に膨らんだ後です。最初の数ページはミリ秒で返ってくるのに、後ろへめくるほど遅くなり、最後の数ページではタイムアウトすることさえあります。この記事に記録するのは、私が会社の会計テーブルで遭遇した深いページネーション（deep pagination）の問題と、一歩ずつ最適化していった過程です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="limit-はなぜ遅いのか">limit はなぜ遅いのか<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#limit-%E3%81%AF%E3%81%AA%E3%81%9C%E9%81%85%E3%81%84%E3%81%AE%E3%81%8B" class="hash-link" aria-label="limit はなぜ遅いのか への直接リンク" title="limit はなぜ遅いのか への直接リンク" translate="no">​</a></h2>
<p>limit は 1 つまたは 2 つの数値パラメータを受け取ります。パラメータは整数の定数でなければなりません。2 つのパラメータを与えた場合、1 つ目は最初に返すレコード行のオフセットを、2 つ目は返すレコード行の最大数を指定します。</p>
<p>鍵はオフセットの実装方式にあります。MySQL は 10 万行目へ「ジャンプ」することはできず、前方の offset 行をすべて読み出してから捨て、最後の数件だけを返します。つまり <code>limit 100000, 20</code> は実際には 100020 行のデータを読んでいるのです。クエリ対象のカラムがインデックスに含まれていなければ、各行はさらに主キーでクラスタインデックスへ戻って完全なレコードを取得（回表）する必要があり、読む行数が多いほどこのコストは増幅されます。</p>
<p>limit はオフセットが 10 万未満のうちは性能がなんとか許容範囲ですが、オフセットが大きくなるにつれて性能は急激に低下します。</p>
<p>会社の単一テーブルの会計データはすでに 230 万件に達しており、limit でページネーションして最後のページのデータを取得すると、20 秒はかからないと結果が返ってこないでしょう。もちろん具体的な時間は、インデックスの有無、カラム数、データ内容、クエリ条件によって変わります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="サブクエリでまず主キーを特定する">サブクエリでまず主キーを特定する<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#%E3%82%B5%E3%83%96%E3%82%AF%E3%82%A8%E3%83%AA%E3%81%A7%E3%81%BE%E3%81%9A%E4%B8%BB%E3%82%AD%E3%83%BC%E3%82%92%E7%89%B9%E5%AE%9A%E3%81%99%E3%82%8B" class="hash-link" aria-label="サブクエリでまず主キーを特定する への直接リンク" title="サブクエリでまず主キーを特定する への直接リンク" translate="no">​</a></h2>
<p>ページネーションの効率問題を解決するために私が採用した方式は、まずサブクエリで主キー id だけを取得し、その id で行全体のデータを取りに行くというものです。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- サブクエリでは主キー id だけを取得し、where 条件を付ける</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- インデックスのスキャンだけで完結し、行全体を取りに戻る必要がない</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">select</span><span class="token plain"> id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">from</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">table</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">limit</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">100000</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token number" style="color:hsl(29, 54%, 61%)">20</span><br></div></code></pre></div></div>
<p>（where 条件を付けてサブクエリにします）</p>
<p>主キー id はもともと主キーインデックスなので、limit の速度・効率は非常に高くなります。さらに条件カラムを複合インデックスに含めることで、効率は質的に向上します。このステップが速い理由は、id だけを取得する場合、クエリ全体がインデックス上で完結し、インデックスのレコード 1 件あたりのサイズが小さいため、同じ offset のスキャン量でも IO コストが行全体をスキャンするよりはるかに低いからです。</p>
<p>ただしソートがある場合、性能はやはり大きく低下します。目測では 30 万件のデータを id や時刻でソートすると 1〜2 秒かかります。</p>
<p>最良の方法は、limit のオフセットをきっぱり捨てて <code>where id &gt; xxx</code> を使い、目的のデータの id 位置をより高速に特定し、その後ソートして limit 20 件を取ることです。これなら百万件レベルでも基本的に耐えられます。この書き方は一般にカーソルページネーション（あるいは keyset ページネーション）と呼ばれます。ページをめくるたびに前のページの最後の 1 件の id を持ってきて、<code>where id &gt; xxx</code> でインデックス上から直接開始位置を特定するので、何ページ目までめくってもスキャンする行数は常に 20 行程度で固定されます。代償として順方向のページ送りしかできず、任意のページへのジャンプはサポートされません。フィード型の一覧やエクスポートのような場面に向いています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="id-リストで行全体のデータを取得する">id リストで行全体のデータを取得する<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#id-%E3%83%AA%E3%82%B9%E3%83%88%E3%81%A7%E8%A1%8C%E5%85%A8%E4%BD%93%E3%81%AE%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%8F%96%E5%BE%97%E3%81%99%E3%82%8B" class="hash-link" aria-label="id リストで行全体のデータを取得する への直接リンク" title="id リストで行全体のデータを取得する への直接リンク" translate="no">​</a></h2>
<p>まず必要な id を取得し、その後条件なしの in idList クエリを実行します：</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 第 2 ステップ：前のステップで得た id リストで全カラムを取得する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- where 条件は付けず、ソートはそのまま残す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">select</span><span class="token plain"> 字段 </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">from</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">table</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">where</span><span class="token plain"> id </span><span class="token operator" style="color:hsl(207, 82%, 66%)">in</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">(</span><span class="token plain">idList</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">)</span><br></div></code></pre></div></div>
<p>（条件は付けません。ソートがあればソートは残しますが、データ量がすでに少ないので問題ありません。外部ソートになっても十分速いです）</p>
<p>リストのクエリでは where in id だけで十分です。その他の条件は select id の時点ですでに適用済みなので、リストデータの取得時に条件を付ける必要は一切ありません。ただしソートは付ける必要があります。</p>
<p>これで非常に高速なページネーションクエリができます。さらにリストのカラム数が少なければカバリングインデックスにでき、クエリ効率はもう一段上がります。しかし一般にリストのカラムは多く、10 数個や 20 数個は普通です。これらすべてにインデックスを張るのは得策ではありません。インデックスのサイズが大きくなりすぎ、データの追加・更新時にインデックスのメンテナンスで効率が下がり、かえってテーブルの性能を落とすことになりかねません。ですからクエリを設計するときは、カラム数、ソート規則、条件の数、インデックスの種類、どのカラムにインデックスを張るかを天秤にかける必要があります。</p>
<p>テーブル結合（JOIN）のような操作は、大量データのもとでは基本的に使用を考えません。冗長カラムを持たせるのが最善です。大きなテーブルでの join は、駆動テーブルの各行ごとに被駆動テーブルへの検索が発生することを意味し、データ量が増えると増幅効果が顕著になります。よく使う関連カラムをメインテーブルに冗長化して持たせ、書き込み時に一部余分に保存する代わりにクエリ時の結合を一回減らすのは、読み込みが多く書き込みが少ないリスト系の場面では通常割に合います。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="explain-で実行計画を検証する">explain で実行計画を検証する<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#explain-%E3%81%A7%E5%AE%9F%E8%A1%8C%E8%A8%88%E7%94%BB%E3%82%92%E6%A4%9C%E8%A8%BC%E3%81%99%E3%82%8B" class="hash-link" aria-label="explain で実行計画を検証する への直接リンク" title="explain で実行計画を検証する への直接リンク" translate="no">​</a></h2>
<p>最適化は勘に頼ってはいけません。SQL を変更したら実行計画を見て確認します。</p>
<p>クエリ SQL を explain で確認し、実行過程を見ます。インデックスを使っているか、インデックスの種類は何か、回表（テーブルへの再アクセス）が発生しているか、スキャンした行数はどれくらいかなどの重要な情報を把握してこそ、データベースの性能は質的に向上します。</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)">-- クエリ文の前に explain を付ければ実行計画を確認できる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- 注目すべきは type（インデックスの種類）、key（実際に使われたインデックス）、</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)">-- rows（推定スキャン行数）、Extra（Using index でカバリングインデックスかどうか）</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token keyword" style="color:hsl(286, 60%, 67%)">explain</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">select</span><span class="token plain"> id </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">from</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">table</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">where</span><span class="token plain"> 条件 </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">limit</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">100000</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">,</span><span class="token number" style="color:hsl(29, 54%, 61%)">20</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><br></div></code></pre></div></div>
<p>テストの結果、このページネーション方式なら 100 万件・1 ページ 20 件のデータで基本的に 1 秒以内にレスポンスが返り、おおよそ 600ms 程度のリクエスト時間になりました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="単一テーブルの先にある道">単一テーブルの先にある道<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#%E5%8D%98%E4%B8%80%E3%83%86%E3%83%BC%E3%83%96%E3%83%AB%E3%81%AE%E5%85%88%E3%81%AB%E3%81%82%E3%82%8B%E9%81%93" class="hash-link" aria-label="単一テーブルの先にある道 への直接リンク" title="単一テーブルの先にある道 への直接リンク" translate="no">​</a></h2>
<p>ページネーションの最適化が解決するのは単一テーブルのクエリ効率ですが、テーブルは今後も成長し続けるので、アーキテクチャの面でも先手を打って逃げ道を用意しておくべきです。</p>
<p>単一テーブルのデータ量がすでに 500 万件を超えているなら、水平分割（シャーディング）を検討してよい段階です。</p>
<p>ビジネスロジックの観点では、単一データベースの性能を高めるために、リード・ライト分離（マスターに書き込み、スレーブから読み込み、複数スレーブなど）を検討できます。</p>
<p>ビジネスに応じてデータベースを垂直分割し、ホットデータとコールドデータを分離してサーバーリソースを配分します。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）サブクエリ方式の前提は、where 条件のカラムに適切な複合インデックスがあることです。そうでなければ第 1 ステップの select id 自体がフルテーブルスキャンに退化し、最適化が無駄になります。</p>
<p>2）<code>where id &gt; xxx</code> のカーソルページネーションは、ソートキーが単調かつ一意であることを要求します。時刻でソートしていて時刻に重複値がある場合、ページ送り時にデータの取りこぼしや重複が起こり得るため、通常は「時刻 + id」の複合ソートで保険をかける必要があります。</p>
<p>3）<code>in (idList)</code> のリストの長さは 1 ページあたりの件数と同じで、一般に 20 件なら問題ありません。ただしこの書き方を一度に数千個の id を渡すような場面に広げてはいけません。</p>
<p>4）explain が示す rows は推定値であり、統計情報が不正確なときはズレが生じます。判断に迷うときはスロークエリログと実際の実行時間を併せて判断してください。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>本番環境でページネーション SQL を変更する前に、まずスレーブやテスト環境で実データ量を使って実行計画を検証してください。深いページネーションの問題は、小さいデータ量では再現できません。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2020/01/15/mysqlbai-mo-shu-ju-fen-ye-sao-cao-zuo#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>深いページネーションが遅い根本原因は、limit のオフセットが行を 1 件ずつスキャンして捨てなければならず、データ量が大きいほどコストが高くなることです。解決の考え方は、スキャンを最小のデータセット上で行わせること。まずインデックス内で主キーを特定してから行全体を取得し、カーソルページネーションが使えるならオフセットを捨てる。変更後は explain でインデックスが本当に効いているかを検証します。単一テーブルで支えきれなくなったら、テーブル分割、リード・ライト分離、ホット・コールド分離といったアーキテクチャの手段を検討しましょう。</p>]]></content:encoded>
            <category>技術</category>
            <category>DB</category>
        </item>
        <item>
            <title><![CDATA[ロゴデザインについての雑感]]></title>
            <link>https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji</link>
            <guid>https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji</guid>
            <pubDate>Tue, 31 Dec 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[企業のポジショニング、色の取捨選択、図形の形態から商標登録まで。ロゴがデザインから実際に使われるまでに考えるべきこと：ロゴは単なる美しい図形ではなく、ブランド記憶の器なのです。]]></description>
            <content:encoded><![CDATA[<p>企業にまだ知名度がないうちにロゴを作れば、人々に一定の識別的な認知を与えられます。そして企業の知名度が高まれば、その図形を人々の記憶にしっかりと結びつけ、企業の知名度をさらに強化できます。まさに錦上添花と言えるでしょう。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ロゴが存在する意味">ロゴが存在する意味<a href="https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji#%E3%83%AD%E3%82%B4%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E6%84%8F%E5%91%B3" class="hash-link" aria-label="ロゴが存在する意味 への直接リンク" title="ロゴが存在する意味 への直接リンク" translate="no">​</a></h2>
<p>ロゴの核心的な価値は、記憶コストを下げることにあります。人間の図形に対する記憶は、文字に対する記憶よりはるかに強固です。あるブランドの話題が出たとき、頭に最初に浮かぶのはその正式名称ではなく、あの図形のシンボルであることが多いのです。人々が図形の記憶を通じて会社のイメージやブランドを覚えられるようにすること。これがロゴの果たすべき第一の仕事です。</p>
<p>逆に言えば、判別しづらく、同業他社と酷似したロゴは、この記憶コストをユーザーに押し返しているのと同じです。ですからロゴの良し悪しを評価する第一の基準は「美しいか」ではなく、「一目見て覚えられるか、数日経っても認識できるか」なのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="企業のポジショニングから出発する">企業のポジショニングから出発する<a href="https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji#%E4%BC%81%E6%A5%AD%E3%81%AE%E3%83%9D%E3%82%B8%E3%82%B7%E3%83%A7%E3%83%8B%E3%83%B3%E3%82%B0%E3%81%8B%E3%82%89%E5%87%BA%E7%99%BA%E3%81%99%E3%82%8B" class="hash-link" aria-label="企業のポジショニングから出発する への直接リンク" title="企業のポジショニングから出発する への直接リンク" translate="no">​</a></h2>
<p>ロゴのデザインは、会社の企業文化、企業が提供するサービスの観点から出発すべきです。分かりやすく言えば、その会社は何をしている会社なのか？会社に必要な気質は、レトロで落ち着いたものか、それとも若々しく活力に満ちたものか？ということです。</p>
<p>このステップはよく飛ばされます。要件を受け取るといきなり絵を描き始める人が多く、その結果、双方が疲れ果てるまで修正を繰り返すことになります。より堅実なやり方は、まずいくつかの問いを書き出すことです：</p>
<ol>
<li class="">
<p>会社のコア事業は何か、ターゲットユーザーは誰か；</p>
</li>
<li class="">
<p>ユーザーがロゴを見たときにどんなキーワードを連想してほしいか。例えば「プロフェッショナル」「親しみやすい」「テック感」；</p>
</li>
<li class="">
<p>主要な競合のロゴはどんな見た目か、どうやって差別化するか。</p>
</li>
</ol>
<p>これらにはっきり答えてから手を動かせば、方向のブレはずっと小さくなります。この事前作業の量が少なくないからこそ、プロのビジュアルデザイナーの価値がここに現れるのです。有名ブランドの中にはロゴのデザイン費用だけで数十万元に達するものも少なくありません。買っているのは数本の線ではなく、その背後にある完全なポジショニングの導出過程なのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="色の取捨選択">色の取捨選択<a href="https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji#%E8%89%B2%E3%81%AE%E5%8F%96%E6%8D%A8%E9%81%B8%E6%8A%9E" class="hash-link" aria-label="色の取捨選択 への直接リンク" title="色の取捨選択 への直接リンク" translate="no">​</a></h2>
<p>テック企業やインターネット企業にとって、若々しく活力に満ちた気質はほぼ必須の選択です。色は多様性を持たせてもよいのですが、全体の色数は多すぎてはいけません。さもないとやや派手に見えてしまうので、色の種類は適度に抑える必要があります。</p>
<p>彩度については、私は控えめに使う方に傾いています。色はやや淡くすべきで、彩度を高くしすぎてはいけません。彩度の高い大面積の色面は、見ていて落ち着かず、見れば見るほど気持ちが急かされます。人の目に映ったときに、より心地よく、より親しみやすく、リラックスした感覚を与えたいなら、低彩度・少ない色相のほうが通常は安全な選択です。</p>
<p>もう一つ見落とされがちなポイントがあります。ロゴは最終的にさまざまな場面に登場します。ウェブサイト、名刺、印刷物、ダークな背景のアプリ画面。デザイン時には、白黒のモノクロでも依然としてはっきり見えるかを併せて検証するのが望ましいでしょう。グラデーションや多色の組み合わせに依存した案の多くは、モノクロに変換した途端にぼやけた塊になってしまいます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="図形とフォント">図形とフォント<a href="https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji#%E5%9B%B3%E5%BD%A2%E3%81%A8%E3%83%95%E3%82%A9%E3%83%B3%E3%83%88" class="hash-link" aria-label="図形とフォント への直接リンク" title="図形とフォント への直接リンク" translate="no">​</a></h2>
<p>図形の形状は会社の事業内容に応じて決めるべきで、会社が求めるのがフォント型（ワードマーク）のロゴなのか、それともやや抽象的な図形なのかにもよります。</p>
<p>フォント型のロゴは会社名をそのままビジュアル化したもので、認知の負担が小さく、見ればすぐ読めるのが利点です。抽象的な図形は連想を築くために長期的な露出に頼ることになりますが、一度確立されれば、識別性も展開力もより強くなります。インターネット企業はしばしば図形にテック的な要素を取り入れますが、具体的なモチーフはやはり会社の企業形態と共鳴するものでなければなりません。</p>
<p>ここで正しておきたい心構えが一つあります。ロゴは一番美しいものである必要はなく、会社のイメージに最も合致したロゴであれば十分だということです。技巧を見せびらかすためにディテールを盛り込むと、かえって小さいサイズでの識別性を犠牲にします。favicon はわずか十数ピクセルしかなく、複雑な図形はそのサイズではほぼ判別不能になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="商標登録を忘れずに">商標登録を忘れずに<a href="https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji#%E5%95%86%E6%A8%99%E7%99%BB%E9%8C%B2%E3%82%92%E5%BF%98%E3%82%8C%E3%81%9A%E3%81%AB" class="hash-link" aria-label="商標登録を忘れずに への直接リンク" title="商標登録を忘れずに への直接リンク" translate="no">​</a></h2>
<p>デザインの完成は半分に過ぎず、法的な保護も同じくらい重要です。ロゴが確定したら必ず商標登録と著作権登録を行いましょう。さもないと権利侵害の事態が起こり得ます。他人があなたの図形を先取り登録する可能性もあれば、あなたのデザインが意図せず既登録の商標と衝突している可能性もあります。商標登録は本質的に、会社と企業のイメージをより良く守るためのものです。</p>
<p>実務上は、デザインの確定前に一度商標検索を行い、目的の区分に類似商標がないことを確認しておくことをおすすめします。苦労して磨き上げた案が登録できないせいで白紙に戻る事態を避けられます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/12/31/qian-tan-logoshe-ji#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>一つのロゴがゼロから形になるまでには、おおよそポジショニング分析、色と図形のデザイン、多場面での検証、商標登録というステップを経ます。すべての人を驚嘆させる必要はなく、会社が何者であるかを正確に伝え、一目見て覚えてもらえれば十分です。「企業イメージに合致すること」を「美しいこと」より前に置けば、大半の取捨選択には自ずと答えが出ます。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[ELK 導入の記録]]></title>
            <link>https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk</link>
            <guid>https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk</guid>
            <pubDate>Thu, 19 Dec 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[会社の集計業務で MySQL クラスタがボトルネックに達し、インデックス最適化でも改善しなかったため ELK を導入。Logstash による増分同期、Elasticsearch での集計クエリ、Kibana での可視化——技術選定と増分同期の方式を記録します。]]></description>
            <content:encoded><![CDATA[<p>最近、会社のシステムで集計処理の負荷が大きくなってきました。MySQL クラスタのクエリがボトルネックに達し、インデックス最適化でも集計結果を素早く返せなくなったため、Elasticsearch、Logstash、Kibana というオープンソース三点セットを導入しました。</p>
<!-- -->
<p>まず背景を説明しておきます。集計系のクエリと通常の業務クエリは別物です。業務クエリの多くは主キーやインデックスで少数の行を取得するもので、MySQL の得意分野です。一方、集計クエリは大量のデータをスキャンしてグルーピングや集約を行うことが多く、こうした場面では B+ ツリーのインデックスはあまり役に立ちません。データ量が増えてくると、一本の集計 SQL が数秒、時には数十秒もかかり、同じ DB 上の業務リクエストまで巻き込んでしまいます。こうなったときの定番のアプローチが、集計負荷を OLTP データベースから切り離し、集約分析専用のエンジンに任せることです。Elasticsearch の内部は転置インデックスとカラムナ形式の doc values で構成されており、フィルタリングと集約に生来向いています。ELK のこの組み合わせはコミュニティで最も取り組みやすい構成のひとつでもあるので、記録しておく価値があります。</p>
<p><a href="https://www.elastic.co/cn/" target="_blank" rel="noopener noreferrer" class="">https://www.elastic.co/cn/</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="技術選定と役割分担">技術選定と役割分担<a href="https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk#%E6%8A%80%E8%A1%93%E9%81%B8%E5%AE%9A%E3%81%A8%E5%BD%B9%E5%89%B2%E5%88%86%E6%8B%85" class="hash-link" aria-label="技術選定と役割分担 への直接リンク" title="技術選定と役割分担 への直接リンク" translate="no">​</a></h2>
<p>Logstash については、実は Alibaba の DataX を使うこともできます。機能面では Logstash よりさらに一段上です。私が採用したのは Logstash のままでした。DataX の存在を知ったのが後になってからだったので、あえて置き換えはしませんでした。</p>
<p>両者の位置づけには少し違いがあります。Logstash は Elastic ファミリーのデータパイプラインで、jdbc input プラグインで定期的にデータを取得し、設定ファイルを書けばすぐ動きます。Elasticsearch への出力連携も標準で用意されています。DataX は Alibaba がオープンソース化した異種データソース同期ツールで、プラグインがカバーするデータソースが多く、バッチスループットも強力です。「MySQL から ES への定期増分同期」という単一の経路であれば Logstash で十分であり、乗り換えのための乗り換えをする必要はありません。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-12-image-1-60438a663c67bcd8701b19a0ae751a56.webp" width="1461" height="1034" class="img_ev3q"></p>
<p>三つのコンポーネントの役割分担は明快です。</p>
<p>Logstash が MySQL の集計データを取得・フィルタリングし、Elasticsearch へ増分同期します。</p>
<p>そしてプロジェクトの Java API から Elasticsearch にクエリを投げます。</p>
<p>Kibana はクラスタやインデックスの状況を Web 上で可視化できます。</p>
<p>つまり、Logstash がデータの入口を、Java アプリケーションがデータの出口を担当し、Kibana がクラスタとインデックスの健全性を人の目に見えるようにする——三者は互いに干渉しません。</p>
<p>Elasticsearch のクエリ文自体はかなりシンプルなのですが、公式サイトの例があまり多くない印象で、複雑な集約の多くは自分で模索する必要があります。たとえば多層ネストの aggregation や、集約結果をさらにソートするような書き方は、ドキュメントには最も基本的な例しか載っていないことが多く、実際の業務では Kibana の Dev Tools で少しずつ調整して作り上げることになります。</p>
<p>全体としての学習難易度は高くありません。すぐに使い始められます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="java-側の接続">Java 側の接続<a href="https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk#java-%E5%81%B4%E3%81%AE%E6%8E%A5%E7%B6%9A" class="hash-link" aria-label="Java 側の接続 への直接リンク" title="Java 側の接続 への直接リンク" translate="no">​</a></h2>
<p>Java API の jar には elasticsearch-rest-high-level-client を使いました。</p>
<p>その後、業務シナリオに合わせて自分でファクトリをラップし、開発メンバー向けに数層のコードを抽象化しました。ラップした目的は単純で、各開発者が直接 <code>SearchSourceBuilder</code> を組み立てたり <code>SearchResponse</code> を解析したりするのを避けたかったからです。よく使う条件フィルタ、ページネーション、集約のパターンをいくつかのメソッドに集約し、業務コードはパラメータを渡して結果を受け取ることだけに集中できるようにしました。</p>
<p>API でのクエリと結果セットの取得は操作がやや面倒に感じるかもしれませんが、クエリ文と対照しながら書けば、実はとても理解しやすいものです。high level client のビルダー構造は Query DSL の JSON 構造とほぼ一対一で対応しているので、まず Kibana で DSL を動くまで調整し、それを Java コードに翻訳すれば、ほとんど間違えることはありません。</p>
<p>全体的な印象としてはかなり親切です。どのソフトウェアもインストールすればすぐ使えますが、設定はいくらか変更する必要があります。ここでは詳しく触れませんが、IP・ポート、言語設定、パスワードなどです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="集計に必要なフィールドだけを同期する">集計に必要なフィールドだけを同期する<a href="https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk#%E9%9B%86%E8%A8%88%E3%81%AB%E5%BF%85%E8%A6%81%E3%81%AA%E3%83%95%E3%82%A3%E3%83%BC%E3%83%AB%E3%83%89%E3%81%A0%E3%81%91%E3%82%92%E5%90%8C%E6%9C%9F%E3%81%99%E3%82%8B" class="hash-link" aria-label="集計に必要なフィールドだけを同期する への直接リンク" title="集計に必要なフィールドだけを同期する への直接リンク" translate="no">​</a></h2>
<p>会社の主な用途は集計なので、Logstash では重要な集計データだけを取得しています。数百万件のデータから重要な集計フィールドだけを取得すると 100MB にも満たない量になりました。これは ES の集約・集計の速度を大きく向上させます。ですから、テーブル全体を丸ごと取り込むのはおすすめしません。最も重要な集計フィールドだけを取るべきです。</p>
<p>この点は掘り下げる価値があります。Elasticsearch の集約はメモリと doc values 上で実行されるため、インデックスがスリムであるほどセグメントファイルは小さくなり、キャッシュできる割合が高くなって、集約は自然と速くなります。しかも ES はデータベースではないので、「全量の明細を保存する」責務を負う必要はありません。明細は常に MySQL が正であり、ES には集計のディメンションと指標フィールドだけを置きます。壊れてもいつでも再構築できるので、心理的な負担もずっと軽くなります。</p>
<p>全体のフローは図のとおりです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2020-01-----8c8c2bbb2ecaab9ad0f92291cf1cef25.webp" width="1600" height="740" class="img_ev3q"></p>
<p>これは私が会社のメンバー向けに描いたフロー図でもあります。</p>
<p>もし取得するデータ量が非常に大きい場合は、途中に Kafka を挟んでバッファリングと再フィルタリングを行うこともできます。私たちの会社はそこまでのデータ量ではなかったので、他のサーバーリソースを余分に消費することはしませんでした。中間にメッセージキューを一層挟む意義はピークカットと疎結合化にあります。上流の取得と下流の書き込みの速度が一致しないとき、キューが突発的なトラフィックを受け止めてくれますし、途中にクレンジングロジックをもう一段挟むのも容易になります。ただし、コンポーネントが一つ増えるごとに運用コストも一つ増えます。データ量が達していないなら導入すべきではありません。</p>
<p>Kibana は今ではとても完成度が高く、公式の Query DSL でもクエリを書けますし、SQL でも書けます。とはいえ公式は、集約とクエリには Query DSL の使用を推奨しています。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2020-01-image-0c13f4afeeb7506820c66bd5ea02aec4.webp" width="1600" height="808" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="増分同期の実装">増分同期の実装<a href="https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk#%E5%A2%97%E5%88%86%E5%90%8C%E6%9C%9F%E3%81%AE%E5%AE%9F%E8%A3%85" class="hash-link" aria-label="増分同期の実装 への直接リンク" title="増分同期の実装 への直接リンク" translate="no">​</a></h2>
<p>増分同期と更新データの同期については、取得対象のデータテーブルに data_version フィールド（楽観ロックの原理によるデータバージョン番号）を設ける方式を採用しました。あるデータが変更されるか新規追加されると、data_version の値が設定されます。私はタイムスタンプを設定しました（会社の業務上の理由からです）。このフィールドには bigint 型を推奨します。timestamp 型では 2028 年までしか使えません。</p>
<p>こうしてデータにバージョンを持たせると、増分・更新データの同期時には data_version が変化したデータだけが取得されます。MySQL サーバー、Logstash、Elasticsearch の負荷を大幅に下げられます。</p>
<p>Logstash は前回の取得時に最後のレコードが持っていた data_version の値を記録できます。これにより次回の取得時には、前回の data_version の値を持って where 条件で有効なデータだけをフィルタリングできます。もちろん SQL の中では data_version でソートする必要があります。Logstash が記録するのは、前回取得時の最後の一件の data_version 値だけだからです。</p>
<p>この裏側にあるのが Logstash jdbc プラグインの tracking column の仕組みです。プラグインは前回実行時の最後のレコードの追跡カラム値をローカルファイルに永続化し、次回の SQL 実行時にパラメータとして where 条件に渡します。だからソートは必須です——結果セットが data_version の昇順で並んでいなければ、記録される「最後の一件」は最大値ではなくなり、次の同期でデータの取りこぼしが発生します。</p>
<p>そうしなければ、毎回の同期のたびに全テーブル同期をするのでしょうか？その負荷は想像に難くありません。そんな方式を採る人はいないでしょう。全テーブル同期は初回の同期時だけで、それ以降はすべて増分・更新データの同期になります。</p>
<p>更新されたデータも同期できるのは、各データが ES に固定のドキュメント ID（通常は MySQL の主キー）で書き込まれるからです。同じデータのバージョンが変わると再度取得され、ES への書き込みは上書き更新になるため、重複ドキュメントは発生しません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）追跡カラムの型はよく考えましょう。前述のとおり、タイムスタンプをバージョン番号にする場合、フィールドには bigint を推奨します。timestamp 型の値の上限問題を避けるためです。また、タイムスタンプの精度が足りないと、同一秒内の複数回の変更が境界で漏れる可能性があります。業務上それを許容できるか、あるいは自動増分のシーケンスに切り替えるかを検討してください。</p>
<p>2）物理削除は同期できません。data_version 方式で検知できるのは新規追加と変更だけです。MySQL 上で直接 delete された行は結果セットに現れなくなり、ES には古いドキュメントが残ります。業務側で論理削除フラグを使い、削除も一度の「変更」として同期するか、あるいは定期的なインデックス再構築で拾うか、どちらかの対策が必要です。</p>
<p>3）ES を唯一のストレージにしないこと。ES 上の集計インデックスは、いつでも MySQL からリプレイして作り直せる状態にしておくべきです。そうすればマッピング設計を間違えたときやフィールドを追加したいとき、インデックスを削除して Logstash を回し直すだけで済み、複雑なオンライン移行をする必要がありません。</p>
<p>4）増分 SQL のソートと境界条件は必ず検証しましょう。where 条件を「より大きい」にするか「以上」にするか、ソートが効いているか。まず手動で SQL を一度実行し、Logstash の二回の実行で記録された値を突き合わせて、取りこぼしや重複取得がないことを確認するのがおすすめです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/12/19/guan-yu-shi-yong-elk#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回の ELK 導入の核心的な成果は、集計・集約処理を MySQL クラスタから切り離せたことです。Logstash が data_version に基づいて増分同期を行い、集計に必要なフィールドだけを取得する。Elasticsearch が集約クエリを担い、Java 側は high level client をラップして業務に提供する。Kibana は DSL のデバッグとクラスタ状態の観察を兼ねる。この構成は余計なコンポーネントを持ち込まず、データ量の実際の規模に合わせて取捨選択したものです。必要十分であることが一番です。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[だんだんハマっていく水泳]]></title>
            <link>https://tanzhuo.xyz/ja/2019/11/25/zhu-jian-shang-yin-de-you-yong</link>
            <guid>https://tanzhuo.xyz/ja/2019/11/25/zhu-jian-shang-yin-de-you-yong</guid>
            <pubDate>Mon, 25 Nov 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[もともとジムにプールがあるのに、会員になったのに何度か泳ぎに行かないのはもったいない気がして。それなら泳ぎに行こうと、コーチについて 3 回レッスンを受けたら、平泳ぎは基本的にマスターできました。ただフォームはまだ十分きれいとは言えません。]]></description>
            <content:encoded><![CDATA[<p>もともとジムにプールがあるのに、会員になったのに何度か泳ぎに行かないのはもったいない気がして。</p>
<!-- -->
<p>それなら泳ぎに行こう、というわけで、コーチについて 3 回レッスンを受けたら、平泳ぎは基本的にマスターできました。ただフォームはまだ十分きれいとは言えません。</p>
<p><img decoding="async" loading="lazy" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABsSFBcUERsXFhceHBsgKEIrKCUlKFE6PTBCYFVlZF9VXVtqeJmBanGQc1tdhbWGkJ6jq62rZ4C8ybqmx5moq6T/wAALCAA3AEIBAREA/8QAGgAAAgMBAQAAAAAAAAAAAAAABAUAAQMCBv/EADQQAAIBAwIDBQUHBQAAAAAAAAECAwAEEQUhEjFRExQiQWEyM3GBsQYjQlKh0fAVJWJykf/aAAgBAQAAPwD0tZSyJDGXkcIo5knAFAnVhJkWdrNc/wCQGF/6a4Nxq7bjT4lHRpQTVd+1CIffaU5HmY3DfoK1XWLQwPIzFCg8UbDDj0xWDTaq6GeOGKJBusLbsw/ejrC6W9tlnQYzsQfI+Yoql2oXEtu0EdvwGWZ8DjG2AD0+VCS2eozXCzXUVvcqnsxByo+O4oy2v+Pt0nhNu1uA0gLBgAQTzHwrqPWNOcYF3GP9tvrRMNxDPnsZkkx+RgaG1DTLe+ALeCUezIvMfvQ9pe3EbSWl0gNyikxkcpQB9az+zsyxxNZygx3CMWKMMbHpTukeric6pYmB0Mni4UcbDqT8vpWz6o9qmb62kh22ZDxq3p6H40onNyshR2aLvz7oxBPDkAZ2yNj+lepMUZQIUUqOQI2oG606w7FpJYI1VAWLIOEgfEULp7alPZxIoEQxvPL4mIz5L+9dXeku0RmW4lkvEPEsjN59AOQFD2Ulxqd9bXXYCMW2VkfO5OOWP5zr0VKT4/tGOLlHb7b+ZNSxXvsjXcnii4sQIeQAyOLHU71ldxva3Et7P2U8EpCPxLho0OwA9N9+tEf0qKIA2s9xb45BJCV+YOaqSxvpY+xlvUljY+PMXCSOmxpkqhECqMADAFdUkvf7XqK3q57tN4ZgPI+R/nr1pjbxpcW8c/B7xQ/PqM0DrdirQzXiO6zRxkbHmPP9K0mFzb6TEbXhMkSKSoXIYAbihrZhqhje6u4nXIbsIhgEj82dz8Ke5FTNVmroPU7UXdjLDjxEZX4jlWdn2vc4OXu1+lFSxrLE0bjKuCpHUGgdJlZUaykb7238O/4k/Cw+VYalo6vKbmCFXY+8hOwf1B8jQ8aqbi2SxvLmMSsyujtkoVGcEHlXo8VMVKlSrpfeWQuWWSN+yuY/YkA5eh6isrbU+K57pdL2dz0XcNtzB8vnXK24OtmURKFEWMgAZYncn5U2q6qpUr//2Q==" width="66" height="55" class="img_ev3q"></p>
<p>でもそれは重要ではありません。重要なのは、もう向こうまで泳いで行って、泳いで帰ってこられるようになったこと。マジで楽しい。ははは。</p>
<p>今では水に入るたびに全身がリラックスして、何も考えなくていい状態になります。ただ泳いで、浮かんでいればいい。</p>
<p>泳げるようになったので、この先は動作、フォーム、スピードの向上に取り組んでいきます。今は基本的に週に最低 1 回は通っています。</p>
<p>特に泳いだ後のシャワーこそが本当に最高です。ただ、泳ぎ終わってプールから上がる瞬間の、あの重力ときたら……</p>]]></content:encoded>
            <category>ライフ</category>
        </item>
        <item>
            <title><![CDATA[ダークソウル1]]></title>
            <link>https://tanzhuo.xyz/ja/2019/11/18/hei-hun-1</link>
            <guid>https://tanzhuo.xyz/ja/2019/11/18/hei-hun-1</guid>
            <pubDate>Mon, 18 Nov 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[ダークソウル1。太古の時代、世界はまだ分かたれておらず、あたり一面は灰色の岩と、高くそびえる大樹と、朽ちぬ古竜ばかりだった。しかしある日、一本の樹の根元に最初の火が灯り、あらゆる差異がそこから生まれた。]]></description>
            <content:encoded><![CDATA[<p>『ダークソウル1』は、日本のゲーム会社 FromSoftware が開発したロールプレイングゲームです。2011 年に初めて発売され、その後世界中で大きな成功と名声を獲得しました。シリーズ第一作として、その奥深さと難易度はゲーム史に残る名作のひとつと評され続けています。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ゲーム概要">ゲーム概要<a href="https://tanzhuo.xyz/ja/2019/11/18/hei-hun-1#%E3%82%B2%E3%83%BC%E3%83%A0%E6%A6%82%E8%A6%81" class="hash-link" aria-label="ゲーム概要 への直接リンク" title="ゲーム概要 への直接リンク" translate="no">​</a></h2>
<p>ゲームの主人公は「不死人」と呼ばれる存在で、ロードランと呼ばれる王国を探索し、邪悪な魔法と魔物がもたらす危機を解決することが使命です。プレイヤーは各地域を探索し、さまざまな敵と戦い、数々の謎や難題を解いていくことになります。</p>
<p>このゲームの特徴のひとつが、その高い難易度です。プレイヤーは巨大な魔物や他のプレイヤーのキャラクターを含む、強力な敵の数々に立ち向かわなければなりません。戦闘では攻撃・防御・回避といった技術を使いこなし、さまざまな武器や装備をうまく活用する必要があります。このゲームにおいて死は終わりではなく、学び成長するための機会です。何度も挑戦と探索を重ねてこそ、最終的な勝利をつかむことができます。</p>
<p>高難易度に加えて、もうひとつの特徴が奥深さと複雑さです。ゲームには複雑な世界観があり、多彩なキャラクターとストーリーが存在します。プレイヤーは探索と謎解きを通じてこの世界を知り、その背景と歴史を少しずつ理解していくことになります。</p>
<p>総じて『ダークソウル1』は、奥深さと難易度においてゲーム史に残る名作と称される、クラシックなロールプレイングゲームです。挑戦しがいのある奥深いゲームが好きなら、きっと手放せなくなるはずです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ゲームのストーリー">ゲームのストーリー<a href="https://tanzhuo.xyz/ja/2019/11/18/hei-hun-1#%E3%82%B2%E3%83%BC%E3%83%A0%E3%81%AE%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AA%E3%83%BC" class="hash-link" aria-label="ゲームのストーリー への直接リンク" title="ゲームのストーリー への直接リンク" translate="no">​</a></h2>
<p>太古の時代、世界はまだ分かたれておらず、あたり一面は灰色の岩と、高くそびえる大樹と、朽ちぬ古竜ばかりでした。世界は混沌のごとく、光もなく、闇もなく、ただ古竜と古樹だけが存在していました。彼らは朽ちることのない、永遠の存在でした。しかしある日、一本の樹の根元に最初の火が灯り、あらゆる差異がそこから生まれたのです。</p>
<p>光と闇、冷と熱、生と死。</p>
<p>闇の中から生まれたいくつかの人に似た種族が、火に引き寄せられ、火のまわりで王のソウルを見出しました。最初の死者ニト、イザリスの魔女、太陽の王グウィンとその麾下の銀騎士たち、そして誰にも見られたことのない小人。彼らは王の力を手に入れ、古竜に戦いを挑みました。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2020-01-29e7e21cea9e2777e86ed69d33d1410a_r-72cbf3a7ba302233f749b96eee217ae7.webp" width="1280" height="720" class="img_ev3q"></p>
<p>しかし古竜は朽ちぬ存在であり、朽ちぬ竜鱗を持つがゆえに、容易には倒せませんでした。ところが朽ちぬ古竜の中に一体の異種、鱗を持たぬ白竜シースがいました。シースは他の古竜のような不朽を持っていませんでした。シースは古竜を裏切り、グウィン王に古竜の不朽の秘密を明かしたのです。</p>
<p>グウィン王は銀騎士たちを率い、雷の魔法——雷の槍で古竜の鱗を貫きました。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2020-05-image-67c219e1dee3e1154c7afe8b84c84df9.webp" width="1076" height="518" class="img_ev3q"></p>
<p>イザリスとその娘たちは、燃え盛る炎で空を覆う古樹を焼き尽くしました。ニトは巨竜たちに恐ろしい死の毒霧を放ち、病苦が古竜の肉体を蝕みました。</p>
<p>古竜は敗れ去りました。こうして、火の時代が幕を開けたのです。</p>
<p>神の国(アノール・ロンド):</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--2--e0e9a8b322c31089e7372f23097f2e5a.webp" width="1600" height="935" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="火継ぎと不死人">火継ぎと不死人<a href="https://tanzhuo.xyz/ja/2019/11/18/hei-hun-1#%E7%81%AB%E7%B6%99%E3%81%8E%E3%81%A8%E4%B8%8D%E6%AD%BB%E4%BA%BA" class="hash-link" aria-label="火継ぎと不死人 への直接リンク" title="火継ぎと不死人 への直接リンク" translate="no">​</a></h2>
<p>しかし火にはいつか消えるときが訪れ、そのときには闇だけが残ります。果てしなく長い歳月を経た今日、火は消えかかり、光は人の世を照らせなくなり、闇夜が終わりなく続いています。そして不死の呪いを受けた不死人が、人々の中に現れ始めました。</p>
<p>不死人は死なないとはいえ、死を重ねるごとにソウルと人間性が失われていきます。やがて亡者となり、亡者は本能のままにソウルを求めるだけの存在になります。しかし不死人が自らの信念を捨てない限り、彼は炎の中で何度でも蘇るのです。</p>
<p>不死人の出現により、いくつかの国では混乱が生じました。不死人たちを処理するため、彼らは北の不死院に一括して収容されることになりました。不死人は殺すことができないため、閉じ込めて世間から隔離するしかなかったのです。北の不死院とは、不死人を収容する牢獄でした。</p>
<p>伝承によれば、北の不死院には運命に選ばれた一人の不死人が現れるといいます。彼は北の不死院を離れ、ロードランの大陸へ巡礼の旅に出るのです。神の国アノール・ロンドも、このロードランの大陸にあります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--1--2-c35f5a7f14fcb7b2f2540c3721cb6773.webp" width="1400" height="875" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--2--1-530d65696c7ce5e59240ef68646f004c.webp" width="1600" height="923" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--3--5ff38817aa68232926a9d6be653d0456.webp" width="1446" height="906" class="img_ev3q"></p>
<p>グウィン王は最初の火を継ぐため、自らのソウルを燃やすことを選び、ソウルの世界における火の時代の寿命を延ばすことに成功しました。この行いは、以後「火継ぎ」と呼ばれるようになります。火を継いだ後のグウィンは、最初の火に焼かれて抜け殻同然となり、人間性もソウルもほとんど燃え尽きてしまいました。象徴だった雷の魔法さえ、もはや使えなくなっていたのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg-2-779d5acc3a2fe4f1317924155d5b9796.jpg" width="1259" height="786" class="img_ev3q"></p>
<p>アノール・ロンドの大書庫。ここは白竜が不朽の結晶を研究した場所でもあります:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-20191118192243_1-99351699dec759d9bc5a042b5495418e.webp" width="1600" height="900" class="img_ev3q"></p>
<p>白竜シース:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-20191123132954_1-dfc9d5f4353f82e44fbe94c23aac2e48.webp" width="1600" height="900" class="img_ev3q"></p>
<p>白竜シースは朽ちぬ竜鱗を持たなかったため、竜鱗に代わる不朽のもの——結晶の探求にのめり込みました。シースは不朽の結晶の研究に成功したものの、自らも結晶に蝕まれ、両目は失明し、下半身は結晶に同化されてしまいました。</p>
<p>最初の火の炉:</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-20191123171440_1-c5d10f28aec4b3e67129843ae8dee376.webp" width="1600" height="900" class="img_ev3q"></p>
<p>老魔女は火継ぎを考えず、自ら新たな最初の火を作り出して火の時代を延ばそうとしました。しかし火を畏れることを忘れていたのです。この狂気じみた行いは実験の暴走を招き、混沌の火を生み出してしまいました。彼女自身とその子どもたち、民、さらには国全体が混沌の怪物の温床と化し、悪魔の都となってしまったのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-20191123163221_1-01caa2a2108e13e383eb8b721b0e241d.webp" width="1600" height="900" class="img_ev3q"></p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[Alibaba Cloud への移行]]></title>
            <link>https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi</link>
            <guid>https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi</guid>
            <pubDate>Sun, 10 Nov 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[独身の日セールに乗じてブログを香港サーバーから Alibaba Cloud へ移行: Docker での丸ごと引っ越し、ドメインの ICP 備案、certbot による Let's Encrypt 証明書の自動更新まで、過程とハマったポイントを記録します。]]></description>
            <content:encoded><![CDATA[<p>以前は香港サーバー（4 コア 4G 90G 5M）を月 150 元で使っていましたが、やや割高に感じていました。独身の日（ダブルイレブン）の Alibaba Cloud セールに乗じて、ブログを丸ごと中国国内に移し、ついでに SSL 証明書の更新も自動化しました。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>これまでブログは香港のサーバーで動かしていました。スペックは 4 コア CPU、4G メモリ、90G ディスク、5M 帯域幅で、月額 150 元。個人ブログにとって、この価格はお得とは言えません——トラフィックは大きくなく、4 コアはほぼ遊んでいて、お金は主に「備案不要」という利便性に払っていたようなものです。</p>
<p>ちょうど独身の日のタイミングで、Alibaba Cloud のセール価格で 2 コア 8G メモリ、40G ディスク、5M 帯域幅の中国国内クラウドサーバーを 3 年 1399 元で入手しました。月あたり約 38 元です。コア数は減りましたが、メモリは倍増し、私のような「ブログ + 自作の小さなサービスをいくつか」という使い方にはむしろ合っています。5M 帯域幅は静的ページや小規模アプリには十分で、小さな企業サイトでもこの程度のものです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="移行の過程">移行の過程<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#%E7%A7%BB%E8%A1%8C%E3%81%AE%E9%81%8E%E7%A8%8B" class="hash-link" aria-label="移行の過程 への直接リンク" title="移行の過程 への直接リンク" translate="no">​</a></h2>
<p>元々ブログは Docker でデプロイしていたので、今回の移行はそのおかげでかなり手間が省けました。コンテナ化デプロイの直接的なメリットの一つは、環境を再構築する必要がないことです。イメージとデータを運んで、コンテナを起動すれば終わりです。</p>
<p>全体の流れは 3 ステップです。</p>
<p>1）新サーバーに Docker をインストールし、元のイメージを pull（またはインポート）する。</p>
<p>2）旧サーバーのデータボリュームディレクトリをアーカイブし、新マシンの対応するパスへ転送して、マウントポイントを一致させる。</p>
<p>3）コンテナが新マシンで正常に動くことを確認したら、ドメインの DNS を新しい IP に切り替える。</p>
<p>データボリュームの引っ越しは、シンプルに tar と scp で片付けられます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 旧サーバー: データボリュームディレクトリをアーカイブする</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">tar</span><span class="token plain"> czf blog-data.tar.gz /data/blog</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># 新サーバーへ転送する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">scp</span><span class="token plain"> blog-data.tar.gz root@new-server:/data/</span><br></div></code></pre></div></div>
<p>ディレクトリ構造とマウント設定さえ変わらなければ、コンテナは新マシンで起動した後、元とまったく同じように動きます。これこそ私が個人サービスのデプロイに Docker を使い続けている理由です。移行コストがほぼゼロなのです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ドメインの-icp-備案">ドメインの ICP 備案<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%81%AE-icp-%E5%82%99%E6%A1%88" class="hash-link" aria-label="ドメインの ICP 備案 への直接リンク" title="ドメインの ICP 備案 への直接リンク" translate="no">​</a></h2>
<p>中国国内サーバーと香港サーバーの最大の違いはここにあります。ドメインを国内 IP に解決させて外部に Web サービスを提供するには、まず ICP 備案（登録手続き）を完了させなければなりません。さもないと 80/443 ポートへのリクエストは遮断されます。</p>
<p>備案は Alibaba Cloud のコンソールから申請すればよく、主体情報とサイト情報を記入し、証明書類をアップロードして、当局の審査を待ちます。私の場合、審査にはおよそ 6 日かかりました。備案期間中はドメインを新サーバーに向けられませんが、サーバー自体は普通に使えるので、先に IP や SSH 経由で環境をすべて調整しておけます。備案が通った瞬間に DNS を切り替えれば、ダウンタイムはほぼゼロにできます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ssl-証明書の自動更新">SSL 証明書の自動更新<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#ssl-%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AE%E8%87%AA%E5%8B%95%E6%9B%B4%E6%96%B0" class="hash-link" aria-label="SSL 証明書の自動更新 への直接リンク" title="SSL 証明書の自動更新 への直接リンク" translate="no">​</a></h2>
<p>今回の移行のついでに、SSL 証明書まわりも徹底的に片付けました。証明書は引き続き無料の Let's Encrypt を使っています。その特徴は、1 枚の証明書の有効期限が 90 日しかないことです。公式の意図はまさに、証明書を手作業で差し替えるのではなく、更新を自動化させることにあります。</p>
<p>私は Docker 版の certbot でこれを実現しています。certbot コンテナが申請と更新を担当し、証明書ディレクトリと検証ディレクトリをデータボリュームとしてマウントし、nginx が同じ証明書ファイルをマウントします。更新コマンドはだいたい次のような形です。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 期限が近いすべての証明書をチェックして更新する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">--rm</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-v</span><span class="token plain"> /data/certbot/conf:/etc/letsencrypt </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-v</span><span class="token plain"> /data/certbot/www:/var/www/certbot </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  certbot/certbot renew</span><br></div></code></pre></div></div>
<p><code>renew</code> コマンドは期限が近い証明書だけを更新するので、crontab に入れて毎日実行しても副作用はありません。更新に成功したら nginx に設定をリロードさせるだけです。ここまで設定してしまえば、SSL 証明書は事実上「永久無料」となり、基本的に放置しておけます。</p>
<p><img decoding="async" loading="lazy" src="data:image/webp;base64,UklGRr4EAABXRUJQVlA4ILIEAAAwFQCdASpCADgAPm0ukkakIiGhKzSc6IANiWkAE994r+SvzD7QceBlD988sO8H3N6CH8i/u28EgA/Jf6b/nvzK8YD0A0inwf2APvA9OX/q8xn0X6mn9l/43SzH7aZwopbEeUejVHQjiUe7iMdyha3QTpAdFCtt4rtbVT9qW1RYvMH6wRspL4rmVOXNbl7Z1bRnHXm8NepN/qutf9g/29zGidS5/8UtWkqpzBUXMFrs/jHgAP7+/+zw7AL7vdNZlL76/MGiozejihWdcU61m+KBtQX9me8SE/TCGavVHNbu7r/h0XYeGplZVLVrS2ylS4Ab5wdzqxYCM/NcWDqmzwzG5ClAca+vnRrIt04XxlcTZvctOexMRY/tKL+pdJPGfHvFE8Yfe7qyR+v2cPi3IQQm+yx65VpN4K87H2iuflEkYl660x6us1VuoD6rYA7BfOuS7Vb7/MFZciEf7AV5plGB9nduxuIMIQU//vlXN9z8MFzt5MV35Eg+qXfBlAWn6cr6/rsnninHamwwHa/mfx9L6+Ed/q4yUhPUY4za5lUw7jdOqEYTLH3T4akIMAgUUFbfBoQ7VLBk1rK3jKvZoTiFIXKBwW2UbwvqzGO/4tl+t1fsheSujkSgqVabbswMGCxNRVgrqm4j4dgp8fZvMeOwycKB3LQAswDKlpftZUVneGvDSpPyQlka1a6W9fqg/+a0w7+wlO2irBx9pioAH8/HASrsChdk5Mmr7VoN89zqyb2gr+85BfXrUudW4px6MuNDZOXj2ysz2xoFFzP1Q6WhnBvg8XeUJXOsOfhJtcYFxskn+LT110h2N9yApVkX/aWCVN+AW0IerNo/Tfw4ftbMIis1b3kOAm2ZUW1VOs/n/TBo4Y4W2fHFG3t22Hvb5F2xqstBS8rt9hhGs+HGmuN+Cu0CgNI27vQKUyA6+ePpSCvdNEO/pJJpI2jVdPV4hMzRB1LRXWE88x4B/bOhc0cGIfs63M9PQ9OxrSTJe/KnQbixRsBBauV3IIG5uM1kL71oH4ICDz10qGLeN9jP71glzqci1i9wfPWm8UYKYcuoPvc1w2rvcKq9Ghj11kdPHGkCOE4H6Apaypkhfxs7bu5dYmJjgyPOwcsU2dctN2GdLk0CF9LJUzg5/TDFrCsJil6SssURKFtloAWtGOm6dHcpPTkaj70C7O5AkpxicIqPqfP3/Kv/RjCa81snx1IATp2jUn5XX+rcA4W7094u3z8pkjHo7pjL5k/NS1qOZBpgcSuRDRMKxYnAdBCb4/gCpHt+6ymmNsaLDyAkxrlNOk61up5xR5Yoj27w/ERnXF6aVBHEVP3xatyjUdWw4bDRv3i/rwFmuRhhl424ei/Xz2az88fSXEedvYgvx5QKrRcvNGAPvsP7pTpuWkIkX+sb/B5jgSOtzJNv/gSFeRTi+T1ZzydBxgNOuHzj/sNe4e8DS2aQTkrv4mewknz+o9GPYrMUrg3zKqa6ead5rhER7WDUtSYSFGmfhyqhVaIRNwH0+ss1mEr8HL1rKlC+DTNDvz/7da44gSVvUNPvKpaPjtwhL+j4m8kizTXqb/94anJuif/MHTQAuOvougbhSvEObhgAAA==" width="66" height="56" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="今後の予定">今後の予定<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#%E4%BB%8A%E5%BE%8C%E3%81%AE%E4%BA%88%E5%AE%9A" class="hash-link" aria-label="今後の予定 への直接リンク" title="今後の予定 への直接リンク" translate="no">​</a></h2>
<p>このマシンには 8G のメモリがあり、ブログ 1 つだけ動かすのはもったいない。今後は自分で書いたサービスを順次デプロイして、自分の demo App のバックエンドとして使っていくつもりです。しかも https と備案はすでに整っています——WeChat ミニプログラムのバックエンド API は、まさにドメインが備案済みかつ https であることを要求しており、この 2 つの前提条件はどちらも満たされています。今後、個人でミニプログラムを書いてみるのも悪くないでしょう。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>1）香港サーバーに Docker をデプロイしたときは、いろいろと不思議な現象に遭遇しました。最も印象深かったのは、ホストマシンが Docker の比較的新しいストレージドライバーのフォーマットをサポートしておらず、ディスクを再フォーマットしてファイルシステムのストレージフォーマットを変えなければ正常に使えなかったことです。Docker のストレージドライバーはカーネルバージョンやファイルシステムのパラメータに要求があり、古い、あるいは非主流のホスト環境ではここで転びやすいのです。こうした問題は Alibaba Cloud では一度も起きず、順風満帆でした。</p>
<p>2）備案の審査期間中はドメインを新サーバーに向けられないため、移行の調整作業を事前に済ませておけば、切り替えのウィンドウを最短に圧縮できます。</p>
<p>3）Alibaba Cloud も完璧ではありません。ときどきネットワークの揺らぎがかなり大きいと感じることがあります。個人ブログなら気になりませんが、遅延にシビアなサービスでは注意が必要です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/11/10/bo-ke-qian-yi-dao-a-li-yun-fu-wu-qi#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回の移行で費やした時間は主に備案待ちで、技術的な部分は Docker のおかげでほとんどコストがかかりませんでした。ついでに証明書の更新を自動化した後、このサーバーは基本的に「放置しておける」状態に入りました。個人開発者にとって、安価な中国国内サーバーと備案済みのドメインがあれば、できることはむしろ増えるのです。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[ちょっとした小さなdemoを作ってみた]]></title>
            <link>https://tanzhuo.xyz/ja/2019/10/24/hao-jiu-mei-xie-bo-ke-liao</link>
            <guid>https://tanzhuo.xyz/ja/2019/10/24/hao-jiu-mei-xie-bo-ke-liao</guid>
            <pubDate>Thu, 24 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[最近またTencent Cloudのキャンペーンで、3年600元のサーバー(1コア2GB、2M回線、40GB)を購入しました。正直まだ割高で、スペックもかなり低いと感じますが、それでも買ってしまいました。買ったあと、放置しておくのももったいないので、自分でいくつか小さなdemoを開発して遊んでみることに。最初のdemoはすでに完成しました!~。]]></description>
            <content:encoded><![CDATA[<p>なぜ長い間ブログを書いていなかったのかというと、実は怠けていたわけではなく、ブログ管理画面のパスワードを忘れてしまったからです。自分のブログの管理画面のパスワードを自分で忘れるとは、我ながらなかなかのものです。</p>
<!-- -->
<p>結局、サーバーにログインしてパスワードをリセットする羽目になりました。</p>
<p><img decoding="async" loading="lazy" src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDABsSFBcUERsXFhceHBsgKEIrKCUlKFE6PTBCYFVlZF9VXVtqeJmBanGQc1tdhbWGkJ6jq62rZ4C8ybqmx5moq6T/wAALCAA0AD4BAREA/8QAGgAAAgIDAAAAAAAAAAAAAAAAAwUBBAACBv/EAC0QAAIBAwMDAwMDBQAAAAAAAAECAwAEEQUSIRMxURRBYSJxkQYjgTNCUnLw/9oACAEBAAA/AOlqrd38NoQr5Zz2RRkmq3qNSuOYLdIV8ynn8VBh1jGfUwZ8BTUdXWIeZIIZV99hwaiTWOsohtEPqXONrjGz5NDufXWEYumuesF/qJjjHkU2ibqRow4BUGg3916SDco3SMdqL5NLDcxabL+7me7fBdicBB4zV6G+LMomj6SyY6cgbKv8exB+9MKUzawguxbQRdVt23JbaM/9xUSxJqJIZTbX0Q3DnkeDn3FBne6vrOWwbat2uM54Dr5FXNLulmj6W3ZLCAjofahSE3Gtxx4OyBC58bj2pLqcckeoTCRSCXLAn3GeK6FYRdaQkW0JviGBzgHHFbb5ZdMZ9rrMYjxgghse381y9tL0LiOXZu2MDtPGa6XUP2XivF4MRw/yhOD98d6HrFo7Bby34nh5/wBh4rTR4mkL30sqSSTgZCjG34qsmoQWWrXfVV2eRgqhV5xTE6nbhQZ0lgyeBLGRViO6t5W2pPEzH2VwTR6A1vA0nVMMZfIO4qM5+9HqCMjmufnnfRLyQLGZIJvqVf8AE01vtPgvU+tcPjhwORS2S0vreEws7XUGQdrDnAOcA9/ahz9KdpGiDTb36iyRxiRl4wVZTyB4raFELQQ2bywyb8uqy7sKO5cdgfincCSJCqyydRx3bbjP8USsqpf2Md6qq45U5q5VDUb70SoekZNxP920DA8+fiq+npDd9VrpEkudxDowH0AdgB4rS1t4xrszQoI0iiCkJwCx+3enVRUVlbUtuI1utUEE31RLCXC+2ckZ/FHOmWZjVOgoC9iOD+e9Kv09I5ublWYvuwxZuTn70/rKysr/2Q==" width="62" height="52" class="img_ev3q"></p>
<p>幸いサーバーのログインパスワードは忘れていませんでした。もし忘れていても、購入したクラウドベンダーの管理画面からリセットできますし、仮にベンダーのログインパスワードまで忘れてしまっても、携帯電話のSMSで取り戻せます。こう考えると、パスワードの心配はまったくしなくてよさそうです。</p>
<p>最近またTencent Cloudのキャンペーンで、3年600元のサーバー(1コア2GB、2M回線、40GB)を購入しました。正直まだ割高で、スペックもかなり低いと感じますが、それでも買ってしまいました。買ったあと、放置しておくのももったいないので、自分でいくつか小さなdemoを開発して遊んでみることに。最初のdemoはすでに完成しました!</p>
<p>すべてのdemoはGitee(码云)でオープンソースとして公開しています。</p>
<p>複数人参加型のWebオンラインチャット demoの例:<a href="http://118.25.113.204/gga_parent/(%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AF%E3%81%A8%E3%81%A3%E3%81%8F%E3%81%AB%E3%82%AA%E3%83%95%E3%83%A9%E3%82%A4%E3%83%B3%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99)" target="_blank" rel="noopener noreferrer" class="">http://118.25.113.204/gga_parent/(サーバーはとっくにオフラインになっています)</a></p>
<p>開発期間:約4時間</p>
<p>今後も新しいアイデアが浮かんだら、面白いdemoを引き続き作っていくつもりです。</p>]]></content:encoded>
            <category>ライフ</category>
        </item>
        <item>
            <title><![CDATA[Flutter フレームワークに触れて]]></title>
            <link>https://tanzhuo.xyz/ja/2019/09/18/flutter</link>
            <guid>https://tanzhuo.xyz/ja/2019/09/18/flutter</guid>
            <pubDate>Wed, 18 Sep 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Flutter に初めて触れた際の所感を記録：Skia レンダリングエンジン、Widget コンポーネントモデル、Dart 言語とホットリロード、そして 1 つのコードベースでマルチプラットフォームに展開できるクロスプラットフォーム能力について。]]></description>
            <content:encoded><![CDATA[<p>Flutter は Google が開発・メンテナンスしているオープンソースのモバイルアプリ開発フレームワークです。Flutter は Dart 言語で記述され、効率的なレンダリングエンジン、豊富なコンポーネントライブラリ、そして高速な開発サイクルを備えており、高品質でクロスプラットフォームなモバイルアプリケーションの構築に利用できます。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜクロスプラットフォームの選択肢に注目するのか">なぜクロスプラットフォームの選択肢に注目するのか<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#%E3%81%AA%E3%81%9C%E3%82%AF%E3%83%AD%E3%82%B9%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%81%AE%E9%81%B8%E6%8A%9E%E8%82%A2%E3%81%AB%E6%B3%A8%E7%9B%AE%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜクロスプラットフォームの選択肢に注目するのか への直接リンク" title="なぜクロスプラットフォームの選択肢に注目するのか への直接リンク" translate="no">​</a></h2>
<p>モバイル開発をしていると避けられない現実的な問題があります。同じプロダクトで Android と iOS の両方をカバーするということは、多くの場合 2 つのコードベース、2 つの技術スタック、2 倍のメンテナンスコストを意味します。かつて流行したハイブリッド方式は、WebView をラップするタイプだとパフォーマンスと体験に明らかな劣化があり、React Native のように JS コンポーネントをプラットフォームのネイティブコントロールにマッピングするタイプだと、クロスプラットフォームの一貫性が今一歩というところでした。</p>
<p>Flutter は別の道を選びました。プラットフォームのネイティブコントロールに依存せず、UI を完全に自前で描画するのです。これにより「1 つのコードベースで、全プラットフォームで一貫した表示」を先達よりも徹底して実現しており、私が時間をかけて触ってみようと思った理由でもあります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="レンダリングエンジン">レンダリングエンジン<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#%E3%83%AC%E3%83%B3%E3%83%80%E3%83%AA%E3%83%B3%E3%82%B0%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%B3" class="hash-link" aria-label="レンダリングエンジン への直接リンク" title="レンダリングエンジン への直接リンク" translate="no">​</a></h2>
<p>Flutter のレンダリングエンジンは Skia グラフィックスライブラリを採用しており、高品質・高パフォーマンスな描画とアニメーション効果を実現できます。Flutter のコンポーネントライブラリには豊富な UI コンポーネントが含まれ、さまざまなスタイルのアプリケーションを手軽に作成でき、カスタムコンポーネントの開発もサポートしています。さらに Flutter は豊富な API とツールを提供しており、高速な開発サイクルと効率的なデバッグを支えています。</p>
<p>ここで自前描画の意味を少し掘り下げておきましょう。Flutter はインターフェースを各プラットフォームのネイティブコントロールに翻訳するのではなく、Skia を通じてキャンバス上にすべてのピクセルを直接描画します。プラットフォーム側は描画可能な Surface とイベント入力を提供するだけでよく、残りのレイアウト・描画・アニメーションはすべて Flutter エンジン自身が完結させます。利点は、同じ UI コードが異なる OS や OS バージョン上でも極めて一貫した描画結果になることです。特定のプラットフォームのコントロールの挙動の違いによって差異が生じることがありません。</p>
<p><a href="https://flutter.cn/" target="_blank" rel="noopener noreferrer" class="">Flutter: あらゆる画面に素晴らしい体験を</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="クロスプラットフォーム能力">クロスプラットフォーム能力<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#%E3%82%AF%E3%83%AD%E3%82%B9%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E8%83%BD%E5%8A%9B" class="hash-link" aria-label="クロスプラットフォーム能力 への直接リンク" title="クロスプラットフォーム能力 への直接リンク" translate="no">​</a></h2>
<p>Flutter のクロスプラットフォーム特性も最大の強みの一つです。Android と iOS のアプリケーションを同時に開発でき、開発したアプリケーションは異なるプラットフォーム上でも同じユーザー体験を実現できます。さらに Flutter は Web やデスクトップアプリケーションの開発もサポートしており、さまざまなユースケースにソリューションを提供できます。</p>
<p>つまり、1 つのコードベースで理論上は Android、iOS、Web、そして Windows や macOS といったデスクトップまでカバーできるということです。もちろんターゲットプラットフォームごとの成熟度は同じではなく、モバイルが Flutter の最初に磨き上げられた主戦場です。しかしツール系や表示系のアプリであれば、マルチプラットフォーム再利用によるコスト削減は実際に効いてきます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="dart-言語">Dart 言語<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#dart-%E8%A8%80%E8%AA%9E" class="hash-link" aria-label="Dart 言語 への直接リンク" title="Dart 言語 への直接リンク" translate="no">​</a></h2>
<p>Flutter は開発言語として Dart を採用しています。Dart はオブジェクト指向で静的型付けの言語であり、強力な型推論と非同期プログラミングのサポートを備え、開発効率とコード品質を高めることができます。Flutter の Dart コンパイラは Dart コードをネイティブコードにコンパイルし、さまざまなプラットフォーム上で高パフォーマンスかつ高速なレンダリングを実現できます。</p>
<p>Dart は Flutter にとって単なる「なんとなく選んだ言語」ではありません。JIT と AOT の 2 つのコンパイルモードを両方サポートしており、開発段階では JIT でコードの変更が即座に反映され、ホットリロードを支えています。リリース段階では AOT でマシンコードにコンパイルされ、実行時にインタプリタが不要で、パフォーマンスはネイティブに迫ります。1 つの言語で開発効率と実行パフォーマンスという 2 つの要求を同時に満たすのは、クロスプラットフォームフレームワークでは決して多くありません。</p>
<p>Java や JavaScript に慣れた人なら Dart の習得にほとんど障壁はありません。構文のスタイルは両者の中間で、async/await による非同期の書き方も直感的です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="widget-コンポーネントモデル">Widget コンポーネントモデル<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#widget-%E3%82%B3%E3%83%B3%E3%83%9D%E3%83%BC%E3%83%8D%E3%83%B3%E3%83%88%E3%83%A2%E3%83%87%E3%83%AB" class="hash-link" aria-label="Widget コンポーネントモデル への直接リンク" title="Widget コンポーネントモデル への直接リンク" translate="no">​</a></h2>
<p>Flutter の UI フレームワークは「Widget」と呼ばれるコンポーネントモデルを採用しています。Widget は Flutter における最も基本的な構成要素で、UI 要素とロジックを含みます。Flutter のすべての UI 要素は Widget であり、テキスト、画像、ボタンなどが含まれます。Flutter の Widget は階層構造を持ち、ネストや組み合わせが可能で、複雑な UI インターフェースを自由に作成できます。</p>
<p>「すべては Widget である」というのが Flutter の中核的な設計哲学です。ボタンやテキストが Widget であるだけでなく、パディング（Padding）、中央揃え（Center）、ジェスチャー認識（GestureDetector）といった「振る舞い」までもが Widget です。複雑なインターフェースは巨大な基底クラスの継承によって実現するのではなく、小さな Widget を何層にも組み合わせることで実現します。「継承よりコンポジション」がここでは存分に体現されています。</p>
<p>Widget は可変状態を持つかどうかで 2 種類に分かれます。StatelessWidget は変化しない UI を記述し、StatefulWidget は State オブジェクトと組み合わせて変化する部分を管理します。状態が変わると、フレームワークが対応する Widget サブツリーを自動的に再構築します。この点を理解すれば、Flutter の宣言的 UI の考え方は基本的に腑に落ちるはずです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ホットリロード">ホットリロード<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#%E3%83%9B%E3%83%83%E3%83%88%E3%83%AA%E3%83%AD%E3%83%BC%E3%83%89" class="hash-link" aria-label="ホットリロード への直接リンク" title="ホットリロード への直接リンク" translate="no">​</a></h2>
<p>Flutter のホットリロード機能は、開発中に UI の変化をリアルタイムでプレビューでき、アプリケーションの再コンパイルや再デプロイが不要なため、開発効率とフィードバックの速さを大幅に高めます。さらに Flutter は豊富なデバッグツールと可視化ツールを提供しており、開発者が問題をすばやく特定して解決するのを助けてくれます。</p>
<p>ホットリロードの原理は、変更後のソースコードを実行中の Dart 仮想マシンに増分注入し、Widget ツリーを再構築するというものです。アプリケーションの現在の状態は保持されます。つまり深い階層のページのスタイルを調整するとき、毎回トップページからクリックして辿り直す必要はなく、変更して保存すれば画面が直接更新されます。UI 調整の体験は、ネイティブ開発と比べて質的な向上と言えます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>触り始める前に知っておくと良い点がいくつかあります。</p>
<p>1）中国国内のネットワーク環境では、SDK のダウンロードや pub の依存関係の取得が非常に遅い、あるいは失敗することがあります。国内ミラーの設定（環境変数 <code>PUB_HOSTED_URL</code> と <code>FLUTTER_STORAGE_BASE_URL</code> の設定）が必要です。</p>
<p>2）Widget のネストが深くなるとコードの可読性が目に見えて低下します。1 つの build メソッドに数百行を積み上げるのではなく、早めに小さな Widget へ分割する習慣をつけることをおすすめします。</p>
<p>3）プラットフォームのネイティブ機能（Bluetooth、プッシュ通知など）を扱う場合は、Platform Channel を通じてネイティブコードと通信する必要があり、この部分では依然としてある程度の Android/iOS ネイティブの知識が求められます。</p>
<p>4）ホットリロードは万能ではありません。変更がグローバルな状態の初期化、main メソッド、enum などに及ぶ場合は、ホットリスタート（hot restart）や完全な再コンパイルが必要になります。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>初めて <code>flutter doctor</code> を実行したときは、指示に従って環境の不足項目を一つずつ埋めておきましょう。後々の原因不明なビルドエラーを大量に省けます。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/09/18/flutter#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>Flutter フレームワークは実に良くできていると感じました。優れたコンポーネントが提供されており、Dart 言語との組み合わせで開発の入門は非常に速く進みます。いくつかデモを書いただけで、もう本格的なアプリ開発に入れる状態になりました。1 つのコードベースで Android、iOS、Windows デスクトップアプリ、Mac のすべてで動きます。自前描画のレンダリングがマルチプラットフォームでの一貫性を保証し、Widget を組み合わせる書き方は明快で直感的、ホットリロードのおかげで UI 調整も苦になりません。クロスプラットフォーム開発を試してみたい人にとって、Flutter は本気で取り組む価値のある選択肢です。</p>]]></content:encoded>
            <category>技術</category>
            <category>フロントエンド</category>
        </item>
        <item>
            <title><![CDATA[フロントエンド技術を学ぶ]]></title>
            <link>https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue</link>
            <guid>https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue</guid>
            <pubDate>Wed, 18 Sep 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Vue.js を学んだときのノートです。双方向データバインディング、コンポーネント指向、Virtual DOM、ライフサイクルといった中核概念に加え、TypeScript がフロントエンド開発にもたらした変化についても触れます。]]></description>
            <content:encoded><![CDATA[<p>Vue.js はユーザーインターフェースを構築するためのプログレッシブ JavaScript フレームワークです。そのコアライブラリはビュー層だけに焦点を当てており、他のライブラリや既存プロジェクトと簡単に統合できます。Vue.js は MVVM パターンを採用し、双方向データバインディングによってビューとデータを同期させることで、開発者がデータとビューをより手軽に管理できるようにしています。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜフロントエンドフレームワークを学ぶのか">なぜフロントエンドフレームワークを学ぶのか<a href="https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue#%E3%81%AA%E3%81%9C%E3%83%95%E3%83%AD%E3%83%B3%E3%83%88%E3%82%A8%E3%83%B3%E3%83%89%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%82%92%E5%AD%A6%E3%81%B6%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜフロントエンドフレームワークを学ぶのか への直接リンク" title="なぜフロントエンドフレームワークを学ぶのか への直接リンク" translate="no">​</a></h2>
<p>このノートを書いたきっかけは単純です。普段バックエンドを主戦場とする開発者として、避けて通れない事実が一つあります。ページは遅かれ早かれ自分で書くことになる、ということです。従来の jQuery 流の書き方では、データが DOM のあちこちに散らばっていて、状態を 1 つ変えるにも対応するノードを手動で探して更新しなければならず、ページが複雑になるとコードは保守困難なスパゲッティと化します。Vue.js が解決するのはまさにこの問題です——「データ」と「ビュー」の同期をフレームワークに任せ、開発者はデータそのものにだけ関心を持てばよいのです。加えて「プログレッシブ」という位置づけにより、プロジェクト全体を作り直す必要はなく、まず 1 つのページや 1 つのモジュールに導入して試すことができます。これは既存プロジェクトを抱えるチームにとって非常にありがたい点です。</p>
<p><a href="https://vuejs.org/" target="_blank" rel="noopener noreferrer" class="">Vue.js - The Progressive JavaScript Framework | Vue.js</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vuejs-の特徴">Vue.js の特徴<a href="https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue#vuejs-%E3%81%AE%E7%89%B9%E5%BE%B4" class="hash-link" aria-label="Vue.js の特徴 への直接リンク" title="Vue.js の特徴 への直接リンク" translate="no">​</a></h2>
<p>Vue.js の特徴：</p>
<p>1）双方向データバインディング：Vue.js は双方向データバインディングによってデータとビューの自動同期更新を実現し、開発者がデータとビューをより手軽に管理できるようにしています。背後の仕組みはデータのインターセプトと Publish-Subscribe です。フレームワークがデータプロパティへの読み書きをインターセプトし、読み取り時に依存関係を収集し、書き込み時に対応するビューへ更新を通知します。開発者が変更するのは普通の JavaScript オブジェクトなのに、ページが自動的に追従して変わるので、もう手動で DOM を操作する必要はありません。</p>
<p>2）コンポーネント指向開発：Vue.js はコンポーネント指向開発をサポートしており、1 つのページを複数の独立したコンポーネントに分割できます。各コンポーネントは自分のデータとビューを持ち、手軽に再利用・保守できます。コンポーネント間は props で下方向にデータを渡し、イベントで上方向に通知するため、境界が明確です。これはバックエンドでモジュールを分割し、責務ごとにクラスを分ける考え方と一致しており、バックエンド出身者にとって理解は難しくありません。</p>
<p>3）軽量で習得しやすい：Vue.js の API はシンプルで分かりやすく、学習曲線が緩やかなため素早く使い始められ、他のフレームワークやライブラリ（React、Angular など）とも共存できます。公式ドキュメントは中国語版があり、サンプルも十分に多く、script タグを 1 つ読み込むだけで書き始められ、ビルドツールの使用を強制しません。この点は入門者にとって非常に重要です。</p>
<p>4）テンプレート指向プログラミング：Vue.js はテンプレート指向プログラミングをサポートしており、HTML と JavaScript のコードを分離できるため、コードがより明快で読みやすくなります。テンプレートは本質的には HTML のままで、補間とディレクティブが加わっただけです。ページ構造を書くときは見たままが得られ、ロジックはコンポーネントの JavaScript 部分に集約されます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="vuejs-の技術的な詳細">Vue.js の技術的な詳細<a href="https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue#vuejs-%E3%81%AE%E6%8A%80%E8%A1%93%E7%9A%84%E3%81%AA%E8%A9%B3%E7%B4%B0" class="hash-link" aria-label="Vue.js の技術的な詳細 への直接リンク" title="Vue.js の技術的な詳細 への直接リンク" translate="no">​</a></h2>
<p>Vue.js の技術的な詳細：</p>
<p>1）Virtual DOM：Vue.js は Virtual DOM 技術を使って効率的な DOM 操作を実現し、パフォーマンスとユーザー体験を高めています。原理としては、まずメモリ上に JavaScript オブジェクトで DOM ツリーを記述しておき、データが変化したら新しい仮想ツリーを生成して古いツリーと diff 比較を行い、最後に本当に変化した部分だけを実際の DOM に適用します。実際の DOM を直接操作するコストは大きいため、この中間構造によって「どこを変えるか」の計算をメモリ内にとどめておけば、コストははるかに小さくて済みます。</p>
<p>2）ライフサイクル：Vue.js のコンポーネントには独自のライフサイクルがあり、作成、マウント、更新、破棄などの段階を含みます。異なる段階で異なる処理を実行できます。たとえば API リクエストの発行は一般的に作成完了後に置き、実際の DOM にアクセスする必要のあるロジックはマウント完了を待ち、タイマーやイベントリスナーといったリソースは破棄段階でクリーンアップすべきです。ライフサイクルの理解は、「自分のコードは一体どのタイミングで実行されるのか」を理解する鍵です。</p>
<p>3）ディレクティブとイベント：Vue.js は v-if、v-for、v-on などのディレクティブとイベントを提供しており、DOM 要素の操作やユーザーイベントの処理を手軽に行えます。v-if は要素をレンダリングするかどうかを制御し、v-for はリストのループを担当し、v-on はイベントコールバックをバインドします。ディレクティブはよくある DOM 操作をテンプレート内に宣言するので、テンプレートを読むだけでページの挙動が分かります。JavaScript のあちこちで addEventListener するより直感的です。</p>
<p>4）プラグインとコンポーネントライブラリ：Vue.js のエコシステムは豊かで、大量のプラグインとコンポーネントライブラリが利用でき、高品質なアプリケーションを素早く構築できます。ルーティングや状態管理には公式のソリューションがあり、UI 面でも成熟したコンポーネントライブラリがあります。管理画面系のページはほぼ組み立てるだけで作れるので、すべての車輪を自分で再発明する必要はありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="typescript-がもたらした変化">TypeScript がもたらした変化<a href="https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue#typescript-%E3%81%8C%E3%82%82%E3%81%9F%E3%82%89%E3%81%97%E3%81%9F%E5%A4%89%E5%8C%96" class="hash-link" aria-label="TypeScript がもたらした変化 への直接リンク" title="TypeScript がもたらした変化 への直接リンク" translate="no">​</a></h2>
<p>現在のフロントエンド技術はすでに大きく変化しており、特に TypeScript が融合してからは、個人的に将来性は非常に明るいと感じています。JavaScript はずっと弱い型付けの言語でしたが、TypeScript と組み合わせることで決まった構文が生まれ、データ型と Class の概念が加わったことで、フロントエンドのロジックはより明快になりました。さらに Vue のコンポーネント指向も相まって、フロントエンドもおおよそ MVC パターンで開発できるようになりました。TypeScript と Vue を組み合わせてコンポーネントや複雑なビジネスロジック処理層を書くことができ、これは以前の技術での書き方に比べて確かにずっと明快になっています。</p>
<p>強い型付けの言語を書き慣れた人にとって、この変化の意義は次の点にあります。API のデータ構造を型定義で記述でき、エディタが補完とチェックをしてくれるので、多くの初歩的なミスがコンパイル段階で明らかになり、ブラウザで undefined エラーが出るまで待つ必要がなくなるのです。フロントエンドプロジェクトの規模が大きいほど、この制約の価値は明確になります。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<p>学習の過程で、いくつか注意しておきたい点があります。</p>
<p>1）双方向バインディングは万能ではありません。データのインターセプトには限界があり、オブジェクトや配列に対する一部の操作方法はフレームワークが感知できません。「データを変えたのにページが動かない」状況に遭遇したら、フレームワークが壊れたと疑うのではなく、まずリアクティビティの制限を調べてみましょう。</p>
<p>2）コンポーネント内で DOM を直接操作しないこと。ビューの管理をフレームワークに任せたからには、データ駆動でページの変化を引き起こすべきです。手動の DOM 操作を混ぜるとフレームワークの更新機構と衝突しやすく、問題の調査が非常に難しくなります。</p>
<p>3）コンポーネントの分割は適度に。細かく分割しすぎると props のバケツリレーが非常につらくなりますし、分割しなければ単一コンポーネントが肥大化します。「独立して再利用できる、責務が単一である」という基準で粒度を把握するのが適切です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2019/09/18/xue-xi-liao-vue#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>Vue.js は双方向データバインディングとコンポーネント指向によって、フロントエンド開発を手作業の DOM 操作から解放しました。Virtual DOM とライフサイクルは、その動作原理を理解するための 2 本の鍵です。バックエンド開発者にとっては、TypeScript と組み合わせることでフロントエンドのコードにも型とレイヤリングによる秩序が生まれ、習得コストは想像より低くなっています。このノートではまず概念レベルの理解を記録しておき、今後実際のプロジェクトで踏んだ落とし穴は、また別途整理することにします。</p>]]></content:encoded>
            <category>技術</category>
            <category>フロントエンド</category>
        </item>
        <item>
            <title><![CDATA[Webプロジェクトで使うフレームワーク技術まとめ]]></title>
            <link>https://tanzhuo.xyz/ja/2019/07/22/zi-ji-da-jian-javawebxiang-mu-kuang-jia</link>
            <guid>https://tanzhuo.xyz/ja/2019/07/22/zi-ji-da-jian-javawebxiang-mu-kuang-jia</guid>
            <pubDate>Mon, 22 Jul 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[日々の業務や学習で触れてきた Java Web 技術スタックを整理。Spring ファミリー、ORM、メッセージキューから、コンテナオーケストレーション、分散トレーシング、監視体系まで、それぞれ一言で位置づけを添えた技術選定メモです。]]></description>
            <content:encoded><![CDATA[<p>以下は、普段の業務や自己学習の中で触れて使ってみた技術の一部を、記録として整理したものです。</p>
<!-- -->
<p>このリストを書き始めたきっかけは単純です。Java Web のエコシステムはあまりにも広く、モノリシックなプロジェクトから始まり、マイクロサービス、コンテナ化、監視・アラートまで、各レイヤーに複数の選択肢があります。普段は断片的に触れるだけなので、しばらく経つと名前しか覚えておらず、位置づけを思い出せなくなります。そこで一箇所にまとめて、それぞれについて「何であるか、どんな問題を解決するか」を明記しておくことにしました。今後の技術選定の際には、改めて検索し直すのではなく、このページを見返せば済みます。リストはおおよそ「開発フレームワーク → データ層 → マイクロサービスガバナンス → インフラ → ビッグデータ・ツール」の順に並べています。</p>
<p>SpringBoot、Spring-cloud、Spring-cloud-alibaba、Spring ファミリー。SpringBoot は自動構成（Auto Configuration）とスターター依存によって設定量を最小限に抑えており、現在の Java プロジェクトのデフォルトの出発点です。Spring Cloud はその上にサービス登録、設定管理、ゲートウェイなどのマイクロサービス機能を提供します。Spring Cloud Alibaba は Nacos、Sentinel、Seata といった中国発エコシステムのコンポーネントを同じ抽象化レイヤーに統合しています。</p>
<p>MyBatis-Plus（楽観ロック、自動ページネーション、コードジェネレーター。service、mapper、entity、controller を生成でき、生成テンプレートもカスタマイズ可能）。MyBatis の上に汎用 CRUD を補完しており、単一テーブルの操作ではほぼ SQL を書く必要がありません。コードジェネレーターとカスタムテンプレートを組み合わせれば、新しいテーブルの CRUD を数分で組み立てられます。</p>
<p>JOOQ：Java の ORM フレームワーク。発想は MyBatis とは逆で、Java の DSL を使って型安全に SQL を組み立てます。フィールド名の間違いをコンパイル時に発見できるため、SQL ロジックが複雑で、かつ強い型保証が欲しい場面に適しています。</p>
<p>Uid-generator：Baidu 製の UID ジェネレーター（分散スノーフレークアルゴリズムによるグローバル一意識別子、Long 型 UID）。シャーディング後はデータベースの自動採番主キーが使えなくなります。スノーフレークアルゴリズムは「タイムスタンプ + マシン ID + シーケンス番号」を組み合わせて単調増加傾向の Long 型 ID を生成し、グローバル一意性とインデックスへの優しさを両立します。</p>
<p>Xxl-job：分散タスクスケジューリングセンター。単一マシンでの定期タスクにある二つの古典的な問題——マシンが落ちるとタスクが失われる、複数インスタンスにデプロイすると重複実行される——を解決します。スケジューリングセンターと実行器を分離し、管理画面、失敗時のリトライ、シャーディング実行を標準搭載しています。</p>
<p>Apache-Shiro、Spring-Security：ログイン・セキュリティフレームワーク。どちらも認証と認可をカバーします。Shiro は軽量で習得が容易、Spring Security は機能がより充実し Spring エコシステムとの統合も深いですが、設定の学習曲線は急です。</p>
<p>Druid ali、HikariCP：よく使われるデータベースコネクションプール。コネクションプールの意義は、データベース接続を再利用して頻繁なハンドシェイクを避けることにあります。Druid は SQL 監視とインジェクション対策の統計画面を備え、HikariCP は性能に定評があり SpringBoot のデフォルトです。</p>
<p>Bcrypt：データベースのユーザーパスワード暗号化方式で、パスワードの安全を保証します。低速ハッシュであり、毎回ソルトが異なる結果を生成するため、たとえデータベースが流出してもレインボーテーブルで一括逆算するのは困難です。MD5/SHA でハッシュをそのまま保存するよりはるかに堅牢です。</p>
<p>JWT：一時トークン生成の戦略・仕様。ユーザー情報に署名を付けてトークン自体に埋め込むため、サーバー側でセッションを保持する必要がなく、複数インスタンスへの水平スケールと自然に相性が良いです。代償として、発行後に能動的に無効化できないため、短い有効期限やブラックリストで補う必要があります。</p>
<p>Undertow、Tomcat、Jboss、Weblogic：Servlet コンテナ。Tomcat が最も汎用的です。Undertow は NIO ベースでメモリ使用量が小さく、SpringBoot の組み込みコンテナの置き換えによく使われます。JBoss、Weblogic はより重量級の商用アプリケーションサーバーです。</p>
<p>Eolinker、Swagger、Knife4j、Yapi：オンライン API ドキュメント・自動化テストツール。核心的な価値は、ドキュメントをコードのアノテーションから自動生成し、コードの更新に追従させることです。「ドキュメントと API が一致しない」というフロントエンド・バックエンド間の典型的な揉め事を回避できます。Knife4j は Swagger の中国製強化 UI です。</p>
<p>MySQL 8.0、PostgreSQL：RDS リレーショナルデータベース。ビジネスデータの主たる保存先です。MySQL はエコシステムと資料が最も豊富、PostgreSQL は機能がより完全で、JSON や GIS のような用途で優れています。</p>
<p>Sqlite：組み込みデータベース。データベース全体が一つのファイルで、独立したプロセスを必要としないため、クライアントのローカルストレージや小さなツールに適しています。</p>
<p>Redis、MongoDB：NoSQL 非リレーショナルデータベース。Redis はメモリベースで、キャッシュ、分散ロック、カウンターによく使われます。MongoDB はドキュメント型データベースで、スキーマが柔軟なため構造が変わりやすいデータに向いています。</p>
<p>Dobbo、Feign：リモートメソッド呼び出し。どちらも、サービス間の呼び出しをローカルメソッドのように書けるようにするものです。Dubbo は独自プロトコル + 長時間接続で性能に優れ、Feign は HTTP ベースの宣言的インターフェースで、Spring Cloud 体系との組み合わせがより自然です。</p>
<p>Sentinel、Hystrix：レートリミット、フォールバック。マイクロサービスの自己防衛手段です。上流からのトラフィックが処理能力を超えたらまずレートリミットし、依存する下流サービスが落ちたら即座に失敗させてフォールバックロジックに切り替えます。スレッドが枯渇して障害が呼び出しチェーンに沿って波及する——いわゆるカスケード障害（雪崩）を防ぎます。Hystrix はすでに新機能開発を停止しており、新規プロジェクトでは通常 Sentinel を選びます。</p>
<p>Skywalking：分散トレーシング（Huawei が開発し Apache に寄贈）。Java Agent によるバイトコード拡張で計装するため、ビジネスコードに侵入することなく、一つのリクエストが複数のサービスをまたぐ完全な呼び出しチェーンを可視化できます。「どの段階が遅いのか」を調査する際に非常に直観的です。</p>
<p>Zookeeper、Eureka、Consul、Nacos、Etcd：サービスレジストリ・設定センター。レジストリは「サービスインスタンスのアドレスが動的に変わる中で、呼び出し側がどうやってそれを見つけるか」という問題を、設定センターは「設定変更のたびに再起動が必要」という問題を解決します。Nacos は両者を一つに統合しており、Spring Cloud Alibaba 体系のデフォルトです。Etcd は Kubernetes の基盤ストレージでもあります。</p>
<p>Seata：Alibaba の分散トランザクションソリューション。一つの業務操作が複数のデータベースや複数のサービスをまたぐ場合、ローカルトランザクションでは全体の整合性を保証できません。Seata の AT モードはデータソースをプロキシしてロールバックログを自動記録するため、ビジネスコードへの侵入が非常に小さいです。</p>
<p>ELK：分散ログ収集、データ集約、分散検索エンジン。つまり Elasticsearch + Logstash + Kibana の組み合わせです。各ノードのログを収集して集中保存し、全文検索と可視化画面で照会します。サービスが増えると、集中型ログなしでは問題調査はほぼ不可能になります。</p>
<p>Micrometer、Prometheus、Grafana：サービスヘルス監視、データ監視、I/O 監視など。Micrometer がアプリケーション側でメトリクスを公開し、Prometheus が定期的にプルして時系列データとして保存し、Grafana がグラフ描画とアラートパネルを担当します。この三つは現在最も一般的な監視の組み合わせです。</p>
<p>Kafka、RabbitMQ、RocketMQ：よく使われるメッセージキュー。ピークカット、疎結合化、非同期化に使います。プロデューサーは投げるだけ、コンシューマーは自分のペースで処理し、トラフィックの洪水はまずキューに落として徐々に消化します。Kafka はスループットが最も高くログ・ストリーム処理向き、RabbitMQ はルーティングが柔軟、RocketMQ はトランザクションメッセージや遅延メッセージの機能がより充実しています。</p>
<p>Nginx、apache-http-service、Traefik：HTTP サーバー。静的リソースのホスティングに加えて、リバースプロキシと L7 ロードバランシングを担うことが多いです。Traefik はコンテナを自動検出してルーティングルールを更新できるため、コンテナ環境との相性が良いです。</p>
<p>Ali-OSS、HUAWEI-OBS：サードパーティの静的リソースストレージ。画像や添付ファイルのようなファイルは、アプリケーションサーバーのディスクではなくオブジェクトストレージに置くことで、運用の手間が減り CDN による高速化とも組み合わせやすくなります。</p>
<p>Ali-NAS：内部ネットワーク共有ファイルストレージのマウント、内部ネットワーク共有ディスク。複数のマシンが同じファイルを読み書きする必要があるとき NAS をマウントすれば、自前で NFS を構築するよりも運用作業がかなり少なくて済みます。</p>
<p>Ali-SLB：L4・L7 ロードバランサー。トラフィック入口の最初の一層で、L4 は TCP を転送し、L7 はドメインやパスで振り分けできます。通常はその後段に Nginx とアプリケーションが続きます。</p>
<p>Findbugs、Sonarqube：静的コード解析。コードを実行せずに、NullPointerException の潜在リスクやリソースの閉じ忘れといった問題を検出できます。SonarQube は CI に組み込んで、品質ゲートで基準未達のコミットをブロックすることもできます。</p>
<p>PDMan：中国の開発者によるデータベース可視化ツール。ER 図の作成、テーブル構造のバージョン管理、CREATE TABLE 用 SQL のエクスポートに使います。チーム協業では口頭でテーブル構造を取り決めるより、はるかに確実です。</p>
<p>Docker、Containerd、CRI-O：代表的なコンテナランタイム。コンテナはアプリケーションと依存関係を一つのイメージにまとめるため、「自分のマシンでは動くのに」という問題はほぼ消えます。Containerd、CRI-O はより低レベルのランタイムで、Kubernetes は CRI インターフェースを通じて直接それらと連携します。</p>
<p>Docker-Compose：コンテナオーケストレーションツール。一つの YAML ファイルで複数コンテナのイメージ、ポート、依存関係を記述し、コマンド一つでローカル環境一式を立ち上げられます。開発時の結合テストにとても便利です。</p>
<p>Kubernetes：コンテナオーケストレーション、運用、リソース管理、リソーススケジューリングのワンストップ管理。核心となる考え方は宣言的アプローチです。望ましい状態を記述すれば、スケジューリング、スケールイン・アウト、障害からの自己修復を Kubernetes が担い、実際の状態を望ましい状態へと収束させ続けます。現在、本番環境のコンテナオーケストレーションにおける事実上の標準です。</p>
<p>Istio：よく使われる Service Mesh フレームワーク。多言語協調でのバックエンド開発、トラフィック制御、フォールバック、サーキットブレーカーに対応します。Service Mesh はレートリミット、サーキットブレーカー、カナリアリリースといったガバナンスロジックをビジネスコードから Sidecar プロキシに下ろすため、異なる言語で書かれたサービスでも同じガバナンス機能を共有できます。</p>
<p>Helm：K8S アプリケーションリソース管理ミドルウェア。Kubernetes のパッケージマネージャーに相当し、大量の YAML を Chart としてテンプレート化して、コマンド一つでインストール、アップグレード、ロールバックを完了できます。</p>
<p>Ali-EsayExcel：Alibaba がオープンソース化した高効率 Excel 処理 jar パッケージ。POI が大きなファイルでメモリ溢れを起こしやすい問題に対してストリーミング読み書きの最適化を施しており、数十万行のインポート・エクスポートでも安定しています。</p>
<p>Netty：よく使われる Java NIO ネットワークプログラミングパッケージ。NIO の煩雑な Selector、Buffer の細部をイベント駆動の Pipeline モデルに包み込んでおり、多くの RPC フレームワークやミドルウェアのネットワーク層の土台になっています。</p>
<p>Disruptor：高性能インメモリキュー（英国の外国為替取引会社 LMAX が開発）。リングバッファでメモリを事前確保し、ロック競合と False Sharing を回避することで、単一マシンでのスレッド間メッセージ受け渡しのレイテンシを極めて低く抑えられます。</p>
<p>Caffeine：JVM ローカル高性能キャッシュコンポーネント。内部で Disruptor を利用して開発されています。プロセス内キャッシュなのでアクセスにネットワークを経由せず、Redis と組み合わせて二段キャッシュを構成するのが定番です。ローカルでホットスポットを受け止め、Redis で共有を保証します。</p>
<p>Jenkins：CI/CD ミドルウェア。自動デプロイ、パイプライン生産。コード取得、コンパイル、テスト、イメージビルド、デプロイをパイプラインとしてつなぎ、コミット後に自動で一連の処理を実行することで、手作業デプロイのミスの余地を減らします。</p>
<p>Apahce-Hdoop：分散ビッグデータストレージとデータ処理。Hadoop の HDFS は大きなファイルをブロックに分割し冗長化して複数マシンに保存し、MapReduce はデータのあるノードの近くで計算を行います。ビッグデータ体系の古参の土台です。</p>
<p>Apahce-Flink：ストリーム処理フレームワークで、ビッグデータにおける準リアルタイム計算に適しています。計算をデータストリーム側に押し下げ、イベントが到着した時点で処理します。状態管理と Exactly-Once セマンティクスをサポートしており、リアルタイムレポートやリアルタイム不正検知でよく使われます。</p>
<p>MyCat2、Apache-Shardingsphere：シャーディング（分庫分表）ミドルウェア。単一テーブルのデータ量がクエリに耐えられないほど増えたときに水平分割を行います。ミドルウェアが SQL を解析し、対象のシャードへルーティングして結果をマージするため、アプリケーションはできるだけ意識せずに済みます。</p>
<p>TiDB、KunlunBase：NewSQL 分散リレーショナルデータベース。同じ問題を別の発想で解決します。データベース自体を分散化し、MySQL プロトコルと互換性を持たせることで、スケールアウトの際にアプリケーション側でシャーディングルールを改修する必要がなくなります。</p>
<p>Arthas：Alibaba がオープンソース化した JVM 診断ツール。フレームグラフ生成、デッドロック診断など。再起動もコード変更もせずに本番の JVM にアタッチして、メソッドの所要時間の確認、クラスの逆コンパイル、呼び出しのトレースができます。本番の難問を調査する際の救命ツールです。</p>
<p>Kettle、DataX、Canal：オープンソースの ETL ツール。データ移行、データクレンジング。前の二つはバッチでの抽出・変換を行います。Canal は少し特殊で、MySQL のスレーブに擬態して binlog を解析することでリアルタイムの増分データを取得でき、キャッシュ同期や異種データ同期によく使われます。</p>
<p>Jmeter：よく使われる API テスト・負荷テストツール。リリース前に一通り負荷をかけて、API のスループット上限とレスポンスタイム分布を把握しておけば、レートリミットの閾値に根拠を持たせられます。勘で決めるのではなく。</p>
<p>Ansible：複数サーバーの運用ツール。SSH ベースで対象マシンに Agent をインストールする必要がなく、YAML で望ましい状態を記述して一括実行します。数十台のマシンの設定を一つ変更するのに、一台ずつログインする必要はありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2019/07/22/zi-ji-da-jian-javawebxiang-mu-kuang-jia#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ul>
<li class="">リストは技術選定そのものではありません。同じレイヤーのコンポーネント（例えば Sentinel と Hystrix、Eureka と Nacos）はどちらか一つを選べば十分で、リストにあるものを全部プロジェクトに詰め込んでも保守コストが増えるだけです。</li>
<li class="">まずは体系に沿って選ぶこと。Spring Cloud Alibaba を使うなら、レジストリ、レートリミット、分散トランザクションはそのまま Nacos、Sentinel、Seata を選ぶのが自然です。異なる体系のコンポーネントを混在させると、互換性の問題を自分で背負うことになります。</li>
<li class="">コンポーネントのメンテナンス状況に注意。エコシステムの移り変わりは速く、Hystrix のようにメンテナンスモードに入ったプロジェクトは新規システムではできるだけ避け、コミュニティが活発に開発を続けている代替品を選びましょう。</li>
<li class="">分散コンポーネントにはどれも運用コストがあります。Kafka、Elasticsearch、Kubernetes はそれぞれ専任者が研究する価値のあるもので、小さなチームは導入前に誰が保守するのかをよく考えるべきです。</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/07/22/zi-ji-da-jian-javawebxiang-mu-kuang-jia#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>このリストは、Java Web プロジェクトにおける開発フレームワーク、データストレージ、マイクロサービスガバナンスから、コンテナ化、ビッグデータまでのおおよその全体像をカバーしています。記録する目的はすべてを使うことではなく、具体的な問題に出会ったときに、どんな既存のソリューションがあり、それぞれの位置づけが何かを知っていることです。エコシステムは変化し続けているので、このページも実際の利用に合わせて追記・修正していきます。</p>]]></content:encoded>
            <category>Java</category>
        </item>
        <item>
            <title><![CDATA[ダークソウル3]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/17/hei-hun-3</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/17/hei-hun-3</guid>
            <pubDate>Mon, 17 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[ダークソウル3 の物語はロスリック王国で展開されます。プレイヤーは、火の時代と先代の闇の刻印の持ち主たちとの衝突が引き起こした終末を阻止しなければなりません。そのためにプレイヤーは薪の王、すなわちかつて火を継いだ英雄たちと対峙することになります。数え切れない英雄たちが火を継いできた今日、炎はすでに極めて弱々しくなっています。]]></description>
            <content:encoded><![CDATA[<p>ダークソウル3 の物語はロスリック王国で展開されます。プレイヤーは、火の時代と先代の闇の刻印の持ち主たちとの衝突が引き起こした終末を阻止しなければなりません。そのためにプレイヤーは薪の王、すなわちかつて火を継いだ英雄たちと対峙することになります。</p>
<!-- -->
<p>数え切れない英雄たちが火を継いできた今日、炎はすでに極めて弱々しくなっています。定められた薪の王は、火継ぎが引き返せない道であることを深く知っており、火を継ごうとしません。火を継ぐのにふさわしい者が見つからないため、かつて火を継いだ薪の王たちを墓から呼び覚まし、再び使命に赴かせるしかありませんでした。しかし、その薪の王たちのうち三人もまた、自らの職責を果たそうとはしなかったのです。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--5--f730f26f57da9cda2e75d95b98193de6.webp" width="1600" height="900" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="火を継ごうとしない薪の王たち">火を継ごうとしない薪の王たち<a href="https://tanzhuo.xyz/ja/2019/06/17/hei-hun-3#%E7%81%AB%E3%82%92%E7%B6%99%E3%81%94%E3%81%86%E3%81%A8%E3%81%97%E3%81%AA%E3%81%84%E8%96%AA%E3%81%AE%E7%8E%8B%E3%81%9F%E3%81%A1" class="hash-link" aria-label="火を継ごうとしない薪の王たち への直接リンク" title="火を継ごうとしない薪の王たち への直接リンク" translate="no">​</a></h2>
<p>深淵の監視者たちは、長きにわたって深淵の力に触れ続けたためにそれに侵食され、狂気に陥っていました。彼らは絶えず同士討ちを繰り返し、火を継ぐどころではありません。エルドリッチは炎が消えた後の深海の時代を予感し、絶望の中で火継ぎを放棄し、初代の神を含む多くの者の命を呑み込み、腐敗物の塊となって初代の王城に居座り、深海の時代の到来を待ち受けています。巨人ヨームは、かつて自らの民を炎の害から守りきれず、都を滅ぼされたことで深い自責と悔恨に沈み、何かを為そうとはしなくなりました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="火継ぎの重責">火継ぎの重責<a href="https://tanzhuo.xyz/ja/2019/06/17/hei-hun-3#%E7%81%AB%E7%B6%99%E3%81%8E%E3%81%AE%E9%87%8D%E8%B2%AC" class="hash-link" aria-label="火継ぎの重責 への直接リンク" title="火継ぎの重責 への直接リンク" translate="no">​</a></h2>
<p>こうして最後に、火継ぎの重責はプレイヤーが演じる灰の人に託されることになります。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--8--7e3b2ec746b4ec84b0251d3e4e510493.webp" width="1600" height="900" class="img_ev3q"></p>
<p>絶望の中にいる薪の王たちが説得だけで自ら玉座に戻るはずもありません。薪の王たちの力を手に入れるため、プレイヤーは道中で幾重もの守りを突破し、彼らを討ち倒して、残された薪を持ち帰らなければなりません。そして最後、五人の薪の王の薪がすべて火継ぎの祭祀場の玉座に戻ったとき、灰の人は彼らの力をすべて体内に取り込みます。</p>
<p>その後、灰の人は始まりの火の炉、すなわち最初の火のある場所へと赴き、そこを守る「薪の王たちの化身」を倒せば、火を継ぐのに十分な力を得られます。選択の違いによって、物語は火継ぎ、火を消す、火の簒奪、そして秘密の、四つのエンディングへと向かいます。</p>
<p>もしかすると、火の時代はまもなく終わりを迎えるのかもしれません。</p>
<p>この動画は Bilibili の原作者から許諾を得ています</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--9--c29bfaa65c9633129d026b8df89b2c03.webp" width="1600" height="1000" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-678476-a199110863b5af78377f27441618cc6e.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-693009-4add49a2cab1f8c2361539bb0a210a09.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--7--fedf2f396fed8ca3ad53e85e631f4ca5.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--4--9b091401291fb68eb68485ba7b52d7a2.jpg" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-timg-3-804faade1b8b9a0f8013db8676d09a7e.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-timg--1--1-c44283e12f92a435b0185c22e21617af.webp" width="1600" height="1024" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--6--aaff85418fc6da5d97fc3d556a6a52ba.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-timg--2--1-2b9c2c6186a9b739d1caa0c9cb9fd3c4.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-timg--3--32744fd45eb4f0b34cffa84c2c5409e2.webp" width="1600" height="716" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-11-timg--3--1-fdff78fdffe6f95b0a340454eb3dd7dc.webp" width="1600" height="1024" class="img_ev3q"></p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[Ghost ブログにプラグインをちょっと足してみた話]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian</guid>
            <pubDate>Thu, 13 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Ghost ブログにコメント機能を追加した試行錯誤の記録。まず Disqus を導入し、設定が有効になったところでブロックされていることが判明。中国国内の訪問者にはまったく表示されない。プロキシ方式を検討した末、最終的に国内で使える Valine に乗り換えました。]]></description>
            <content:encoded><![CDATA[<p>もともと Ghost に海外の共有コメントサービスである Disqus を統合して、ブログにコメント機能を持たせようと考えていました。しかし面倒くさがりなので、他人が作ってくれた出来合いのものを使うことにしました。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="背景">背景<a href="https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian#%E8%83%8C%E6%99%AF" class="hash-link" aria-label="背景 への直接リンク" title="背景 への直接リンク" translate="no">​</a></h2>
<p>Ghost はとてもクリーンなブログシステムです。クリーンすぎて、コメント機能すら内蔵されていません。公式の姿勢も明確で、コメントはサードパーティサービスの領分であり、ブログシステムはコンテンツだけを担当する、というものです。この設計には私も納得しています。コメントシステムはユーザー認証、スパム対策、データストレージが絡み、自前で維持するコストは低くありません。専門のサービスに任せたほうが楽です。</p>
<p>というわけで問題は、「どこのコメントサービスを選ぶか、そしてそれをどう Ghost に組み込むか」になりました。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ghost-にサードパーティコメントを組み込む仕組み">Ghost にサードパーティコメントを組み込む仕組み<a href="https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian#ghost-%E3%81%AB%E3%82%B5%E3%83%BC%E3%83%89%E3%83%91%E3%83%BC%E3%83%86%E3%82%A3%E3%82%B3%E3%83%A1%E3%83%B3%E3%83%88%E3%82%92%E7%B5%84%E3%81%BF%E8%BE%BC%E3%82%80%E4%BB%95%E7%B5%84%E3%81%BF" class="hash-link" aria-label="Ghost にサードパーティコメントを組み込む仕組み への直接リンク" title="Ghost にサードパーティコメントを組み込む仕組み への直接リンク" translate="no">​</a></h2>
<p>Ghost にはプラグインマーケットこそありませんが、2 つの入り口が用意されています。</p>
<p>1）<strong>Code Injection（コードインジェクション）</strong>。管理画面の設定から、サイト全体の <code>&lt;head&gt;</code> や <code>&lt;body&gt;</code> の末尾に HTML/JS のスニペットを直接差し込めます。テーマファイルを変更する必要がなく、コメントサービスを手早く試すのに向いています。</p>
<p>2）<strong>テーマテンプレートの修正</strong>。コメントサービスの埋め込みコードをテーマの <code>post.hbs</code> 記事テンプレートに書き込み、記事ページだけで読み込む方法です。こちらのほうが正式なやり方で、コメント欄の位置もコントロールしやすくなります。</p>
<p>サードパーティコメントサービスの組み込み方は基本的にどこも同じです。ページにプレースホルダーの <code>div</code> を置き、JS を読み込ませると、スクリプトがサービス提供者のサーバーからコメントデータを取得してプレースホルダー要素にレンダリングします。コメントデータはサービス提供者側に保存され、ブログ本体とは完全に分離されています。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="disqus-の導入">Disqus の導入<a href="https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian#disqus-%E3%81%AE%E5%B0%8E%E5%85%A5" class="hash-link" aria-label="Disqus の導入 への直接リンク" title="Disqus の導入 への直接リンク" translate="no">​</a></h2>
<p>Disqus は海外で最も使われているコメントサービスで、Ghost の公式ドキュメントでも例として取り上げられています。公式ドキュメントを見たところ、設定は非常に簡単でした。アカウントを登録し、サイトを作成して shortname を取得し、埋め込みコードを記事テンプレートに貼り付ければ完了です。</p>
<p>設定を終えてみると、案の定、事はそう単純ではありませんでした。</p>
<p>設定自体は確かに有効になっていました。外部ネットワークにアクセスできる回線でブログを開くと、コメント欄は正常に表示されます。ところが中国国内の一般的なネットワークに切り替えて見ると、コメント欄があるはずの場所は真っ白。原因は単純で、Disqus の埋め込みスクリプトと API のドメインが国内でブロックされているため、スクリプトがそもそも読み込めず、コメント欄がレンダリングされようがないのです。</p>
<p>もう呆れるしかありませんでした。</p>
<p>読者が主に中国国内にいる中国語ブログにとって、これはコメント機能を作っていないのと同じです。さらに厄介なことに、読み込みに失敗するスクリプトはページを遅くする可能性もあります。ブラウザはタイムアウトするまでそこで待ち続けるからです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="プロキシ方式の検討">プロキシ方式の検討<a href="https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian#%E3%83%97%E3%83%AD%E3%82%AD%E3%82%B7%E6%96%B9%E5%BC%8F%E3%81%AE%E6%A4%9C%E8%A8%8E" class="hash-link" aria-label="プロキシ方式の検討 への直接リンク" title="プロキシ方式の検討 への直接リンク" translate="no">​</a></h2>
<p>ブロックされたサービスにまったく手がないわけではありません。コミュニティで流行っている発想は、リバースプロキシの自前構築です。自分のサーバー（あるいは CDN や Serverless 関数）で Disqus の API リクエストを中継し、フロントエンド側でも埋め込みスクリプトのリクエスト先を書き換えます。こうすれば国内ユーザーのリクエストはまず自分のプロキシに届き、プロキシが Disqus にアクセスします。</p>
<p>時間ができたら GitHub で誰かが書いたプロキシのコードを探すことにしました。自分で書くのも時間がかかりますし、出来合いのものを探すのは悪くない選択です。</p>
<p>とはいえ冷静に考えてみると、この方式には避けて通れない問題がいくつかあります：</p>
<ul>
<li class="">プロキシ自体のデプロイと保守が必要になり、コメント欄一つのためにサービスを一つ余計に飼うことになる；</li>
<li class="">コメントのログイン認可フローも Disqus を経由するため、プロキシが処理すべき API は 1 つや 2 つでは済まず、漏れが出やすい；</li>
<li class="">Disqus の無料版にはそもそも広告が付く。</li>
</ul>
<p>コメント機能のためにこれだけの負担を背負うのは、コストパフォーマンスが良くありません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="valine-への乗り換え">Valine への乗り換え<a href="https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian#valine-%E3%81%B8%E3%81%AE%E4%B9%97%E3%82%8A%E6%8F%9B%E3%81%88" class="hash-link" aria-label="Valine への乗り換え への直接リンク" title="Valine への乗り換え への直接リンク" translate="no">​</a></h2>
<p>-2019-12-19 更新</p>
<p>最終的に、中国国内でそのまま使えるコメントシステム <a href="https://valine.js.org/" target="_blank" rel="noopener noreferrer" class="">Valine</a> を選びました。広告なし、無料で使えます。</p>
<p>Valine の発想は Disqus とは少し違います。自前のバックエンドサービスを持たず、コメントデータは LeanCloud のような BaaS プラットフォームに保存されます。フロントエンドは JS ファイルを 1 つ読み込み、自分で申請したアプリの App ID と App Key を記入するだけで動きます。強制ログインはなく、訪問者はニックネームを入れればコメントできます。個人ブログには十分な軽さです。</p>
<p>組み込み方は前述のとおりで、初期化コードを Code Injection またはテーマテンプレート経由で記事ページに置くだけです。国内からのアクセスに何の障害もなく、ページの読み込みが引っかかることもなくなりました。</p>
<div class="theme-admonition theme-admonition-tip admonition_xJq3 alert alert--success"><div class="admonitionHeading_Gvgb"><span class="admonitionIcon_Rf37"><svg viewBox="0 0 12 16"><path fill-rule="evenodd" d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"></path></svg></span>ヒント</div><div class="admonitionContent_BuS1"><p>コメントシステムを選ぶ前に、まずターゲット読者のネットワーク環境で実際にページを開いてみてください。開発者自身のネットワークではすべて正常でも、環境が変わればまったく別の話、というサービスは少なくありません。</p></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/06/13/guan-yu-gei-ghostbo-ke-xi-tong-jia-dian-cha-jian#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>今回の試行錯誤から得た教訓はシンプルです。サードパーティサービスを選ぶときは、可用性を機能より優先すること。Disqus がどれだけ多機能でも、読者が開けなければゼロと同じです。Valine の機能は素朴ですが、中国国内向けの中国語ブログにとってはむしろ実務的な選択でした。Ghost の Code Injection の仕組みのおかげでこうした乗り換えのコストは非常に低く、一巡り試行錯誤しても半日で済む話です。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[certbot で docker nginx の https を設定する]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker</guid>
            <pubDate>Thu, 13 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[certbot で取得した Let's Encrypt 証明書はシンボリックリンクで、nginx コンテナにマウントしても読み取れません。docker volumes とシンボリックリンクにまつわるこの落とし穴と、正しいマウント方法を記録します。]]></description>
            <content:encoded><![CDATA[<p>環境：Docker-1.13.1、nginx-1.15.12、certbot</p>
<!-- -->
<p>サイトを HTTPS 化すること自体は、物理マシン上で nginx を直接設定するだけなら難しくなく、チュートリアルも検索すればいくらでも出てきます。しかし nginx が docker コンテナ内で動いている場合、証明書ファイルは volumes でコンテナにマウントする必要があり、そこにファイルシステムの間接層が一枚挟まります。折悪しく certbot が生成する証明書ファイルはシンボリックリンクであり、この 2 つが重なると、ドキュメントを調べないとなかなか理解できない落とし穴になります。この記事では当時の調査過程を記録しておきます。</p>
<p>昨今は https でないと安全でないサイトとして表示されてしまうため、設定することにしました。無料の <strong>Let's Encrypt</strong> 証明書を申請するだけなので大した手間ではないはずでしたが、docker の nginx コンテナに ssl 証明書を設定するところにまさかの罠が潜んでいるとは思いもしませんでした。私は独学の叩き上げで docker のドキュメントを一度も読んだことがなく、ここで見事にハマりました。機会があれば公式ドキュメントにはやはりじっくり目を通しておくべきですね。</p>
<p><img decoding="async" loading="lazy" src="data:image/jpeg;base64,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" width="66" height="58" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="証明書はどこから来るのか">証明書はどこから来るのか<a href="https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker#%E8%A8%BC%E6%98%8E%E6%9B%B8%E3%81%AF%E3%81%A9%E3%81%93%E3%81%8B%E3%82%89%E6%9D%A5%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="証明書はどこから来るのか への直接リンク" title="証明書はどこから来るのか への直接リンク" translate="no">​</a></h2>
<p>まず私の状況を説明しておくと、certbot で無料のドメイン ssl 証明書を申請した結果、得られた</p>
<p>fullchain.pem、privkey.pem ファイルはシンボリックリンクファイルでした。</p>
<p>これは偶然ではなく、certbot の意図的な設計です。実際の証明書ファイルは <code>/etc/letsencrypt/archive/&lt;ドメイン&gt;/</code> 配下に保存され、更新のたびに新しいファイル一式が生成されます。一方、<code>/etc/letsencrypt/live/&lt;ドメイン&gt;/</code> 配下の fullchain.pem、privkey.pem は archive 内の最新版を指すシンボリックリンクにすぎません。こうすることで nginx の設定には live のパスを固定で書いておけば、更新後も設定を変更する必要がありません。物理マシン上ではこの仕組みは実に快適なのですが、コンテナに持ち込むと問題が起きます。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="コンテナへのマウント">コンテナへのマウント<a href="https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker#%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%B8%E3%81%AE%E3%83%9E%E3%82%A6%E3%83%B3%E3%83%88" class="hash-link" aria-label="コンテナへのマウント への直接リンク" title="コンテナへのマウント への直接リンク" translate="no">​</a></h2>
<p>次に docker volumes で ssl 設定フォルダを nginx コンテナ内部にマウントし、あわせて nginx.conf を設定します（nginx コンテナの nginx.conf 設定ファイルには少し癖があり、nginx.conf は /etc/nginx/conf.d/ 配下の *.conf ファイルを読み込みます。この細かい点には注意が必要です。またコンテナの 443 ポートのマッピングも忘れないでください）。</p>
<p>サイトの設定はだいたい次のようになります。</p>
<div class="language-nginx codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-nginx codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># /etc/nginx/conf.d/ 配下に置くと、メイン設定の include で読み込まれる</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token directive keyword" style="color:hsl(286, 60%, 67%)">server</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token directive keyword" style="color:hsl(286, 60%, 67%)">listen</span><span class="token directive"> </span><span class="token directive number" style="color:hsl(29, 54%, 61%)">443</span><span class="token directive"> ssl</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token directive keyword" style="color:hsl(286, 60%, 67%)">server_name</span><span class="token directive"> example.com</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token comment" style="color:hsl(220, 10%, 40%)"># ここに書くのはコンテナ内のパス。マウント後の実際の位置と一致していなければならない</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token directive keyword" style="color:hsl(286, 60%, 67%)">ssl_certificate</span><span class="token directive">     /etc/letsencrypt/live/example.com/fullchain.pem</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">    </span><span class="token directive keyword" style="color:hsl(286, 60%, 67%)">ssl_certificate_key</span><span class="token directive"> /etc/letsencrypt/live/example.com/privkey.pem</span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="落とし穴はどこにあるのか">落とし穴はどこにあるのか<a href="https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker#%E8%90%BD%E3%81%A8%E3%81%97%E7%A9%B4%E3%81%AF%E3%81%A9%E3%81%93%E3%81%AB%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="落とし穴はどこにあるのか への直接リンク" title="落とし穴はどこにあるのか への直接リンク" translate="no">​</a></h2>
<p>ここに落とし穴があります。証明書ファイルはシンボリックリンクファイルであるため、コンテナ内にマウントしても使えないのです。</p>
<p>nginx はエラーを出し、エラーメッセージは「証明書ファイルが見つからない」というものになります。しかしコンテナに入ってみると、証明書ファイルは確かにコンテナ内にあるのです。ところが！この証明書ファイルは読み取ることができません。権限の問題ではなく、docker volumes 自体がハードリンクの情報しかマッピングできないことが原因です。シンボリックリンクはマッピングできず、コンテナに残っているのはシンボリックリンクの抜け殻にすぎません。</p>
<p>この種の問題を調査する簡単な方法があります。コンテナに入って <code>ls -l</code> で確認し、証明書ファイルがどこかのパスを指すリンクとして表示されていたら、続けて <code>cat</code> で読めるか試してみます。読み出せなければ、リンクが指す先がコンテナ内に存在しないのがほぼ確実です。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="なぜこうなるのか">なぜこうなるのか<a href="https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker#%E3%81%AA%E3%81%9C%E3%81%93%E3%81%86%E3%81%AA%E3%82%8B%E3%81%AE%E3%81%8B" class="hash-link" aria-label="なぜこうなるのか への直接リンク" title="なぜこうなるのか への直接リンク" translate="no">​</a></h2>
<p>実は理解するのは難しくありません。シンボリックリンクは単なるショートカットにすぎないからです。もし volumes の中にあるホストマシンのショートカットからホストのハードリンクにアクセスできてしまったら、そのほうが問題です。シンボリックリンクは /home ボリューム配下にあり、そのシンボリックリンクが対応する実ファイルは実際には /usr ボリューム配下にあります。しかし私が設定した volumes は /home ボリュームしかマッピングしておらず、/usr ボリュームのデータにはアクセスできません。ここでは対応するリンクファイルの実体パスまで正確にマッピングしなければならず、リンクファイルのディレクトリだけではだめで、リンクファイルを直接マウントするのもだめです。</p>
<p>別の角度から言えば、シンボリックリンク自体はターゲットのパスを 1 本記録しているだけです。docker はリンクをそのままコンテナに置き、コンテナはそのパスに従って<strong>自分自身の</strong>ファイルシステム内でターゲットを探します。ターゲットのパスが一緒にマウントされていなければ、このリンクはデッドリンクになります。これはコンテナの隔離として当然あるべき姿でもあります。ショートカットを 1 つマウントするだけで、それを辿ってホストマシン上の任意の場所のファイルを読めてしまったら、隔離は有名無実になってしまいます。</p>
<p><strong>マウントパスが証明書のリンクファイルの完全なパス構造を指していて初めて有効になります。docker 内の nginx は起動時に、リンクファイルが対応するファイルアドレスから証明書ファイルを取得できるようになります。</strong></p>
<p>つまり鍵となるのは、シンボリックリンクが指すターゲットのパスがコンテナ内に実在するようにすることです。certbot の場合、live と archive の 2 つのディレクトリを一緒にコンテナに入れて初めて、リンクが解決できるようになります。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># コンテナ起動時に letsencrypt ディレクトリ全体をマウントする。</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># live 配下のシンボリックリンクと archive 配下の実ファイルが両方あって初めてリンクが正常に解決される</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"> run </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-d</span><span class="token plain"> </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">80</span><span class="token plain">:80 </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-p</span><span class="token plain"> </span><span class="token number" style="color:hsl(29, 54%, 61%)">443</span><span class="token plain">:443 </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-v</span><span class="token plain"> /etc/letsencrypt:/etc/letsencrypt:ro </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  </span><span class="token parameter variable" style="color:hsl(207, 82%, 66%)">-v</span><span class="token plain"> /data/nginx/conf.d:/etc/nginx/conf.d </span><span class="token punctuation" style="color:hsl(220, 14%, 71%)">\</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">  nginx</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ハマりどころと注意点">ハマりどころと注意点<a href="https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker#%E3%83%8F%E3%83%9E%E3%82%8A%E3%81%A9%E3%81%93%E3%82%8D%E3%81%A8%E6%B3%A8%E6%84%8F%E7%82%B9" class="hash-link" aria-label="ハマりどころと注意点 への直接リンク" title="ハマりどころと注意点 への直接リンク" translate="no">​</a></h2>
<ol>
<li class="">
<p>nginx 公式イメージのメイン設定はグローバル設定のみを行い、サイト設定は include <code>/etc/nginx/conf.d/*.conf</code> に頼っています。自分の server ブロックは正しい場所に置かないと、変更しても反映されません。</p>
</li>
<li class="">
<p>443 ポートは忘れがちです。コンテナ内の nginx が 443 をリッスンしていても、ホストマシン側でマッピングしていなければブラウザは接続できず、しかも nginx のログには何の異常も現れません。</p>
</li>
<li class="">
<p>Let's Encrypt の証明書の有効期間は 90 日しかありません。更新後は archive ディレクトリ配下に新しい版のファイルが追加され、live 配下のシンボリックリンクもそれに合わせて更新されます。もし当初、手間を惜しんで解決後の実ファイルパスを設定にハードコードしていた場合、更新後にパスが合わなくなります。これがディレクトリ全体をマウントし、設定では live のパスを参照する方式が推奨されるゆえんでもあります。更新後はコンテナ内の nginx に設定をリロードさせることを忘れないでください。</p>
</li>
<li class="">
<p>コンテナ内でファイルが「存在して見える」ことは、読めることを意味しません。シンボリックリンクに対して <code>ls</code> しても問題は見えません。<code>cat</code> してみるか、nginx のエラーメッセージを直接見るほうが手っ取り早いです。</p>
</li>
</ol>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="まとめ">まとめ<a href="https://tanzhuo.xyz/ja/2019/06/13/li-yong-docker#%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="まとめ への直接リンク" title="まとめ への直接リンク" translate="no">​</a></h2>
<p>この落とし穴の本質は、それぞれ単体では合理的な 2 つの設計が衝突したことにあります。certbot はシンボリックリンクで「固定パス」と「変化する証明書ファイル」を分離し、docker volumes はリンクそのものを忠実に運ぶだけでリンクの先までは追いません。シンボリックリンクは「パスを記録しているだけ」という点を理解すれば、問題はもう不思議ではありません。リンクとその指し先を一緒にコンテナにマウントし、パスがコンテナ内で閉じるようにすれば、証明書は自然に読み取れるようになります。docker のドキュメントを読んでこなかった授業料を、一度のハマりで払ったということにしておきましょう。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[ジムをめぐる心の動き]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/09/jian-shen-zhen-tmlei</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/09/jian-shen-zhen-tmlei</guid>
            <pubDate>Sun, 09 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[ジムに行く前日：ああ、いいね、また体を鍛えられるぞ！最高👍 ジムまであと数時間：なんだかあまり行きたくないなあ！やっぱり家でゴロゴロしてるほうが楽だ。まあいい、行くって決めたんだから行こう。 トレーニング中：俺はバカだ トレーニング後：俺はバカだ]]></description>
            <content:encoded><![CDATA[<p>ジムに行く前日：</p>
<!-- -->
<p>ああ、いいね、また体を鍛えられるぞ！最高👍</p>
<p>ジムまであと数時間になったとき：</p>
<p>なんだかあまり行きたくないなあ！やっぱり家でゴロゴロしてるほうが楽だ。まあいい、行くって決めたんだから、行くとしよう。</p>
<p>トレーニング中：</p>
<p>俺はバカだ</p>
<p>トレーニング後：</p>
<p>俺はバカだ</p>]]></content:encoded>
            <category>ライフ</category>
        </item>
        <item>
            <title><![CDATA[Factorio（ファクトリオ）]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/07/factorio-yi-xing-gong-han</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/07/factorio-yi-xing-gong-han</guid>
            <pubDate>Fri, 07 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Factorio は工業生産ラインを建設し、それを効率よく稼働させ続けるゲームです。ゲームの中では、鉱石を掘り、研究を進め、工場を建て、自動生産ラインを構築しながら、異星の虫たちと殺し合うことになります。あなたは無一文の状態から苦難のスタートを切ります。]]></description>
            <content:encoded><![CDATA[<p><strong>Factorio</strong> は工業生産ラインを建設し、それを効率よく稼働させ続けるゲームです。ゲームの中では、鉱石を掘り、研究を進め、工場を建て、自動生産ラインを構築しながら、異星の虫たちと殺し合うことになります。</p>
<!-- -->
<p>あなたは無一文の状態から苦難のスタートを切ります。斧を振るって木を切り、ツルハシで鉱石を掘り、手作業でロボットアームやベルトコンベアを組み立てる。しかし、こんなやり方をいつまで続けても、たいして意味はありません。</p>
<p>だからこそ、効率的な大規模発電所、巨大な石油化学体系、壮観な全自動産業チェーン、そしてあなたの代わりに東奔西走してくれるロボット部隊が必要なのです。物資の備蓄が潤沢な工業帝国の、真の支配者になりましょう！ しかし、あなたに危害を加えようとする輩は常にいるものです。この星の土着の虫の群れは、あなたが自分たちの裏庭で好き勝手に暴れ回る行為が気に入りません。いつの日か、この連中は団結してあなたに面倒を起こしに来るでしょう。だからこそ、武器を製造し、防衛を築き、鎮圧の準備を整えて、誰が真の支配者なのかを奴らに思い知らせてやる必要があるのです。</p>
<p><strong>マルチプレイ</strong> ではさまざまな陣営に加わることができ、上級者たちに導かれながら、仲間と分業・協力して、途方もなく壮大な工業パークを一緒に建設できます。Factorio の <strong>Mod サポート</strong> は世界中のデザイナーを惹きつけ、ゲームの改良と革新に参加させています。最適化調整からゲーム補助、さらにはゲームの徹底的なリニューアルまで、日進月歩の Mod があなたに絶えず新しい楽しみを提供してくれるでしょう。</p>
<p>遊び始めたばかりの頃：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190101145512_1-8279c66667d49aac7576aeac78719403.webp" width="1600" height="900" class="img_ev3q"></p>
<p>数時間プレイした後：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190101161133_1-0ce1893596a944b10f607a51512f0227.webp" width="1600" height="900" class="img_ev3q"></p>
<p>新しいマップで始めた後：</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190607132540_1-92484dbcb8dd7a163a5c0d98beab9f79.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190607132643_1-baa5b7381b4312367d5adae2c3cce1a6.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190607132629_1-e94bae9e0831cf11276af1e3bec58940.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190607132610_1-bdf12ef8ad6cf98766ce0cdee6b4a84e.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190404232212_1-90a444e605839209545df0db0802724b.webp" width="1600" height="900" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-20190404232753_1-d42628f86e01577d527736e1193b517b.webp" width="1600" height="900" class="img_ev3q"></p>
<p>最後にたどり着いた結論：マジで廃人ゲーである</p>]]></content:encoded>
            <category>ゲーム</category>
        </item>
        <item>
            <title><![CDATA[CentOS の docker でデフォルトのイメージ保存パスを変更する]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing</guid>
            <pubDate>Wed, 05 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[docker のデフォルトイメージ保存パスを変更した際のハマり記録です。--graph パラメータからストレージドライバと d_type サポートまで、最終的にデータディスクをマウントする方法で、再起動後にイメージが消える問題を解決しました。]]></description>
            <content:encoded><![CDATA[<p>環境：CentOS-7、docker 1.13.1。docker のデフォルトイメージ保存パスを変更した際の、ハマりどころ満載の一部始終を記録します。</p>
<!-- -->
<p>（ご自身のシステムと docker のバージョンに注意してください。必ず記事を最後まで読んでから操作してください。そうしないと自己責任になります）</p>
<p>docker のデフォルトのイメージ保存パスは /var/lib/docker です。通常はデフォルトの保存場所を使いません。イメージファイルは一般的にサイズが大きく、構築するサービスが増えるほどホストのシステムディスクへの負荷が大きくなりますし、各イメージにはそれぞれマウントボリュームなどもあります（これらは本記事の範囲外です）。したがってデータをシステムディスクに置くべきではなく、対応するデータディスクのパスに切り替えるべきです。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="保存パスの変更">保存パスの変更<a href="https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing#%E4%BF%9D%E5%AD%98%E3%83%91%E3%82%B9%E3%81%AE%E5%A4%89%E6%9B%B4" class="hash-link" aria-label="保存パスの変更 への直接リンク" title="保存パスの変更 への直接リンク" translate="no">​</a></h2>
<p>本題に入りましょう。docker の設定ファイル /usr/lib/systemd/system/docker.service を修正します。</p>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-image-2-c8d543b950a8f529a3452dca4033e4b6.png" width="561" height="662" class="img_ev3q"></p>
<p>graph 設定 <code>--graph="your path" \</code> を追加し、修正して保存した後、設定ファイルをリロードして docker サービスを再起動します。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># systemd の設定をリロード</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">systemctl daemon-reload</span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain"></span><span class="token comment" style="color:hsl(220, 10%, 40%)"># docker サービスを再起動</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">systemctl restart docker.service</span><br></div></code></pre></div></div>
<p><code>docker info</code> を実行して docker サービスの現在の情報を確認します。</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="267" height="33" class="img_ev3q"></p>
<p>Docker Root Dir が "your path" と表示されていれば、設定は成功です。イメージを 1 つダウンロードしてから、対応するフォルダを見て反映されているか確認することをおすすめします。その後 reboot でサーバーを再起動し、docker 内のイメージがまだ残っているかを確認しましょう。</p>
<p><strong>docker images を実行した結果、ダウンロードしたイメージが消えていたら。</strong></p>
<p>おめでとうございます、私と同じ状況です。人生に疑問を感じるかもしれません。再度 docker info を実行すると、Docker Root Dir のパスには何の問題もありません。ここでの私のやり方は、元の /var/lib/docker フォルダを削除することでした。もちろん削除する前に、中のデータがまだ必要であれば、先にコピーを取ってから削除するか、フォルダを移動して名前を変えることをおすすめします。名前を変えなくても構いませんが、重要なのは /var/lib/docker を存在しない状態にすることです。その後もう一度試してみれば、おそらく問題ないはずです。少なくとも私はそうでした。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="追記再起動後にまたイメージが消えた2019-06-16">追記：再起動後にまたイメージが消えた（2019-06-16）<a href="https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing#%E8%BF%BD%E8%A8%98%E5%86%8D%E8%B5%B7%E5%8B%95%E5%BE%8C%E3%81%AB%E3%81%BE%E3%81%9F%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8%E3%81%8C%E6%B6%88%E3%81%88%E3%81%9F2019-06-16" class="hash-link" aria-label="追記：再起動後にまたイメージが消えた（2019-06-16） への直接リンク" title="追記：再起動後にまたイメージが消えた（2019-06-16） への直接リンク" translate="no">​</a></h2>
<p>これで設定完了だと思っていたのですが、案の定そう単純な話ではありませんでした。やはり私の docker への理解はまだまだ足りなかったのです。</p>
<p>今回はサーバー事業者が突然サーバーのハードウェアを修理し、大部分のサーバーが強制シャットダウンされました（だからやはり、安さにつられて無名ブランドのクラウドサーバーを買うのはやめましょう）。私のサーバーもシャットダウンされ、修理完了後にサーバーが再起動しましたが、ブログには依然としてアクセスできませんでした。docker の自動起動とコンテナの追従起動を設定していたのに、どうやら効いていなかったようで、当然ながら問題の原因を調べる必要がありました。（前回、本当に設定完了だと思っていたのに、どうやら顔をひっぱたかれた気分です）</p>
<p>サーバーにログインして docker images を実行すると、イメージが前回と同じようにまた消えていました。つまり前回の操作では実際には問題を解決できていなかったのです——正確に言えば、設定自体は有効になっていたものの、ホストの再起動に伴って設定に問題が発生したのです。</p>
<p>docker info で見える Docker Root Dir のパスには問題がないものの、実際にはすでに障害が起きています。docker images と docker ps -a を実行しても何の情報も表示されません。しかもこの状態で <code>service docker stop</code> を実行して docker サービスを停止すると、二度と docker を起動できなくなります。<code>service docker start</code> で起動しようとするとエラーが出ます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">Job </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">for</span><span class="token plain"> docker.service failed because the control process exited with error code. See </span><span class="token string" style="color:hsl(95, 38%, 62%)">"systemctl status docker.service"</span><span class="token plain"> and </span><span class="token string" style="color:hsl(95, 38%, 62%)">"journalctl -xe"</span><span class="token plain"> </span><span class="token keyword" style="color:hsl(286, 60%, 67%)">for</span><span class="token plain"> details.</span><br></div></code></pre></div></div>
<p>指示どおり systemctl status docker.service や journalctl -xe でエラー詳細を確認しても、まったく要領を得ません。具体的なエラー情報を見たければ、docker デーモンをフォアグラウンドで直接起動すれば起動ログが表示されるので、そこからエラーを調べられます。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># docker デーモンをフォアグラウンドで起動し、起動ログを直接出力する</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">dockerd</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ストレージドライバの落とし穴">ストレージドライバの落とし穴<a href="https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing#%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E3%83%89%E3%83%A9%E3%82%A4%E3%83%90%E3%81%AE%E8%90%BD%E3%81%A8%E3%81%97%E7%A9%B4" class="hash-link" aria-label="ストレージドライバの落とし穴 への直接リンク" title="ストレージドライバの落とし穴 への直接リンク" translate="no">​</a></h2>
<p>このエラーを調査する過程で多くの知識を学びました。たとえば docker の 5 種類のストレージドライバモードです。</p>
<p>簡書：<a href="https://www.jianshu.com/p/00ffd8df6010" target="_blank" rel="noopener noreferrer" class="">https://www.jianshu.com/p/00ffd8df6010</a></p>
<p>他の方のブログ：<a href="https://blog.csdn.net/qq_34018840/article/details/89853119" target="_blank" rel="noopener noreferrer" class="">https://blog.csdn.net/qq_34018840/article/details/89853119</a></p>
<p><strong>AUFS、Btrfs、Device mapper、OverlayFS、ZFS</strong></p>
<p>最終的に私は OverlayFS のアップグレード版である Overlay2 を選びました。私が元々使っていたドライバモードは Device mapper でした——docker info を実行すると一番下にヒント情報が表示され、Device mapper というモードはテスト環境で自動選択されたものにすぎず、本番環境では自分で切り替えてよいと教えてくれます。これで Overlay2 に切り替える決心がいっそう固まりましたが、急いで切り替えることはせず、まずこの不思議な現象の調査を続けました。ホストを再起動した後、なぜ docker は /home/docker_data 配下のイメージファイルを読み取れないのでしょうか？</p>
<p>google、百度、github、知乎、フォーラム、簡書、公式ドキュメントを探してもこの問題は見つからず、最後に docker の公式コミュニティで、私とほぼ同じ状況に遭遇した海外の方を見つけました。彼らの議論ではこれはバージョンの問題だとのことで、私は「なるほど、では新しめのバージョンを入れる必要があるようだ」と考えました。</p>
<p>そこで docker を完全に削除してから再インストールしました（注意：docker はバージョン 1.13 以降、タイムラインベースのバージョン番号を採用しており、コミュニティ版 CE とエンタープライズ版 EE に分かれています）。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># 旧バージョンの docker を完全にアンインストール</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">yum remove </span><span class="token function" style="color:hsl(207, 82%, 66%)">docker</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">yum remove docker-selinux</span><br></div></code></pre></div></div>
<p>docker-ce-17.12.0.ce バージョン（比較的安定したバージョンだと聞きました）を再インストールし、/etc/docker/daemon.json を設定しました。</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="505" height="78" class="img_ev3q"></p>
<p>docker を起動すると、私の linux カーネルが overlay2 ストレージドライバ方式をサポートしていないことが判明したため、カーネルをアップグレードしました。サポートには 4.0 以上のカーネルが必要です。</p>
<p>その後 docker info を実行すると、今度はディスクが d_type をサポートしていないというヒントが表示されました。そこでマウントディスクをフォーマットし直し、ftype=1 の xfs にフォーマットしました。</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-background-color:hsl(220, 13%, 18%);--prism-color:hsl(220, 14%, 71%)"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="background-color:hsl(220, 13%, 18%);color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token comment" style="color:hsl(220, 10%, 40%)"># ファイルシステム情報を確認。ftype=1 は d_type をサポートしていることを示す</span><span class="token plain"></span><br></div><div class="token-line" style="color:hsl(220, 14%, 71%);text-shadow:0 1px rgba(0, 0, 0, 0.3)"><span class="token plain">xfs_info /home</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" src="https://tanzhuo.xyz/ja/assets/images/2019-06-image-5-58cf24accf2b008eebe5ea5ec87f3add.png" width="818" height="180" class="img_ev3q"></p>
<p>そして docker のデフォルトイメージ保存場所を、この d_type をサポートするデータマウントディスクに設定し、docker を再起動しました。</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="415" height="138" class="img_ev3q"></p>
<p>再度 docker info を実行すると警告情報は一切なくなり、d_type は有効になり、ストレージ方式も overlay2 に変更されていました。</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="438" height="268" class="img_ev3q"></p>
<p>今回はもう完璧だと感じました。なにしろ docker info を実行しても警告が 1 つもないのですから。すぐに tomcat イメージをダウンロードしてみると、イメージは指定したフォルダに置かれています。素晴らしい！すぐさま reboot でホストを再起動しました。</p>
<p><strong>再起動後、docker images のイメージがまた消えました！！！</strong></p>
<p><strong>私は誰で、何をしていて、こんなに頑張って一体何をしているのでしょうか？</strong></p>
<p>本当にお手上げでした。どのステップで何を間違えたのか分かりません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="最終的な解決">最終的な解決<a href="https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing#%E6%9C%80%E7%B5%82%E7%9A%84%E3%81%AA%E8%A7%A3%E6%B1%BA" class="hash-link" aria-label="最終的な解決 への直接リンク" title="最終的な解決 への直接リンク" translate="no">​</a></h2>
<p>最後に私はこう解決しました。データディスクを /var/lib/docker フォルダに直接マウントし、起動時の自動マウントを設定したうえで、/etc/docker/daemon.json 内の data-root の設定を削除しました。</p>
<p><img decoding="async" loading="lazy" src="data:image/png;base64,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" width="505" height="78" class="img_ev3q"></p>
<p>その後ホストを再起動して docker images を実行すると、イメージが表示され、コンテナも自動的に起動していました。今度は停電もシャットダウンも怖くありません。ホストの再起動後、docker は自動的にコンテナを起動してくれます。</p>
<p>しかしその後アリババクラウドのサーバーに乗り換えたところ、何事もなくすべて正常だったので、もしかするとやはりハードウェアの問題だったのかもしれません。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小まとめ">小まとめ<a href="https://tanzhuo.xyz/ja/2019/06/05/centos-docker-xiu-gai-mo-ren-jing-xiang-cun-chu-lu-jing#%E5%B0%8F%E3%81%BE%E3%81%A8%E3%82%81" class="hash-link" aria-label="小まとめ への直接リンク" title="小まとめ への直接リンク" translate="no">​</a></h2>
<p>イメージ保存パスの変更は --graph パラメータを 1 つ追加するだけの話に見えますが、実際にはストレージドライバ、カーネルバージョン、ファイルシステムの d_type サポートといった一連の問題を芋づる式に引き起こしました。さんざん格闘した末の私の最終案は、逆転の発想でした。docker のパス設定は変えず、データディスクを /var/lib/docker に直接マウントして起動時の自動マウントを設定するのです。これで再起動後もイメージとコンテナは正常に復旧できます。もしあなたも再起動後にイメージが消える状況に遭遇したら、まずストレージドライバとディスクマウントのタイミングの問題を調べてみてください。格安サーバー自体のハードウェアの不具合という可能性も否定できません。クラウド事業者選びは、やはり安さを追いすぎないほうがよいでしょう。</p>]]></content:encoded>
            <category>技術</category>
        </item>
        <item>
            <title><![CDATA[hello、Ghost]]></title>
            <link>https://tanzhuo.xyz/ja/2019/06/04/first_post</link>
            <guid>https://tanzhuo.xyz/ja/2019/06/04/first_post</guid>
            <pubDate>Tue, 04 Jun 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[docker でいくつものコンテナサービスをデプロイしたついでに、オープンソースの ghost ブログシステムも立ち上げました。技術的な知識や日々の生活のあれこれを記録していきます~]]></description>
            <content:encoded><![CDATA[<p>docker でいくつものコンテナサービスをデプロイしたついでに、オープンソースの ghost ブログシステムも立ち上げました。技術的な知識のメモや、日々の生活のあれこれを記録していこうと思います~</p>
<!-- -->
<p><img decoding="async" loading="lazy" src="data:image/jpeg;base64,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" width="156" height="158" class="img_ev3q"></p>]]></content:encoded>
            <category>ライフ</category>
        </item>
    </channel>
</rss>