---
title: "幾個常見在 K8s 上的映像檔建置方法：Docker(DinD DooD)/Kaniko/BuildKit"
description: "在 K8s 上建置容器映像的各種做法與取捨，涵蓋 DinD、DooD、Kaniko、BuildKit 與實戰範例。"
canonical_url: "https://blog.markkulab.net/post/k8s-build-image-methods"
author: "Mark Ku"
author_url: "https://blog.markkulab.net/author/mark-ku"
site: "Mark Ku's Blog"
date_published: "2025-09-02 02:01:00 +0800"
category: "DevOps"
tags: ["kubernetes", "docker", "ci/cd", "gitlab runner", "kaniko", "buildkit", "dind", "dood", "devops"]
language: "zh-TW"
license: "CC BY 4.0"
license_url: "https://creativecommons.org/licenses/by/4.0/"
attribution: "轉載或引用請註明作者並附上原文連結"
---

# 幾個常見在 K8s 上的映像檔建置方法：Docker(DinD DooD)/Kaniko/BuildKit

## 概述

本文快速比較 **DinD**、**DooD**、**Kaniko**、**BuildKit**、**Buildah** 的差異，並提供在 GitLab Runner on K8s 的最小可用配置與常見踩雷排解。

> **TL;DR：** 在 K8s 上建置映像，如果你追求相容性與安全性，優先選 **Kaniko**；追求效能與快取，選 **BuildKit**；DinD/DooD 僅在受信任環境或暫時性需求下使用。

## 工具比較表

| 工具 | 是否需 daemon | 是否需 privileged | 效能 | 相容性 | 適合場景 |
|------|---------------|-------------------|------|--------|----------|
| **DinD** | ✅ | ✅ | 中 | 高 | 小團隊快速 CI |
| **DooD** | ✅ (宿主) | ✅ | 高 | 高 | 內部自用 CI |
| **Kaniko** | ❌ | ❌ | 中 | 高 | 雲原生 CI/CD |
| **BuildKit** | ✅ (buildkitd) | ✅/rootless | 高 | 高 | 需要快取/效能 |
| **Buildah** | ❌ | ❌ (可 rootless) | 中 | 高 | OpenShift / Red Hat 系統 |

## DinD vs DooD

大多人常常會很困惑，常常會看到兩個詞，下圖可以快速讓你了解差異：

