<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Buildx on Tarragon</title><link>https://tarrragon.github.io/blog/tags/buildx/</link><description>Recent content in Buildx on Tarragon</description><generator>Hugo -- gohugo.io</generator><language>zh-TW</language><copyright>Tarragon (CC BY 4.0)</copyright><lastBuildDate>Mon, 06 Jul 2026 00:00:00 +0800</lastBuildDate><atom:link href="https://tarrragon.github.io/blog/tags/buildx/index.xml" rel="self" type="application/rss+xml"/><item><title>BuildKit 與跨平台 build</title><link>https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/docker/buildkit-cross-platform/</link><pubDate>Mon, 06 Jul 2026 00:00:00 +0800</pubDate><guid>https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/docker/buildkit-cross-platform/</guid><description>&lt;p>build 每次重下載套件、要同時出 amd64 與 arm64、build 時要用私有憑證卻不想烤進 image——這三個進階需求，是會寫 Dockerfile（&lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/docker/dockerfile-design/" data-link-title="Dockerfile 設計：指令、layer 與 multi-stage" data-link-desc="image 大得離譜、code 一改就整包重 build、或 multi-stage 複製 binary 後 container 起不來說找不到 library 時回來讀 — Dockerfile 指令怎麼變成 layer、build 與 runtime 怎麼分離">Dockerfile 設計&lt;/a>）之後才撞上的。上游是 &lt;a href="https://tarrragon.github.io/blog/backend/05-deployment-platform/vendors/docker/" data-link-title="Docker" data-link-desc="Container runtime / image 標準">Docker vendor overview&lt;/a>。&lt;/p>
&lt;h2 id="舊-builder-做不好的三件事">舊 builder 做不好的三件事&lt;/h2>
&lt;p>理解 BuildKit 最快的方式，是先看它取代的舊 builder 卡在哪。Docker 最初的 legacy builder 逐層線性執行 Dockerfile，三個常見需求它都處理得很勉強：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>build 加速&lt;/strong>：每次 build，&lt;code>RUN apt-get install&lt;/code> 或 &lt;code>npm install&lt;/code> 都重新下載一次套件，就算內容一樣。legacy builder 只有 layer cache（整層命中或整層重跑），沒有「跨 build 保留下載快取」的機制。&lt;/li>
&lt;li>&lt;strong>跨架構&lt;/strong>：要同時出 amd64 跟 arm64 的 image（M 系列 Mac 開發、amd64 伺服器部署），legacy builder 一次只能 build 當前主機的架構。&lt;/li>
&lt;li>&lt;strong>build-time secret&lt;/strong>：build 時要用私有 registry 憑證或 SSH key 抓私有依賴，用 &lt;code>COPY&lt;/code> 或 &lt;code>ENV&lt;/code> 帶進去，secret 就永久烤進某層 image、&lt;code>docker history&lt;/code> 挖得出來。&lt;/li>
&lt;/ul>
&lt;p>BuildKit 是重寫的 build engine，針對這三件重新設計；buildx 是它的前端命令，管 builder 實例與跨平台輸出。現代 Docker 預設就用 BuildKit，但上面三個能力要用對語法才吃得到。&lt;/p>
&lt;h2 id="核心概念mount-與-builder-實例">核心概念：mount 與 builder 實例&lt;/h2>
&lt;p>BuildKit 帶來兩個 legacy builder 沒有的核心概念：&lt;/p>
&lt;ul>
&lt;li>&lt;strong>build-time mount&lt;/strong>：&lt;code>RUN --mount=...&lt;/code> 讓某個 &lt;code>RUN&lt;/code> 在執行當下掛載一塊空間，但那塊空間&lt;strong>不進最終 image layer&lt;/strong>。cache mount（保留套件下載快取）、secret mount（暫時給憑證）、ssh mount（轉發 SSH agent）都是它的形態。關鍵是「build 時用得到、build 完不留痕跡」。&lt;/li>
&lt;li>&lt;strong>builder 實例與 driver&lt;/strong>：build 由一個 builder 執行。預設的 &lt;code>docker&lt;/code> driver 綁在本機 daemon、功能受限；要跨平台這類進階能力，得建一個 &lt;code>docker-container&lt;/code> driver 的 builder（跑在獨立 container 裡，功能完整）。這是很多人卡住的地方，見故障演練。&lt;/li>
&lt;/ul>
&lt;h2 id="配置三個需求逐一">配置：三個需求逐一&lt;/h2>
&lt;p>三個能力的 &lt;code>RUN --mount&lt;/code> 語法屬於較新的 Dockerfile frontend，Dockerfile 第一行要用 syntax directive 明確 opt-in：&lt;/p>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># syntax=docker/dockerfile:1&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>這行告訴 BuildKit「用 dockerfile:1 這個 frontend 解析」，&lt;code>--mount=type=cache/secret/ssh&lt;/code> 才認得。少了它、舊 parser 會把 &lt;code>--mount&lt;/code> 當語法錯誤。現代 Docker 對多數新語法會自動啟用，但 mount 這類要顯式標，養成習慣寫上去。&lt;/p>
&lt;h3 id="cache-mount套件下載快取跨-build-保留">cache mount：套件下載快取跨 build 保留&lt;/h3>





