Kubernetes 提供的動態准入控制器允許開發者在資源生命週期中插入自定義邏輯,實作更精細的控制。本文詳細介紹了驗證和變更 Webhook 的實作步驟,並提供 Python 程式碼範例,示範如何使用 Flask 框架建構 Webhook 伺服器,以及如何利用 JSON Patch 修改資源組態。同時,也說明瞭如何將 Webhook 佈署到 Kubernetes 叢集,並組態相關的 ValidatingWebhookConfiguration 和 MutatingWebhookConfiguration 資源,確保 Webhook 能夠正常運作。此外,文章也探討了 Kubernetes 網路模型的核心概念,包含 CNI 和 kube-proxy 的運作機制,以及如何選擇合適的 CNI 外掛,並說明瞭服務網格如何增強 Pod 間的安全性。
動態准入控制器:驗證與變更 Webhook 的 Kubernetes 實踐
Kubernetes 的動態准入控制器提供了一種強大的機制,能夠在資源建立、更新或刪除時進行驗證或修改。本文將探討驗證與變更 Webhook 的設計原理、實作方法以及實際應用案例。
驗證 Webhook 的設計與實作
驗證 Webhook 主要用於檢查資源是否符合特定的策略或規範。以下是一個使用 Flask 框架實作的簡單驗證 Webhook 伺服器範例:
import json
import os
from flask import jsonify, Flask, request
app = Flask(__name__)
@app.route("/", methods=["POST"])
def validation():
review = request.get_json()
app.logger.info("Validating AdmissionReview request: %s", json.dumps(review, indent=4))
response = {}
# 檢查 Pod 是否具有 environment 標籤
if "environment" not in review["request"]["object"]["metadata"]["labels"]:
response["allowed"] = False
response["status"] = {"code": 403, "message": "Every Pod requires an 'environment' label."}
else:
# 檢查 environment 標籤的值是否為 'dev' 或 'prod'
environment = review["request"]["object"]["metadata"]["labels"]["environment"]
if environment not in ["dev", "prod"]:
response["allowed"] = False
response["status"] = {"code": 403, "message": "'environment' label must be one of 'dev' or 'prod'"}
else:
response["allowed"] = True
review["response"] = response
return jsonify(review), 200
context = (
os.environ.get('WEBHOOK_CERT', '/tls/webhook.crt'),
os.environ.get('WEBHOOK_KEY', '/tls/webhook.key'),
)
app.run(host='0.0.0.0', port='443', debug=True, ssl_context=context)
內容解密:
- 檢查 Pod 資源的標籤:程式碼首先檢查傳入的 AdmissionReview 請求中,Pod 資源是否包含
environment標籤。如果沒有,則拒絕請求。 - 驗證
environment標籤的值:如果environment標籤存在,則進一步檢查其值是否為dev或prod。如果不是,則同樣拒絕請求。 - 使用 Flask 處理請求:Webhook 伺服器使用 Flask 框架來處理來自 Kubernetes API 伺服器的 POST 請求,並傳回處理結果。
- TLS 組態:伺服器使用 TLS 加密通訊,證書和私鑰的路徑透過環境變數
WEBHOOK_CERT和WEBHOOK_KEY設定。
將驗證 Webhook 佈署到 Kubernetes
為了使驗證 Webhook 生效,需要將其佈署到 Kubernetes 叢集,並組態 ValidatingWebhookConfiguration:
apiVersion: admissionregistration.k8s.io/v1beta1
kind: ValidatingWebhookConfiguration
metadata:
name: label-validation
webhooks:
- name: admission.example.com
rules:
- apiGroups:
- ""
apiVersions:
- v1
operations:
- CREATE
resources:
- pods
clientConfig:
service:
namespace: infrastructure
name: label-validation
caBundle: <base64 encoded bundle>
內容解密:
- 組態 Webhook 名稱和規則:定義了 Webhook 的名稱為
admission.example.com,並指定了觸發規則:當建立 Pod 資源時,API 伺服器會將請求轉發到此 Webhook。 - 指定服務和 CA Bundle:組態了提供 Webhook 服務的 Kubernetes Service,以及用於驗證 TLS 連線的 CA Bundle。
變更 Webhook 的設計與實作
變更 Webhook 用於在資源建立或更新時自動修改資源的組態。以下是一個注入 sidecar 容器的變更 Webhook 範例:
import base64
import json
import os
from flask import jsonify, Flask, request
app = Flask(__name__)
@app.route("/", methods=["POST"])
def mutation():
review = request.get_json()
app.logger.info("Mutating AdmissionReview request: %s", json.dumps(review, indent=4))
response = {}
patch = [{
'op': 'add',
'path': '/spec/containers/0',
'value': {
'image': 'nginx',
'name': 'proxy-sidecar',
}
}]
response['allowed'] = True
response['patch'] = base64.b64encode(json.dumps(patch).encode()).decode()
response['patchType'] = 'application/json-patch+json'
review['response'] = response
return jsonify(review), 200
context = (
os.environ.get("WEBHOOK_CERT", "/tls/webhook.crt"),
os.environ.get("WEBHOOK_KEY", "/tls/webhook.key"),
)
app.run(host='0.0.0.0', port='443', debug=True, ssl_context=context)
內容解密:
- 使用 JSON Patch 修改資源:程式碼透過 JSON Patch 語法,在 Pod 的
spec.containers中新增一個名為proxy-sidecar的容器。 - 傳回 Patch 結果:Webhook 將修改後的結果以 Base64 編碼,並傳回給 Kubernetes API 伺服器。
網路連線
如同任何分散式系統,Kubernetes 依賴網路提供服務之間的連線,以及將外部使用者連線到已暴露的工作負載。
在傳統的應用程式架構中,網路管理一直是個挑戰。在許多組織中,開發人員會建立應用程式,而操作人員則負責執行它們。隨著應用程式的發展,網路基礎設施的需求可能會發生變化。在最好的情況下,應用程式根本無法運作,操作人員會採取糾正措施。然而,在最壞的情況下,網路安全等領域可能會出現重大漏洞。
Kubernetes 允許開發人員定義網路資源和策略,這些資源和策略可以與應用程式佈署清單一起存在。這些資源和策略可以由叢集管理員進行適當的範圍設定,並可以使用常見的抽象層利用各種最佳技術實作。透過將開發人員與網路工作的細節分離,並將基礎設施的需求與應用程式的需求結合起來,我們可以更好地確保我們的應用程式能夠以一致且安全的方式交付。
容器網路介面(CNI)
在討論如何將使用者與容器化工作負載連線之前,我們需要了解 Pod 如何與其他 Pod 進行通訊。這些 Pod 可能位於同一節點上、同一子網路中的不同節點上,甚至位於不同資料中心的節點上。如圖 10-1 所示,無論網路結構如何,我們都旨在以無縫、可路由的方式連線 Pod。
圖 10-1:CNI 網路
Kubernetes 使用 CNI 規範與網路進行介面。這個開放規範的目標是標準化容器協調平台如何將容器與底層網路連線,並以可插拔的方式實作。有數十種解決方案,每種都有自己的架構和功能。大多數是開源解決方案,但也有來自雲原生生態系統中不同供應商的專有解決方案。無論您在哪個環境中佈署叢集,都一定會有適合您需求的外掛。
雖然 Kubernetes 中的網路有多個方面,但 CNI 的作用只是促進 Pod 與 Pod 之間的連線。這種方式相對簡單。容器執行環境(例如 Docker)呼叫 CNI 外掛可執行檔(例如 Calico),以新增或移除容器網路名稱空間中的介面。這些被稱為沙箱介面。
正如您所回憶的,每個 Pod 都分配了一個 IP 位址,而 CNI 外掛負責其分配和指派給 Pod。
您可能會問自己,“如果一個 Pod 可以有多個容器,CNI 如何知道要連線哪一個?”如果您曾經查詢過 Docker 以列出在給定 Kubernetes 節點上執行的容器,您可能已經注意到與每個 Pod 相關聯的多個暫停容器。這些暫停容器在計算上沒有任何意義。它們只是作為每個 Pod 容器網路的佔位符。因此,它們是第一個啟動的容器,也是 Pod 生命週期中最後一個死亡的容器。
在外掛代表容器執行環境執行所需的工作之後,它會傳回執行的狀態,就像任何其他 Linux 程式一樣:0 表示成功,其他傳回程式碼表示失敗。作為成功操作的一部分,CNI 外掛還會傳回由外掛在過程中操作的 IP、路由和 DNS 條目的詳細資訊。
除了將容器連線到網路之外,CNI 還具有 IP 位址管理(IPAM)的功能。IPAM 確保 CNI 始終清楚哪些位址正在使用,以及哪些位址可用於組態新介面。
選擇外掛
在為您的環境選擇 CNI 外掛時,有兩個主要考慮因素需要牢記:
- 網路拓撲是什麼?
- 網路拓撲決定了您最終能夠在環境中佈署的大部分內容。例如,如果您要佈署到公有雲中的多個可用區,您可能需要實作支援某種封裝(也稱為疊加網路)的外掛。
- 哪些功能對您的組織至關重要?
- 您需要考慮哪些功能對您的佈署很重要。如果 Pod 之間需要相互 TLS,您可能需要使用提供此功能的外掛。同樣,不是每個外掛都支援 NetworkPolicy。在佈署叢集之前,請務必評估外掛提供的功能。
使用 Sidecar 模式實作服務網格
CNI 不是在 Pod 之間強制實施相互 TLS 的唯一機制。使用稱為服務網格的 Sidecar 模式,叢集管理員可以要求工作負載僅透過啟用 TLS 的本地代理進行通訊。服務網格不僅提供端對端加密,還可以啟用更高階的功能,例如斷路、藍綠佈署和分散式追蹤。它也可以對最終使用者透明地啟用。
kube-proxy
即使有了 Pod 與 Pod 之間的網路連線,如果 Kubernetes 不提供一些額外的抽象層來超越直接的 IP 到 IP 連線,那麼它仍然相對原始。例如,如果一個 Deployment 有多個副本,因此有多個服務 IP,我們該如何處理?我們只是選擇其中一個 IP,並希望它在未來的某個時候不會被移除嗎?如果我們有一個穩定的端點來連線到我們的應用程式,而不是一組不斷變化的 IP 位址,那不是很好嗎?
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: my-app
ports:
- name: http
port: 80
targetPort: 8080
內容解密:
此 YAML 組態定義了一個名為 my-service 的 Kubernetes Service 物件。它使用 app: my-app 選擇器來選擇目標 Pod,並將流量從 Service 的 80 埠轉發到目標 Pod 的 8080 埠。這樣,無論 Pod 的 IP 位址如何變化,使用者都可以使用穩定的 Service 端點來存取應用程式。
kube-proxy 是 Kubernetes 中的一個關鍵元件,它負責維護節點上的網路規則,以便允許 Pod 與 Service 之間的通訊。它可以以多種模式執行,包括使用者空間代理模式、iptables 代理模式和 IPVS 代理模式。每種模式都有其優缺點,適用於不同的場景。
// iptables 模式下的 kube-proxy 程式碼片段
func (proxier *Proxier) syncProxyRules() {
// ...
// 更新 iptables 規則
proxier.iptablesData.Reset()
proxier.iptablesData.Write(proxier.iptablesChainBuffer.Bytes())
// ...
}
內容解密:
此 Go 程式碼片段展示了 kube-proxy 在 iptables 模式下的部分實作。它呼叫 syncProxyRules 方法來同步代理規則,其中包括更新 iptables 規則以確保 Service 和 Pod 之間的正確通訊。這種方式使得 kube-proxy 能夠有效地管理網路流量。
總之,Kubernetes 的網路模型透過 CNI 和 kube-proxy 等元件提供了強大的網路功能,使得應用程式能夠以一致且安全的方式被佈署和存取。瞭解這些元件的工作原理對於設計和維護高效、可靠的 Kubernetes 叢集至關重要。