在第 1 部分中,我們講解了 Kubernetes 的核心元件,Kubernetes 是一種開源容器編排器,用於在分散式環境中部署和擴充套件應用程式;我們還講解了如何在叢集中部署一個簡單的應用程式,然後更改其副本數量以擴大或縮小其規模。
在本文中,我們將為您深入講解 Kubernetes 提供的網路和監控功能,為您將自己的應用推廣到具有公開網路服務和良好可觀察性的生產環境中做準備。
Kubernetes 包含一套全面的網路功能,用於將 Pod 連線在一起,並讓它們在叢集外可見。這裡總結了一些較常使用的基礎元件。
Service 是 Kubernetes 物件,用於公開在叢集中的 Pod 中執行的網路應用程式。網路流量流經服務,被引到正確的 Pod。
Service 有時會讓開發人員感到困惑,因為其術語有時會與傳統觀點重疊。開發人員通常認為 Service 就是他們在叢集中執行的應用程式,但在 Kubernetes 中,Service 具體是指允許存取應用程式的網路元件,是將 Kubernetes 物件聯網的基本資源。在部署工作負載時,如果 Pods 需要在它們之間或叢集外部進行通訊,就需要使用服務。
Serivce 模型在部署副本之間分配流量時需要被使用到。例如,如果您為一個應用程式介面部署了四個副本,那麼這四個 Pod 就應該共用網路流量。建立 Service 可以實現這一點,您的應用程式可以連線到服務的 IP 地址,然後該 IP 地址會將網路流量轉發到其中一個相容的 Pod。每個 Service 還分配了一個可預測的叢集內 DNS 名稱,以方便自動發現服務。
Kubernetes 支援幾種不同型別的 Service,以適應常見的網路用例。主要有三種:
ClusterIP
:這種型別的服務分配了一個 IP 地址,只能在群集記憶體取。這可以防止 Pod 被外部呼叫。由於這是最安全的服務型別,因此在未指定其他服務型別時,它也是預設服務型別。
NodePort
:這些服務也會分配一個內部 IP 地址,但會額外繫結到節點上的指定埠。如果在埠 80 上建立了 NodePort
服務,且其 Pod 由節點 192.168.0.1
託管,則可以通過本地網路上的 192.168.0.1:80
連線到 Pod。
LoadBalancer
:LoadBalancer
Service 將外部 IP 地址對映到群集中。在使用亞馬遜 EKS 或 Google GKE 等託管 Kubernetes 解決方案時,建立LoadBalancer
Service 會自動在雲賬戶中提供一個新的LoadBalancer
資源。LoadBalancer
公共 IP 的流量會路由到 Service 背後的 Pod。當打算將 Pod 公開暴露在群集之外時,請使用此 Service 型別。LoadBalancer
允許您首先在群集的物理節點之間路由流量,然後將流量路由到每個節點上的正確 Pod。
1. 建立 Service
下面的 YAML 清單範例定義了一個 ClusterIP
服務,該服務將其 80 埠的流量導向帶有 app.kubernetes.io/name: demo
標籤的 Pod 的 8080 埠:
<span class="hljs-attribute">apiVersion</span>: v1
<span class="hljs-attribute">kind</span>: Service
<span class="hljs-attribute">metadata</span>:
<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">spec</span>:
<span class="hljs-attribute">selector</span>:
app.kubernetes.io/<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">ports</span>:
- <span class="hljs-attribute">protocol</span>: TCP
<span class="hljs-attribute">port</span>: <span class="hljs-number">80</span>
<span class="hljs-attribute">targetPort</span>: <span class="hljs-number">8080</span>
將檔案儲存為 service.yaml
,然後使用 kubectl 將其應用到叢集中:
$ kubectl <span class="hljs-built_in">apply</span> -f service.yaml
service/<span class="hljs-built_in">demo</span> created
執行 get services
命令顯示分配給 Service 的群集 IP:
$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
demo ClusterIP <span class="hljs-number">10.99</span>
<span class="hljs-number">.219</span>
<span class="hljs-number">.177</span> <none>
<span class="hljs-number">80</span>/TCP
<span class="hljs-number">10</span>s
叢集中的 Pod 現在可以與該 IP 地址(10.99.219.177
)通訊,以連線標有 app.kubernetes.io/name: demo
的相鄰 Pod。由於 Service 會自動發現,您還可以使用分配給它的 DNS 主機名存取服務,其形式為
<service-name>.<namespace-name>.svc.<cluster-domain>
在本例中,主機名為
demo.default.svc.cluster.local
。
2. 針對內部和外部 IP 使用正確的 Service 型別
不同情況不同選擇,正確的 Service 型別取決於您需要實現的目標。
如果 Pod 只需要在叢集內部存取,例如其他叢集內應用程式使用的資料庫,則使用 ClusterIP
即可。這樣可以防止 Pod 意外暴露在外部,提高叢集安全性。對於應從外部存取的應用程式,如 API 和網站部署,則應使用 LoadBalancer
。
NodePort
Service 需要謹慎。它們允許你設定自己的負載平衡解決方案,但經常被誤用而造成意想不到的後果。當你手動指定埠範圍時,請確保避免衝突。NodePort
服務還會繞過大多數 Kubernetes 網路安全控制,讓 Pod 暴露在外。
Service 只能在 IP 和埠級別上執行,因此它們通常與 Ingress 物件配對。Ingress 是用於 HTTP 和 HTTPS 路由的專用資源。Ingress 根據主機名和 URI 等請求特徵,將 HTTP 流量對映到叢集中的不同服務。它們還提供負載平衡和 SSL 終止功能。
重要的是,Ingresses 本身並不是服務。它們位於服務前面,將服務暴露給外部流量。您可以使用 LoadBalancer 服務直接公開一組 Pod,但這會在沒有任何過濾或路由支援的情況下推播流量。而使用 Ingress,您可以在服務之間切換流量,例如將 api.example.com
傳送到您的 API 以及將 app.example.com
傳送到您的前端。
要使用 Ingress,必須在叢集中安裝一個 Ingress 控制器。它負責將傳入流量與您的 Ingress 物件進行匹配。
Kubernetes 預設情況下不捆綁任何選項;NGINX Ingress 和 Traefik 是易於設定的推薦選項。
Ingress 定義了一個或多個 HTTP 路由及其對映到的 Service。下面是一個將來自 example.com
的流量導向您的 demo
服務的基本範例:
<span class="hljs-attribute">apiVersion</span>: networking.k8s.io/v1
<span class="hljs-attribute">kind</span>: Ingress
<span class="hljs-attribute">metadata</span>:
<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">spec</span>:
<span class="hljs-attribute">ingressClassName</span>: nginx
<span class="hljs-attribute">rules</span>:
- <span class="hljs-attribute">host</span>: example.com
<span class="hljs-attribute">http</span>:
<span class="hljs-attribute">paths</span>:
- <span class="hljs-attribute">path</span>: /
<span class="hljs-attribute">pathType</span>: Prefix
<span class="hljs-attribute">backend</span>:
<span class="hljs-attribute">service</span>:
<span class="hljs-attribute">name</span>: demo
<span class="hljs-attribute">port</span>:
<span class="hljs-attribute">number</span>: <span class="hljs-number">80</span>
spec.ingressClassName
的正確值取決於您使用的 Ingress 控制器。
網路策略是一種機制,用於控制哪些 Pod 可以相網際網路。在沒有網路策略的情況下,無論 Pods 是否被 Service 公開,都可以自由通訊。
每個策略使用選擇器(selector)針對一個或多個 Pod。策略可以列出單獨的 Ingress 和 Egress 規則: Ingress規則定義目標 Pod 可以從哪些 Pod 接收流量;Egress 規則限制目標 Pod 的流量流向。
下面是一個範例:
<span class="hljs-attribute">apiVersion</span>: networking.k8s.io/v1
<span class="hljs-attribute">kind</span>: NetworkPolicy
<span class="hljs-attribute">metadata</span>:
<span class="hljs-attribute">name</span>: demo-policy
<span class="hljs-attribute">spec</span>:
<span class="hljs-attribute">podSelector</span>:
<span class="hljs-attribute">matchLabels</span>:
<span class="hljs-attribute">app-component</span>: database
<span class="hljs-attribute">policyTypes</span>:
- Ingress
- Egress
<span class="hljs-attribute">ingress</span>:
- <span class="hljs-attribute">from</span>:
- <span class="hljs-attribute">ipBlock</span>:
<span class="hljs-attribute">cidr</span>: <span class="hljs-number">172.17</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">16</span>
- <span class="hljs-attribute">podSelector</span>:
<span class="hljs-attribute">matchLabels</span>:
<span class="hljs-attribute">app-component</span>: api
<span class="hljs-attribute">ports</span>:
- <span class="hljs-attribute">protocol</span>: TCP
<span class="hljs-attribute">port</span>: <span class="hljs-number">3306</span>
<span class="hljs-attribute">egress</span>:
- <span class="hljs-attribute">to</span>:
- <span class="hljs-attribute">ipBlock</span>:
<span class="hljs-attribute">cidr</span>: <span class="hljs-number">172.17</span>.<span class="hljs-number">0.0</span>/<span class="hljs-number">16</span>
- <span class="hljs-attribute">podSelector</span>:
<span class="hljs-attribute">matchLabels</span>:
<span class="hljs-attribute">app-component</span>: api
<span class="hljs-attribute">ports</span>:
- <span class="hljs-attribute">protocol</span>: TCP
<span class="hljs-attribute">port</span>: <span class="hljs-number">3306</span>
該策略規定,只有標有 app-component: api
的 Pod 可以與標有 app-component: database
的 Pod 通訊。
策略可以包括多個 Ingress 和 Egress 規則,也可以選擇完全忽略一種流量型別。如果流量型別被排除在外,過濾則不適用。如果多個網路策略針對一個 Pod,那麼流量需要滿足組合列表中的每一條規則。
為所有 Pod 設定網路策略是一種良好做法。這樣有助於防止受到攻擊的 Pod 向群集中的其他工作負載傳送惡意流量。雖然預設情況下流量不進行過濾,但您可以建立名稱空間級的 deny all
規則,防止與缺乏更具體策略的 Pod 進行通訊:
<span class="hljs-symbol">apiVersion:</span> networking.k8s.io/v1
<span class="hljs-symbol">kind:</span> NetworkPolicy
<span class="hljs-symbol">metadata:</span>
<span class="hljs-symbol"> name:</span> deny-all
<span class="hljs-symbol">spec:</span>
<span class="hljs-symbol"> podSelector:</span> {}
<span class="hljs-symbol"> policyTypes:</span>
- Ingress
- Egress
Kubernetes 叢集中的工作負載經常使用到機密資訊,如資料庫密碼和 API 金鑰。這些值一旦暴露,就會構成嚴重的安全威脅。Kubernetes ConfigMap 物件是向 Pod 提供鍵值資料的標準方式。它們封裝了 Pod 所需的任意設定值。
下面是帶有幾個資料欄位的簡單 ConfigMap 的 YAML 定義:
<span class="hljs-symbol">apiVersion:</span> v1
<span class="hljs-symbol">kind:</span> ConfigMap
<span class="hljs-symbol">metadata:</span>
<span class="hljs-symbol"> name:</span> app-config
<span class="hljs-symbol">data:</span>
<span class="hljs-symbol"> default_auth_token_lifetime:</span> <span class="hljs-number">900</span>
<span class="hljs-symbol"> default_user_name:</span> <span class="hljs-string">"admin"</span>
<span class="hljs-symbol"> external_auth_enabled:</span> true
你可以在 Pod 中以環境變數或掛載卷的形式使用 ConfigMap。前一種策略允許你通過存取命名的環境變數來檢索 ConfigMap 金鑰的值,而後一種策略則使用掛載卷將 ConfigMap 的內容存入容器檔案系統中的檔案。
由於 ConfigMap 資料是以純文字形式未加密儲存的,因此使用者不會將其用於密碼和 API 金鑰等用途。相反,在與敏感資料互動時,請建立一個 Secret 物件。Secrets 物件的工作原理與 ConfigMap 類似,但它們是專為更安全的憑證處理而設計的。Secrets 物件預設以 Base64 編碼顯示值,並將其與應用程式的常規設定分開,從而降低了無意暴露的風險。這樣就能最大限度地降低意外暴露的風險。
你可以在啟動叢集時設定 Kubernetes API 伺服器,選擇啟用 Secrets 資料加密。
繼安全性之後,有效的監控和紀錄檔記錄功能應成為 Kubernetes 的下一個優先事項。對工作負載效能的良好可見效能讓你在新出現的問題造成更大問題之前快速做反應。您可以使用指標、警報和紀錄檔來跟蹤叢集活動。
Kubernetes Metrics Server 是一個可以安裝在叢集中的外掛。它提供一個 API,用於從節點和 Pod 中提取資源利用率資料。
執行以下 kubectl 命令來部署 Metrics Server:
$ kubectl apply -f https:<span class="hljs-regexp">//gi</span>thub.com<span class="hljs-regexp">/kubernetes-sigs/m</span>etrics-server<span class="hljs-regexp">/releases/</span>latest<span class="hljs-regexp">/download/</span>components.yaml
安裝完成後,使用 kubectl top
檢視叢集節點和 Pod 的 CPU 和記憶體利用率:
$ kubectl top nodes
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
minikube <span class="hljs-number">265</span>m
<span class="hljs-number">6</span>%
<span class="hljs-number">640</span>Mi
<span class="hljs-number">8</span>%
$ kubectl top pod
NAME CPU(cores) MEMORY(bytes)
nginx <span class="hljs-number">0</span>m
<span class="hljs-number">4</span>Mi
Kube State Metrics 是另一個度量資料來源。它提供與叢集內物件相關的指標,如執行中的部署、失敗的 Pod 和有效任務的數量。
如果你必須手動查詢才能獲得所需的資訊,那麼度量指標就不會特別有用。而設定警報和儀表板可確保您瞭解重要的叢集事件,如資源利用率攀升或 Pod 出現故障。
Kubernetes 專案沒有為這些功能提供內建解決方案。因此最好部署一個專用的可觀察性平臺,如 Prometheus。Prometheus 的 Alertmanager 可以在特定事件發生時向通訊平臺傳送通知。將 Prometheus 與 Grafana 等儀表盤工具搭配使用,可將資料視覺化並行現指標趨勢。
設定操作儀表盤是監控叢集的有效方法,可讓您對 Kubernetes 和應用程式的健康狀況和效能一目瞭然。
顯然,瞭解指標變化或 Pod 失效的原因至關重要,應用程式生成的紀錄檔應該能揭示這些資訊。
您可以使用 kubectl logs
命令按 Pod 檢索紀錄檔,但使用 Fluentd、Logstash 或 Loki 等整合工具可以更輕鬆地收集、過濾和歸檔記錄。這些解決方案能將叢集中的紀錄檔流式傳輸到單獨的儲存平臺,以供長期參考。
紀錄檔整合還能讓搜尋紀錄檔和分析重複訊息變得更容易。它們會對報文內容進行索引,因此您無需使用 shell 工具手動解析紀錄檔,就能對其進行查詢。這有助於您發現應用程式中的新錯誤,追蹤問題的根本原因,並在出現異常時及時發現。
本系列由兩部分組成,介紹了 Kubernetes 的基礎知識,包括為什麼它是開發人員亟需的技能,以及它的抽象如何有效地模擬不同的應用元件。通過該文章,您應該已經瞭解瞭如何建立部署、設定網路和安全以及監控叢集內的工作負載。
無論您是從事開發還是運維工作,Kubernetes 知識都很有價值。它能讓你更深入地瞭解雲原生應用如何在生產中執行、操作員為何偏愛某些技術、哪裡可能出現問題,以及您的系統如何對映到基礎設施資源。當然,使用本地 Kubernetes 叢集進行開發也有助於避免環境之間的差異。
參考連結:
https://komodor.com/blog/a-software-developers-guide-to-getting-started-with-kubernetes-part-1/