&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-dockerfile" data-lang="dockerfile">&lt;span class="line">&lt;span class="ln">1&lt;/span>&lt;span class="cl">&lt;span class="c"># syntax=docker/dockerfile:1&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">2&lt;/span>&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">FROM&lt;/span>&lt;span class="s"> debian:bookworm&lt;/span>&lt;span class="err">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">3&lt;/span>&lt;span class="cl">&lt;span class="err">&lt;/span>&lt;span class="k">RUN&lt;/span> --mount&lt;span class="o">=&lt;/span>&lt;span class="nv">type&lt;/span>&lt;span class="o">=&lt;/span>cache,target&lt;span class="o">=&lt;/span>/var/cache/apt &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="ln">4&lt;/span>&lt;span class="cl">&lt;span class="se">&lt;/span> apt-get update &lt;span class="o">&amp;amp;&amp;amp;&lt;/span> apt-get install -y --no-install-recommends build-essential&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>&lt;code>--mount=type=cache,target=/var/cache/apt&lt;/code> 把 apt 的下載快取目錄掛成一塊持久快取。第一次 build 下載套件、存進快取；之後就算這層因為別的原因重跑，套件直接從快取拿、不重新下載。npm（&lt;code>target=/root/.npm&lt;/code>）、pip（&lt;code>target=/root/.cache/pip&lt;/code>）、Go module（&lt;code>target=/go/pkg/mod&lt;/code>）同理。&lt;/p></description><content:encoded><![CDATA[<p>build 每次重下載套件、要同時出 amd64 與 arm64、build 時要用私有憑證卻不想烤進 image——這三個進階需求，是會寫 Dockerfile（<a href="/blog/backend/05-deployment-platform/vendors/docker/dockerfile-design/" data-link-title="Dockerfile 設計：指令、layer 與 multi-stage" data-link-desc="image 大得離譜、code 一改就整包重 build、或 multi-stage 複製 binary 後 container 起不來說找不到 library 時回來讀 — Dockerfile 指令怎麼變成 layer、build 與 runtime 怎麼分離">Dockerfile 設計</a>）之後才撞上的。上游是 <a href="/blog/backend/05-deployment-platform/vendors/docker/" data-link-title="Docker" data-link-desc="Container runtime / image 標準">Docker vendor overview</a>。</p>
<h2 id="舊-builder-做不好的三件事">舊 builder 做不好的三件事</h2>
<p>理解 BuildKit 最快的方式，是先看它取代的舊 builder 卡在哪。Docker 最初的 legacy builder 逐層線性執行 Dockerfile，三個常見需求它都處理得很勉強：</p>
<ul>
<li><strong>build 加速</strong>：每次 build，<code>RUN apt-get install</code> 或 <code>npm install</code> 都重新下載一次套件，就算內容一樣。legacy builder 只有 layer cache（整層命中或整層重跑），沒有「跨 build 保留下載快取」的機制。</li>
<li><strong>跨架構</strong>：要同時出 amd64 跟 arm64 的 image（M 系列 Mac 開發、amd64 伺服器部署），legacy builder 一次只能 build 當前主機的架構。</li>
<li><strong>build-time secret</strong>：build 時要用私有 registry 憑證或 SSH key 抓私有依賴，用 <code>COPY</code> 或 <code>ENV</code> 帶進去，secret 就永久烤進某層 image、<code>docker history</code> 挖得出來。</li>
</ul>
<p>BuildKit 是重寫的 build engine，針對這三件重新設計；buildx 是它的前端命令，管 builder 實例與跨平台輸出。現代 Docker 預設就用 BuildKit，但上面三個能力要用對語法才吃得到。</p>
<h2 id="核心概念mount-與-builder-實例">核心概念：mount 與 builder 實例</h2>
<p>BuildKit 帶來兩個 legacy builder 沒有的核心概念：</p>
<ul>
<li><strong>build-time mount</strong>：<code>RUN --mount=...</code> 讓某個 <code>RUN</code> 在執行當下掛載一塊空間，但那塊空間<strong>不進最終 image layer</strong>。cache mount（保留套件下載快取）、secret mount（暫時給憑證）、ssh mount（轉發 SSH agent）都是它的形態。關鍵是「build 時用得到、build 完不留痕跡」。</li>
<li><strong>builder 實例與 driver</strong>：build 由一個 builder 執行。預設的 <code>docker</code> driver 綁在本機 daemon、功能受限；要跨平台這類進階能力，得建一個 <code>docker-container</code> driver 的 builder（跑在獨立 container 裡，功能完整）。這是很多人卡住的地方，見故障演練。</li>
</ul>
<h2 id="配置三個需求逐一">配置：三個需求逐一</h2>
<p>三個能力的 <code>RUN --mount</code> 語法屬於較新的 Dockerfile frontend，Dockerfile 第一行要用 syntax directive 明確 opt-in：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># syntax=docker/dockerfile:1</span></span></span></code></pre></div><p>這行告訴 BuildKit「用 dockerfile:1 這個 frontend 解析」，<code>--mount=type=cache/secret/ssh</code> 才認得。少了它、舊 parser 會把 <code>--mount</code> 當語法錯誤。現代 Docker 對多數新語法會自動啟用，但 mount 這類要顯式標，養成習慣寫上去。</p>
<h3 id="cache-mount套件下載快取跨-build-保留">cache mount：套件下載快取跨 build 保留</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="ln">1</span><span class="cl"><span class="c"># syntax=docker/dockerfile:1</span><span class="err">
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="s"> debian:bookworm</span><span class="err">
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="err"></span><span class="k">RUN</span> --mount<span class="o">=</span><span class="nv">type</span><span class="o">=</span>cache,target<span class="o">=</span>/var/cache/apt <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>    apt-get update <span class="o">&amp;&amp;</span> apt-get install -y --no-install-recommends build-essential</span></span></code></pre></div><p><code>--mount=type=cache,target=/var/cache/apt</code> 把 apt 的下載快取目錄掛成一塊持久快取。第一次 build 下載套件、存進快取；之後就算這層因為別的原因重跑，套件直接從快取拿、不重新下載。npm（<code>target=/root/.npm</code>）、pip（<code>target=/root/.cache/pip</code>）、Go module（<code>target=/go/pkg/mod</code>）同理。</p>
<p>這跟 layer cache 是兩回事：layer cache 是「整層沒變就跳過」，cache mount 是「就算這層要重跑，它用到的下載內容不必重來」。兩者疊加才是最快的 build。</p>
<h3 id="secret-mountbuild-時用憑證但不留在-image">secret mount：build 時用憑證但不留在 image</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="ln">1</span><span class="cl"><span class="k">RUN</span> --mount<span class="o">=</span><span class="nv">type</span><span class="o">=</span>secret,id<span class="o">=</span>npmtoken <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>    <span class="nv">NPM_TOKEN</span><span class="o">=</span><span class="k">$(</span>cat /run/secrets/npmtoken<span class="k">)</span> npm install</span></span></code></pre></div>




<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker buildx build --secret <span class="nv">id</span><span class="o">=</span>npmtoken,src<span class="o">=</span><span class="nv">$HOME</span>/.npmtoken -t app .</span></span></code></pre></div><p>secret 在 build 當下以檔案形式出現在 <code>/run/secrets/</code>，<code>RUN</code> 結束就消失、不寫進任何 layer。<code>docker history</code> 挖不到、image 送出去也不帶 secret。這取代了「<code>ENV NPM_TOKEN=...</code> 或 <code>COPY .npmrc</code>」那種會把 secret 烤進 image 的錯誤做法。</p>
<h3 id="跨平台-build一次出多架構">跨平台 build：一次出多架構</h3>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl"><span class="c1"># 先建一個支援多平台的 builder（docker driver 不支援，見故障演練）</span>
</span></span><span class="line"><span class="ln">2</span><span class="cl">docker buildx create --name multi --driver docker-container --use
</span></span><span class="line"><span class="ln">3</span><span class="cl">
</span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="c1"># 同時 build amd64 + arm64，直接推到 registry</span>
</span></span><span class="line"><span class="ln">5</span><span class="cl">docker buildx build --platform linux/amd64,linux/arm64 <span class="se">\
</span></span></span><span class="line"><span class="ln">6</span><span class="cl"><span class="se"></span>    -t ghcr.io/org/app:1 --push .</span></span></code></pre></div><p>Docker Desktop / OrbStack 內建了跨架構所需的 QEMU binfmt handler、開箱能模擬；裸 Linux 主機與 CI runner（GitHub Actions ubuntu runner 是跨平台 build 最主要的落點）沒有，跨架構前要先註冊一次，否則模擬會 <code>exec format error</code>：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker run --privileged --rm tonistiigi/binfmt --install all</span></span></code></pre></div><p><code>--platform</code> 帶多個架構，BuildKit 用 QEMU 模擬各架構分別 build，產出一個 multi-arch manifest（同一個 tag 底下有多架構、pull 時自動選對的）。cache backend 可以進一步把快取存到 registry 或 CI，讓不同機器 / CI job 共用：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker buildx build --platform linux/amd64,linux/arm64 <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>    --cache-to <span class="nv">type</span><span class="o">=</span>registry,ref<span class="o">=</span>ghcr.io/org/app:cache <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>    --cache-from <span class="nv">type</span><span class="o">=</span>registry,ref<span class="o">=</span>ghcr.io/org/app:cache <span class="se">\
</span></span></span><span class="line"><span class="ln">4</span><span class="cl"><span class="se"></span>    -t ghcr.io/org/app:1 --push .</span></span></code></pre></div><p>CI 上更常用平台原生 cache——GitHub Actions 用 <code>type=gha</code>（工作流先掛 <code>docker/setup-buildx-action</code>）：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker buildx build --platform linux/amd64,linux/arm64 <span class="se">\
</span></span></span><span class="line"><span class="ln">2</span><span class="cl"><span class="se"></span>    --cache-to <span class="nv">type</span><span class="o">=</span>gha,mode<span class="o">=</span>max --cache-from <span class="nv">type</span><span class="o">=</span>gha <span class="se">\
</span></span></span><span class="line"><span class="ln">3</span><span class="cl"><span class="se"></span>    -t ghcr.io/org/app:1 --push .</span></span></code></pre></div><h2 id="故障演練driver-與-secret-的邊界">故障演練：driver 與 secret 的邊界</h2>
<h3 id="default-docker-driver-不支援多平台">default docker driver 不支援多平台</h3>
<p>直接 <code>docker build --platform linux/amd64,linux/arm64</code> 或用預設 builder，會直接失敗：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="ln">1</span><span class="cl">ERROR: failed to build: Multi-platform build is not supported for the docker driver.</span></span></code></pre></div><p>徵兆很明確、錯誤訊息就講了。根因是預設的 <code>docker</code> driver（綁本機 daemon）不支援多平台輸出。修法是建一個 <code>docker-container</code> driver 的 builder 再用它：</p>





<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="ln">1</span><span class="cl">docker buildx create --name multi --driver docker-container --use
</span></span><span class="line"><span class="ln">2</span><span class="cl">docker buildx build --builder multi --platform linux/amd64,linux/arm64 -t app .</span></span></code></pre></div><p>換上 container driver builder 後，同一條指令就能同時 build 出兩個架構（實測 amd64 + arm64 各自完成、產出 multi-arch）。這是跨平台 build 的第一道門檻，卡最多人。</p>
<h3 id="多平台-build-不能-load-回本機">多平台 build 不能 &ndash;load 回本機</h3>
<p>建好 multi-arch 之後想 <code>--load</code> 到本機 image store，會失敗——本機的 docker image store 一個 tag 只能存單一架構。多平台結果要嘛 <code>--push</code> 到 registry（registry 支援 multi-arch manifest），要嘛輸出成 OCI tarball。判讀：本機測單一架構用 <code>--load</code> + 單一 <code>--platform</code>；要發佈多架構就 <code>--push</code>。想在本機留多架構的期待，本機 image store 這層就擋住了。</p>
<h3 id="qemu-模擬非原生架構很慢">QEMU 模擬非原生架構很慢</h3>
<p>在 arm64 主機 build（或跑）amd64 image，BuildKit 靠 <a href="/blog/backend/knowledge-cards/qemu-binfmt-emulation/" data-link-title="QEMU binfmt Emulation（跨架構模擬）" data-link-desc="docker 跑非原生架構的 image 報 exec format error、mysql:5.7 在 Apple Silicon 要 platform: linux/amd64、或跨平台 build 特別慢時回來讀 — 非原生 image 怎麼被模擬跑起來">QEMU binfmt 模擬</a>，效能明顯掉。這在跑舊服務時特別有感——例如 MySQL 5.7 沒有 arm64 原生 image，在 Apple Silicon 上只能 <code>platform: linux/amd64</code> 走模擬，啟動與運算都比原生慢。這不是壞掉、是模擬的固有成本。判讀：dev 階段可以忍模擬的慢（換來跟 amd64 prod 的架構對齊）；如果模擬慢到影響開發，考慮在原生架構的遠端 builder 上 build。實際應用見 <a href="/blog/linux/dotfile/10-prod-parity/prod-parity-runtime/" data-link-title="對齊 prod 的 runtime container" data-link-desc="要開發一個線上跑 PHP 7.2 / MySQL 5.7 舊環境的專案、或要在本機重現線上事故時回來讀 — 對齊哪些維度、怎麼從線上抄設定、什麼時候值得">對齊 prod 的 runtime container</a> 的 arm64 段。</p>
<h3 id="secret-用錯機制還是進了-layer">secret 用錯機制，還是進了 layer</h3>
<p><code>ENV</code> / <code>COPY</code> 都會產生 layer、內容永久記錄，所以拿它們帶 secret（<code>ENV TOKEN=xxx</code>、<code>COPY .npmrc</code>）等於把 secret 烤進 image——<code>docker history --no-trunc &lt;image&gt;</code> 或把 image 拆開就翻得到 token。改用 <code>--mount=type=secret</code>（前面配置段）才不落 layer，secret 只在 <code>RUN</code> 當下以檔案存在。驗證：build 完 <code>docker history</code> 確認翻不到 secret。</p>
<h2 id="容量什麼時候需要這些">容量：什麼時候需要這些</h2>
<p>這些是進階能力、不是每個專案都要開：</p>
<ul>
<li><strong>cache mount</strong>：build 頻繁（CI 每次 commit build）、或依賴下載很重（大量 npm / apt 套件）時收益明顯。單機偶爾 build 一次的內部工具，layer cache 就夠。</li>
<li><strong>跨平台</strong>：要發佈給不同架構使用者（open source image、公司同時有 M 系列 Mac 與 amd64 伺服器）才需要。只在單一架構部署就不必承受 QEMU 的複雜與慢。</li>
<li><strong>cache backend 選型</strong>：本機開發用預設 local cache；CI 用 registry cache（<code>--cache-to/from type=registry</code>）或平台原生 cache（如 GitHub Actions 的 <code>type=gha</code>），讓每次 CI job 不必冷啟動重建。選哪個看 CI 環境提供什麼。</li>
</ul>
<h2 id="整合與下一步">整合與下一步</h2>
<ul>
<li>Dockerfile 指令與 layer 的基礎（cache mount 疊在 layer cache 之上），見 <a href="/blog/backend/05-deployment-platform/vendors/docker/dockerfile-design/" data-link-title="Dockerfile 設計：指令、layer 與 multi-stage" data-link-desc="image 大得離譜、code 一改就整包重 build、或 multi-stage 複製 binary 後 container 起不來說找不到 library 時回來讀 — Dockerfile 指令怎麼變成 layer、build 與 runtime 怎麼分離">Dockerfile 設計</a>。</li>
<li>多 service 的 dev 環境編排，見 <a href="/blog/backend/05-deployment-platform/vendors/docker/docker-compose/" data-link-title="Docker Compose：多 service dev 環境編排" data-link-desc="一個 app 要好幾個 container(DB / cache / web)、手動 docker run 串不起來、或 compose 起來後 app 連不到 DB 或 DB 還沒 ready 就被連時回來讀 — 多 service 怎麼宣告式編排">Docker Compose 深度設計</a>。</li>
<li>image build 進 CI/CD pipeline、跟供應鏈掃描接起來，見 <a href="/blog/ci/docker-deploy/" data-link-title="Docker / Image 部署 CI/CD" data-link-desc="整理 container image 的 build、tag、scan、registry、promotion 與 runtime 部署注意事項">Docker / Image 部署 CI/CD</a>。</li>
</ul>
]]></content:encoded></item></channel></rss>