# knative 和 kustomization 介紹 先來個白話介紹, - 若將 k8s 想成是一個設備超齊全的中央廚房 (功能強大但操作複雜) - 那 Knative 就像是安裝在這個中央廚房中的智能管理系統,開發者只要提供一部分程式碼,系統就會幫你處理好一切 --- ## Knative Serving  這張圖展示了 Knative Serving 宣告式 API 的核心,也就是開發者主要接觸的四種 Kubernetes 自訂資源定義 (CRD)。 1. **Service (`service.serving.knative.dev`)**: * **角色**: 這是最高層級的抽象,是使用者與 Knative 互動的主要入口點。一個 `Service` 物件旨在管理一個無伺服器工作負載的完整生命週期。 * **職責**: 當您建立一個 `Service` 物件時,Knative 的控制器會自動為您生成並管理對應的 `Configuration` 和 `Route` 子物件。它的目標是將一個應用的部署、網路配置和版本管理濃縮在單一的 YAML 檔案中,大幅簡化操作。 2. **Configuration (`configuration.serving.knative.dev`)**: * **角色**: 定義了您工作負載的「期望狀態 (Desired State)」。它是一個範本,包含了部署所需的所有設定,例如容器映像檔 (`image`)、環境變數、資源請求/限制等。 * **職責**: 它的核心職責是實現「程式碼與配置分離」。當此物件的 `spec.template` 部分發生任何變更時(例如更新了容器映像檔),它不會修改現有狀態,而是會觸發建立一個新的、不可變的 `Revision`。 3. **Revision (`revision.serving.knative.dev`)**: * **角色**: 一個 **不可變 (Immutable) 的時間點快照 (Point-in-time Snapshot)**。它捕捉了 `Configuration` 在某一特定時刻的程式碼和配置。 * **職責**: 不可變性是其關鍵特性。這保證了每次部署都是可預測且穩定的。由於每個 `Revision` 都是一個獨立的、可運行的實體,這使得版本追蹤、灰度發布 (Canary Release) 和即時回滾 (Instant Rollbacks) 變得極其簡單和安全。每個 `Revision` 都可以獨立地進行自動縮放。 4. **Route (`route.serving.knative.dev`)**: * **角色**: 負責網路流量管理。它將一個公開的網路端點 (URL) 映射到一個或多個 `Revision`。 * **職責**: `Route` 的主要工作是根據您設定的規則來分配流量。您可以將 100% 的流量指向最新的 `Revision`(預設行為),也可以精確地切分流量,例如將 90% 流量導向穩定的 v1 版本,10% 流量導向新的 v2 版本進行 A/B 測試或灰度驗證。它會將這些規則轉譯給底層的網路層 (如 Istio, Contour) 來執行。 ### Knative Serving Architecture  這張圖展示了 Knative 的控制平面 (Control Plane) 和資料平面 (Data Plane) 的關鍵組件及其互動關係。 #### **控制平面 (Control Plane) - "大腦"** 控制平面負責監控和管理叢集狀態,確保實際狀態與您的期望狀態一致。 * **Controller**: Knative 的核心控制器,它不斷地監聽 (watch) Knative 資源 (如 `Service`) 的變化。當偵測到變更時,它會執行協調迴圈 (Reconciliation Loop),建立或更新依賴的資源 (如 `Configuration`, `Route`, Kubernetes `Deployment` 等)。 * **Autoscaler**: 這是實現自動伸縮的核心。它持續從 `Activator` 和 `Queue-Proxy` 收集流量指標 (如請求併發數),並根據這些指標和使用者定義的策略,動態調整每個 `Revision` 對應的 Pod 副本數量。 * **Webhooks**: Kubernetes 的准入控制器 (Admission Controller),在資源被寫入 etcd 之前進行攔截。它負責驗證 (validate) 您的 YAML 是否合法,以及變更/注入 (mutate) 預設值,確保系統的穩定性。 * **DomainMapping**: 允許您將自訂的網域名稱對應到一個 Knative `Service`,而非使用 Knative 自動產生的 URL。 #### **資料平面 (Data Plane) - "交通系統"** 資料平面是實際處理和轉發使用者請求的組件。 * **Ingress Gateway**: 所有外部和內部流量的統一入口。它是一個可插拔的網路層 (如 Istio, Contour, Kourier),根據 `Route` 資源設定的規則,將請求導向後端。如圖所示,它有兩種工作模式: * **Proxy Mode**: 將流量轉發給 `Activator`。此模式用於服務縮減至零或流量突發的場景。 * **Serve Mode**: 將流量直接轉發給應用的 Pod (`Queue-Proxy`)。此模式用於服務已有可用 Pod 的高流量場景。 * **Activator**: 在資料路徑中扮演關鍵的緩衝和喚醒角色。當一個 `Revision` 的副本數為零時,`Activator` 會接收並暫存 (buffer) 傳入的請求,同時 "Poke" (戳一下) `Autoscaler` 觸發擴容。一旦有 Pod 可用,它便會將請求轉發過去。 * **Queue-Proxy**: 一個強制注入到每個使用者 Pod 中的 **邊車容器 (Sidecar Container)**。它的職責包括: 1. 精確測量進入使用者容器的併發請求數 (`containerConcurrency`),並將指標上報給 `Autoscaler`。 2. 當請求超過設定的併發上限時,可以作為請求佇列。 3. 管理 Pod 的優雅終止 (graceful shutdown)。 * **User-Container**: 這就是您自己開發的應用程式容器。 ### HTTP Request Flows  這張圖詳細描繪了一個 HTTP 請求從進入到被處理的完整路徑。 #### **場景 A: 從零擴容 (Cold Start / Proxy Mode)** 當一個 `Revision` 沒有任何運行的 Pod 時 (scaled to zero): 1. **請求到達**: 使用者的請求首先到達 `HTTP Router` (即 Ingress Gateway)。 2. **路由至 Activator**: `Router` 根據路由表發現目標 `Revision` 沒有可用的 Pod IP,於是將請求轉發到共享的 `Activator`。 3. **觸發擴容**: `Activator` 接收請求,並立即通知 `Autoscaler` 需要資源。`Autoscaler` 隨即與 `Kubernetes apiserver` 通信,將該 `Revision` 對應的 `ReplicaSet` (或 `Deployment`) 的副本數從 0 調整為 1。 4. **Pod 創建**: Kubernetes 排程器創建一個新的 Pod。此 Pod 內含 `Queue-Proxy` 和您的 `User container`。 5. **請求轉發**: 一旦 Pod 啟動並回報為 Ready 狀態,`Activator` 就會將其暫存的請求轉發到新 Pod 的 `Queue-Proxy`,最終由您的 `User container` 處理。從步驟 1 到 5 的延遲就是「冷啟動」時間。 #### **場景 B: 高流量 (Serve Mode)** 當一個 `Revision` 已經有至少一個運行的 Pod 時: 1. **請求到達**: 使用者的請求到達 `HTTP Router`。 2. **直接路由**: `Router` 的路由表此時已包含該 `Revision` 的 Pod IP 位址。因此,請求會繞過 `Activator`,被直接轉發到其中一個健康 Pod 的 `Queue-Proxy`。 3. **請求處理**: `Queue-Proxy` 接收請求,在檢查併發數後,將其傳遞給 `User container` 進行處理。同時,`Queue-Proxy` 會持續上報流量指標給 `Autoscaler`,以便後者進行更精準的擴縮容決策。 這個路徑移除了 `Activator` 這個中間躍點,提供了最低的請求延遲。Knative 會根據流量動態地在這兩種模式之間切換,以兼顧成本效益和效能。 --- 理論結合實踐是最好的學習方式。 接下來我們將透過幾個由淺入深的 YAML 範例,來實際教學如何撰寫 Knative 設定檔。 --- ### 範例一:部署第一個 Knative 應用 (All-in-One Service) 對於 90% 的場景,您只需要撰寫一個 `Service` 物件,Knative 就會為您處理一切。這是最常見且最推薦的入門方式。 假設我們要部署一個簡單的 "Hello World" 應用。 **`service.yaml`** ```yaml # API 版本宣告,使用 Knative Serving v1 apiVersion: serving.knative.dev/v1 # 資源類型為 Service kind: Service metadata: # 此服務的名稱,將會成為 URL 的一部分 name: helloworld-go spec: # 'template' 區塊定義了 Revision 的模板。 # 未來所有基於此設定的 Revision 都會依此建立。 template: spec: containers: # 指定您的容器映像檔 - image: gcr.io/knative-samples/helloworld-go # 設定環境變數 env: - name: TARGET value: "World" ``` **解說:** * 這個單一的 `Service` 檔案已經包含了 `Configuration` 和 `Route` 的所有必要資訊。 * 當您透過 `kubectl apply -f service.yaml` 部署它時,Knative 會: 1. 建立一個名為 `helloworld-go` 的 `Configuration`。 2. 基於 `spec.template` 的內容,立即建立第一個 `Revision` (例如 `helloworld-go-00001-xyz`)。 3. 建立一個 `Route`,將 100% 的流量都導向這個剛剛建立的 `Revision`。 4. 提供一個公開的 URL,例如 `http://helloworld-go.default.example.com`。 ----- ### 範例二:更新應用與版本管理 現在,假設我們要更新這個應用,將它回應的文字從 "Hello World\!" 改為 "Hello Knative\!"。我們只需修改環境變數並重新套用即可。 **`service-updated.yaml`** ```yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go spec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET # 這一行是我們修改的地方 value: "Knative" ``` **解說:** * 當您套用這個更新後的檔案時,Knative 偵測到 `spec.template` 發生了變化。 * 它**不會**修改舊的 `Revision`,而是會: 1. 基於新的設定,建立一個**全新的、不可變的 `Revision`** (例如 `helloworld-go-00002-abc`)。 2. 預設情況下,`Route` 會被自動更新,將 **100% 的流量**無縫切換到這個新的 `Revision` 上。 * 舊的 `Revision` (`...-00001-xyz`) 依然存在,只是沒有流量導入。這意味著如果您發現新版本有問題,可以隨時快速回滾。 ----- ### 範例三:進階應用 - 灰度發布 (Canary Release) 假設新版本 (`...-00002-abc`) 存在風險,我們不想一次性將所有使用者都切換過去。我們希望先導入 10% 的流量到新版本,觀察其穩定性。這時我們就需要手動控制 `traffic` 區塊。 首先,您需要知道舊版和新版 `Revision` 的確切名稱。您可以透過以下指令取得: `kubectl get revisions` 假設我們得到的兩個版本名稱分別是: * 穩定版 (v1): `helloworld-go-00001-xyz` * 新候選版 (v2): `helloworld-go-00002-abc` 現在我們可以撰寫一個新的 YAML 來精確控制流量分配。 **`service-canary.yaml`** ```yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: helloworld-go spec: # 注意:即使手動控制流量,template 區塊依然是必需的。 # 它定義了 "下一次" 更新時 Revision 的樣板。 template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: "Knative" # 這是控制流量分配的核心區塊 traffic: # 第一個流量目標 - tag: stable-version # 為此流量目標設定一個標籤,方便識別 revisionName: helloworld-go-00001-xyz # 指向舊的、穩定的 Revision percent: 90 # 分配 90% 的流量 # 第二個流量目標 - tag: candidate-version # 為新版本設定標籤 revisionName: helloworld-go-00002-abc # 指向新的、待驗證的 Revision percent: 10 # 分配 10% 的流量 # 您也可以使用 latestRevision: true 來自動指向最新的 Revision ``` **解說:** * 這個設定明確地告訴 Knative 的 `Route` 物件: * 將 90% 的請求發送到 `helloworld-go-00001-xyz`。 * 將剩餘 10% 的請求發送到 `helloworld-go-00002-abc`。 * `tag` 會產生對應的獨立 URL (例如 `stable-version-helloworld-go...` 和 `candidate-version-helloworld-go...`),讓您可以單獨對某個版本進行測試,非常方便。 * 當您確認新版本穩定後,只需修改此檔案,將 `percent` 調整為 0 和 100,然後再次套用,即可完成發布。 ### 總結 從這些範例中可以看出,Knative 的設計哲學是透過簡單、聲明式的 YAML,來管理複雜的應用生命週期和部署策略。開發者可以從一個極簡的 `Service` 開始,並在需要時逐步引入如 `traffic` 這樣的進階設定,而無需關心底層是如何創建 `Deployment`、`Service`、`Ingress` 以及如何協調它們的。 --- ### Kustomize 是什麼?為什麼它和 Knative 是絕配?  source: https://razorops.com/blog/kustomize 簡單來說,**Kustomize 是一個無需模板、原生於 Kubernetes 的組態管理工具**。它的核心哲學是「客製化」而非「模板化」。 想像一下您在用樂高積木蓋房子: * **基礎 YAML 檔案 (如 Knative 的 `service.yaml`)**:這是一套通用的樂高積木組,定義了房子的核心結構(`base`)。 * **不同環境的需求 (開發、測試、生產)**:您想為這棟房子打造不同的「主題外觀」。開發版可能想漆成藍色,加個小花園;生產版則要漆成紅色,並蓋上堅固的屋頂。這些就是「疊加層」(`overlay`)。 傳統作法是為每個環境複製整套積木(YAML),然後手動修改,這非常沒效率且容易出錯。而 Kustomize 就像一個**智慧的樂高裝飾師**,它允許您: 1. **保留一套基礎積木 (Base)**:您只需維護一份核心的 YAML 檔案。 2. **建立不同的裝飾方案 (Overlay)**:針對每個環境,您只需定義「差異」的部分,例如「把所有牆壁變成藍色」、「在屋頂加上一個天線」等。 當您需要某個環境的最終設計圖時,Kustomize 會聰明地將「基礎積木」和您指定的「裝飾方案」合併,產生出最終的、完整的 YAML 組態。 **它與 Knative 是絕配的原因:** Knative 應用程式在不同環境中通常需要不同的設定,例如: * **開發環境**:使用開發用的資料庫環境變數,Pod 資源需求較低。 * **測試環境**:需要開啟特定的偵錯標籤 (label)。 * **生產環境**:使用正式的容器映像檔標籤 (image tag),設定更高的 Pod 副本數,並配置更嚴格的資源限制。 Kustomize 讓您能優雅地管理這些差異,而不需要為每個環境複製和維護幾乎一模一樣的 Knative `Service` YAML 檔案。 ----- ### Kustomize 的核心概念 在深入了解 Built-ins 之前,先掌握這幾個關鍵詞彙: * **`kustomization.yaml`**:Kustomize 的核心檔案,如同一個「說明書」或「配方」,告訴 Kustomize 要讀取哪些基礎資源,以及要對它們執行哪些修改。 * **`base`**:一個基礎目錄,包含了通用的、不分環境的 Kubernetes YAML 檔案。 * **`overlay`**:一個疊加層目錄,它會引用一個 `base`,並在其之上應用特定的客製化修改。我們通常會為 `development`, `staging`, `production` 各建立一個 overlay。 一個典型的專案結構如下: ``` my-app/ ├── base/ │ ├── kustomization.yaml │ └── service.yaml # 基礎的 Knative Service └── overlays/ ├── staging/ │ ├── kustomization.yaml │ └── patch-env.yaml # 專用於 staging 的補丁 └── production/ ├── kustomization.yaml └── patch-resources.yaml # 專用於 production 的補丁 ``` 要建構生產環境的設定,您只需執行 `kustomize build overlays/production`。 ----- ### Kustomize Built-Ins 詳解 (採用現代語法) Kustomize 的強大之處在於其內建的「產生器 (Generators)」和「轉換器 (Transformers)」。它們是 `kustomization.yaml` 中用來描述「如何修改」的關鍵欄位。 #### 1\. 資源產生器 (Generators) - 無中生有 它們負責在建構過程中建立新的 Kubernetes 資源。 * **`configMapGenerator`**: 從檔案或字面值產生 `ConfigMap`。 * **用途**:將設定檔或環境變數與應用程式容器分離,實現更好的配置管理。 * **常用選項**: `name`, `files`, `literals`, `options` (可在此單獨設定標籤/註解,或用 `disableNameSuffixHash: true` 關閉名稱的 hash 後綴)。 * **範例**: ```yaml configMapGenerator: - name: my-app-config files: - config/app-settings.ini literals: - LOG_LEVEL=info - FEATURE_FLAG=true ``` * **`secretGenerator`**: 與 `configMapGenerator` 類似,但用於產生 `Secret`。 * **用途**:安全地管理敏感資料,如 TLS 憑證、API 金鑰、資料庫密碼等。 * **範例**: ```yaml secretGenerator: - name: my-db-credentials literals: - a_secret_key=s3cr3t-v4lu3 files: - tls.crt - tls.key type: "kubernetes.io/tls" ``` #### 2\. 全域轉換器 (Global Transformers) - 全面修改 它們會將修改應用到**所有**的資源上。 * **`namePrefix` / `nameSuffix`**: 為所有資源的名稱加上前綴或後綴。 * **用途**:在多租戶或多環境的叢集中,避免資源名稱衝突。 * **範例**:`namePrefix: staging-` 會將 `my-app-svc` 變成 `staging-my-app-svc`。 * **`namespace`**: 將所有資源設定到指定的命名空間。 * **用途**:確保整個應用被部署到正確的隔離環境中。 * **`commonLabels` / `commonAnnotations`**: 為所有資源(包括 Pod 範本中的選擇器)加上相同的標籤或註解。 * **用途**:方便統一追蹤、管理和篩選,例如標示應用負責團隊 `owner: devops-team`。 #### 3\. 特定目標轉換器 (Targeted Transformers) - 精準打擊 它們只會修改符合特定條件的資源或欄位。 * **`images`**: 修改容器映像檔的名稱、標籤 (tag) 或摘要 (digest)。 * **用途**:**這是 Kustomize 最常用的功能之一**。在不同環境中使用不同的映像檔版本。 * **範例**: ```yaml images: - name: my-app-image # 原始映像檔名稱 newName: gcr.io/my-repo/my-app # 可選,修改映像檔路徑 newTag: v1.2.0-stable # 新的標籤 ``` * **`replicas`**: 修改 Deployment, StatefulSet 等資源的副本數量。 * **用途**:在生產環境增加副本數以應對高流量。 * **範例**: ```yaml replicas: - name: my-app-deployment # 目標 Deployment 的名稱 count: 5 # 設定副本數為 5 ``` * **`patches`**: **這是 Kustomize 的終極武器**,也是推薦使用的現代補丁語法。 * **原理**:一個統一的欄位,可同時處理「策略性合併補丁」和「JSON 補丁」,並透過 `target` 欄位精準鎖定要修改的資源。 * **用途**:進行任何形式的修改,從簡單的欄位值變更到複雜的列表元素操作。 * **範例 1:策略性合併 (Strategic Merge Patch)** 修改名為 `my-app` 的 Deployment,增加 CPU 請求。 `kustomization.yaml`: ```yaml patches: - path: increase-cpu.yaml # 補丁檔案路徑 target: kind: Deployment name: my-app ``` `increase-cpu.yaml`: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: template: spec: containers: - name: my-app-container resources: requests: cpu: "250m" ``` * **範例 2:JSON 補丁 (JSON Patch) - 使用行內補丁** 修改 `my-app` 的 Deployment,替換第一個容器的映像檔。 `kustomization.yaml`: ```yaml patches: - target: kind: Deployment name: my-app patch: |- # 這是一種標準化的操作語法 - op: replace path: /spec/template/spec/containers/0/image value: nginx:stable ``` ----- ### 綜合實戰範例:Kustomize + Knative 多環境管理 讓我們用現代語法重新實作一個更完整的 Knative 應用場景。 **專案結構:** ``` knative-app/ ├── base/ │ ├── kustomization.yaml │ └── service.yaml └── overlays/ ├── staging/ │ └── kustomization.yaml └── production/ ├── kustomization.yaml └── patch-production-resources.yaml ``` **1. Base: 基礎 Knative Service** `base/service.yaml`: ```yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: knative-greeter spec: template: spec: containers: - image: gcr.io/knative-samples/helloworld-go env: - name: TARGET value: "World" ``` `base/kustomization.yaml`: ```yaml resources: - service.yaml ``` **2. Staging Overlay: 測試環境** 我們希望測試環境: * 有 `staging-` 名稱前綴以避免衝突。 * 使用 `dev` 版本的映像檔。 * 將 `TARGET` 環境變數改為 `Staging`。 * 加上 `env: staging` 標籤。 `overlays/staging/kustomization.yaml`: ```yaml resources: - ../../base namePrefix: staging- commonLabels: env: staging images: - name: gcr.io/knative-samples/helloworld-go newTag: dev patches: - target: kind: Service name: knative-greeter patch: |- - op: replace path: /spec/template/spec/containers/0/env/0/value value: "Staging" ``` **3. Production Overlay: 生產環境** 我們希望生產環境: * 使用 `stable` 版本的映像檔。 * 設定更高的資源請求與限制。 * 將 `TARGET` 環境變數改為 `Production`。 `overlays/production/patch-production-resources.yaml`: ```yaml apiVersion: serving.knative.dev/v1 kind: Service metadata: name: knative-greeter spec: template: spec: containers: - name: helloworld-go env: - name: TARGET value: "Production" resources: requests: cpu: 200m memory: 256Mi limits: cpu: 500m memory: 512Mi ``` `overlays/production/kustomization.yaml`: ```yaml resources: - ../../base images: - name: gcr.io/knative-samples/helloworld-go newTag: stable patches: - path: patch-production-resources.yaml target: kind: Service name: knative-greeter ``` **如何使用?** 當您要部署到測試環境,只需執行:`kustomize build overlays/staging | kubectl apply -f -`。 當您要部署到生產環境,只需執行:`kustomize build overlays/production | kubectl apply -f -`。 透過這種方式,您用一套 `base` 和幾個描述差異的 `overlay`,清晰、可維護地管理了複雜的多環境應用配置。 ## 參考文件: - Knative: https://knative.dev/docs/serving/ - kustomization: https://kubectl.docs.kubernetes.io/references/kustomize/
×
Sign in
Email
Password
Forgot password
or
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.