![](https://blog.markkulab.net/content/markku/posts/k8s-build-image-methods/images/attachments/372ac1ee-0f92-480b-a43c-188bfabdd491.png)

### DinD (Docker-in-Docker)

* CI/CD pipeline 中，不需要掛載 docker.sock
* 可以透過 TCP 連線
* 適合隔離環境的 CI/CD

**優點：**
- 設定簡單、相依關係清楚，與 Docker 指令完全相容
- 容器內部隔離，方便一次性測試

**缺點：**
- 必須 privileged，風險較高
- 效能中等，nested cgroups 帶來額外開銷
- 在雲端受管 K8s/VM 上常遇到網路與權限限制

### DooD (Docker-outside-of-Docker)

* 容器內不運行自己的 Docker
* 直接掛載宿主機的 Docker socket (`/var/run/docker.sock`) 給容器使用
* 效能較好，但安全性較低

**優點：**
- 效能最佳，重用宿主 Docker cache
- 啟動速度快

**缺點：**
- 等同把宿主 Docker 權限暴露給容器，不適合多租戶
- 與節點相依，K8s 可攜性與彈性較差

#### 為什麼網路上的範例都大多採用 Docker 20.10？

Docker 20.10 是 2020 年底發布的長期穩定版本，一直都有安全更新和修補，很多 Linux 系統都內建這個版本，整個生態圈支援最好，大部分的自動化工具（GitLab Runner、Drone、Jenkins 等）最早都是用這個版本測試的，所以最穩定可靠。

從 Docker 23.x 開始，很多功能都改來改去，一些設定和 API 都變了，導致舊的範例程式碼直接壞掉，在 Kubernetes 或 GitLab Runner 上可能會遇到建置失敗的情況。

## 上述幾種的建構映像檔的部署指南

### 1. K8s DinD 部署（GitLab Runner 需設定 privileged=true）

試了蠻多遍的，在 k8s 中 DinD，我並沒有在同一個 Pod 中設定成功，但原理應該是透過 2375 遠端管理 Port 連線Docker去建置，基於我們公司的安全考量，我就沒繼續研究了。

### 2. K8s DooD 部署

需要掛載 `/var/run/docker.sock`，共享宿主的 Docker daemon

#### 預先準備

在宿主主機安裝 Docker

#### GitLab CI 配置

```yaml
stages:
  - build

variables:
  IMAGE: $CI_REGISTRY_IMAGE/$CI_BUILD_REF_NAME:$CI_PIPELINE_ID     
  K8S_NAMESPACE: "kong-api-gateway"
  KONG_SECRET_NAME: "kong-api-gateway-secret"
  DOCKER_TLS_CERTDIR: ""
  DOCKER_DRIVER: overlay2
 
build:
  stage: build
  image: docker:20.10
  services:
    - name: docker:20.10
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - echo "=== 建置 Kong API Gateway Docker 映像檔 (使用 DooD) ==="
    - echo "目標映像檔:${IMAGE}"
    - echo "建構上下文:${CI_PROJECT_DIR}"
    - docker build -f Dockerfile.kong -t "${IMAGE}" "${CI_PROJECT_DIR}"
    - docker push "${IMAGE}"
  only:
    - main
  tags:
    - K8s-Runner
```

#### GitLab Runner（Helm Values）

```yaml
gitlabUrl: https://gitlab.com
runnerRegistrationToken: "xxx"
unregisterRunners: true

fullnameOverride: "k8s-cd-gitlab-runner"

serviceAccount:
  create: true
  name: gitlab-runner

runners:
  privileged: true
  tags: "deploy"
  config: |
    [[runners]]
      [runners.kubernetes]
        image = "docker:20.10"
        service_account = "gitlab-runner"
        service_account_overwrite_allowed = ".*"
        [runners.kubernetes.pod_security_context]
          run_as_non_root = false
          run_as_user = 0
        [runners.kubernetes.container_security_context]
          privileged = true
        [runners.kubernetes.resources]
          limits = { "cpu" = "1000m", "memory" = "2Gi" }
          requests = { "cpu" = "500m", "memory" = "1Gi" }
        [runners.kubernetes.environment]
          DOCKER_OPTS = "--insecure-registry 192.168.50.57:30000"
        [[runners.kubernetes.volumes.host_path]]
          name = "docker-socket"
          mount_path = "/var/run/docker.sock"
          host_path = "/var/run/docker.sock"
          mount_propagation = "HostToContainer"

securityContext:
  allowPrivilegeEscalation: true
  readOnlyRootFilesystem: false
  runAsNonRoot: false
  privileged: true
  capabilities:
    add: ["SYS_ADMIN"]

podSecurityContext:
  runAsUser: 0
  fsGroup: 0
```

#### RBAC 配置

```yaml
# gitlab-runner-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: gitlab-runner
  namespace: k8s-cd-gitlab-runner
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: k8s-cd-gitlab-runner
  name: gitlab-runner-role
rules:
- apiGroups: [""]
  resources: ["pods", "pods/attach", "pods/exec", "pods/log", "pods/portforward", "pods/proxy", "pods/status"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["secrets", "configmaps", "persistentvolumeclaims", "services", "endpoints"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""]
  resources: ["events"]
  verbs: ["get", "list", "watch", "create", "update", "patch"]
- apiGroups: [""]
  resources: ["namespaces"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["apps"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["batch"]
  resources: ["jobs", "cronjobs"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["extensions"]
  resources: ["deployments", "statefulsets", "daemonsets"]
  verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: gitlab-runner-rolebinding
  namespace: k8s-cd-gitlab-runner
subjects:
- kind: ServiceAccount
  name: gitlab-runner
  namespace: k8s-cd-gitlab-runner
roleRef:
  kind: Role
  name: gitlab-runner-role
  apiGroup: rbac.authorization.k8s.io
```

#### 部署命令

```bash
# 部署
helm repo add gitlab https://charts.gitlab.io
helm repo update
kubectl create namespace k8s-cd-gitlab-runner
kubectl apply -f ./rbac.yaml 
helm install gitlab-runner -f values.yaml gitlab/gitlab-runner --namespace k8s-cd-gitlab-runner --create-namespace

# 更新
helm upgrade gitlab-runner -f values.yaml gitlab/gitlab-runner --namespace k8s-cd-gitlab-runner
```

> **注意：** 這個方法在地端自架的Ubuntu K8s 可行， 但在RKE2 可能因為一些安全性設定無法挷定 /var/run/docker.sock

### 3. Kaniko 部署

安全性最佳，建構過程不需 Docker daemon 或 privileged，在受管 K8s 上相容性極佳，支援常見 Dockerfile 指令，易於遷移除錯，但建構時會比 DinD 或 DooD 慢 20% ~ 30%。

#### Helm Values 配置

```yaml
gitlabUrl: https://gitlab.com/
runnerRegistrationToken: ""  # 在 GitLab -> Settings -> CI/CD -> Runners 裡看到的 token
unregisterRunners: true

fullnameOverride: "k8s-cd-gitlab-runner"

serviceAccount:
  create: false
  name: gitlab-runner

runners:
  privileged: false
  tags: "K8s-Runner"
  config: |
    [[runners]]
      [runners.kubernetes]
        image = "gcr.io/kaniko-project/executor:debug"
        service_account = "gitlab-runner"
        service_account_overwrite_allowed = ".*"
        privileged = false
```

#### 部署命令

```bash
helm install gitlab-runner -f values.yaml gitlab/gitlab-runner --namespace k8s-cd-gitlab-runner --create-namespace
```

#### GitLab CI 配置

```yaml
stages:
  - build

variables:
  IMAGE: $CI_REGISTRY_IMAGE/$CI_BUILD_REF_NAME:$CI_PIPELINE_ID
  K8S_NAMESPACE: "kong-api-gateway"
  KONG_SECRET_NAME: "kong-api-gateway-secret"

build:
  stage: build
  image: gcr.io/kaniko-project/executor:debug
  variables:
    DOCKER_CONFIG: /kaniko/.docker
  before_script:
    - mkdir -p /kaniko/.docker
    - echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
  script:
    - echo "=== 建置 Kong API Gateway Docker 映像檔 (使用 Kaniko) ==="
    - echo "目標映像檔:${IMAGE}"
    - echo "建構上下文:${CI_PROJECT_DIR}"
    - /kaniko/executor --context "${CI_PROJECT_DIR}" --dockerfile "Dockerfile.kong" --destination "${IMAGE}" --cache=true --cleanup
  only:
    - main
  tags:
    - K8s-Runner

create-secret:
  stage: create-secret
  image:
    name: bitnami/kubectl:latest
  script:
    - echo "=== 準備目標命名空間與鏡像拉取密鑰 ==="
    - kubectl get namespace ${K8S_NAMESPACE} || kubectl create namespace ${K8S_NAMESPACE}
    - kubectl delete secret ${KONG_SECRET_NAME} -n ${K8S_NAMESPACE} --ignore-not-found=true || true
    - kubectl create secret docker-registry ${KONG_SECRET_NAME} \
        --docker-server=$CI_REGISTRY \
        --docker-username=$CI_REGISTRY_USER \
        --docker-password=$CI_REGISTRY_PASSWORD \
        --docker-email=none \
        -n ${K8S_NAMESPACE}
  only:
    - main
  tags:
    - K8s-Runner
```

### 4. BuildKit 部署

Docker 公司推出的新指令集及映像檔，但也都基於 DinD 或是 Dood 來運作。

BuildKit 有兩種常見模式：

#### 模式 1: Docker (DinD / Dood) + BuildKit

只要設定環境變數：

```yaml
variables:
  DOCKER_BUILDKIT: "1"
  BUILDKIT_PROGRESS: plain
```

#### 模式 2: GitLab Runner + BuildKit Pod

* 在 K8s 裡部署一個 buildkitd DaemonSet 或 Deployment
* 每個 runner job 透過 buildctl CLI，呼叫 cluster 內的 buildkitd 服務去 build
* 不需要 mount `/var/run/docker.sock`，安全性比 DinD 高

![GitLab Runner透過BuildKit Pod建置映像檔流程](https://blog.markkulab.net/content/markku/posts/k8s-build-image-methods/images/buildKit-pod.png)

## 部署建議

### 選擇建議

1. **小團隊快速 CI**: 使用 DinD
2. **內部自用 CI**: 使用 DooD
3. **雲原生 CI/CD**: 使用 Kaniko
4. **需要快取/效能**: 使用 BuildKit
5. **OpenShift/Red Hat 系統**: 使用 Buildah

### 安全性考量

* **DinD 和 DooD** 需要 privileged 模式，安全性較低
* **Kaniko 和 BuildKit** 不需要 privileged 模式，安全性較高
* 建議在生產環境使用 Kaniko 或 BuildKit

> **進階：moby/BuildKit** 設定更複雜，暫時我就沒有研究了，需要額外部署 buildkitd Pod 都是基於 DinD / DooD 來運作。

## 結論

配置這環境蠻麻煩的，很多時候環境一點點的差距就會有不一樣的行為，多會幾種建置方法，才可以應對不同的環境，不過隨著時間及技術的迭代，這類問題應該會越來越少。  

其實在比較好的還是CI和CD 拆開來，這樣才會比較安全，設定也比較簡單，也不會遇到一堆在K8s上CI的相容性錯誤。

## 參考資料

* [GitLab Runner 無法呼叫 host 的 docker](https://johnnyexplores.medium.com/gitlab-runner%E7%84%A1%E6%B3%95%E5%91%BC%E5%8F%ABhost%E7%9A%84docker-e82dd3f5ae27)

---

## 關於本文與作者

本文出自 [Mark Ku's Blog](https://blog.markkulab.net/post/k8s-build-image-methods)

授權條款： [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — 轉載或引用請註明作者並附上原文連結

### 關於作者

**[Mark Ku](https://blog.markkulab.net/author/mark-ku)** — Software Solution Provider

- 10+ 年資深軟體工程師，現為 AI 應用 Builder
- 專注大型平台架構設計，從北美電商到AI SaaS訂閱收費系統
- 結合 AI Agent 與自動化，打造高效可演進的產品技術基礎

### 作者開發的免費工具

以下工具皆可免費使用：

- [免費 PDF 簽名工具](https://blog.markkulab.net/tools/pdf-sign): 線上 PDF 簽名工具，瀏覽器內完成手繪、打字、上傳簽名，可拖曳放置、縮放、下載。所有處理都在你的裝置完成，檔案不會上傳。
- [VS Code Refactory](https://blog.markkulab.net/tools/refactory): Refactory 是一款 VS Code 重構擴充套件：34 個重構動作、37 條 code smell 檢查、Code Health 儀表板、18 種語言、534 支測試。懂你的專案慣例：介面放哪、DI 註冊寫在哪、'use client' 該不該加；還會用 git 修改頻率 × 複雜度排出「該先修哪個檔案」，並一鍵把壞味道交給你自己電腦上的 Claude Code 修。免費使用，原始碼不離開你的機器。
- [DB-Kit 資料庫管理工具](https://blog.markkulab.net/tools/db-kit): DB-Kit 是一個用 Tauri + Rust + React 打造的輕量跨平台資料庫管理工具，用單一一致的介面同時管理 MySQL、MariaDB、PostgreSQL、SQL Server、Oracle、SQLite、MongoDB、Redis、Kafka、Elasticsearch 與 RabbitMQ 十一種資料來源：連線密碼以 OS keychain 加密、SSH Tunnel、完整 CRUD、視覺化查詢建構器、多結果集同時顯示、跨連線資料傳輸與比對同步、Excel / CSV 匯入匯出、執行計畫視覺化、ER 圖、排程備份、SQL 壓力測試（p50～p99 延遲百分位）、15 條規則的 SQL 審查、Kafka 訊息瀏覽與監控告警；繁中 / 英文雙語介面，內建 AI 助手（自然語言生成 SQL、AI 審查與調校建議）與命令列工具 dbk。免費開源（MIT），提供 Windows / macOS / Linux 安裝檔。
- [VS Code Super Mermaid](https://blog.markkulab.net/tools/super-mermaid): Super Mermaid 是一款 VS Code 擴充套件：開箱即用的漂亮 Mermaid 圖表，自動上色、即時預覽、滑鼠平移縮放、PNG / SVG 高解析匯出，內建 21 種範本與多種主題。免費開源（MIT）。
- [React Super Mermaid](https://blog.markkulab.net/tools/react-super-mermaid): react-super-mermaid 是一個開源 React 元件庫：一行 <MermaidViewer> 即可渲染漂亮的 Mermaid 圖表，內建 colorful / sketch 主題、平移縮放、圖內搜尋、SVG / PNG 高解析匯出。輕量、SSR 安全、完整 TypeScript 型別。免費開源（MIT）。
- [Jira / Confluence Super Mermaid](https://blog.markkulab.net/tools/jira-super-mermaid): Atlassian Forge app：在 Jira issue 與 Confluence 內文直接寫 Mermaid 語法，畫流程圖、時序圖、狀態機與甘特圖。11 種圖表、SVG / PNG 匯出、明暗主題、完整中日韓文字支援。取得 Runs on Atlassian 資格：圖表存在你自己的站台，app 不呼叫任何第三方服務。免費，即將上架 Atlassian Marketplace。
- [Mermaid 線上預覽](https://blog.markkulab.net/tools/mermaid-preview): 在瀏覽器裡寫 Mermaid、即時看圖，整張圖表壓進網址就能分享。免註冊、不上傳伺服器，相容 mermaid.live 的分享連結。
- [React Intl Phone Number](https://blog.markkulab.net/tools/react-intl-phone-number): react-intl-phone-number 是一個開源 React 元件：framework-agnostic、不依賴 antd，提供 E.164 進出、可搜尋國旗 / 國碼下拉、可配置驗證等級（strict / mobile-strict / loose）、可主題化 CSS 與 i18n，電話邏輯由 google-libphonenumber 驅動。輕量、完整 TypeScript 型別。免費開源（MIT）。
- [Uptime Kuma Cluster](https://blog.markkulab.net/tools/uptime-kuma-cluster): 把單機版 Uptime Kuma 改造成高可用叢集：OpenResty + Lua 智慧負載平衡、MariaDB 共享狀態、健康檢查與自動 Failover，附叢集管理 REST API，一行 Docker Compose 啟動。免費開源（MIT）。
- [特教專案](https://blog.markkulab.net/education): 為特殊教育學生製作的學習教材

### 每日 Podcast

- [科技新鮮事](https://blog.markkulab.net/category/tech-news): 每日精選 AI 與科技趨勢，透過語音摘要快速掌握最新技術動態，涵蓋 AI 應用、軟體架構、DevOps 與工程實戰。 — RSS: https://blog.markkulab.net/feed.xml
- [AI股市蝦聊](https://blog.markkulab.net/category/ai-stock-chat): 每個交易日用 AI 分析台股盤勢，以雙人對話聊當天的盤中觀察與隔日預測。 — RSS: https://blog.markkulab.net/ai-stock-chat/feed.xml

### 電子報

[訂閱電子報](https://blog.markkulab.net/subscribe) — 第一時間收到新文章通知，無垃圾信、隨時可取消訂閱。
