{
    "version": "https://jsonfeed.org/version/1",
    "title": "別在圍城時走散",
    "subtitle": "",
    "icon": "https://8loser.github.io/assets/favicon.ico",
    "description": "我在八樓閒晃",
    "home_page_url": "https://8loser.github.io",
    "items": [
        {
            "id": "https://8loser.github.io/2026/02/10/linux-hid-permission/",
            "url": "https://8loser.github.io/2026/02/10/linux-hid-permission/",
            "title": "Linux HID 裝置權限問題",
            "date_published": "2026-02-09T17:21:00.000Z",
            "content_html": "<h1 id=\"問題描述\"><a class=\"anchor\" href=\"#問題描述\">#</a> 問題描述</h1>\n<p>ErgoKB 在 Chrome 進行 Remap 時，選擇 HID 裝置後出現 <code>Could not open</code> 錯誤訊息。</p>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<ul>\n<li>OS: EndeavourOS</li>\n<li>鍵盤: ErgoKB Phoenix v2 分離式人體工學機械鍵盤</li>\n</ul>\n<h1 id=\"diagnosis\"><a class=\"anchor\" href=\"#diagnosis\">#</a> Diagnosis</h1>\n<p>查看 HID 裝置權限，全部都只有 root 才有讀寫權限，應該是權限問題（這邊看不出每個 HID 是什麼裝置）</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">ls</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -l</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/hidraw</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">*</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">crw-------</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 243,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  2月</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  9</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 12:50</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/hidraw0</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">crw-------</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 243,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  2月</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  9</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 12:50</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/hidraw1</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">crw-------</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 243,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 2</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  2月</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  9</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 12:50</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/hidraw2</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">crw-------</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 243,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 3</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  2月</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  9</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 12:50</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/hidraw3</span></span></code></pre>\n<h1 id=\"solution\"><a class=\"anchor\" href=\"#solution\">#</a> Solution</h1>\n<p>查看裝置 VID, PID, 得知 VID/PID 為 <code>1209:2304</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">lsusb</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 001</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 001:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1d6b:0002</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Linux</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Foundation</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 2.0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> hub</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 001</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 003:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 046d:c52b</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Logitech,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Inc.</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Unifying</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Receiver</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 001</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 004:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 8087:0029</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Intel</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Corp.</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> AX200</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Bluetooth</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 001</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 007:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1209:2304</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Generic</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Phoenix-v2</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 002</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 001:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1d6b:0003</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Linux</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Foundation</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 3.0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> hub</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 003</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 001:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1d6b:0002</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Linux</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Foundation</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 2.0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> hub</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Bus</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 004</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Device</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 001:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1d6b:0003</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Linux</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Foundation</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 3.0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> hub</span></span></code></pre>\n<p>建立裝置專用 rule 檔案 <code>/etc/udev/rules.d/99-ergokb.rules</code></p>\n<p>寫入設定</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">KERNEL</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">hidraw*</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> SUBSYSTEM</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">hidraw</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  ATTRS&#123;idVendor&#125;==</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">1209</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ATTRS&#123;idProduct&#125;==</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">2304</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MODE=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">0666</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span></code></pre>\n<p><strong>說明：</strong></p>\n<ul>\n<li>VID/PID 為剛剛查詢到的 <code>1209:2304</code></li>\n<li><code>MODE=&quot;0666&quot;</code> 設定裝置權限為 <code>rw-rw-rw-</code>，允許所有使用者讀寫</li>\n<li>原本裝置權限是 <code>crw------- (600)</code>，只有 root 可以存取</li>\n<li>改為 <code>0666</code> 後，一般使用者（包含 Chrome）也能存取 HID 裝置</li>\n</ul>\n<p>重新載入設定</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> udevadm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> control</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --reload-rules</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 重新載入 udev 規則檔案</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> udevadm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> trigger</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">                  # 觸發事件，將新規則應用到現有設備</span></span></code></pre>\n<ul>\n<li><code>udevadm control --reload-rules</code>：重新讀取 <code>/etc/udev/rules.d/</code> 目錄下的所有規則檔案</li>\n<li><code>udevadm trigger</code>：觸發 udev 事件，讓系統重新掃描所有設備並套用新規則</li>\n</ul>\n<p>執行完後 <span class=\"spoiler\" title=\"你知道得太多了\">如果還是不行我勸你鍵盤拔掉重插啦</span>，重新開啟 remap 頁面即可正常使用。 🎉</p>\n<p>對了，如果在 Linux OS, 這篇就有可能遇到這個問題; <a href=\"/2025/06/23/node-usb-relay/\">Node.JS 控制 USB 繼電器</a></p>\n",
            "tags": [
                "Trouble",
                "Linux",
                "HID",
                "udev"
            ]
        },
        {
            "id": "https://8loser.github.io/2026/02/07/rag-tutorial/",
            "url": "https://8loser.github.io/2026/02/07/rag-tutorial/",
            "title": "RAG 教學",
            "date_published": "2026-02-07T02:00:00.000Z",
            "content_html": "<h1 id=\"架構\"><a class=\"anchor\" href=\"#架構\">#</a> 架構</h1>\n<p>RAG（Retrieval-Augmented Generation; 檢索增強生成）</p>\n<ul>\n<li>\n<p>R - Retrieval (檢索)<br />\n資料端： 原始文件經過 Embedding Model 轉為向量，存入 Vector DB。<br />\n查詢端： 使用者問題同樣轉為向量，去資料庫進行相似度檢索（Similarity Search）。</p>\n</li>\n<li>\n<p>A - Augmentation (增強)<br />\n組合： 將檢索出來的「外部知識（Context）」與「原始問題（Query）」放入一個 Prompt Template 中。<br />\n任務： 這個動作是為了讓 LLM 擁有它原本訓練數據中沒有的特定資訊。</p>\n</li>\n<li>\n<p>G - Generation (生成)<br />\n輸出： 將加強過的 Prompt 丟給 LLM。<br />\n回覆： LLM 根據給定的參考資料產出精準的答案。</p>\n</li>\n</ul>\n<p>我理解的架構圖<img loading=\"lazy\" src=\"rag-diagram.png\" alt=\"RAG diagram\" /></p>\n<details class=\"danger\"><summary>Gemini 畫的啥玩意兒</summary><div>\n<p><img loading=\"lazy\" src=\"rag-diagram-gemini.png\" alt=\"RAG diagram - Gemini\" /></p>\n</div></details>\n<h1 id=\"需求\"><a class=\"anchor\" href=\"#需求\">#</a> 需求</h1>\n<ul>\n<li>embed model：將聲音、圖片、影片、文字等轉成向量數值使用</li>\n<li>vector db：儲存向量數據及對應內容</li>\n<li>LLM：生成式 AI</li>\n</ul>\n<h1 id=\"embedding\"><a class=\"anchor\" href=\"#embedding\">#</a> Embedding</h1>\n<p>使用 Sentence-Transformers 載入 embed model，將文字轉成高維度向量特徵。</p>\n<details class=\"info\"><summary>安裝依賴套件</summary><div>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pip</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sentence-transformers</span></span></code></pre>\n</div></details>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-python\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 加上 huggingface 的鏡像站點環境變數，避免下載模型太慢</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> os</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">os</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">environ</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">HF_ENDPOINT</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">https://hf-mirror.com</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> sentence_transformers </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> SentenceTransformer</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 載入模型</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">model </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> SentenceTransformer</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">all-MiniLM-L6-v2</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 準備文字</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">sentences </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">這是一段測試文字</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">EndeavourOS 開發環境</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 取得 Feature Vector</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">embeddings </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> model</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">encode</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">sentences</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 查看 Shape [句子數量, 每個句子的維度]</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 輸出會是 (2, 384)，代表 2 個句子，每個句子 384 維, 是 all-MiniLM-L6-v2 模型的特徵維度。</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"向量形狀 (embeddings.shape): </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">embeddings</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">shape</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 查看具體數值 (只印出第一句的前 5 個特徵值作為範例)</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"第一句的前 5 個特徵值: </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">embeddings</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">5</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 執行結果</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 向量形狀 (embeddings.shape): (2, 384)</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 第一句的前 5 個特徵值: [0.03097137 0.09432378 0.04806018 0.02745436 0.02937624]</span></span></code></pre>\n<div class=\"note info\">\n<ul>\n<li>除了 Sentence-Transformers 也可以用 Ollama 載入 embed model，呼叫 Ollama API 取得向量值</li>\n<li>選對模型很重要，改成 <code>paraphrase-multilingual-MiniLM-L12-v2</code> 模型，對中文理解程度比較高</li>\n<li>聲音、圖片、影片、文字轉為高維度向量特徵，這個過程叫 <code>embedding</code></li>\n</ul>\n</div>\n<h1 id=\"vector-db\"><a class=\"anchor\" href=\"#vector-db\">#</a> Vector DB</h1>\n<p>將向量與內容存入 vector db，後續可以搜尋跟問題相似度高的內容，這裡選用 Qdrant</p>\n<details class=\"info\"><summary>安裝依賴套件</summary><div>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pip</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sentence-transformers</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> qdrant-client</span></span></code></pre>\n</div></details>\n<details class=\"info\"><summary>可以用 docker 或 podman 啟動 Qdrant</summary><div>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">podman</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> run</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 6333:6333</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 6334:6334</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -v</span><span style=\"color:#999999;--shiki-dark:#666666\"> $</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#998418;--shiki-dark:#B8A965\">pwd</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/qdrant_storage:/qdrant/storage:z</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    docker.io/qdrant/qdrant</span></span></code></pre>\n</div></details>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-python\"><span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> os</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">os</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">environ</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">HF_ENDPOINT</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">https://hf-mirror.com</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> sentence_transformers </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> SentenceTransformer</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> qdrant_client </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> QdrantClient</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> qdrant_client</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">models </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> Distance</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> VectorParams</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> PointStruct</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 載入 Embed Model, 改用 paraphrase-multilingual-MiniLM-L12-v2 對中文理解程度比較好</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">model </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> SentenceTransformer</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">paraphrase-multilingual-MiniLM-L12-v2</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 建立 Qdrant 客戶端</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">client </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> QdrantClient</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">127.0.0.1</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> port</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">6333</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># collection 不存在則建立</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># size 為 384, 必須跟 embed model 維度搭配</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">collection_name </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">multilingual_notes</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">if</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> not</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> client</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">collection_exists</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">collection_name</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    client</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">create_collection</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">        collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">        vectors_config</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">VectorParams</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">size</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">384</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> distance</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Distance</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">COSINE</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 準備寫入的資料</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">documents </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">id</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">EndeavourOS 使用 pacman 作為套件管理器。</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">id</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 2</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Python 的虛擬環境可以透過 venv 模組建立。</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">id</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 3</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Vector Database 負責儲存高維度的向量數據。</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">id</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 4</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">等春節特賣叫隔壁王叔叔買給你</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">id</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 5</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">牛仔褲特賣會在下週末舉行，別忘了去看看！</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 批次產生每個內容的向量, 利用 GPU 加速批次處理省效能</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">texts </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">doc</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> doc </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">in</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> documents</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">vectors </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> model</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">encode</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">texts</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 建立向量與內容資料點</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">points </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> i</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> doc </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">in</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> enumerate</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">documents</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    points</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">append</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">PointStruct</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">        id</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">doc</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">id</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">        vector</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">vectors</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">i</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">tolist</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">(</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">        payload</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">page_content</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> doc</span><span style=\"color:#a13865;--shiki-dark:#d9739f\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">text</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#a13865;--shiki-dark:#d9739f\">]</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">&#125;</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 新增資料點到 Qdrant</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">client</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">upsert</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> points</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">points</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 語義搜尋測試</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query_text </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">媽媽我好想要一台 Steam Deck</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query_vector </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> model</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">encode</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query_text</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">tolist</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">search_result </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> client</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query_points</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">    collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">    query</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query_vector</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    # 搜尋相似度最高的 5 筆</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">    limit</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">5</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">points</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 顯示搜尋結果</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"--- 針對「</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query_text</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">」搜尋到前 </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#998418;--shiki-dark:#B8A965\">len</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">search_result</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">)</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 筆相關資料 ---\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> i</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> res </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">in</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> enumerate</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">search_result</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    content </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> res</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">payload</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">page_content</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    score </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> res</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">score</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">i</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">+</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">1</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">. [分數: </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">score</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">:.4f</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">] </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">content</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 執行結果</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># --- 針對「媽媽我好想要一台 Steam Deck」搜尋到前 5 筆相關資料 ---</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 1. [分數: 0.2213] 等春節特賣叫隔壁王叔叔買給你</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 2. [分數: 0.1929] 牛仔褲特賣會在下週末舉行，別忘了去看看！</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 3. [分數: -0.0284] Python 的虛擬環境可以透過 venv 模組建立。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 4. [分數: -0.0446] Vector Database 負責儲存高維度的向量數據。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 5. [分數: -0.0561] EndeavourOS 使用 pacman 作為套件管理器。</span></span></code></pre>\n<div class=\"note info\">\n<ul>\n<li>餘弦相似度: 數值在 <em><strong>-1 ~ 1</strong></em> 之間 分數越高越好, 代表語義越接近。可以調整 distance=Distance.COSINE 修改成其他演算法。</li>\n<li>Vector db 選型須根據使用的 embed model，需要可以儲存該 model 使用的維度</li>\n<li>可以建立不同維度的 collection</li>\n</ul>\n</div>\n<details class=\"success\"><summary>Qdrant 有 web 界面</summary><div>\n<p><img loading=\"lazy\" src=\"qdrant.png\" alt=\"Qdrant\" /></p>\n</div></details>\n<h1 id=\"rag\"><a class=\"anchor\" href=\"#rag\">#</a> RAG</h1>\n<p>要進行 RAG 還差 LLM (Large Language Model), LLM 屬於 RAG 的 G (Generate), 這邊使用 Ollama 載入 llama3</p>\n<details class=\"info\"><summary>安裝依賴套件</summary><div>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pip</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> langchain-huggingface</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> langchain-qdrant</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> langchain-ollama</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> langchain-core</span></span></code></pre>\n</div></details>\n<details class=\"info\"><summary>在 Arch Linux 安裝 Ollama</summary><div>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">yay</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ollama-cuda</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> systemctl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> start</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ollama</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 檢查是否啟用成功</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">systemctl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> is-active</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ollama</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 載入 llama3 模型</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">ollama</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> run</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> llama3</span></span></code></pre>\n</div></details>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-python\"><span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> os</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 鏡像加速（如果模型已下載則會自動跳過）</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">os</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">environ</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">HF_ENDPOINT</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">https://hf-mirror.com</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> langchain_huggingface </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> HuggingFaceEmbeddings</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> langchain_qdrant </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> QdrantVectorStore</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> langchain_ollama </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> ChatOllama</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> langchain_core</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">prompts </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> ChatPromptTemplate</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> langchain_core</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">runnables </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> RunnablePassthrough</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">from</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> langchain_core</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">output_parsers </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">import</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> StrOutputParser</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 統一模型名稱，要跟寫入 vector db 時使用的模型一致</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">EMBEDDING_MODEL_NAME</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">paraphrase-multilingual-MiniLM-L12-v2</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">embeddings </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> HuggingFaceEmbeddings</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">model_name</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">EMBEDDING_MODEL_NAME</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Retrieval - 用於檢索的向量資料庫</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">vector_store </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> QdrantVectorStore</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">from_existing_collection</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">    embedding</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">embeddings</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">    collection_name</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">multilingual_notes</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">    url</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">http://127.0.0.1:6333</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Augmentation - 定義詢問 LLM 的 prompt 模板</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">template </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"\"\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">你是一個基於本地知識庫的 AI 助手。</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">請根據以下提供的「參考資料」回答「問題」。</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">如果參考資料中沒有相關答案，請誠實告知你不知道。</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">[參考資料]:</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">&#123;context&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">[問題]:</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">用中文回答 `</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">&#123;question&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">`</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"\"\"</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">prompt </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> ChatPromptTemplate</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">from_template</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">template</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 檢索設定, 檢索相似度最高的 2 筆資料</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">retriever </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> vector_store</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">as_retriever</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">search_kwargs</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">k</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 2</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 文檔格式化函數</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">def</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> format_docs</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">docs</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    return</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">join</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">doc</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">page_content </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> doc </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">in</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> docs</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Generation - 使用的生成模型</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">llm </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> ChatOllama</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">model</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">llama3</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> base_url</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">http://localhost:11434</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 構建 RAG 鏈, 執行：搜尋 Qdrant -> 填入模板 -> 丟給 Llama3 -> 輸出文字</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">rag_chain </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">        \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">context</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> retriever </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">|</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> format_docs</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">      # 從向量庫檢索並格式化文檔</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">        \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">question</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> RunnablePassthrough</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">(</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">)</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        # 原樣傳遞用戶問題</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">    |</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> RunnablePassthrough</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">assign</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        # prompt - 接收 &#123;context, question&#125;，生成格式化的提示詞</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        # llm - 將提示詞發送給 Llama3 模型，得到 AI 回答</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        # StrOutputParser() - 將 LLM 的輸出（通常是個 AIMessage 對象）轉換成純文字字符串</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        # | 是 LangChain 的 LCEL 語法</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">        answer</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">prompt </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">|</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> llm </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">|</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> StrOutputParser</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">(</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">)</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 執行對話測試</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">if</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> __name__</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> ==</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">__main__</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    query </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">EndeavourOS 適合什麼樣的開發者？</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">提問：</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    </span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    # R - Retrieval：從向量庫檢索相關文檔</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">----------R Retrieval 檢索階段----------</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    docs </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> retriever</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">invoke</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    formatted_context </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> format_docs</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">docs</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"檢索到的參考資料（</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#998418;--shiki-dark:#B8A965\">len</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">docs</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">)</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 筆）：</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">formatted_context</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    </span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    # A - Augmentation：將檢索結果填入提示詞</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">----------A Augmentation 增強階段----------</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    augmented_prompt </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> prompt</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">invoke</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">context</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> formatted_context</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">question</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> query</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"生成的提示詞：</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">augmented_prompt</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">to_string</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">(</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">)</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    </span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    # G - Generation：LLM 生成回答</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">----------G Generation 生成階段----------</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    result </span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> rag_chain</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">invoke</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">query</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    print</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">f</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"AI 回答：</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">result</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">answer</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 執行結果</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 提問：EndeavourOS 適合什麼樣的開發者？</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># ----------R Retrieval 檢索階段----------</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 檢索到的參考資料（2 筆）：</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># EndeavourOS 使用 pacman 作為套件管理器。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Python 的虛擬環境可以透過 venv 模組建立。</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># ----------A Augmentation 增強階段----------</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 生成的提示詞：</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Human: 你是一個基於本地知識庫的 AI 助手。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 請根據以下提供的「參考資料」回答「問題」。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 如果參考資料中沒有相關答案，請誠實告知你不知道。</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># [參考資料]:</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># EndeavourOS 使用 pacman 作為套件管理器。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Python 的虛擬環境可以透過 venv 模組建立。</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># [問題]:</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 用中文回答 `EndeavourOS 適合什麼樣的開發者？`</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># ----------G Generation 生成階段----------</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># AI 回答：</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 根據提供的參考資料，EndeavourOS 使用 pacman 作為套件管理器。因此，我們可以知道 EndeavourOS 是一種 Linux 發行版。</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 結論：EndeavourOS 适合 Linux 開發者的需求。</span></span></code></pre>\n<div class=\"note info\">\n<ul>\n<li>如果原本用 A 模型建立的 db 後來想改 B 模型怎麼辦? 要進行 Re-indexing</li>\n<li>後續可以增加避免幻覺機制，相似度閥值判定, 參考資料不足處理機制等等...</li>\n</ul>\n</div>\n",
            "tags": [
                "Tutorial",
                "RAG",
                "AI",
                "檢索增強生成"
            ]
        },
        {
            "id": "https://8loser.github.io/2025/12/21/deploy-docker-nginx/",
            "url": "https://8loser.github.io/2025/12/21/deploy-docker-nginx/",
            "title": "多服務 Docker 部署與 Nginx 設定筆記",
            "date_published": "2025-12-21T00:58:03.000Z",
            "content_html": "<h1 id=\"文章說明\"><a class=\"anchor\" href=\"#文章說明\">#</a> 文章說明</h1>\n<p>這是將服務部署到雲端的專案。<br />\nVM 建立好後，將專案 pull 到 <code>/opt</code> 下即可快速部署。</p>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<p>每個服務程式碼由 GitHub 託管，並設置自動打包成 Docker image。<br />\n機器安裝 Docker，部署時 pull Docker image 啟動多個服務 container。<br />\n由 Nginx 控制請求進入服務。</p>\n<h1 id=\"專案資料夾結構\"><a class=\"anchor\" href=\"#專案資料夾結構\">#</a> 專案資料夾結構</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>.</span></span>\n<span class=\"line\"><span>├── init.sh                # 機器初始化</span></span>\n<span class=\"line\"><span>├── prod</span></span>\n<span class=\"line\"><span>│   ├── docker-compose.yml # 正式站 Docker Swarm Stack 設定</span></span>\n<span class=\"line\"><span>│   └── nginx.conf         # 正式站 Nginx 設定</span></span>\n<span class=\"line\"><span>├── start-prod.sh          # 啟動</span></span>\n<span class=\"line\"><span>└── stop-prod.sh           # 停止</span></span></code></pre>\n<p>首先執行 <code>init.sh</code> 更新系統、安裝常用程式，參考 <a href=\"/2023/01/04/gcp-init/\">GCP VM 建置筆記</a>。</p>\n<h1 id=\"start-prodsh\"><a class=\"anchor\" href=\"#start-prodsh\">#</a> <a href=\"http://start-prod.sh\">start-prod.sh</a></h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-sh\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">#!/bin/sh</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 啟動正式站 container</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">docker</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> stack</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> deploy</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --compose-file</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> prod/docker-compose.yml</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --with-registry-auth</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> company</span></span></code></pre>\n<p>這邊沒什麼好介紹的，就是用 <code>docker stack deploy</code> 啟動 Docker Swarm Stack。</p>\n<p><code>--with-registry-auth</code> 會把本機 <code>docker login</code> 的 registry 認證一併傳給 Swarm 節點，<br />\n讓節點在拉私有 image 時能順利通過授權；不加的話，私有倉庫常會出現拉不到 image 的錯誤。</p>\n<h1 id=\"docker-composeyml\"><a class=\"anchor\" href=\"#docker-composeyml\">#</a> docker-compose.yml</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 正式站 container</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">version</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">3.8</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">services</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  nginx</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    image</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> nginx</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    restart</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> unless-stopped</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    volumes</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">      # Nginx 設定檔</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">      -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ./nginx.conf:/etc/nginx/conf.d/default.conf</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    environment</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      TZ</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Asia/Taipei</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    ports</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">      -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> target</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 80</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        published</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 80</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        protocol</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> tcp</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        mode</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> host</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 後端 API</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  backend</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    image</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ghcr.io/8loser/backend:main</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    restart</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> unless-stopped</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    environment</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      TZ</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Asia/Taipei</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 前端</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  frontend</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    image</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ghcr.io/8loser/frontend:main</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    restart</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> unless-stopped</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    environment</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      TZ</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Asia/Taipei</span></span></code></pre>\n<p>說明：</p>\n<ul>\n<li>有 3 個服務; Nginx、frontend、backend。</li>\n<li>Nginx 不用 <code>depends_on backend, frontend</code> 避免某個服務異常導致 Nginx 被牽連停止。</li>\n<li>Nginx <code>mode: host</code> 直接用主機 80 對外提供服務，Load Balancer 流量導入 Nginx 80 port，僅有跑該服務的節點會開 80。</li>\n<li>Nginx 設定檔直接掛載 <code>prod/nginx.conf</code> 檔案。</li>\n<li>Stack 會使用 Swarm overlay network，<code>proxy_pass http://frontend/</code> 與 <code>proxy_pass http://backend/</code> 會解析到同一個 Stack 的 service。</li>\n<li><code>ghcr.io</code> 是 GitHub Container Registry，用於存放 Docker image。</li>\n</ul>\n<h1 id=\"nginxconf\"><a class=\"anchor\" href=\"#nginxconf\">#</a> nginx.conf</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-nginx\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 正式站 Nginx</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 預設 server; 使用 IP 或未匹配的域名都會進入</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">server</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  listen </span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">80</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  server_name </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">_</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 不合法 Host 訪問 /，不返回內容</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> / </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">      return</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 204</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">server</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  listen </span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">80</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 允許的域名</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  server_name </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">www.*******.com *******.com</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # Load Balancer Public IP address</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  set_real_ip_from </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">&#x3C;</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">LB_IP</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">/32</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # 填入你的 LB IP</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # Google CIDR 網段</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  set_real_ip_from </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">130.211.0.0/22</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  set_real_ip_from </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">35.191.0.0/16</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  real_ip_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  real_ip_recursive on</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 阻擋的 User Agent</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  if</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">http_user_agent</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> ~* </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"ApacheBench|pingback|YisouSpider|MJ12bot|AhrefsBot|360JK|Jorgee|FeedDemon|BOT/0.1 (BOT for JCE)|CrawlDaddy|Java|Jullo|Feedly|UniversalFeedParser|Swiftbot|YandexBot|jikeSpider|ZmEu phpmyadmin|WinHttp|EasouSpider|HttpClient|Microsoft URL Control|YYSpider|jaunty|oBot|Python-urllib|Indy Library|FlightDeckReports Bot|Linguee Bot\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">return</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 101</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">;</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 正式站設定 robots.txt 允許爬蟲</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> =</span><span style=\"color:#AB5E3F;--shiki-dark:#C4704F\"> /robots.txt </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    add_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Content-Type text/plain</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    return</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 200</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> \"User-agent: *</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Allow: /</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\n</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 前端</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> / </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_pass </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">http://frontend/</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Host </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">host</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Real-IP </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">remote_addr</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">proxy_add_x_forwarded_for</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Custom-Referrer smc_identity_layer</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 後端</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> /api/ </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_pass </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">http://backend/</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Host </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">host</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Real-IP </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">remote_addr</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">proxy_add_x_forwarded_for</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Custom-Referrer smc_identity_layer</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<p>說明：</p>\n<ul>\n<li>預設 server 用於攔截未匹配的 Host，直接回 204。</li>\n<li><code>/</code> 走 frontend、<code>/api/</code> 走 backend，使用 <code>proxy_pass</code> 轉發請求。</li>\n<li><code>set_real_ip_from</code> 用來限定可信任的轉發來源（LB 與 Google CIDR），才能正確還原 client IP，避免被偽造的 <code>X-Forwarded-For</code> 影響。</li>\n<li><code>proxy_pass http://backend/</code> 的 <code>backend</code> 對應 <code>docker-compose.yml</code> 的 service 名稱。</li>\n</ul>\n",
            "tags": [
                "Deploy",
                "GCP",
                "Nginx",
                "log",
                "Docker",
                "Deploy"
            ]
        },
        {
            "id": "https://8loser.github.io/2025/06/23/node-usb-relay/",
            "url": "https://8loser.github.io/2025/06/23/node-usb-relay/",
            "title": "Node.JS 控制 USB 繼電器",
            "date_published": "2025-06-23T06:36:42.000Z",
            "content_html": "<h1 id=\"usb-relay\"><a class=\"anchor\" href=\"#usb-relay\">#</a> USB Relay</h1>\n<p>USB relay 模組就是這個，預計用 Node.js 程式輸出電氣訊號控制機械手臂，這個是兩路的（兩個 relay），還有一路或者更多路的。</p>\n<p><img loading=\"lazy\" src=\"USB-relay.png\" alt=\"USB Relay\" /></p>\n<p>輸出接點跟一般繼電器的接點一樣</p>\n<table>\n<thead>\n<tr>\n<th>端點</th>\n<th>全名</th>\n<th>功能描述</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>COM</td>\n<td>Common（共用端）</td>\n<td>中心端點，根據控制情況與 NC 或 NO 接通</td>\n</tr>\n<tr>\n<td>NC</td>\n<td>Normally Closed（常閉）</td>\n<td>在 <code>未通電</code> 狀態下，<code>與 COM 接通</code></td>\n</tr>\n<tr>\n<td>NO</td>\n<td>Normally Open（常開）</td>\n<td>在 <code>未通電</code> 狀態下，<code>與 COM 斷開</code>，通電後才接通</td>\n</tr>\n</tbody>\n</table>\n<p>接線方式使用 USB-A 轉 USB-B，USB-A 接電腦，USB-B 接模組</p>\n<p><img loading=\"lazy\" src=\"USB-A-to-USB-B.png\" alt=\"USB-A to USB-B\" /></p>\n<h1 id=\"程式測試\"><a class=\"anchor\" href=\"#程式測試\">#</a> 程式測試</h1>\n<p>使用 <a href=\"https://www.npmjs.com/package/node-hid\">node-hid</a> 套件，請先確定使用的模組支援 HID</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">const</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> HID</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> require</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">node-hid</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 尋找連接裝置中 USBRelay 名稱的裝置</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">const</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> deviceList</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> HID</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">devices</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">const</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> connectedRelays</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> deviceList</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">filter</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#999999;--shiki-dark:#666666\">></span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">product</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> &#x26;&#x26;</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">product</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">indexOf</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">USBRelay</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> !==</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> -</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">const</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> device</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> new</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> HID</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">HID</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">connectedRelays</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">path</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 啟動第一個 relay</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">sendFeatureReport</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x01</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 啟動第二個 relay</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">sendFeatureReport</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x02</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 關閉第一個 relay</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">sendFeatureReport</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x01</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 關閉第二個 relay</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">sendFeatureReport</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x02</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span></code></pre>\n<p>如果正常啟動繼電器時，模組上對應的繼電器旁 LED 會亮</p>\n<h1 id=\"設定-bytes-說明\"><a class=\"anchor\" href=\"#設定-bytes-說明\">#</a> 設定 Bytes 說明</h1>\n<table>\n<thead>\n<tr>\n<th>Byte 位置</th>\n<th>說明</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>命令標頭（通常是固定值）</td>\n</tr>\n<tr>\n<td>1</td>\n<td>控制命令</td>\n</tr>\n<tr>\n<td>2</td>\n<td>要控制的繼電器位元組（bitmask）</td>\n</tr>\n<tr>\n<td>3~8</td>\n<td>充填位元</td>\n</tr>\n</tbody>\n</table>\n<h1 id=\"程式設計思路\"><a class=\"anchor\" href=\"#程式設計思路\">#</a> 程式設計思路</h1>\n<ul>\n<li>使用 class</li>\n<li>constructor 進行 init, 可指定 device path 或自動搜尋</li>\n<li>每個 relay on/off 使用的 bytes 用 list 儲存</li>\n<li>setOff(channelNumber), setOn(channelNumber)</li>\n<li>根據機械手臂動作定義, ActionOK(), ActionNG()</li>\n<li>切為 on 之後多久復歸原狀態可調整</li>\n</ul>\n<h1 id=\"部份程式碼\"><a class=\"anchor\" href=\"#部份程式碼\">#</a> 部份程式碼</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 啟動第幾個 relay</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">setOn</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">number</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">    const</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> commandList</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // all</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFE</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第一個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x01</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第二個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x02</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第三個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x03</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第四個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x04</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第五個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x05</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第六個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x06</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第七個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x07</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第八個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFF</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x08</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    this</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">sendFeatureReport</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">commandList</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">number</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">// 關閉第幾個 relay</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">setOff</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">number</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">    const</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> commandList</span><span style=\"color:#999999;--shiki-dark:#666666\"> =</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">[</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // all</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFC</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第一個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x01</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第二個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x02</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第三個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x03</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第四個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x04</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第五個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x05</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第六個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x06</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第七個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x07</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">        // 第八個 relay</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        </span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0xFD</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x08</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0x00</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">]</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    this</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">device</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">sendFeatureReport</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">commandList</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">[</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">number</span><span style=\"color:#a65e2b;--shiki-dark:#d4976c\">]</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">ActionOK</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    // relay 1 作為 OK 訊號</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    this</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">setOn</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">1</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-javascript\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">ActionNG</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    // relay 2 作為 NG 訊號</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    this</span><span style=\"color:#999999;--shiki-dark:#666666\">.</span><span style=\"color:#59873A;--shiki-dark:#80A665\">setOn</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">2</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<h1 id=\"備註\"><a class=\"anchor\" href=\"#備註\">#</a> 備註</h1>\n<ul>\n<li>注意輸出控制可承受負載範圍，通常在繼電器上會標示</li>\n<li>需要 HID 支援的模組</li>\n<li>參考 <a href=\"https://github.com/josephdadams/USBRelay\">https://github.com/josephdadams/USBRelay</a></li>\n<li>模組由 USB 供電, 拔掉後 relay 狀態會重置</li>\n</ul>\n",
            "tags": [
                "Node.js",
                "USB Relay"
            ]
        },
        {
            "id": "https://8loser.github.io/2024/11/08/gcp-mutual-tls/",
            "url": "https://8loser.github.io/2024/11/08/gcp-mutual-tls/",
            "title": "GCP mTLS(mutual TLS) 憑證設定",
            "date_published": "2024-11-08T15:38:07.000Z",
            "content_html": "<h1 id=\"緣由\"><a class=\"anchor\" href=\"#緣由\">#</a> 緣由</h1>\n<p>有一網站需要限制只有特定人員才可瀏覽，通常會使用來源 IP 進行限制，若是無固定辦公室人員（業務）則使用 VPN 連入公司網路獲得允許 IP。</p>\n<p>但因為公司沒有自己的網路，所以沒辦法使用這個方法，因此採用 mTLS。</p>\n<p>一般網站憑證只是單向用於驗證網站是否合法，mTLS 則是雙向驗證 <span class=\"spoiler\" title=\"你知道得太多了\">雙向奔赴的憑證</span>，除了驗證網站是否合法外，也驗證連接端 (client, browser) 是否合法。</p>\n<p>官方文件其實有教學(<a href=\"https://cloud.google.com/load-balancing/docs/https/setting-up-mtls-ccm\">Set up mutual TLS with user-provided certificates</a>)，但是官方文件都是透過 gcloud 設定，所以這篇紀錄一下怎麼使用 GUI 方式設定。</p>\n<p>思路：從 load balance 下手，在 load balance 進行驗證，client 合法才導入後端。</p>\n<h1 id=\"需求\"><a class=\"anchor\" href=\"#需求\">#</a> 需求</h1>\n<p>先安裝 openssl 用於產生憑證</p>\n<h1 id=\"產生根憑證\"><a class=\"anchor\" href=\"#產生根憑證\">#</a> 產生根憑證</h1>\n<h2 id=\"憑證設定檔\"><a class=\"anchor\" href=\"#憑證設定檔\">#</a> 憑證設定檔</h2>\n<p>建立憑證設定檔 <code>example.cnf</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>[req]</span></span>\n<span class=\"line\"><span>distinguished_name = empty_distinguished_name</span></span>\n<span class=\"line\"><span>[empty_distinguished_name]</span></span>\n<span class=\"line\"><span># Kept empty to allow setting via -subj command line arg.</span></span>\n<span class=\"line\"><span>[ca_exts]</span></span>\n<span class=\"line\"><span>basicConstraints=critical,CA:TRUE</span></span>\n<span class=\"line\"><span>keyUsage=keyCertSign</span></span>\n<span class=\"line\"><span>extendedKeyUsage=clientAuth</span></span></code></pre>\n<h2 id=\"根憑證與密鑰\"><a class=\"anchor\" href=\"#根憑證與密鑰\">#</a> 根憑證與密鑰</h2>\n<p>使用 openssl 產生根憑證密鑰 <code>root.key</code> 與根憑證 <code>root.cert</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -x509</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -new</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -sha256</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -newkey</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> rsa:2048</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -nodes</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -days</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 3650</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -subj</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/CN=root</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -config</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> example.cnf</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -extensions</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ca_exts</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -keyout</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root.cert</span></span></code></pre>\n<h1 id=\"產生中繼憑證\"><a class=\"anchor\" href=\"#產生中繼憑證\">#</a> 產生中繼憑證</h1>\n<h2 id=\"中繼憑證簽名請求\"><a class=\"anchor\" href=\"#中繼憑證簽名請求\">#</a> 中繼憑證簽名請求</h2>\n<p>產生中繼憑證密鑰 <code>int.key</code> 跟中繼憑證簽名請求 <code>int.req</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -new</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -sha256</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -newkey</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> rsa:2048</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -nodes</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -subj</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/CN=int</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -config</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> example.cnf</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -extensions</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ca_exts</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -keyout</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> int.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> int.req</span></span></code></pre>\n<h2 id=\"中繼憑證\"><a class=\"anchor\" href=\"#中繼憑證\">#</a> 中繼憑證</h2>\n<p>產生中繼憑證 <code>int.cert</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> x509</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -CAkey</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -CA</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root.cert</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -set_serial</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -days</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 3650</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -extfile</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> example.cnf</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -extensions</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ca_exts</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#A65E2B;--shiki-dark:#C99076\">    -in</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> int.req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> int.cert</span></span></code></pre>\n<h1 id=\"建立信任設定\"><a class=\"anchor\" href=\"#建立信任設定\">#</a> 建立信任設定</h1>\n<p>從 <a href=\"https://console.cloud.google.com/security/ccm/list/trustConfigs\">https://console.cloud.google.com/security/ccm/list/trustConfigs</a> 建立信任設定<br />\n把前面步驟產生的 <code>root.cert</code> 內容增加到信任錨點、<code>int.cert</code> 內容增加到中繼 CA。</p>\n<p><img loading=\"lazy\" src=\"trust-config.jpg\" alt=\"信任設定\" /></p>\n<h1 id=\"建立用戶端驗證\"><a class=\"anchor\" href=\"#建立用戶端驗證\">#</a> 建立用戶端驗證</h1>\n<p>從 <a href=\"https://console.cloud.google.com/net-security/clientauth/serverTlsPolicies\">https://console.cloud.google.com/net-security/clientauth/serverTlsPolicies</a> 建立用戶端驗證<br />\n用戶端驗證模式選擇 <code>負載平衡</code> 以及 <code>拒絕無效的用戶憑證</code>，信任設定選取前一步驟建立的設定檔。<br />\n<img loading=\"lazy\" src=\"client-auth.jpg\" alt=\"用戶端驗證\" /></p>\n<h1 id=\"負載平衡設定\"><a class=\"anchor\" href=\"#負載平衡設定\">#</a> 負載平衡設定</h1>\n<p><a href=\"https://console.cloud.google.com/net-services/loadbalancing/list/loadBalancers\">https://console.cloud.google.com/net-services/loadbalancing/list/loadBalancers</a><br />\n在負載平衡的前端設定處，選取剛建立的用戶端驗證，可能不會馬上生效，如果生效後開啟網址會是 <code>ERR_CONNECTION_CLOSED</code> 的訊息。</p>\n<p><img loading=\"lazy\" src=\"load-balance.jpg\" alt=\"負載平衡\" /></p>\n<h1 id=\"建立用戶端憑證\"><a class=\"anchor\" href=\"#建立用戶端憑證\">#</a> 建立用戶端憑證</h1>\n<h2 id=\"用戶端設定檔\"><a class=\"anchor\" href=\"#用戶端設定檔\">#</a> 用戶端設定檔</h2>\n<p>建立用戶端設定檔 <code>client.config</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>[req]</span></span>\n<span class=\"line\"><span>default_bits              = 2048</span></span>\n<span class=\"line\"><span>req_extensions            = extension_requirements</span></span>\n<span class=\"line\"><span>distinguished_name        = dn_requirements</span></span>\n<span class=\"line\"><span>prompt                    = no</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>[extension_requirements]</span></span>\n<span class=\"line\"><span>basicConstraints          = critical, CA:FALSE</span></span>\n<span class=\"line\"><span>keyUsage                  = critical, nonRepudiation, digitalSignature, keyEncipherment</span></span>\n<span class=\"line\"><span>extendedKeyUsage          = clientAuth</span></span>\n<span class=\"line\"><span></span></span>\n<span class=\"line\"><span>[dn_requirements]</span></span>\n<span class=\"line\"><span>countryName               = US</span></span>\n<span class=\"line\"><span>stateOrProvinceName       = California</span></span>\n<span class=\"line\"><span>localityName              = San Francisco</span></span>\n<span class=\"line\"><span>0.organizationName        = example</span></span>\n<span class=\"line\"><span>organizationalUnitName    = test</span></span>\n<span class=\"line\"><span>commonName                = test.example.com</span></span>\n<span class=\"line\"><span>emailAddress              = test@example.com</span></span></code></pre>\n<h2 id=\"產生用戶密鑰與簽名請求\"><a class=\"anchor\" href=\"#產生用戶密鑰與簽名請求\">#</a> 產生用戶密鑰與簽名請求</h2>\n<p>會產生 <code>client.key</code>, <code>client.csr</code>。這邊會詢問金鑰密碼，後面匯出時會用到。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -new</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -keyout</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.csr</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -config</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.config</span></span></code></pre>\n<h2 id=\"產生用戶端憑證\"><a class=\"anchor\" href=\"#產生用戶端憑證\">#</a> 產生用戶端憑證</h2>\n<p>使用 <code>int.cert</code>、<code>int.key</code>、<code>client.csr</code> 產生用戶端憑證 <code>client.cert</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> x509</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -req</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -in</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.csr</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.cert</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -extfile</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.config</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -extensions</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> extension_requirements</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -days</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 365</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -CA</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> int.cert</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -CAkey</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> int.key</span></span></code></pre>\n<p>測試</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">curl</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -v</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -k</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --key</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --cert</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.cert</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://網址</span></span></code></pre>\n<h1 id=\"瀏覽器匯入憑證\"><a class=\"anchor\" href=\"#瀏覽器匯入憑證\">#</a> 瀏覽器匯入憑證</h1>\n<p>server 端大致上設置完成，還需設定 client 端，client 匯入憑證這邊使用 <code>PKCS #12</code> 格式。<br />\n首先將 <code>client.key</code>、<code>client.cert</code> 封裝。會產生 <code>client.p12</code>。<br />\n這邊會詢問剛剛產生 <code>client.key</code> 時的金鑰密碼，驗證後會再詢問金鑰匯出密碼，在匯入時會需要 (可不設置)。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">openssl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pkcs12</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -export</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -inkey</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.key</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -in</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.cert</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -out</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client.p12</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -name</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">client-cert</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span></span></code></pre>\n<h2 id=\"憑證匯入\"><a class=\"anchor\" href=\"#憑證匯入\">#</a> 憑證匯入</h2>\n<p>在 Windows、Linux 桌面環境大致上都直接點兩下 <code>client.p12</code> 後輸入匯出密碼即可。之後可開啟瀏覽器查看網站是否可正常瀏覽。</p>\n<p>macOS 可能會出現一直詢問密碼 (匯出密碼錯誤) 的情況，強者我同事說是因為 <code>openssl</code> 版本差異關係，使用舊版的 <code>openssl</code> 重新匯出 p12 憑證即可。</p>\n<p>macOS 還需要從 <code>鑰匙圈</code> 設定憑證的存取權限為一律允許，否則在瀏覽網站的時候，會一直出現詢問密碼請求允許的情況。</p>\n",
            "tags": [
                "Step By Step",
                "GCP",
                "mutual TLS",
                "憑證",
                "SSL"
            ]
        },
        {
            "id": "https://8loser.github.io/2024/06/17/dev-container-bind-source-error/",
            "url": "https://8loser.github.io/2024/06/17/dev-container-bind-source-error/",
            "title": "bind source path does not exist",
            "date_published": "2024-06-16T16:38:53.000Z",
            "content_html": "<h1 id=\"問題描述\"><a class=\"anchor\" href=\"#問題描述\">#</a> 問題描述</h1>\n<p>DevContainer 啟動 container 錯誤，查看錯誤訊息如下<br />\n<code>Error response from daemon: invalid mount config for type &quot;bind&quot;: bind source path does not exist: /run/user/1000/wayland-0</code></p>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<ul>\n<li>OS: EndeavourOS</li>\n<li>在 VSCode 使用 Dev Container 套件啟動 container</li>\n</ul>\n<h1 id=\"修正方法\"><a class=\"anchor\" href=\"#修正方法\">#</a> 修正方法</h1>\n<p>刪除下面兩個檔案</p>\n<ul>\n<li>/run/user/1000/wayland-0</li>\n<li>/run/user/1000/wayland-0.lock</li>\n</ul>\n",
            "tags": [
                "Trouble",
                "VSCode",
                "Remote Container"
            ]
        },
        {
            "id": "https://8loser.github.io/2024/06/16/shokaX-waline-comment/",
            "url": "https://8loser.github.io/2024/06/16/shokaX-waline-comment/",
            "title": "Hexo ShokaX 主題使用 Waline 留言版",
            "date_published": "2024-06-15T17:17:17.000Z",
            "content_html": "<h1 id=\"佈署-waline-server-端\"><a class=\"anchor\" href=\"#佈署-waline-server-端\">#</a> 佈署 Waline server 端</h1>\n<p>參考 Waline 佈署教學網站 <a href=\"https://waline.js.org/guide/deploy/vercel.html\">https://waline.js.org/guide/deploy/vercel.html</a> ，點選 Deploy 按鈕，會跳轉 Vercel 頁面建立 Waline server。</p>\n<p><img loading=\"lazy\" src=\"vercel-deploy.png\" alt=\"Vercel deploy\" /></p>\n<p>如果使用該方式，在 Vercel 出現下面錯誤訊息，改用這個 <a href=\"https://vercel.com/new/austin-lees-projects/clone?repository-url=https%3A%2F%2Fgithub.com%2Fwalinejs%2Fwaline%2Ftree%2Fmain%2Fexample\">[Deploy]</a> 試試</p>\n<div class=\"note danger\">\n<p>Error: No Output Directory named &quot;dist&quot; found after the Build completed. You can configure the Output Directory in your  Project Settings.</p>\n</div>\n<div class=\"note info\">\n<p>除了 Vercel 作為 Waline 服務之外，也有其他選擇 <a href=\"https://waline.js.org/guide/get-started/server.html\">https://waline.js.org/guide/get-started/server.html</a></p>\n</div>\n<h1 id=\"建立-waline-使用的資料庫\"><a class=\"anchor\" href=\"#建立-waline-使用的資料庫\">#</a> 建立 Waline 使用的資料庫</h1>\n<p>Waline 需要資料庫儲存留言資料，Waline 支援多種資料庫 <a href=\"https://waline.js.org/guide/database.html\">https://waline.js.org/guide/database.html</a> 。<br />\nVercel 也有提供資料庫，所以這邊使用 Vercel 內建的資料庫服務，建立 PostgreSQL。</p>\n<p><img loading=\"lazy\" src=\"vercel-create-database.png\" alt=\"Create Database Step1\" /></p>\n<p><img loading=\"lazy\" src=\"vercel-create-postgres.png\" alt=\"Create Database Step2\" /></p>\n<div class=\"note info\">\n<p>需要的資料表請參考<br />\n<a href=\"https://github.com/walinejs/waline/blob/main/assets/waline.pgsql\">https://github.com/walinejs/waline/blob/main/assets/waline.pgsql</a></p>\n</div>\n<h1 id=\"將-waline-服務與資料庫連接\"><a class=\"anchor\" href=\"#將-waline-服務與資料庫連接\">#</a> 將 Waline 服務與資料庫連接</h1>\n<p>進入 App<br />\n<img loading=\"lazy\" src=\"enter-waline-app.png\" alt=\"Enter Waline App\" /><br />\n連接剛剛建立的 PostgreSQL<br />\n<img loading=\"lazy\" src=\"database-connect.png\" alt=\"Database connect\" /></p>\n<h1 id=\"設定-waline-環境變數\"><a class=\"anchor\" href=\"#設定-waline-環境變數\">#</a> 設定 Waline 環境變數</h1>\n<p><img loading=\"lazy\" src=\"setting-environment-variable.png\" alt=\"Setting environment variable\" /></p>\n<p>如果資料庫連接成功，會看到紅框內的環境變數。<br />\n<img loading=\"lazy\" src=\"waline-environment-variable.png\" alt=\"Connected Success Variable\" /></p>\n<p>但這樣還無法讓 Waline server 成功連接資料庫，還需要設定下面的環境變數</p>\n<ul>\n<li>PG_SSL\n<ul>\n<li>設為 true</li>\n</ul>\n</li>\n<li>PG_PORT\n<ul>\n<li>設為 5432</li>\n</ul>\n</li>\n</ul>\n<div class=\"note info\">\n<p>Waline 可設定的 PostgreSQL 環境變數<br />\n<a href=\"https://waline.js.org/guide/database.html#postgresql\">https://waline.js.org/guide/database.html#postgresql</a></p>\n</div>\n<h1 id=\"測試-waline-功能\"><a class=\"anchor\" href=\"#測試-waline-功能\">#</a> 測試 Waline 功能</h1>\n<p>進入 Waline project 查看 Waline 留言頁面。</p>\n<p><img loading=\"lazy\" src=\"app-visit-test.png\" alt=\"Visit Waline app\" /></p>\n<p>如果正常的話會看到如下畫面，可以留言測試看看是否成功。</p>\n<p><img loading=\"lazy\" src=\"waline-comment-page.png\" alt=\"Waline comment page\" /></p>\n<h1 id=\"設定-shokax\"><a class=\"anchor\" href=\"#設定-shokax\">#</a> 設定 ShokaX</h1>\n<p>修改 Hexo ShokaX 設定檔；Hexo 資料夾 <code>/_config.shokax.yml</code> 或 <code>/_config.shokaX.yml</code></p>\n<p>增加下面設定</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yml\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">waline</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  enable</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> true</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # 啟用狀態</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  serverURL</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">https://waline-three-wine.vercel.app</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # Waline server 網址，從 Vercel Project 查看 domain</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  lang</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">zh-TW</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # comment 界面語言</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  meta</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # comment 可填寫項目</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> nick</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> mail</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  requiredMeta</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # comment 必填項目</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> nick</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">    -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> mail</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  wordLimit</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 400</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # comment 字數上限</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  pageSize</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 10</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # 每頁顯示筆數</span></span></code></pre>\n<div class=\"note info\">\n<p>Hexo ShokaX 的 Waline 其他設定項目<br />\n<a href=\"https://docs.kaitaku.xyz/guide/comment.html#valine-%E7%B3%BB%E8%AF%84%E8%AE%BA%E7%B3%BB%E7%BB%9F\">https://docs.kaitaku.xyz/guide/comment.html#valine-系评论系统</a></p>\n</div>\n<h1 id=\"使用紀錄\"><a class=\"anchor\" href=\"#使用紀錄\">#</a> 使用紀錄</h1>\n<h2 id=\"202406\"><a class=\"anchor\" href=\"#202406\">#</a> 2024/06</h2>\n<ul>\n<li>meta 設定無效，只設定 nick, mail 但是界面上還是會顯示網址欄位</li>\n<li><code>最新評論</code> 顯示 emoji 無效，會變成 HTML</li>\n</ul>\n",
            "tags": [
                "Step By Step",
                "Hexo theme",
                "Waline"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/10/30/spawn-node-gyp-ENOENT/",
            "url": "https://8loser.github.io/2023/10/30/spawn-node-gyp-ENOENT/",
            "title": "spawn node-gyp ENOENT",
            "date_published": "2023-10-29T17:04:26.000Z",
            "content_html": "<h1 id=\"問題描述\"><a class=\"anchor\" href=\"#問題描述\">#</a> 問題描述</h1>\n<p>要把 theme 轉為 <a href=\"https://docs.kaitaku.xyz/\">shokaX</a> 時，執行 <code>SXC install shokaX</code> 出現錯誤訊息</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">error</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /workspaces/page/node_modules/deasync:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Command</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> failed.</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Exit</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> code:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Command:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> node</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ./build.js</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Arguments:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Directory:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /workspaces/page/node_modules/deasync</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Output:</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">node:events:495</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">      throw</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> er</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> //</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Unhandled</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">error</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> event</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">      ^</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Error:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> spawn</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> node-gyp</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ENOENT</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    at</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ChildProcess._handle.onexit</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">node:internal/child_process:284:19</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    at</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> onErrorNT</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">node:internal/child_process:477:16</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    at</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> process.processTicksAndRejections</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">node:internal/process/task_queues:82:21</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Emitted</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">error</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> event</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> on</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ChildProcess</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> instance</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> at:</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    at</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ChildProcess._handle.onexit</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">node:internal/child_process:290:12</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    at</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> onErrorNT</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">node:internal/child_process:477:16</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    at</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> process.processTicksAndRejections</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">node:internal/process/task_queues:82:21</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  errno:</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -2,</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  code:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">ENOENT</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  syscall:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">spawn node-gyp</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  path:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">node-gyp</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">,</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  spawnargs:</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:rgba(255, 18, 18, 0.8);--shiki-dark:rgba(255, 18, 18, 0.8)\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">rebuild</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ]</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Node.js</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> v18.18.2</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">info</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Visit</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://yarnpkg.com/en/docs/cli/add</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> documentation</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> about</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> this</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> command.</span></span></code></pre>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<ul>\n<li>Docker image: node:18-slim</li>\n</ul>\n<h1 id=\"修正方法\"><a class=\"anchor\" href=\"#修正方法\">#</a> 修正方法</h1>\n<p>安裝下面套件，不確定 <code>make</code>, <code>g++</code> 需不需要，因為之前遇過類似的問題都裝這三個，所以直接複製過來用。如果沒有安裝的話，在安裝 <code>node-gyp</code> 會出現其他錯誤。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -y</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> python3</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> make</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> g++</span></span></code></pre>\n<p>安裝 <code>node-gyp</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pnpm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> add</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -g</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> node-gyp</span></span></code></pre>\n",
            "tags": [
                "Trouble",
                "Docker",
                "Hexo",
                "Shoka",
                "Node.js"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/08/27/static-web-page-refresh-404/",
            "url": "https://8loser.github.io/2023/08/27/static-web-page-refresh-404/",
            "title": "靜態網頁 refresh 出現 404",
            "date_published": "2023-08-26T16:40:55.000Z",
            "content_html": "<h1 id=\"trouble\"><a class=\"anchor\" href=\"#trouble\">#</a> Trouble</h1>\n<p>Vue 開發網頁打包成靜態網頁檔案，使用 docker compose 結合 nginx 佈署，網頁切換到有路徑的頁面後按 F5 重新整理頁面會出現 404錯誤 。</p>\n<p>靜態頁面打包後有再用 <a href=\"https://acme.com/software/thttpd/\">thttpd</a> 作為 web 服務並打包成 container 方便佈署，實際錯誤情況如下圖，如果不同的 web 服務會有不同畫面。</p>\n<p><img loading=\"lazy\" src=\"404-not-found.png\" alt=\"404 Not Found\" /></p>\n<h1 id=\"docker-composeyml\"><a class=\"anchor\" href=\"#docker-composeyml\">#</a> Docker-compose.yml</h1>\n<p>有修改過的內容，實際不只有兩個 container</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-dockerfile\"><span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">services:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  nginx:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    image: nginx</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    restart: unless-stopped</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    volumes:</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">      # nginx 設定檔</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">      - ./nginx.conf:/etc/nginx/conf.d/default.conf</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">      - ./robots.txt.test:/usr/share/nginx/html/robots.txt</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    environment:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">      TZ: Asia/Taipei</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    ports:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">      - target: 80</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">        published: 80</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">        protocol: tcp</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">        mode: host</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 前端 web service</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  branded:</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    image: ***馬賽克***</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">    restart: unless-stopped</span></span></code></pre>\n<h1 id=\"nginxconf\"><a class=\"anchor\" href=\"#nginxconf\">#</a> Nginx.conf</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-nginx\"><span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">server</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  listen </span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">80</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  server_name </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"\"</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # LB Public IP address</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  set_real_ip_from </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">***馬賽克***</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  set_real_ip_from </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">130.211.0.0/22</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  set_real_ip_from </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">35.191.0.0/16</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  real_ip_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  real_ip_recursive on</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 阻擋的 User Agent</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">  if</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">http_user_agent</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> ~* </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">***不要告訴泥*** </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">return</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 101</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">;</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> /robots.txt </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    alias </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">/usr/share/nginx/html/robots.txt</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> /healthcheck </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    access_log off</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    return</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 200</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 'ok'</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 前端</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> / </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_pass </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">http://branded/</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Host </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">host</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Real-IP </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">remote_addr</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">proxy_add_x_forwarded_for</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Custom-Referrer smc_identity_layer</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<h1 id=\"solution\"><a class=\"anchor\" href=\"#solution\">#</a> Solution</h1>\n<p>在 nginx.conf 加上</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>proxy_intercept_errors on;</span></span>\n<span class=\"line\"><span>error_page 404 /index.html;</span></span></code></pre>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-nginx\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">  # 前端</span></span>\n<span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">  location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> / </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_pass </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">http://branded/</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Host </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">host</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Real-IP </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">remote_addr</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">proxy_add_x_forwarded_for</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Custom-Referrer smc_identity_layer</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    # 高情商(?)加下面這兩行</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_intercept_errors on</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    error_page </span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">404</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> /index.html</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">  </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n",
            "tags": [
                "Trouble",
                "Deploy",
                "Nginx",
                "static web page",
                "Docker Compose"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/07/14/fixing-mixed-content/",
            "url": "https://8loser.github.io/2023/07/14/fixing-mixed-content/",
            "title": "瀏覽器出現 Mixed Content 訊息",
            "date_published": "2023-07-14T11:25:05.000Z",
            "content_html": "<p>Chrome Console 出現 <code>Mixed Content</code> 訊息</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-text\"><span class=\"line\"><span>Mixed Content: The page at 'https://www.***.com/' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://192.168.10.200:3000/'. </span></span>\n<span class=\"line\"><span>This request has been blocked; the content must be served over HTTPS.</span></span></code></pre>\n<p>出現該訊息是因為在 https 頁面內，呼叫 http 協定的 API，基於安全問題，瀏覽器會封鎖該請求。解決方式是把 API 換成 https 即可，但這個 API 是我們放在客戶端存取客戶內部網路的 service，讓客戶在我們網站透過呼叫該 service 存取他們內網資料。</p>\n<p>如果要為了這個 service 建立域名、申請 SSL 憑證有點麻煩（我就懶），所以請客戶調整瀏覽器設定，允許不安全內容。</p>\n<p><img loading=\"lazy\" src=\"allow-insecure-content.png\" alt=\"Allow insecure conten\" /></p>\n<h1 id=\"相關參考\"><a class=\"anchor\" href=\"#相關參考\">#</a> 相關參考</h1>\n<ul>\n<li>\n<p><a href=\"https://support.google.com/chrome/answer/114662?hl=zh-Hant&amp;co=GENIE.Platform%3DDesktop#zippy=%2C%E7%9E%AD%E8%A7%A3%E5%8F%AF%E8%AE%8A%E6%9B%B4%E7%9A%84%E6%AC%8A%E9%99%90\">變更網站設定權限</a></p>\n</li>\n<li>\n<p><a href=\"https://developer.mozilla.org/zh-TW/docs/Web/Security/Mixed_content\">混合內容</a></p>\n</li>\n</ul>\n",
            "tags": [
                "Trouble",
                "Mixed Content",
                "Browser"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/05/28/yay-is-corrupted/",
            "url": "https://8loser.github.io/2023/05/28/yay-is-corrupted/",
            "title": "yay 更新顯示檔案毀損",
            "date_published": "2023-05-28T10:03:54.000Z",
            "content_html": "<h1 id=\"問題描述\"><a class=\"anchor\" href=\"#問題描述\">#</a> 問題描述</h1>\n<p>EndeavourOS 更新時出現錯誤訊息，且每次更新時都出現。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">::</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 進行安裝嗎？</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Y/n</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">::</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 正在接收軟體包…</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\"> libusbmuxd-2.0.2-3-x86_64</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">            31.8</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> KiB</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   529</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> KiB/s</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 00:00</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\"> libplist-2.3.0-2-x86_64</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">             140.7</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> KiB</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  1315</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> KiB/s</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 00:00</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\"> libimobiledevice-1.3.0-9-x86_64</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">     481.5</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> KiB</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  2.35</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MiB/s</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 00:00</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\"> yay-12.0.5-1-x86_64</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">                   3.0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MiB</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  2.69</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MiB/s</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 00:01</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\"> 總共</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">4/4</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                            3.7 MiB  3.23 MiB/s 00:01 </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">78/78</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> 正在檢查鑰匙圈中的鑰匙</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                                   </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">78/78</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> 正在檢查軟體包完整性</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                                     </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">------------------------------------</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">錯誤：yay：來自</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> manuel</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">manuel@endeavouros.co</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">m</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 的簽章信任等級不明::</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  /var/cache/pacman/pkg/yay-12.0.5-1-x86_64.pkg.tar.zst</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 檔案毀損</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">套件不正確或損毀（PGP</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 簽章）</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">。你要刪除它嗎?</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Y/n</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">錯誤：無法提交處理</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">套件不正確或損毀（PGP </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">簽章）</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">發生錯誤，沒有軟體包被更新。</span></span></code></pre>\n<h1 id=\"修正\"><a class=\"anchor\" href=\"#修正\">#</a> 修正</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -Sy</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> archlinux-keyring</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> endeavouros-keyring</span></span></code></pre>\n<p>修正後執行 <code>yay -Syu</code> 可正常更新了。</p>\n",
            "tags": [
                "Trouble",
                "EndeavourOS",
                "Linux"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/05/06/dbeaver-backup-start-unavailable/",
            "url": "https://8loser.github.io/2023/05/06/dbeaver-backup-start-unavailable/",
            "title": "DBeaver backup start 按鈕沒有作用",
            "date_published": "2023-05-06T14:57:59.000Z",
            "content_html": "<h1 id=\"問題描述\"><a class=\"anchor\" href=\"#問題描述\">#</a> 問題描述</h1>\n<p>在 Linux 內 DBeaver backup <code>Start</code> 按鈕無法點選，也無法 <code>Next</code></p>\n<p><img loading=\"lazy\" src=\"start-unavailable.png\" alt=\"start unavailable\" /></p>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<ul>\n<li>OS: EndeavourOS, BUILD_ID=2023.03.06</li>\n<li>DBeaver\tCommunity 23.0.3</li>\n<li>Database: 雲端 PostgreSQL</li>\n</ul>\n<h1 id=\"修正方法\"><a class=\"anchor\" href=\"#修正方法\">#</a> 修正方法</h1>\n<h2 id=\"安裝-postgresql\"><a class=\"anchor\" href=\"#安裝-postgresql\">#</a> 安裝 PostgreSQL</h2>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> postgresql</span></span></code></pre>\n<h2 id=\"查看-pg_dump-位置\"><a class=\"anchor\" href=\"#查看-pg_dump-位置\">#</a> 查看 pg_dump 位置</h2>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> whereis</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pg_dump</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pg_dump:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /usr/bin/pg_dump</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /usr/share/man/man1/pg_dump.1.gz</span></span></code></pre>\n<p>知道 <code>pg_dump</code> 程式位置在 <code>/usr/bin/</code></p>\n<h2 id=\"dbeaver-設定-driver\"><a class=\"anchor\" href=\"#dbeaver-設定-driver\">#</a> DBeaver 設定 Driver</h2>\n<p>從 DBeaver 選單開啟 Database/Driver Manager，點選 PostgreSQL 按 <code>Edit</code><br />\n<img loading=\"lazy\" src=\"driver-manager.png\" alt=\"Driver Manager\" /></p>\n<p>選取 <code>Native Client</code>/<code>Add Home</code></p>\n<p><img loading=\"lazy\" src=\"native-client.png\" alt=\"Native Client\" /></p>\n<p>設定 Home 為 <code>/usr/bin/</code></p>\n<p><img loading=\"lazy\" src=\"add-home.png\" alt=\"Add Home\" /></p>\n<h1 id=\"測試\"><a class=\"anchor\" href=\"#測試\">#</a> 測試</h1>\n<p><code>Next</code> 可以點選了<br />\n<img loading=\"lazy\" src=\"backup-next.png\" alt=\"Backup Next\" /></p>\n<p>點選 <code>Next</code> 後，選取備份路徑按 <code>Start</code> 就可以備份了<br />\n<img loading=\"lazy\" src=\"backup-start.png\" alt=\"Backup Start\" /></p>\n",
            "tags": [
                "Trouble",
                "Linux",
                "DBeaver"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/03/12/reduce-frontend-docker-image/",
            "url": "https://8loser.github.io/2023/03/12/reduce-frontend-docker-image/",
            "title": "縮減靜態網頁 Docker image 大小",
            "date_published": "2023-03-12T13:59:19.000Z",
            "content_html": "<h1 id=\"前言\"><a class=\"anchor\" href=\"#前言\">#</a> 前言</h1>\n<p>通常 Vue 部屬都是先打包成靜態網頁再部屬到 web 服務器上，但如此無法做到程式碼上傳版控後自動部署，因為上傳的是原始碼，不是打包後的靜態網頁。</p>\n<p>所以 Vue 搭配 Docker 時，有種作法是把原始碼直接直接 build 成執行 <code>npm run dev</code> 的 docker image 後放在 server 執行。</p>\n<p><img loading=\"lazy\" src=\"jsut-do-it.jpg\" alt=\"\" /></p>\n<div class=\"note danger\">\n<p>這種作法產生的 docker image 很大，枉費還用 Vue</p>\n</div>\n<h1 id=\"縮減方式\"><a class=\"anchor\" href=\"#縮減方式\">#</a> 縮減方式</h1>\n<p>使用 <a href=\"https://docs.docker.com/build/building/multi-stage/\">Multi-stage builds</a>，在第一個 stage build 出靜態網頁，複製到第二個 stage 跟 web service 打包成 image。</p>\n<p>假設原本 Dockerfiler 如下</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-dockerfile\"><span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">FROM</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> node:18-slim</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">COPY</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> . .</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">RUN</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> npm install --production=false</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">EXPOSE</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 5173</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">CMD</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"npm\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"run\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"dev\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span></code></pre>\n<p>加上第二個 stage，並且使用 <a href=\"https://acme.com/software/thttpd/\">thttpd</a> 作為靜態網頁的 web service</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-dockerfile\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 第一個 stage</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">FROM</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> node:18-slim </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">AS</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> builder</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">COPY</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> . .</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">RUN</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> npm install --production=false</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># build static html 檔案，檔案會產生在 /dist 內</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">RUN</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> npm run build</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 第二個 stage</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">FROM</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> alpine</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 安裝 thttpd</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">RUN</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> apk update &#x26;&#x26; apk upgrade &#x26;&#x26; apk add --no-cache thttpd</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 複製第一個 stage 產生的 static html 到 /front</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">COPY</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> --from=builder /dist /front</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">EXPOSE</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 5173</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># container 啟動時執行 thttpd</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">CMD</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"thttpd\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-D\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-h\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"0.0.0.0\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-p\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"5173\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-d\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"/front\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-l\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"-M\"</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">, </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"60\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span></code></pre>\n<p><img loading=\"lazy\" src=\"finish.gif\" alt=\"\" /></p>\n<h1 id=\"比較\"><a class=\"anchor\" href=\"#比較\">#</a> 比較</h1>\n<table>\n<thead>\n<tr>\n<th style=\"text-align:center\"></th>\n<th style=\"text-align:center\">Docker image 大小</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td style=\"text-align:center\">修改前</td>\n<td style=\"text-align:center\">504MB</td>\n</tr>\n<tr>\n<td style=\"text-align:center\">修改後</td>\n<td style=\"text-align:center\">11.1MB</td>\n</tr>\n</tbody>\n</table>\n<p><img loading=\"lazy\" src=\"whoa.gif\" alt=\"\" /></p>\n",
            "tags": [
                "Docker"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/03/11/EndeavourOS-init/",
            "url": "https://8loser.github.io/2023/03/11/EndeavourOS-init/",
            "title": "EndeavourOS 常用程式安裝",
            "date_published": "2023-03-11T13:23:40.000Z",
            "content_html": "<h1 id=\"參數說明\"><a class=\"anchor\" href=\"#參數說明\">#</a> 參數說明</h1>\n<ul>\n<li><code>--noconfirm</code> 自動確認</li>\n<li><code>--needed</code> 已安裝的套件不重複安裝</li>\n</ul>\n<h1 id=\"注音輸入法-新酷音\"><a class=\"anchor\" href=\"#注音輸入法-新酷音\">#</a> 注音輸入法 (新酷音)</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --noconfirm</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fcitx5</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fcitx5-im</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fcitx5-chinese-addons</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fcitx5-configtool</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fcitx5-chewing</span></span></code></pre>\n<p>修改 <code>/etc/environment</code> 檔案，增加下面設定</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">GTK_IM_MODULE</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">fcitx</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">QT_IM_MODULE</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">fcitx</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">XMODIFIERS</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">@</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">im</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">fcitx</span></span></code></pre>\n<h1 id=\"藍芽\"><a class=\"anchor\" href=\"#藍芽\">#</a> 藍芽</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --noconfirm</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> bluez</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> bluez-utils</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> blueberry</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 設定開機自動啟用</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">systemctl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> enable</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> bluetooth</span></span></code></pre>\n<h1 id=\"visual-studio-code\"><a class=\"anchor\" href=\"#visual-studio-code\">#</a> Visual Studio Code</h1>\n<p>安裝 AUR 的 bin 版本才有 Remote Development 套件</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">yay</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --noconfirm</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span></span></code></pre>\n<h1 id=\"docker\"><a class=\"anchor\" href=\"#docker\">#</a> Docker</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --noconfirm</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> docker</span></span></code></pre>\n<h1 id=\"docker-desktop\"><a class=\"anchor\" href=\"#docker-desktop\">#</a> Docker Desktop</h1>\n<p>要先下載安裝檔案</p>\n<div class=\"note info\">\n<p>下載版本, 可參考 <a href=\"https://docs.docker.com/desktop/release-notes/\">https://docs.docker.com/desktop/release-notes/</a></p>\n</div>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 下載套件檔案</span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">dockerDesktopFile</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">docker-desktop-4.17.0-x86_64.pkg.tar.zst</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">wget</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://desktop.docker.com/linux/main/amd64/</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">$dockerDesktopFile</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -P</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /tmp</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 安裝套件</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -U</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --noconfirm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /tmp/</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">$dockerDesktopFile</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 移除下載的檔案</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">rm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /tmp/</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">$dockerDesktopFile</span></span></code></pre>\n<div class=\"note info\">\n<p>手動安裝可參考 <a href=\"https://docs.docker.com/desktop/install/archlinux/\">https://docs.docker.com/desktop/install/archlinux/</a></p>\n</div>\n<h1 id=\"google-chrome\"><a class=\"anchor\" href=\"#google-chrome\">#</a> Google Chrome</h1>\n<p>AUR 才有</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">yay</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -S</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --noconfirm</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> google-chrome</span></span></code></pre>\n<h1 id=\"設定-ram-disk\"><a class=\"anchor\" href=\"#設定-ram-disk\">#</a> 設定 RAM disk</h1>\n<p>建立檔案 <code>/etc/tmpfiles.d/shmFolder.conf</code><br />\n使用者名稱的位置記得修改</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># 下載資料夾</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">d</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/shm/Downloads</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0755</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">使用者名</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">稱</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">使用者名</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">稱</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">L+</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /home/</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">使用者名</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">稱</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/Downloads</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/shm/Downloads</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"># Chrome 暫存資料夾</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">d</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/shm/ChromeCache</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0700</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">使用者名</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">稱</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">使用者名</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">稱</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">L+</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /home/</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">使用者名</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">稱</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/.cache/google-chrome</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/shm/ChromeCache</span></span></code></pre>\n<p>執行使設定檔生效，或者重新開機</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">systemd-tmpfiles</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --create</span></span></code></pre>\n<h1 id=\"完整-shell-script\"><a class=\"anchor\" href=\"#完整-shell-script\">#</a> 完整 Shell Script</h1>\n<p>在這裡 <a href=\"https://gist.github.com/8loser/a99f0e946861db9a368693ab747c5506\">EndeavourOS-init.sh</a></p>\n",
            "tags": [
                "EndeavourOS",
                "Linux",
                "Shell script"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/02/23/linux-recovering-journal/",
            "url": "https://8loser.github.io/2023/02/23/linux-recovering-journal/",
            "title": "Linux 開機顯示 recovering journal",
            "date_published": "2023-02-23T08:51:28.000Z",
            "content_html": "<h1 id=\"描述\"><a class=\"anchor\" href=\"#描述\">#</a> 描述</h1>\n<p>Linux 作業系統開機顯示 recovering journal，無法正常進入桌面環境。<br />\n完整訊息如下，出現 <code>recovering journal</code> 後，要等個幾分鐘才會再出現後續的訊息。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">/dev/nvme0n1p5:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> recovering</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> journal</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">/dev/nvme0n1p5:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> clean,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1217915/16416000</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 17252131/65536000</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> blocks</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> TIME </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> Timed out waiting </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> device /dev/sda2.</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">DEPEND</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> Dependency failed </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> /home/user/test.</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">DEPEND</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> Dependency failed </span><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> Local File System.</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">You</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> are</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> in</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> emergency</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> mode.</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> After</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> logging</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> in,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> type</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">journalctl -xb</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> view</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> system</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> logs,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">systemctl reboot</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> reboot,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">systemctl default</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> or</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">exit</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> boot</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> into</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> default</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> mode.</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Give</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> root</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> password</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> maintenance</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">or</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> press</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Control-D</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> continue</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#998418;--shiki-dark:#B8A965\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">...</span></span></code></pre>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> lsb_release</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -a</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">LSB</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Version:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tn/a</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Distributor</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tManjaroLinux</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Description:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tManjaro</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Linux</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Release:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">\t22.0.4</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Codename:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tSikaris</span></span></code></pre>\n<h1 id=\"修正\"><a class=\"anchor\" href=\"#修正\">#</a> 修正</h1>\n<p>使用 root 權限編輯 <code>/etc/fstab</code>，把 <code>/dev/sda2</code> (錯誤訊息中顯示的) 那行刪除，儲存，reboot。</p>\n<h1 id=\"原因\"><a class=\"anchor\" href=\"#原因\">#</a> 原因</h1>\n<p>應該是我前一次關機前，插外接硬碟又用 partition 管理工具 mount 外接硬碟 partition 導致。</p>\n",
            "tags": [
                "Trouble",
                "Linux"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/02/20/terminals-pty-host-is-unresponsive/",
            "url": "https://8loser.github.io/2023/02/20/terminals-pty-host-is-unresponsive/",
            "title": "Dev Containers 出現 terminal's pty host process is unresponsive",
            "date_published": "2023-02-20T06:10:40.000Z",
            "content_html": "<h1 id=\"錯誤訊息\"><a class=\"anchor\" href=\"#錯誤訊息\">#</a> 錯誤訊息</h1>\n<div class=\"note success\">\n<p>Visual Studio Code 1.76.1 版本已不會有此問題</p>\n</div>\n<p>更新 Visual Studio Code 後，進入 Dev Containers 出現錯誤 <code>The connection to the terminal's pty host process is unresponsive</code>，且 terminal 無法使用。<br />\n<img loading=\"lazy\" src=\"error-message.png\" alt=\"Error message\" /></p>\n<p>點選 <code>Restart pty host</code> 後 terminal 仍無法正常顯示</p>\n<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<ul>\n<li>作業系統</li>\n</ul>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> lsb_release</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">  -a</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">LSB</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Version:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tn/a</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Distributor</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ID:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tManjaroLinux</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Description:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tManjaro</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Linux</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Release:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">\t22.0.4</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Codename:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\tSikaris</span></span></code></pre>\n<ul>\n<li>Visual Studio Code 1.75.1 (會出現錯誤的版本)</li>\n</ul>\n<h1 id=\"嘗試修正\"><a class=\"anchor\" href=\"#嘗試修正\">#</a> 嘗試修正</h1>\n<p>Visual Studio Code 設定 terminal.integrated.windowsEnableConpty: false，重啟後異常狀況一樣。</p>\n<h1 id=\"使用-downgrade-降版\"><a class=\"anchor\" href=\"#使用-downgrade-降版\">#</a> 使用 downgrade 降版</h1>\n<p>無法降版，可能因為我先把 VSCode 刪除再重新安裝，而重新安裝是最新版的，所以電腦內沒有保留舊版本。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> downgrade</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Downgrading</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> from</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> A.L.A.</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> is</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disabled</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> on</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> the</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> stable</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> branch.</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> To</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> override</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> this</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> behavior,</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> set</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> DOWNGRADE_FROM_ALA</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1.</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">See</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://wiki.manjaro.org/index.php/Downgrading_packages</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> more</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> details.</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">No</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> results</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> found</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Unable</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> downgrade</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span></span></code></pre>\n<h1 id=\"直接安裝舊版本\"><a class=\"anchor\" href=\"#直接安裝舊版本\">#</a> 直接安裝舊版本</h1>\n<h2 id=\"取得舊版本\"><a class=\"anchor\" href=\"#取得舊版本\">#</a> 取得舊版本</h2>\n<p>從 <a href=\"https://aur.archlinux.org/packages/\">AUR repository</a>找到使用的套件<a href=\"https://aur.archlinux.org/packages/visual-studio-code-bin\">visual-studio-code-bin</a>，點選 <code>View Changes</code> 查看舊版本。</p>\n<p><img loading=\"lazy\" src=\"aur-package.png\" alt=\"AUR package\" /></p>\n<p>選擇可以正常的版本; 1.74.3，下載套件。</p>\n<p><img loading=\"lazy\" src=\"aur-package-changes.png\" alt=\"AUR package changes\" /><br />\n<img loading=\"lazy\" src=\"old-package-download.png\" alt=\"old package download\" /></p>\n<h2 id=\"建立-package\"><a class=\"anchor\" href=\"#建立-package\">#</a> 建立 package</h2>\n<p>解壓縮後進入</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ls</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -a1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">.</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">..</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">code.desktop</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">code-url-handler.desktop</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">PKGBUILD</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">.SRCINFO</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-bin.install</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-bin.sh</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-workspace.xml</span></span></code></pre>\n<p>建立 package</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> makepkg</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -s</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Making</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1.74.3-1</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">廿廿三年二月廿日 </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">週一</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 廿時47分十三秒</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> runtime</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> dependencies...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> buildtime</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> dependencies...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Retrieving</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sources...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Found</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> code.desktop</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Found</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> code-url-handler.desktop</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Found</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-workspace.xml</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Found</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin.sh</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Downloading</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> code_x64_1.74.3.tar.gz...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  %</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Total</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    %</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Received</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> %</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Xferd</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  Average</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Speed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   Time</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    Time</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Time</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  Current</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">                                 Dload</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  Upload</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   Total</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   Spent</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    Left</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  Speed</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">100</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   134</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  100</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   134</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">    0</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">     0</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">    159</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">      0</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --:--:--</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --:--:--</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --:--:--</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   159</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">100</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  131M</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  100</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  131M</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">    0</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">     0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  3979k</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">      0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  0:00:33</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">  0:00:33</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --:--:--</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 4091k</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Validating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> source</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> with</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sha256sums...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    code.desktop</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ...</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Passed</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    code-url-handler.desktop</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ...</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Passed</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    visual-studio-code-workspace.xml</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ...</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Passed</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    visual-studio-code-bin.sh</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ...</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Passed</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Validating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> source_x86_64</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> with</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sha256sums...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    code_x64_1.74.3.tar.gz</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ...</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Passed</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Extracting</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sources...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Extracting</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> code_x64_1.74.3.tar.gz</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> with</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> bsdtar</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Entering</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fakeroot</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> environment...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Starting</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Tidying</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Removing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> libtool</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Purging</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> unwanted</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Removing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> static</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> library</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Stripping</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> unneeded</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> symbols</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> from</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> binaries</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> and</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> libraries...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Compressing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> man</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> and</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> info</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pages...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> packaging</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> issues...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Creating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">visual-studio-code-bin</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Generating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> .PKGINFO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> file...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Generating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> .BUILDINFO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> file...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Adding</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> file...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Generating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> .MTREE</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> file...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">  -</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Compressing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package...</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Leaving</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> fakeroot</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> environment.</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Finished</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> making:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1.74.3-1</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">廿廿三年二月廿日 </span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">週一</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">)</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 廿時48分二秒</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span></code></pre>\n<p>建立 package 後會產生需要的檔案，檢查有沒有產生 <code>visual-studio-code-bin-1.74.3-1-x86_64.pkg.tar.zst</code>。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ls</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -a1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">.</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">..</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">code.desktop</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">code-url-handler.desktop</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">code_x64_1.74.3.tar.gz</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pkg</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">PKGBUILD</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">src</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">.SRCINFO</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-bin-1.74.3-1-x86_64.pkg.tar.zst</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-bin.install</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-bin.sh</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">visual-studio-code-workspace.xml</span></span>\n<span class=\"line\"></span></code></pre>\n<h2 id=\"安裝\"><a class=\"anchor\" href=\"#安裝\">#</a> 安裝</h2>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pacman</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -U</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin-1.74.3-1-x86_64.pkg.tar.zst</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">loading</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> packages...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">resolving</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> dependencies...</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">looking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> conflicting</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> packages...</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Packages</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> visual-studio-code-bin-1.74.3-1</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Total</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Installed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Size:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">  331.05</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MiB</span></span>\n<span class=\"line\"></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">::</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Proceed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> with</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> installation?</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Y/n</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> y</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> keys</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> in</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> keyring</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                                   </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">####################################</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> integrity</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                                 </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">####################################</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> loading</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> files</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                                      </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">####################################</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> file</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> conflicts</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                                </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">####################################</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> checking</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> available</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> disk</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> space</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                              </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">####################################</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">::</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Processing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> package</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> changes...</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/1</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> installing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">                          </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">####################################</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> 100%</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">==</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">NOTE:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Custom</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> flags</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> should</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> be</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> put</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> directly</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> in:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ~/.config/code-flags.conf</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Optional</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> dependencies</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> visual-studio-code-bin</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    glib2:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> move</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> to</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> trash</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> functionality</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">installed</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    libdbusmenu-glib:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> KDE</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> global</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> menu</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">installed</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    org.freedesktop.secrets:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> settings</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sync</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">installed</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">    icu69:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Needed</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> for</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> live</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> share</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">::</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Running</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> post-transaction</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> hooks...</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">1/4</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> Arming</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ConditionNeedsUpdate...</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">2/4</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> Updating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> the</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MIME</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> type</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> database...</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">3/4</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> Refreshing</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> PackageKit...</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">4/4</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> Updating</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> the</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> desktop</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> file</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MIME</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> type</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> cache...</span></span></code></pre>\n<h1 id=\"注意事項\"><a class=\"anchor\" href=\"#注意事項\">#</a> 注意事項</h1>\n<p>在 Linux 環境要安裝 <code>visual-studio-code-bin</code> 的版本，才有 <code>Remote Development</code> 套件可以安裝。</p>\n<p><img loading=\"lazy\" src=\"package-visual-studio-code-bin.png\" alt=\"Visual Studio Code bin\" /></p>\n<h1 id=\"參考資料\"><a class=\"anchor\" href=\"#參考資料\">#</a> 參考資料</h1>\n<ul>\n<li><a href=\"https://github.com/microsoft/vscode-remote-release/issues/7999\">https://github.com/microsoft/vscode-remote-release/issues/7999</a></li>\n<li><a href=\"https://github.com/microsoft/vscode/issues/130320\">https://github.com/microsoft/vscode/issues/130320</a></li>\n</ul>\n",
            "tags": [
                "Trouble",
                "VSCode",
                "Remote Development"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/02/19/newbie-guide/",
            "url": "https://8loser.github.io/2023/02/19/newbie-guide/",
            "title": "新手指南",
            "date_published": "2023-02-19T07:04:11.000Z",
            "content_html": "<h1 id=\"新手指南\"><a class=\"anchor\" href=\"#新手指南\">#</a> 新手指南</h1>\n<p>這是一篇新手進公司後整理內部開發流程的 <s>事故</s> 故事</p>\n<h1 id=\"要有光\"><a class=\"anchor\" href=\"#要有光\">#</a> 要有光</h1>\n<p>上班第一天，要緊頭一件事就是 <span class=\"spoiler\" title=\"你知道得太多了\">認清自己在食物鏈中的位置</span> 熟悉開發環境，目前公司開發到佈署的流程是原始碼在每個人的環境都有一份，如果有修改再傳修改的檔案給知道怎麼佈署的人去佈署，這方式有時後會漏掉檔案，也很難搞清哪個才是最新版本的程式。而且每個人開發環境不同，剛拿到專案原始碼時啟動失敗，同學會教我一些奇計淫巧至今令我匪夷所思的方法（為什麼要修改 node_modules 內的檔案?）。</p>\n<ul>\n<li>改的檔案是最新版的嗎？</li>\n<li>覆蓋別人給的檔案後，原本改好的程式不見了。</li>\n<li>你給的檔案無法執行，說有缺少 function。</li>\n<li>『可以只給提供修改的 function 嗎？』「我其實也不太確定耶，還是你整個貼上後試試看有沒有錯誤」</li>\n</ul>\n<h1 id=\"新手村\"><a class=\"anchor\" href=\"#新手村\">#</a> 新手村</h1>\n<p>村長跟我介紹村子內 NPC 的功能，還有 server 的佈署方式，程式碼上傳到 GCP linux VM 後使用 docker build 成 image，再使用 docker compose 啟動多個 container 運行服務。有兩個 VM 每個 VM 都有 5 個 container，除了前端、後端程式外還有串接其他服務，使用的程式語言有 Vue.js, Node.js, .NET Core 。</p>\n<h1 id=\"程式一致\"><a class=\"anchor\" href=\"#程式一致\">#</a> 程式一致</h1>\n<p>為了解決程式碼同步問題，挑了可以免費 private 託管的版本控制服務，同學可以遠端上傳程式碼，其他人也可以同步。</p>\n<ol>\n<li class=\"quiz\">使用的服務為 <span class=\"gap\"></span> 。\n<ul class=\"options\">\n<li class=\"correct\">GitHub</li>\n<li>PornHub</li>\n</ul>\n</li>\n</ol>\n<p>為什麼要選 GitHub 而不是 <s>PornHub</s> GitLab？忘記了，好像跟 CI/CD 的免費項目有關。難的不是選擇使用的版控服務，而是要上傳哪些程式碼？通常在 server 上運行的就是最新的程式碼，使用 <a href=\"https://winscp.net/\">WinSCP</a> 抓下來後當作基準，因為其他同學還有改到一半未上傳的程式碼，所以也收集其他同學的程式碼進行整理合併。整理時發現不是每個人的開發環境設置都一樣，有的人用 Windows 有的人用 Mac。</p>\n<p><div class=\"links\"><div class=\"item\" title=\"WinSCP\" style=\"--block-color:#e9546b;\"><a href=\"https://winscp.net/\" class=\"image\" data-background-image=\"https://winscp-static-746341.c.cdn77.org/assets/images/logos/logo.png?v=7008\"></a>\n        <div class=\"info\">\n        <a href=\"https://winscp.net/\" class=\"title\">WinSCP</a>\n        <p class=\"desc\">https://winscp.net/</p>\n        </div></div></div></p>\n<h1 id=\"環境一致\"><a class=\"anchor\" href=\"#環境一致\">#</a> 環境一致</h1>\n<p>雖然程式碼一致了，但相同的程式碼在不同開發環境卻會出現異常，<span class=\"spoiler\" title=\"你知道得太多了\">你就用 node 14 就好了阿，沒事幹麻升級自找麻煩 😒</span> 所以開發環境也要一致。最簡單的方法是大家都裝相同的 node 版本，凡是都有個 BUT，全部的專案使用的 node 並不是相同版本，而且不是每個人都會用 <a href=\"https://github.com/nvm-sh/nvmNVM\">NVM</a>。<br />\n雖然有的人用 Mac 有的人用 Windows，但是大家都是用 Visual Studio Code，所以把全部的專案都加上 <a href=\"https://code.visualstudio.com/docs/devcontainers/tutorial\">Dev Containers</a>，之後所有人開發的時候，都使用 <a href=\"https://code.visualstudio.com/docs/devcontainers/tutorial\">Dev Containers</a> 進入 container 進行開發。</p>\n<p><div class=\"links\"><div class=\"item\" title=\"visual Studio Code\" style=\"--block-color:#e9546b;\"><a href=\"https://code.visualstudio.com/docs/devcontainers/containers\" class=\"image\" data-background-image=\"/assets/404.png\"></a>\n        <div class=\"info\">\n        <a href=\"https://code.visualstudio.com/docs/devcontainers/containers\" class=\"title\">Developing inside a Container</a>\n        <p class=\"desc\">https://code.visualstudio.com/docs/devcontainers/containers</p>\n        </div></div><div class=\"item\" title=\"八樓的人\" style=\"--block-color:#e9546b;\"><a href=\"/2022/05/13/Remote-Containers/\" class=\"image\" data-background-image=\"/assets/avatar.jpg\"></a>\n        <div class=\"info\">\n        <a href=\"/2022/05/13/Remote-Containers/\" class=\"title\">使用 VSCode 在 Docker container 內進行開發</a>\n        <p class=\"desc\">/2022/05/13/Remote-Containers/</p>\n        </div></div></div></p>\n<h1 id=\"docker-image-auto-build\"><a class=\"anchor\" href=\"#docker-image-auto-build\">#</a> Docker image auto build</h1>\n<p>上面提到程式最後都在 server 上 build image 後，使用 docker compose 建立 container 執行。 <ins>你終究要 image 的那為什麼不一開始就 image？</ins> 所以把每個專案都加上 <a href=\"https://docs.github.com/en/actions/quickstart\">GitHub Action</a>，引用原本就有的 Dockerfile 修改，程式上傳後會自動打包成 docker image 且 push 到 <a href=\"https://docs.github.com/en/packages/learn-github-packages/introduction-to-github-packages\">GitHub Package</a></p>\n<p>在 GitHub Action 使用下面的 yml，程式碼上傳到 GitHub 後會自動打包成 docker image</p>\n<p><a href=\"https://gist.github.com/8loser/4f78378d21358e8d4174bb9e8d872af3\">Gist Auto build docker image yml</a></p>\n<p>如果設定成功 push 程式碼後可以在 GitHub Action 看到執行動作<br />\n<img loading=\"lazy\" src=\"github-action.png\" alt=\"GitHub Action\" /></p>\n<p><div class=\"links\"><div class=\"item\" title=\"Quickstart for GitHub Actions\" style=\"--block-color:#e9546b;\"><a href=\"https://docs.github.com/en/actions/quickstart\" class=\"image\" data-background-image=\"/assets/404.png\"></a>\n        <div class=\"info\">\n        <a href=\"https://docs.github.com/en/actions/quickstart\" class=\"title\">Quickstart for GitHub Actions</a>\n        <p class=\"desc\">https://docs.github.com/en/actions/quickstart</p>\n        </div></div><div class=\"item\" title=\"docker/build-push-action\" style=\"--block-color:#e9546b;\"><a href=\"https://github.com/docker/build-push-action\" class=\"image\" data-background-image=\"/assets/404.png\"></a>\n        <div class=\"info\">\n        <a href=\"https://github.com/docker/build-push-action\" class=\"title\">docker/build-push-action</a>\n        <p class=\"desc\">https://github.com/docker/build-push-action</p>\n        </div></div></div></p>\n<h1 id=\"docker-管理\"><a class=\"anchor\" href=\"#docker-管理\">#</a> Docker 管理</h1>\n<p>鑑於不是所有同學都習慣用 linux，所以在 server 裝上 <a href=\"https://www.portainer.io\">portainer</a>，可以使用 Web 界面控制 server 上的 docker。一天又平安的過去了，感謝飛天小女警的努力。</p>\n<p><div class=\"links\"><div class=\"item\" title=\"Install Portainer CE with Docker on Linux\" style=\"--block-color:#e9546b;\"><a href=\"https://docs.portainer.io/start/install-ce/server/docker/linux\" class=\"image\" data-background-image=\"/assets/404.png\"></a>\n        <div class=\"info\">\n        <a href=\"https://docs.portainer.io/start/install-ce/server/docker/linux\" class=\"title\">Install Portainer CE with Docker on Linux</a>\n        <p class=\"desc\">https://docs.portainer.io/start/install-ce/server/docker/linux</p>\n        </div></div></div></p>\n<h1 id=\"自動佈署\"><a class=\"anchor\" href=\"#自動佈署\">#</a> 自動佈署</h1>\n<p>出現 bug 急著修正的時候，可能一天要上傳程式到 server 八百次，但不是所有人都知道怎麼更新到 server，<span class=\"spoiler\" title=\"你知道得太多了\">所以維運的同學要多體諒工程師</span> 所以在 portainer 開啟 swarm 功能後，service 就可以啟用 webhook 接收更新通知，收到通知後自動 pull 新的 docker image 進行佈署，不知道怎麼佈署的人可以 <code>POST</code> 呼叫 webhook 進行 service 更新。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">docker</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> swarm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> init</span></span></code></pre>\n<p>設定完成後可以在 portainer 選單看到 service 項目<br />\n<img loading=\"lazy\" src=\"portainer-menu.png\" alt=\"portainer menu\" /></p>\n<p>進入 service 可以開啟 <code>Service webhook</code>，產生的網址就是用來接收通知拉取新 image 重新佈署的<br />\n<img loading=\"lazy\" src=\"portainer-service.png\" alt=\"portainer service\" /></p>\n<p>更新 service 還要呼叫 webhook 有點麻煩，就交給 GitHub Action 吧！<br />\n在 GitHub Action 加入下面設定後，每次 push/pull_request (根據 main.yml設定) 就會自動更新。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yml\"><span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">-</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Call webhook update service</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  uses</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> muinmomin/webhook-action@v1.0.0</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  with</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    url</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # 要通知的 webhook URL</span></span></code></pre>\n<p><div class=\"links\"><div class=\"item\" title=\"portainer.io\" style=\"--block-color:#e9546b;\"><a href=\"https://docs.portainer.io/user/docker/services/webhooks#enabling-a-service-webhook\" class=\"image\" data-background-image=\"/assets/404.png\"></a>\n        <div class=\"info\">\n        <a href=\"https://docs.portainer.io/user/docker/services/webhooks#enabling-a-service-webhook\" class=\"title\">Enabling a service webhook</a>\n        <p class=\"desc\">https://docs.portainer.io/user/docker/services/webhooks#enabling-a-service-webhook</p>\n        </div></div><div class=\"item\" title=\"muinmomin/webhook-action\" style=\"--block-color:#e9546b;\"><a href=\"https://github.com/muinmomin/webhook-action\" class=\"image\" data-background-image=\"/assets/404.png\"></a>\n        <div class=\"info\">\n        <a href=\"https://github.com/muinmomin/webhook-action\" class=\"title\">muinmomin/webhook-action</a>\n        <p class=\"desc\">https://github.com/muinmomin/webhook-action</p>\n        </div></div></div></p>\n<h1 id=\"正式站測試站\"><a class=\"anchor\" href=\"#正式站測試站\">#</a> 正式站/測試站</h1>\n<p>當業務外出跟客戶展示系統功能時，如果網站一直更新重啟服務中斷，在客戶端的同學會很尷尬，<span class=\"spoiler\" title=\"你知道得太多了\">所以有業務在外展示時工程師可以放假</span> 所以把線上系統分為正式站跟測試站。</p>\n<ul class=\"task-list\">\n<li class=\"task-list-item\"><input type=\"checkbox\" id=\"cbx_0\" checked=\"true\" disabled=\"true\" /><label for=\"cbx_0\"> GCP VM 兩台變四台</label></li>\n<li class=\"task-list-item\"><input type=\"checkbox\" id=\"cbx_1\" checked=\"true\" disabled=\"true\" /><label for=\"cbx_1\"> 設定測試站 subdomain</label></li>\n<li class=\"task-list-item\"><input type=\"checkbox\" id=\"cbx_2\" checked=\"true\" disabled=\"true\" /><label for=\"cbx_2\"> GitHub 設定 main/test 兩個分支</label></li>\n<li class=\"task-list-item\"><input type=\"checkbox\" id=\"cbx_3\" checked=\"true\" disabled=\"true\" /><label for=\"cbx_3\"> GitHub Action 設定只有 test 分支 push 時才自動更新到測試站</label></li>\n<li class=\"task-list-item\"><input type=\"checkbox\" id=\"cbx_4\" disabled=\"true\" /><label for=\"cbx_4\"> 你升職，我加薪，大家開香檳～ＹＡ</label></li>\n</ul>\n<p><img loading=\"lazy\" src=\"Love-on-Delivery.gif\" alt=\"破壞之王\" /></p>\n",
            "tags": [
                "Need to Know",
                "Story"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/02/11/redis-cluster/",
            "url": "https://8loser.github.io/2023/02/11/redis-cluster/",
            "title": "GKE 佈署 Redis 叢集",
            "date_published": "2023-02-11T10:07:24.000Z",
            "content_html": "<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<p>在 <a href=\"https://cloud.google.com/kubernetes-engine?hl=zh-tw\">Google Kubernetes Engine (GKE)</a> 內建置 Redis 叢集</p>\n<ul>\n<li>GKE node: g1-small * 2 (version 1.24.8-gke.2000)</li>\n<li>Redis 7</li>\n</ul>\n<h1 id=\"建立-configmap\"><a class=\"anchor\" href=\"#建立-configmap\">#</a> 建立 ConfigMap</h1>\n<p>建立給 redis 節點使用的共用設定檔，名稱為 <code>redis-cluster</code>，之後建立為 volume 讓每個容器使用</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">apiVersion</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> v1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">kind</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ConfigMap</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">metadata</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">data</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  update-node.sh</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> |</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    #!/bin/sh</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    REDIS_NODES=\"/data/nodes.conf\"</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    sed -i -e \"/myself/ s/[0-9]\\&#123;1,3\\&#125;\\.[0-9]\\&#123;1,3\\&#125;\\.[0-9]\\&#123;1,3\\&#125;\\.[0-9]\\&#123;1,3\\&#125;/$&#123;POD_IP&#125;/\" $&#123;REDIS_NODES&#125;</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    exec \"$@\"</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  redis.conf</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> |</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">+</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    cluster-enabled yes</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    cluster-require-full-coverage no</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    cluster-node-timeout 15000</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    cluster-config-file /data/nodes.conf</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    cluster-migration-barrier 1</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    appendonly yes</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    protected-mode no</span></span></code></pre>\n<p>建立成功後可以在 GKE 界面的 <code>Secret與ConfigMap</code> 內看到新增的 <code>redis-cluster</code></p>\n<p><img loading=\"lazy\" src=\"gke-configmap.png\" alt=\"ConfigMap\" /></p>\n<h1 id=\"建立-statefulset\"><a class=\"anchor\" href=\"#建立-statefulset\">#</a> 建立 StatefulSet</h1>\n<ul>\n<li>StatefulSet 需要使用 Kubernetes v1.9 或之後的版本才支援</li>\n<li>.spec.selector.matchLabels 要跟 .spec.template.metadata.labels 相同</li>\n<li>.spec.replicas 至少要 6 個，redis cluster 至少要 6 個節點; 3 個 master, 3 個 slave, 不過還沒查到為什麼。</li>\n</ul>\n<p>參考文章在 .spec.selector.template.spec.containers.volumes.defaultMode 是寫 <code>0755</code>(八進位)，但是我在 Lens 內執行時會出現下面錯誤，所以改成十進位的 <code>493</code></p>\n<div class=\"note warning\">\n<p>create Pod redis-cluster-0 in StatefulSet redis-cluster failed error: Pod &quot;redis-cluster-0&quot; is invalid: [spec.volumes[1].configMap.defaultMode: Invalid value: 755: must be a number between 0 and 0777 (octal), both inclusive, spec.containers[0].volumeMounts[0].name: Not found: &quot;conf&quot;]</p>\n</div>\n<p>完整 yaml</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">apiVersion</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> apps/v1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">kind</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> StatefulSet</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">metadata</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">spec</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  serviceName</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  replicas</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 6</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # by default is 1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  selector</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    matchLabels</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      app</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # has to match .spec.template.metadata.labels</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  template</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    metadata</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      labels</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        app</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\"> # has to match .spec.selector.matchLabels</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    spec</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      containers</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">      -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        image</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis:7</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        ports</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> containerPort</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 6379</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> containerPort</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 16379</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> gossip</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        command</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/conf/update-node.sh</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">redis-server</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">/conf/redis.conf</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        env</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> POD_IP</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          valueFrom</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">            fieldRef</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">              fieldPath</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> status.podIP</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        volumeMounts</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> conf</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          mountPath</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /conf</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          readOnly</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> false</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">        -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> data</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          mountPath</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /data</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          readOnly</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> false</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      volumes</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">      -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> conf</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        configMap</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">          # defaultMode: 0755</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          defaultMode</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 493</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  volumeClaimTemplates</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">  -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> metadata</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> data</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    spec</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      accessModes</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">[</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">ReadWriteOnce</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\"> </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">]</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">      resources</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">        requests</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">          storage</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 1Gi</span></span></code></pre>\n<p>執行完等一下<br />\n<img loading=\"lazy\" src=\"Let_the_Bullets_Fly.png\" alt=\"讓子彈飛一會兒\" /></p>\n<p>之後可以在 GKE 的 <code>工作負載</code> 內看到 6 個 Pod 建立完成<br />\n<img loading=\"lazy\" src=\"gke-stateful.png\" alt=\"Pods\" /></p>\n<p><code>儲存空間</code> 也會產生 6 個 PVC (PersistentVolumeClaim)<br />\n<img loading=\"lazy\" src=\"gke-pvc.png\" alt=\"PVC\" /></p>\n<p>或者使用指令查看</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">NAME</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">              READY</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   STATUS</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    RESTARTS</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   AGE</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster-0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1/1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Running</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          8m31s</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster-1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1/1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Running</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          8m19s</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster-2</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1/1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Running</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          8m7s</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster-3</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1/1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Running</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          7m56s</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster-4</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1/1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Running</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          7m39s</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster-5</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1/1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">     Running</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">          7m28s</span></span></code></pre>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pv</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">NAME</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">                                       CAPACITY</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   ACCESS</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> MODES</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   RECLAIM</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> POLICY</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   STATUS</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   CLAIM</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">                          STORAGECLASS</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   REASON</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   AGE</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pvc-01fc7eb6-e99a-4111-8923-53229eb112b4</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1Gi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        RWO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            Delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">           Bound</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    default/data-redis-cluster-1</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   standard-rwo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            21m</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pvc-4bc387e4-dd4c-4400-b99a-e75a720e509b</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1Gi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        RWO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            Delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">           Bound</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    default/data-redis-cluster-2</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   standard-rwo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            21m</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pvc-af768f00-7889-4fa5-9ef8-cb95337ef38a</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1Gi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        RWO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            Delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">           Bound</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    default/data-redis-cluster-5</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   standard-rwo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            20m</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pvc-b6dadea9-b10e-42cb-8ae4-729b186e8b7c</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1Gi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        RWO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            Delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">           Bound</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    default/data-redis-cluster-0</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   standard-rwo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            21m</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pvc-c42ebfa4-a726-42f5-b146-18c28de544a4</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1Gi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        RWO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            Delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">           Bound</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    default/data-redis-cluster-4</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   standard-rwo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            20m</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">pvc-d1d4de96-2f91-4050-9707-8fd802bd7fb4</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   1Gi</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        RWO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            Delete</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">           Bound</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    default/data-redis-cluster-3</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   standard-rwo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            21m</span></span></code></pre>\n<h1 id=\"建立-service\"><a class=\"anchor\" href=\"#建立-service\">#</a> 建立 Service</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-yaml\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">apiVersion</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> v1</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">kind</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Service</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">metadata</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">spec</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  ports</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">  -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> port</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 6379</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    targetPort</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 6379</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> client</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">  -</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> port</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 16379</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    targetPort</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 16379</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    name</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> gossip</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">  selector</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">    app</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span></code></pre>\n<p>執行完檢查 Service <code>redis-cluster</code> 是否成功建立<br />\n<img loading=\"lazy\" src=\"gke-service.png\" alt=\"service\" /></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> service</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">NAME</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">            TYPE</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        CLUSTER-IP</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">    EXTERNAL-IP</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   PORT</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">S</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">              AGE</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">redis-cluster</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   ClusterIP</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\">   10.80.5.184</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">   </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">non</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">e</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">        6379/TCP,16379/TCP</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">   2m50s</span></span></code></pre>\n<h1 id=\"建立-redis-叢集\"><a class=\"anchor\" href=\"#建立-redis-叢集\">#</a> 建立 Redis 叢集</h1>\n<p>參考的文章使用的指令如下</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> exec</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -it</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster-0</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cli</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --cluster</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> create</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --cluster-replicas</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#999999;--shiki-dark:#666666\"> $</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -l</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> app=redis-cluster</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -o</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> jsonpath=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">&#123;range.items[*]&#125;&#123;.status.podIP&#125;:6379 </span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span></code></pre>\n<p>實際實行結果會出現如下錯誤</p>\n<div class=\"note danger\">\n<p>kubectl exec -it redis-cluster-0 -- redis-cli --cluster create --cluster-replicas 1 $(kubectl get pods -l app=redis-cluster -o jsonpath='{range.items[*]}{.status.podIP}:6379 ')</p>\n</div>\n<p>查看原因是 <code>kubectl get pods -l app=redis-cluster -o jsonpath='&#123;range.items[*]&#125;&#123;.status.podIP&#125;</code> 會多一組空的 IP</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -l</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> app=redis-cluster</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -o</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> jsonpath=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">&#123;range.items[*]&#125;&#123;.status.podIP&#125;:6379 </span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.2.30:6379</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 10.76.1.32:6379</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 10.76.2.31:6379</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 10.76.1.33:6379</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 10.76.2.32:6379</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 10.76.1.34:6379</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> :6379</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\"> </span></span></code></pre>\n<p>可以使用下面指令查看比較明顯</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">$</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -l</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> app=redis-cluster</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -o</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> jsonpath=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">&#123;range.items[*]&#125;&#123;.status.podIP&#125;:6379&#123;\"\\n\"&#125;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.2.30:6379</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.1.32:6379</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.2.31:6379</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.1.33:6379</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.2.32:6379</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">10.76.1.34:6379</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">:6379</span></span></code></pre>\n<p>修改後指令如下</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> exec</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -it</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster-0</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cli</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --cluster</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> create</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --cluster-replicas</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 1</span><span style=\"color:#999999;--shiki-dark:#666666\"> $</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> pods</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -o=jsonpath=</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">&#123;range .items[*]&#125;&#123;.status.podIP&#125;:6379 &#123;end&#125;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span></code></pre>\n<p><code>Can I set the above configuration?</code> 輸入 <code>yes</code>，執行結果可以看到建立了 3 個 redis master 節點，3 個 slave 節點。</p>\n<details class=\"info\"><summary>執行結果</summary><div>\n<p>$ kubectl exec -it redis-cluster-0 -- redis-cli --cluster create --cluster-replicas 1 $(kubectl get pods -o=jsonpath='{range .items[*]}{.status.podIP}:6379 {end}')<br />\n&gt;&gt;&gt; Performing hash slots allocation on 6 nodes...<br />\nMaster[0] -&gt; Slots 0 - 5460<br />\nMaster[1] -&gt; Slots 5461 - 10922<br />\nMaster[2] -&gt; Slots 10923 - 16383<br />\nAdding replica 10.76.2.32:6379 to 10.76.2.30:6379<br />\nAdding replica 10.76.1.34:6379 to 10.76.1.32:6379<br />\nAdding replica 10.76.1.33:6379 to 10.76.2.31:6379<br />\nM: 05ecf3912ed0a5517ab74fd17f81d02a3314a833 10.76.2.30:6379<br />\nslots:[0-5460] (5461 slots) master<br />\nM: 9da23852cfd0a7489c2f53ec743363e48dea378b 10.76.1.32:6379<br />\nslots:[5461-10922] (5462 slots) master<br />\nM: 379127b6974b6acc59e5fb194361749e682bdc97 10.76.2.31:6379<br />\nslots:[10923-16383] (5461 slots) master<br />\nS: 5105cdd3a6637e3844316c557f81c68b231703e7 10.76.1.33:6379<br />\nreplicates 379127b6974b6acc59e5fb194361749e682bdc97<br />\nS: dde0a9ef27e044cd124814529305732c9ab0d9dd 10.76.2.32:6379<br />\nreplicates 05ecf3912ed0a5517ab74fd17f81d02a3314a833<br />\nS: 8a89a45f3161b606b13f1ede95fec240de2bcf9d 10.76.1.34:6379<br />\nreplicates 9da23852cfd0a7489c2f53ec743363e48dea378b<br />\nCan I set the above configuration? (type 'yes' to accept): yes<br />\n&gt;&gt;&gt; Nodes configuration updated<br />\n&gt;&gt;&gt; Assign a different config epoch to each node<br />\n&gt;&gt;&gt; Sending CLUSTER MEET messages to join the cluster<br />\nWaiting for the cluster to join</p>\n<p>&gt;&gt;&gt; Performing Cluster Check (using node 10.76.2.30:6379)<br />\nM: 05ecf3912ed0a5517ab74fd17f81d02a3314a833 10.76.2.30:6379<br />\nslots:[0-5460] (5461 slots) master<br />\n1 additional replica(s)<br />\nS: dde0a9ef27e044cd124814529305732c9ab0d9dd 10.76.2.32:6379<br />\nslots: (0 slots) slave<br />\nreplicates 05ecf3912ed0a5517ab74fd17f81d02a3314a833<br />\nS: 5105cdd3a6637e3844316c557f81c68b231703e7 10.76.1.33:6379<br />\nslots: (0 slots) slave<br />\nreplicates 379127b6974b6acc59e5fb194361749e682bdc97<br />\nM: 379127b6974b6acc59e5fb194361749e682bdc97 10.76.2.31:6379<br />\nslots:[10923-16383] (5461 slots) master<br />\n1 additional replica(s)<br />\nM: 9da23852cfd0a7489c2f53ec743363e48dea378b 10.76.1.32:6379<br />\nslots:[5461-10922] (5462 slots) master<br />\n1 additional replica(s)<br />\nS: 8a89a45f3161b606b13f1ede95fec240de2bcf9d 10.76.1.34:6379<br />\nslots: (0 slots) slave<br />\nreplicates 9da23852cfd0a7489c2f53ec743363e48dea378b<br />\n[OK] All nodes agree about slots configuration.<br />\n&gt;&gt;&gt; Check for open slots...<br />\n&gt;&gt;&gt; Check slots coverage...<br />\n[OK] All 16384 slots covered.</p>\n</div></details>\n<h1 id=\"驗證-redis-叢集\"><a class=\"anchor\" href=\"#驗證-redis-叢集\">#</a> 驗證 Redis 叢集</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> exec</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -it</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster-0</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cli</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> cluster</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> info</span></span></code></pre>\n<details class=\"info\"><summary>執行結果</summary><div>\n<p>$ kubectl exec -it redis-cluster-0 -- redis-cli cluster info<br />\ncluster_state:ok<br />\ncluster_slots_assigned:16384<br />\ncluster_slots_ok:16384<br />\ncluster_slots_pfail:0<br />\ncluster_slots_fail:0<br />\ncluster_known_nodes:6<br />\ncluster_size:3<br />\ncluster_current_epoch:6<br />\ncluster_my_epoch:1<br />\ncluster_stats_messages_ping_sent:273<br />\ncluster_stats_messages_pong_sent:278<br />\ncluster_stats_messages_sent:551<br />\ncluster_stats_messages_ping_received:273<br />\ncluster_stats_messages_pong_received:273<br />\ncluster_stats_messages_meet_received:5<br />\ncluster_stats_messages_received:551<br />\ntotal_cluster_links_buffer_limit_exceeded:0</p>\n</div></details>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">for</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> x</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> in</span><span style=\"color:#999999;--shiki-dark:#666666\"> $</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">seq</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 0</span><span style=\"color:#2F798A;--shiki-dark:#4C9A91\"> 5</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> do</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> echo</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">redis-cluster-$x</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> kubectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> exec</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cluster-</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">$x</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> redis-cli</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> role</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span><span style=\"color:#998418;--shiki-dark:#B8A965\"> echo</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span><span style=\"color:#1E754F;--shiki-dark:#4D9375\"> done</span></span></code></pre>\n<details class=\"info\"><summary>執行結果</summary><div>\n<p>$ for x in <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mo stretchy=\"false\">(</mo><mi>s</mi><mi>e</mi><mi>q</mi><mn>05</mn><mo stretchy=\"false\">)</mo><mo separator=\"true\">;</mo><mi>d</mi><mi>o</mi><mi>e</mi><mi>c</mi><mi>h</mi><mi>o</mi><mi mathvariant=\"normal\">&quot;</mi><mi>r</mi><mi>e</mi><mi>d</mi><mi>i</mi><mi>s</mi><mo>−</mo><mi>c</mi><mi>l</mi><mi>u</mi><mi>s</mi><mi>t</mi><mi>e</mi><mi>r</mi><mo>−</mo></mrow><annotation encoding=\"application/x-tex\">(seq 0 5); do echo &quot;redis-cluster-</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:1em;vertical-align:-0.25em;\"></span><span class=\"mopen\">(</span><span class=\"mord mathnormal\">se</span><span class=\"mord mathnormal\" style=\"margin-right:0.03588em;\">q</span><span class=\"mord\">05</span><span class=\"mclose\">)</span><span class=\"mpunct\">;</span><span class=\"mspace\" style=\"margin-right:0.1667em;\"></span><span class=\"mord mathnormal\">d</span><span class=\"mord mathnormal\">oec</span><span class=\"mord mathnormal\">h</span><span class=\"mord mathnormal\">o</span><span class=\"mord\">&quot;</span><span class=\"mord mathnormal\">re</span><span class=\"mord mathnormal\">d</span><span class=\"mord mathnormal\">i</span><span class=\"mord mathnormal\">s</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span><span class=\"mbin\">−</span><span class=\"mspace\" style=\"margin-right:0.2222em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:0.7778em;vertical-align:-0.0833em;\"></span><span class=\"mord mathnormal\">c</span><span class=\"mord mathnormal\" style=\"margin-right:0.01968em;\">l</span><span class=\"mord mathnormal\">u</span><span class=\"mord mathnormal\">s</span><span class=\"mord mathnormal\">t</span><span class=\"mord mathnormal\" style=\"margin-right:0.02778em;\">er</span><span class=\"mord\">−</span></span></span></span>x&quot;; kubectl exec redis-cluster-$x -- redis-cli role; echo; done<br />\nredis-cluster-0<br />\nmaster<br />\n504<br />\n10.76.2.32<br />\n6379<br />\n504</p>\n<p>redis-cluster-1<br />\nmaster<br />\n504<br />\n10.76.1.34<br />\n6379<br />\n504</p>\n<p>redis-cluster-2<br />\nmaster<br />\n490<br />\n10.76.1.33<br />\n6379<br />\n490</p>\n<p>redis-cluster-3<br />\nslave<br />\n10.76.2.31<br />\n6379<br />\nconnected<br />\n490</p>\n<p>redis-cluster-4<br />\nslave<br />\n10.76.2.30<br />\n6379<br />\nconnected<br />\n504</p>\n<p>redis-cluster-5<br />\nslave<br />\n10.76.1.32<br />\n6379<br />\nconnected<br />\n504</p>\n</div></details>\n<h1 id=\"yaml-檔案\"><a class=\"anchor\" href=\"#yaml-檔案\">#</a> YAML 檔案</h1>\n<p><a href=\"https://github.com/8loser/redis-sts\">GitHub: 8loser/redis-sts</a></p>\n<h1 id=\"參考\"><a class=\"anchor\" href=\"#參考\">#</a> 參考</h1>\n<p><a href=\"https://www.suse.com/c/rancher_blog/deploying-redis-cluster-on-top-of-kubernetes/\">Deploying Redis Cluster on Top of Kubernetes February 22, 2019 | By: Rancher Admin</a></p>\n",
            "tags": [
                "Redis",
                "GKE",
                "Kubernetes"
            ]
        },
        {
            "id": "https://8loser.github.io/2023/01/04/gcp-init/",
            "url": "https://8loser.github.io/2023/01/04/gcp-init/",
            "title": "GCP VM 初始化",
            "date_published": "2023-01-04T07:32:51.000Z",
            "content_html": "<h1 id=\"主旨\"><a class=\"anchor\" href=\"#主旨\">#</a> 主旨</h1>\n<p>把專案運行的 GCP VM 環境佈署動作整理，建立 shell script 進行快速佈署。使用作業系統為 Linux Ubuntu</p>\n<h1 id=\"執行之前\"><a class=\"anchor\" href=\"#執行之前\">#</a> 執行之前</h1>\n<p>先執行下面指令，切換為 root 角色</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-shell\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> su</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> -</span></span></code></pre>\n<h1 id=\"要新增的-linux-帳號\"><a class=\"anchor\" href=\"#要新增的-linux-帳號\">#</a> 要新增的 Linux 帳號</h1>\n<p>把要新增的帳號、密碼作為變數寫在 script 最上面，為了給共同維護的人登入用的。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">ACCOUNT</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">帳號</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">PASSWORD</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">密碼</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span></span></code></pre>\n<h1 id=\"github-帳號\"><a class=\"anchor\" href=\"#github-帳號\">#</a> GitHub 帳號</h1>\n<p>執行環境會把 docker image 從 GitHub registry 抓下來執行，所以在 script 最上面把 GitHub 權限設定為變數，之後執行讀取變數即可。密碼為 PAT（personal access token），建立方法請參考 <a href=\"https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token\">建立 PAT</a>。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">GITHUB_ACCOUNT</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">github帳號</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span></span>\n<span class=\"line\"><span style=\"color:#B07D48;--shiki-dark:#BD976A\">GITHUB_PAT</span><span style=\"color:#999999;--shiki-dark:#666666\">=</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">github</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> PAT</span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">></span></span></code></pre>\n<h1 id=\"設定時區\"><a class=\"anchor\" href=\"#設定時區\">#</a> 設定時區</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">timedatectl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> set-timezone</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> Asia/Taipei</span></span></code></pre>\n<h1 id=\"作業系統更新\"><a class=\"anchor\" href=\"#作業系統更新\">#</a> 作業系統更新</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> update</span><span style=\"color:#999999;--shiki-dark:#666666\"> &#x26;&#x26;</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> apt</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> upgrade</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -y</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> clean</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -y</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> autoremove</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -y</span></span></code></pre>\n<h1 id=\"啟用-google-bbr\"><a class=\"anchor\" href=\"#啟用-google-bbr\">#</a> 啟用 Google BBR</h1>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">modprobe</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> tcp_bbr</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">tcp_bbr</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/modules-load.d/modules.conf</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">net.core.default_qdisc=fq</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/sysctl.conf</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">net.ipv4.tcp_congestion_control=bbr</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/sysctl.conf</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">sysctl</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span></span></code></pre>\n<h1 id=\"install-ops-agent\"><a class=\"anchor\" href=\"#install-ops-agent\">#</a> Install Ops Agent</h1>\n<p>安裝 <a href=\"https://cloud.google.com/stackdriver/docs/solutions/agents/ops-agent\">GCP agent</a>，如果不是使用 GCP 可以不用執行</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">curl</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -sSO</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">bash</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> add-google-cloud-ops-agent-repo.sh</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --also-install</span></span></code></pre>\n<h1 id=\"安裝-docker\"><a class=\"anchor\" href=\"#安裝-docker\">#</a> 安裝 Docker</h1>\n<p>所有專案都以 container 形式使用 docker compose 運行。安裝 docker 沒毛病。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> update</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ca-certificates</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> curl</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> gnupg</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> lsb-release</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -y</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">mkdir</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/apt/keyrings</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">curl</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -fsSL</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> https://download.docker.com/linux/ubuntu/gpg</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> |</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> gpg</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --dearmor</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -o</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/apt/keyrings/docker.gpg</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> \\</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">  \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">deb [arch=</span><span style=\"color:#999999;--shiki-dark:#666666\">$(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">dpkg</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --print-architecture</span><span style=\"color:#999999;--shiki-dark:#666666\">)</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu </span><span style=\"color:#A65E2B;--shiki-dark:#C99076\">\\</span></span>\n<span class=\"line\"><span style=\"color:#999999;--shiki-dark:#666666\">  $(</span><span style=\"color:#59873A;--shiki-dark:#80A665\">lsb_release</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -cs</span><span style=\"color:#999999;--shiki-dark:#666666\">)</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> stable</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> |</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> sudo</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> tee</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/apt/sources.list.d/docker.list</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /dev/null</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> update</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">apt-get</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> install</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> docker-ce</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> docker-ce-cli</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> containerd.io</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> docker-compose-plugin</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -y</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -q</span></span></code></pre>\n<h1 id=\"安裝-portainer-with-docker-swarm\"><a class=\"anchor\" href=\"#安裝-portainer-with-docker-swarm\">#</a> 安裝 Portainer with Docker Swarm</h1>\n<p>使用 portainer 管理 container，portainer 很棒，是 web 界面。加上 docker swarm 是為了讓程式上傳後可以通知 server 去 pull 新的 image 自動佈署。<code>一言不合就 CI/CD</code></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">docker</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> volume</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> create</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> portainer_data</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">docker</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> run</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -d</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 8000:8000</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 9443:9443</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -p</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> 9000:9000</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --name</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> portainer</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --restart=always</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -v</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /var/run/docker.sock:/var/run/docker.sock</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -v</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> portainer_data:/data</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> portainer/portainer-ce:latest</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">docker</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> swarm</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> init</span></span></code></pre>\n<h1 id=\"建立-crond-docker-prune\"><a class=\"anchor\" href=\"#建立-crond-docker-prune\">#</a> 建立 crond docker-prune</h1>\n<p>每天自動清理 docker 垃圾，每次更新產生沒用的 image、cointainer 太多會造成主機容量不足掛掉。<br />\n<s>不然每次掛掉老闆都罵我，我也不知道為什麼有人一天要 commit 600次</s></p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">cat</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">></span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/cron.daily/docker-prune</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> </span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\">&#x3C;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> EOL</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">#!/bin/bash</span></span>\n<span class=\"line\"><span style=\"color:#B56959;--shiki-dark:#C98A7D\">docker system prune -af</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">EOL</span></span>\n<span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">chmod</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ugo+x</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /etc/cron.daily/docker-prune</span></span></code></pre>\n<h1 id=\"建立-linux-帳號\"><a class=\"anchor\" href=\"#建立-linux-帳號\">#</a> 建立 Linux 帳號</h1>\n<p>使用最上面設定的變數建立帳號，讓其他維護的同學登入。</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">useradd</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -c</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">update user</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -s</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> /bin/bash</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -G</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> sudo</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> -m</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> $ACCOUNT</span></span>\n<span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> \"</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">$ACCOUNT:$PASSWORD</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">\"</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> |</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> chpasswd</span></span></code></pre>\n<h1 id=\"登入-docker-registry\"><a class=\"anchor\" href=\"#登入-docker-registry\">#</a> 登入 Docker registry</h1>\n<p>登入 GitHub registry 之後 docker pull 就不用輸入密碼，讚！</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-bash\"><span class=\"line\"><span style=\"color:#998418;--shiki-dark:#B8A965\">echo</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> $GITHUB_PAT</span><span style=\"color:#AB5959;--shiki-dark:#CB7676\"> |</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> docker</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> login</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\"> ghcr.io</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --username</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\"> $GITHUB_ACCOUNT</span><span style=\"color:#A65E2B;--shiki-dark:#C99076\"> --password-stdin</span></span></code></pre>\n<h1 id=\"完整-shell-script\"><a class=\"anchor\" href=\"#完整-shell-script\">#</a> 完整 shell script</h1>\n<ul>\n<li><a href=\"https://gist.github.com/8loser/f54b673b93a2df296887c2bd6edf96e8\">https://gist.github.com/8loser/f54b673b93a2df296887c2bd6edf96e8</a></li>\n</ul>\n",
            "tags": [
                "Linux",
                "Shell script",
                "GCP"
            ]
        },
        {
            "id": "https://8loser.github.io/2022/10/21/nginx-websocket/",
            "url": "https://8loser.github.io/2022/10/21/nginx-websocket/",
            "title": "Nginx websocket 無法連接",
            "date_published": "2022-10-21T09:10:38.000Z",
            "content_html": "<h1 id=\"環境\"><a class=\"anchor\" href=\"#環境\">#</a> 環境</h1>\n<p>在 docker compose 中，使用 nginx 導向 websocket 到 container 內</p>\n<h1 id=\"錯誤訊息\"><a class=\"anchor\" href=\"#錯誤訊息\">#</a> 錯誤訊息</h1>\n<p>websocket 連接不上，查看 request headers 顯示 <code>Provisional headers are shown</code></p>\n<p><img loading=\"lazy\" src=\"provisional_headers_are_shown.png\" alt=\"provisional headers are shown\" /></p>\n<h1 id=\"solution\"><a class=\"anchor\" href=\"#solution\">#</a> Solution</h1>\n<p>搜尋解決方法是在 nginx.conf 內加上</p>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-nginx\"><span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Upgrade </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">http_upgrade</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Connection </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"upgrade\"</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span></code></pre>\n<h2 id=\"範例\"><a class=\"anchor\" href=\"#範例\">#</a> 範例</h2>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-nginx\"><span class=\"line\"><span style=\"color:#AB5959;--shiki-dark:#CB7676\">location</span><span style=\"color:#59873A;--shiki-dark:#80A665\"> /backend/ </span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#123;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_pass </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">http://backend/</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Host </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">host</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Real-IP </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">remote_addr</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Forwarded-For </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">proxy_add_x_forwarded_for</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">X-Custom-Referrer smc_identity_layer</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#A0ADA0;--shiki-dark:#758575DD\">    # websocket 要加下面兩個</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Upgrade </span><span style=\"color:#999999;--shiki-dark:#666666\">$</span><span style=\"color:#B07D48;--shiki-dark:#BD976A\">http_upgrade</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#1E754F;--shiki-dark:#4D9375\">    proxy_set_header </span><span style=\"color:#393A34;--shiki-dark:#DBD7CAEE\">Connection </span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">\"upgrade\"</span><span style=\"color:#999999;--shiki-dark:#666666\">;</span></span>\n<span class=\"line\"><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">&#125;</span></span></code></pre>\n<h1 id=\"驗證\"><a class=\"anchor\" href=\"#驗證\">#</a> 驗證</h1>\n<p>比較有修正前跟修正後，後端實際收到的 request header</p>\n<h2 id=\"修正前-request-headers\"><a class=\"anchor\" href=\"#修正前-request-headers\">#</a> 修正前 request headers</h2>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Headers</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">host</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">localhost</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">x-real-ip</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">192.168.224.1</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">x-forwarded-for</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">192.168.224.1</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">x-custom-referrer</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">smc_identity_layer</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">connection</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">close</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">pragma</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">no-cache</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">cache-control</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">no-cache</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">user-agent</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">origin</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">chrome-extension://cbcbkhdmedgianpaifchdaddpnmgnknn</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">sec-websocket-version</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">13</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">accept-encoding</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">gzip, deflate, br</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">accept-language</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">sec-websocket-key</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">iBVj1v1X8xkD324lsXZSUA==</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">sec-websocket-extensions</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">permessage-deflate; client_max_window_bits</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span></code></pre>\n<h2 id=\"修正後-request-headers\"><a class=\"anchor\" href=\"#修正後-request-headers\">#</a> 修正後 request headers</h2>\n<pre class=\"shiki shiki-themes vitesse-light vitesse-dark\" style=\"background-color:#ffffff;--shiki-dark-bg:#121212;color:#393a34;--shiki-dark:#dbd7caee\" tabindex=\"0\"><code class=\"language-js\"><span class=\"line\"><span style=\"color:#59873A;--shiki-dark:#80A665\">Headers</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">(</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#123;</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">host</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">localhost</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">x-real-ip</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">192.168.208.1</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">x-forwarded-for</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">192.168.208.1</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">x-custom-referrer</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">smc_identity_layer</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">upgrade</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">websocket</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">connection</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">upgrade</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">pragma</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">no-cache</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">cache-control</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">no-cache</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">user-agent</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/106.0.0.0 Safari/537.36</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">origin</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">chrome-extension://cbcbkhdmedgianpaifchdaddpnmgnknn</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">sec-websocket-version</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">13</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">accept-encoding</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">gzip, deflate, br</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">accept-language</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">zh-TW,zh;q=0.9,en-US;q=0.8,en;q=0.7</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">sec-websocket-key</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">j2NwT86WmJt6rIyatTa5rw==</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">,</span></span>\n<span class=\"line\"><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">sec-websocket-extensions</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#999999;--shiki-dark:#666666\">:</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\"> '</span><span style=\"color:#B56959;--shiki-dark:#C98A7D\">permessage-deflate; client_max_window_bits</span><span style=\"color:#B5695977;--shiki-dark:#C98A7D77\">'</span><span style=\"color:#1e754f;--shiki-dark:#4d9375\">&#125;</span><span style=\"color:#2993a3;--shiki-dark:#5eaab5\">)</span></span></code></pre>\n",
            "tags": [
                "Trouble",
                "Nginx",
                "WebSocket"
            ]
        }
    ]
}