<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Posts on Hallblazzar: 開發者日誌</title>
    <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/</link>
    <description>Recent content in Posts on Hallblazzar: 開發者日誌</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <lastBuildDate>Sat, 23 May 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://dev-journal.hallblazzar.dev/zh-tw/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>NetBird 引起的 DNS 解析問題</title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_may_23_netbird_dns/</link>
      <pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_may_23_netbird_dns/</guid>
      <description>&lt;p&gt;如果您使用 NetBird 作為 VPN 解決方案，且偶爾會遇到 DNS 解析問題，本文或許能有所幫助。&lt;/p&gt;&#xA;&lt;h1 id=&#34;背景&#34;&gt;&#xA;  背景&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#%e8%83%8c%e6%99%af&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h1&gt;&#xA;&lt;p&gt;我使用 &lt;a href=&#34;https://netbird.io/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;NetBird&lt;/a&gt; 來作為我的 HomeLab 的連線方案已經有一段時間了。我選擇它的原因有以下幾點：&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;自主權 — 我可以自行部署並完全掌控所有元件。&lt;/li&gt;&#xA;&lt;li&gt;開箱即用 — NetBird 提供了優秀的網頁版 GUI 來管理用戶端與憑證。我可以透過設計良好的網頁介面控制一切，不需要與設定檔或 CLI 頻繁互動。&lt;/li&gt;&#xA;&lt;li&gt;自訂 DNS — 為了讓我的 HomeLab 服務支援 HTTPS/TLS，我想將公開網域綁定並映射到 HomeLab 上的服務的 IP。然而，像 CloudFlare 這類的 DNS 提供商會限制公開網域與私有 IP 以預防 &lt;a href=&#34;https://en.wikipedia.org/wiki/DNS_rebinding&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DNS rebinding&lt;/a&gt;。因此，另一種替代方法是將私有 IP 綁定到私有 DNS 紀錄，並透過 CNAME 將它們映射到公開 DNS 紀錄。這種方法需要 VPN 支援自訂私有 DNS。雖然有些人可能完全依賴私有 DNS 和私有 IP，但這意味著他們必須自己管理憑證，因為一般像 &lt;a href=&#34;https://letsencrypt.org/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Let&amp;rsquo;s Encrypt&lt;/a&gt; 這類現代 SSL 發行機構都需要 DNS 驗證，而 Public Domain 對此是不可或缺的（特別是對於免費的解決方案而言）。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;其他 VPN 解決方案（例如 &lt;a href=&#34;https://www.zerotier.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;ZeroTier&lt;/a&gt; 和 &lt;a href=&#34;https://tailscale.com/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;TailScale&lt;/a&gt;）雖然也支援上述部分功能，但它們的 Control Plane 基本上不是開源的，且無法自建。&lt;/p&gt;</description>
    </item>
    <item>
      <title>關於遷移 Kubernetes CNI Plugin 的一些經驗談</title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_may_02_cni_migration/</link>
      <pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_may_02_cni_migration/</guid>
      <description>&lt;div class=&#34;notice tip&#34;&gt;&#xA;  &lt;div class=&#34;notice-title&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-lightbulb&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Tip&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;notice-content&#34;&gt;TL;DR：如果你正在為你的 Kubernetes 尋找替代的 CNI Plugin 與對應的遷移計劃，Cilium 是一個很好的選擇。它支援從幾乎任何 CNI 進行 Live-Migration ，並提供 Network Policy 與完整的的可觀測性生態系。&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;對於正在運行的 Kubernetes 叢集（特別是在 Production 上）而言，遷移 CNI Plugin 並不是簡單的任務。 一個簡易的策略是 &lt;a href=&#34;https://www.redhat.com/en/topics/devops/what-is-blue-green-deployment&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;藍綠部署（Blue/Green deployment）&lt;/a&gt;，即設定一個帶有目標 CNI 的新叢集，並將工作負載從舊叢集轉移到新叢集上。 這種方法能確保停機時間最少（仍需要額外設定，例如 Load Balancer 以確保可用性），且能忽略不同 CNI 之間的差異。 然而，如果必須進行「原地遷移」呢？ 雖然這種場景不常見且具挑戰性，但對於運行有 Stateful 或受託管的工作負載的叢集通常是必要的。 例如，平台透過 Kubernetes 提供運算資源，但營運人員無法控制部署在其中的服務。 如果營運人員希望在不讓使用者察覺或參與的情況下遷移 CNI，則仍需要原地遷移。&lt;/p&gt;&#xA;&lt;p&gt;原地遷移可以分為兩種類型：替換與 Live-Migration 。 替換意味著移除舊的 CNI 並安裝新的，這代表在移除與安裝之間會發生網路的連接中斷，且會令遷移結果不可預測；相較之下， Live-Migration 在遷移過程能夠中保持網路連接，以將對工作負載的影響降至最低。 大多數 CNI Plugin 完全不或僅有限度地支援 Live-Migration ，例如 &lt;a href=&#34;https://ovn-kubernetes.io/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;OVN-Kubernetes&lt;/a&gt;、&lt;a href=&#34;https://www.kube-router.io/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kube-Router&lt;/a&gt; 和 &lt;a href=&#34;https://github.com/flannel-io/flannel&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Flannel&lt;/a&gt;。 雖然有些 CNI 支援 Live-Migration ，但僅在特定條件下運作，例如 &lt;a href=&#34;https://docs.tigera.io/calico/latest/about/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Calico&lt;/a&gt; 支援&lt;a href=&#34;https://docs.tigera.io/calico/latest/getting-started/kubernetes/flannel/migration-from-flannel&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;從 Flannel 和 Canal 遷移&lt;/a&gt;，但不支援從其他 CNI Plugin 遷移。 缺乏 Live-Migration 支援源於 CNI Plugin 的設計。 &lt;a href=&#34;https://www.cni.dev/docs/spec/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;CNI Specification&lt;/a&gt; 定義了 Container Runtime 如何選擇 CNI Plugin 及其應支援的操作，但並未提供跨不同 CNI Plugin 運行時， Pod 之間能夠保持連通性的 Interface。有些使用者可能會考慮使用 &lt;a href=&#34;https://github.com/k8snetworkplumbingwg/multus-cni&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Multus&lt;/a&gt;。 該 Plugin 可以作為不同 CNI 之間的中介層運行，這使得支援任意 CNI Plugin 間的 Live-Migration 成為可能。 然而，根據我的經驗，混合不同 CNI 的 Network Interface 及相關的 IP table 規則時，Multus 並不總是能正常運作（取決於目標 CNI Plugin ），這使得 Multus 並非一個可靠的解決方案。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kubernetes Dynamic Client 需要指定複數形式的 Resource Type </title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_may_01_k8s_dyn_plural/</link>
      <pubDate>Fri, 01 May 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_may_01_k8s_dyn_plural/</guid>
      <description>&lt;p&gt;在 GoLang 中，除了調用 kubectl 之外，另一種存取 Kubernetes 叢集上 CRD 的方法是 Dynamic client。它允許使用者以程式化方式存取 CRD，而無需依賴 exec 以及 kubectl，後者需要處理 shell、exit code以及 STDIN/STDOUT。然而，這個 module 的文件似乎不夠完善。使用者經常遇到的一種常見錯誤類型是與 resource type not found 相關的錯誤，這可能會由以下程式碼觸發：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-go&#34; data-lang=&#34;go&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kd&#34;&gt;func&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;GetNetworkPolicy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Context&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;dynamic&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Interface&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;string&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kt&#34;&gt;error&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#x9;&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gvr&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;schema&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GroupVersionResource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Group&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;cilium.io&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Version&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;v2&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;Resource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;ciliumclusterwidenetworkpolicy&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;:=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;client&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Resource&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;gvr&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;).&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Get&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;ctx&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;policy&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;metav1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;GetOptions&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{})&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;!=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;nil&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;        &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;return&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;fmt&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nf&#34;&gt;Errof&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s&#34;&gt;&amp;#34;Error getting policy: %v&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;err&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;    &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;&#x9;&lt;/span&gt;&lt;span class=&#34;c1&#34;&gt;// ... strip&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;這裡的問題在於資源類型。雖然我沒有發現有文件直接提到這一點，但 &lt;code&gt;GroupVersionResource&lt;/code&gt; Structure 中， &lt;code&gt;Resource&lt;/code&gt; 的值應該要是&lt;strong&gt;複數&lt;/strong&gt;。因此，若要使上述程式碼正常運作，&lt;code&gt;ciliumclusterwidenetworkpolicy&lt;/code&gt; 應改為 &lt;code&gt;ciliumclusterwidenetworkpolicies&lt;/code&gt;。這與以 YAML 格式撰寫 K8s Object 時使用單數的情況不同。相反地，如果問題是找不到目標 Object，使用者應該會收到類似 &lt;code&gt;ciliumclusterwidenetworkpolicy not found&lt;/code&gt; 的錯誤。&lt;/p&gt;</description>
    </item>
    <item>
      <title>在開發流程中使用 Dev Containers</title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_20_devcontainer/</link>
      <pubDate>Mon, 20 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_20_devcontainer/</guid>
      <description>&lt;p&gt;這篇短文簡要介紹了我如何在開發流程中採用 Dev Container。如果你希望在使用現代 IDE 的同時，又能從管理不同程式語言/框架的複雜開發環境中解脫出來，Dev Containers 會是一個不錯的選擇。&lt;/p&gt;&#xA;&lt;p&gt;如果你是一名需要同時處理多個專案的軟體開發人員，有時可能需要在不同版本的程式語言/框架之間切換。雖然現代程式語言支援多版本管理器，例如 NodeJS &lt;a href=&#34;https://github.com/nvm-sh/nvm&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;NVM&lt;/a&gt; 和 Python 的 &lt;a href=&#34;https://docs.python.org/3/library/venv.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Virtual Environment&lt;/a&gt;，但使用者仍需仔細設定/選擇環境，以避免改動全域的 Runtime。&lt;a href=&#34;https://www.google.com/search?q=https://github.com/asdf-vm/asdf&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;asdf&lt;/a&gt; 是一個非常棒的全能解決方案，但它需要為每個專案進行額外設定。&lt;/p&gt;&#xA;&lt;p&gt;先前，我解決這種複雜性的方法是使用 Container。一般而言，我常用的專案結構如下：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; /path/to/project&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |-- deployment                   # 切換不同的目錄以使用不同的環境&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |   |-- production&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |   |   `-- docker-compose.yaml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |   `-- development&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |       `-- docker-compose.yaml&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |-- dockerfile                   # 針對不同環境的實際 Dockerfiles&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |   |-- production&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |   |   `-- Dockerfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |   `-- development&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; |       `-- Dockerfile&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt; `-- src&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;對於本地環境開發，Source Code 會透過 &lt;code&gt;docker-compose.yaml&lt;/code&gt; 和 &lt;code&gt;Dockerfile&lt;/code&gt; 掛載到Container中，並安裝所有必要的 Language Runtime 與套件。因此，我可以在 IDE 中編輯程式碼並立即得到最新結果，而無需在機器上安裝所有 Dependencies 。然而，IDE 本身仍需要 Runtime。例如，在 JetBrains 系列 IDE 中，如果沒有這些環境，Syntax Highlighting 和 Debuggin 功能將無法運作，這使得 IDE 變成了單純高度消耗資源的文字編輯器。雖然有一些遠端開發的解決方案，但它們是實際上是透過 SSH 實作（如 &lt;a href=&#34;https://code.visualstudio.com/docs/remote/remote-overview&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;VSCode&lt;/a&gt; 或 &lt;a href=&#34;https://www.jetbrains.com/guide/remote/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;JetBrains&lt;/a&gt; 的方案），可能需要額外的設定。&lt;/p&gt;</description>
    </item>
    <item>
      <title>我如何為筆記型電腦選擇作業系統？</title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_19_choose_os/</link>
      <pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_19_choose_os/</guid>
      <description>&lt;div class=&#34;notice tip&#34;&gt;&#xA;  &lt;div class=&#34;notice-title&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-lightbulb&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Tip&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;notice-content&#34;&gt;&lt;strong&gt;TL;DR：&lt;/strong&gt; 如果你是一名正在為筆記型電腦/工作站選擇 Linux 桌面環境而苦惱的開發者，Debian 基本上是最可靠的方案。否則，建議從 &lt;a href=&#34;https://distrowatch.com/dwres.php?resource=popularity&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;DistroWatch&lt;/a&gt; 中挑選並測試，找出最適合你的一個。&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;p&gt;這是一個關於我如何為新筆記型電腦選擇作業系統的故事，也是一段為期 2 + 3 個月的旅程。&lt;/p&gt;&#xA;&lt;p&gt;去年 11 月（2025 年），我趁黑五優惠花了約 1,200 美元買了一台 Dell Pro 14 筆記型電腦（64 GB RAM, AMD Ryzen 5 220），用來替換我的 Surface Laptop 3（32 GB RAM, i7-1065G7）。Surface Laptop 3 對開發者來說其實很不錯，但 Windows 11 和近期的桌面應用程式（加上 WSL2）對它來說似乎太沉重。即便重新安裝整個系統，它還是會隨著時間運行得越來越慢。因此，在去年年底，我決定更換它。&lt;/p&gt;&#xA;&lt;p&gt;第一個問題是：我應該使用哪個作業系統？&lt;/p&gt;&#xA;&lt;p&gt;事實上，我腦中浮現的第一個念頭是選擇 Windows 以外的選項。我使用 Windows 已經超過 20 年了。我仍然認為它擁有最好的使用者體驗。使用者不需要花太多時間去理解如何使用，所有的操作都很直觀。此外，我日常使用的大多數現代應用程式都是基於 Windows 的，例如 Notion Desktop、&lt;a href=&#34;https://www.stardock.com/products/groupy/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Groupy&lt;/a&gt; 和 Notepad++。然而，我還是決定離開它。&lt;/p&gt;&#xA;&lt;p&gt;最顯著的原因是系統資源消耗。在 Surface Laptop 3 上，Windows 佔用了約 8 GB 的記憶體來運行大量用途不明的 Background Processes。當我運行應用程式時，情況變得更糟。在日常工作中，我習慣同時運行超過 100 個標籤頁的 Firefox、多個基於 Jetbrains IDE Instances 以及 WSL。在這種情況下，工作管理員無法完全反映出的記憶體與 CPU 使用狀況。例如，當我看到系統 CPU 和記憶體使用率已達到近 90% 時，工作管理員的 Process 中所記錄到的資源消耗總數往往對應不起來。即使我嘗試關閉所有應用程式與 WSL，系統資源消耗仍然高於剛開機時的狀態。&lt;/p&gt;</description>
    </item>
    <item>
      <title>在 Cloud Run 使用 Cloud Storage Volume 的一些注意事項</title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_13_gcp/</link>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_13_gcp/</guid>
      <description>&lt;p&gt;分享我最近在 GCP 上部署掛載 Cloud Storage Volume 的 Cloud Run Service時，遇到的一些值得一提的經驗（&lt;del&gt;坑&lt;/del&gt;）。&lt;/p&gt;&#xA;&lt;h2 id=&#34;container-無故結束運行且未回報錯誤&#34;&gt;&#xA;  Container 無故結束運行且未回報錯誤&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#container-%e7%84%a1%e6%95%85%e7%b5%90%e6%9d%9f%e9%81%8b%e8%a1%8c%e4%b8%94%e6%9c%aa%e5%9b%9e%e5%a0%b1%e9%8c%af%e8%aa%a4&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;通常在建立掛載 Cloud Storage Volume 的 Cloud Run Service 時，執行 Create Service 的 &lt;strong&gt;Service Account&lt;/strong&gt; 應具備 &lt;strong&gt;Storage Object User&lt;/strong&gt; 或 &lt;strong&gt;Admin&lt;/strong&gt; Role。 如果權限不足，可能會見到以下狀況：&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;在 Log Explorer 中看到與 Container Exit 相關的錯誤：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;textPayload&amp;#34;: &amp;#34;Application exec likely failed&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&amp;#34;textPayload&amp;#34;: &amp;#34;terminated: Application failed to start: The container may have exited abnormally.&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;&#xA;&lt;li&gt;&#xA;&lt;p&gt;Log Explorer 顯示 Cloud Storage Volume 似乎已成功掛載：&lt;/p&gt;</description>
    </item>
    <item>
      <title>lbhelper：基於 Python 的 Debian Live Build Wrapper</title>
      <link>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_12_lbhelper/</link>
      <pubDate>Sun, 12 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://dev-journal.hallblazzar.dev/zh-tw/posts/2026_apr_12_lbhelper/</guid>
      <description>&lt;div class=&#34;notice tip&#34;&gt;&#xA;  &lt;div class=&#34;notice-title&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-lightbulb&#34; aria-hidden=&#34;true&#34;&gt;&lt;/i&gt;Tip&#xA;  &lt;/div&gt;&#xA;  &lt;div class=&#34;notice-content&#34;&gt;&lt;p&gt;TL;DR：如果你正嘗試建構自己的 Debian Images （針對桌面環境），我開發了一個基於 Python3 的函式庫 &lt;a href=&#34;https://hallblazzar.github.io/lbhelper/source/index.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;lbhelper&lt;/a&gt;，可以協助你簡化這部分的開發工作。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;官方文件 - &lt;a href=&#34;https://hallblazzar.github.io/lbhelper/source/index.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://hallblazzar.github.io/lbhelper/source/index.html&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;GitHub - &lt;a href=&#34;https://github.com/HallBlazzar/lbhelper&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://github.com/HallBlazzar/lbhelper&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;基於這個函式庫建構的 Debian Images ，也是我目前運行在我的筆電上運行的系統 - &lt;a href=&#34;https://github.com/HallBlazzar/myos&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://github.com/HallBlazzar/myos&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;PyPi - &lt;a href=&#34;https://pypi.org/project/lbhelper/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://pypi.org/project/lbhelper/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;/ul&gt;&lt;/div&gt;&#xA;&lt;/div&gt;&#xA;&#xA;&lt;h2 id=&#34;image-customization-的解決方案&#34;&gt;&#xA;  Image Customization 的解決方案&#xA;  &lt;a class=&#34;heading-link&#34; href=&#34;#image-customization-%e7%9a%84%e8%a7%a3%e6%b1%ba%e6%96%b9%e6%a1%88&#34;&gt;&#xA;    &lt;i class=&#34;fa-solid fa-link&#34; aria-hidden=&#34;true&#34; title=&#34;Link to heading&#34;&gt;&lt;/i&gt;&#xA;    &lt;span class=&#34;sr-only&#34;&gt;Link to heading&lt;/span&gt;&#xA;  &lt;/a&gt;&#xA;&lt;/h2&gt;&#xA;&lt;p&gt;如果你正試圖為幫你的桌機或筆電建構自訂的 Debian Images ，你可能會發現不容易找到合適的解決方案。通常一般使用者傾向於在首次開機後運行 Shell Scripts 或 &lt;a href=&#34;https://docs.ansible.com/projects/ansible/latest/playbook_guide/playbooks_intro.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Ansible playbooks&lt;/a&gt; 來直接設定系統。這種方式簡單直觀。但由於這些 Script 很少被執行而且需要手動執行，容易隨著 Package 或 Config 過期而失效。雖然直接使用 &lt;a href=&#34;https://penguins-eggs.net/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Penguin&amp;rsquo;s Egg&lt;/a&gt; 打包系統能夠直接獲得可以工作的 Image ，但由於遺漏了建構與變更 Package 或 Config 的過程，容易導致最終的 Image 往往難以重現（non-reproducible）。有些人可能會考慮 &lt;a href=&#34;https://fai-project.org/FAIme/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;FAI&lt;/a&gt;，但除了安裝套件之外，它提供的自訂功能有限。&lt;a href=&#34;https://developer.hashicorp.com/packer&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Packer&lt;/a&gt; 能讓結果便於重現，但由於依賴 VM 進行建構，導致執行的過程需要更長的時間以及更多系統資源。&lt;/p&gt;&#xA;&lt;p&gt;除了上述通用型工具（可用於多數 Linux Distribution ）外，&lt;a href=&#34;https://live-team.pages.debian.net/live-manual/html/live-manual/index.en.html&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Debian Live Build&lt;/a&gt; 則是專為 Debian 開發並由其社群維護的工具。除了官方的 Debian Images 外，也被用於基於 Debian 的發行版，例如 &lt;a href=&#34;https://www.kali.org/docs/development/live-build-a-custom-kali-iso/&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Kali Linux&lt;/a&gt;。Live Build 支援基於檔案與 Script 的設定方式，可以建立如 Ansible 或 Packer 一樣建立結構化的設定檔，並且在 chroot 環境中運行（關於透過純 chroot 建構 Image，參見&lt;a href=&#34;https://dev.to/vaiolabs_io/how-to-create-custom-debian-based-iso-4g37&#34;  class=&#34;external-link&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;這篇文章&lt;/a&gt;）。此外，Live Build 支援建立 Live Image，可以在不需要直接安裝到目標機器或虛擬機的情況下，就能夠運作（甚至可以自訂 Live Images 的開機選單），使驗證最終的系統和發行你的 Images 更容易。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
