在 Kubernetes (K8s) 的微服務架構中,了解 Pod 之間的連線細節是至關重要的一環。透過這篇文章的例子,深度探討 Kubernetes 的工作原理,讓讀者不僅理解 Pod 與 Service 之間的基本關聯,還能掌握其背後的機制與細節。我們將從 Pod 的概念和建立開始,進一步講解 Service 的角色和功能,並討論如何實現兩者間的連線。希望通過這篇文章,讀者能夠更具信心地運用 Kubernetes,無論是管理現有的微服務,還是設計新的應用架構。我們將嘗試將這些概念以最簡潔明了的方式呈現,使初學者和專業人士都能從中獲益。本文章帶你深入淺出,一窺 Kubernetes 的核心,理解與掌握 Pod 連線的關鍵知識。
範例需求
- 建置二個 Deployment 而讓他們能夠內網互相溝通
- 用一個 LoadBalancer 對應到其中一個 Deployment
配置範例
以下是一個 Kubernetes 配置範例,建立兩個 Deployment 並讓它們能夠內網互相溝通,以及一個 LoadBalancer 服務對應到其中一個 Deployment:
deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-1
spec:
replicas: 2
selector:
matchLabels:
app: app-1
template:
metadata:
labels:
app: app-1
spec:
containers:
- name: container-1
image: j796160836/simple-test-http:latest
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
spec:
replicas: 2
selector:
matchLabels:
app: app-2
template:
metadata:
labels:
app: app-2
spec:
containers:
- name: container-2
image: j796160836/simple-test-http:latest
ports:
- containerPort: 80
service.yml
apiVersion: v1
kind: Service
metadata:
name: service-1
spec:
selector:
app: app-1
ports:
- protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
name: service-2
spec:
selector:
app: app-2
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
這個範例中:
-
建立了兩個 Deployment,分別名為
deployment-1
和deployment-2
。每個 Deployment 都有 2 個副本,分別使用標籤app: app-1
和app: app-2
。 -
為
deployment-1
和deployment-2
建立了兩個對應的 ClusterIP 服務,分別名為service-1
和service-2
。這兩個 ClusterIP 服務會將流量轉發到標籤為app: app-1
和app: app-2
的 Pod。 -
為
deployment-2
建立了一個名為service-2
的 LoadBalancer 服務,將外部流量轉發到標籤為app: app-2
的 Pod。
通過這個配置,兩個 Deployment 的 Pod 可以通過 ClusterIP 服務在內網進行通信,而 LoadBalancer 服務則允許外部流量來存取其中一個 Deployment 的 Pod。
備註:type: LoadBalancer
這個設定值只會在雲端服務
(例如:GCP (Google Cloud Platform) 裡面的 Google Kubernetes Engine (GKE) 、
AWS (Amazon Web Services) 的 Amazon Elastic Kubernetes Service (EKS)、
Microsoft 的 Azure Kubernetes Service (AKS))才會生效,
自行架設 on-premise 的 Kubernetes 叢集不會有動作,除非有另外做一些設定。
準備就緒,我們把它部署上來
$ kubectl apply -f deployment.yaml
然後可以用 kubectl get pods
來查看 Pod 運作情形
$ kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
deployment-1-79c659f4ff-kkvgx 1/1 Running 0 112s
deployment-1-79c659f4ff-wf4kk 1/1 Running 0 112s
deployment-2-76d567869f-cgts7 1/1 Running 0 112s
deployment-2-76d567869f-fpqsm 1/1 Running 0 111s
別忘記部署 service
$ kubectl apply -f service.yaml
我們使用 kubectl get services
來查看 services 的運作情況
$ kubectl get services -n default
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service-1 ClusterIP 10.54.3.115 <none> 80/TCP 48s
service-2 LoadBalancer 10.54.3.33 34.xxx.xxx.123 80:32103/TCP 48s
筆記備註:
deployment 為一個部署計劃,裡面定義了 Pod spec,意指這個 Pod 樣板裡面有什麼 container。
用 replicas
標籤來定義這個樣板要跑幾個副本
通常一個 Pod 裡面只會有一個 container,在其他情況(例如需要 sidecar 的時候)才會一個 Pod 裡面有不只一個 container。
我的 app-2 要怎麼 ping 到 app-1 ?
想必你也跟我有一樣的問題, app-2 要怎麼 ping 到 app-1?讓我們一一解釋。
在 Kubernetes 集群中,你可以使用內部的 Service DNS 名稱來讓 app-2
的 Pod 連接到 app-1
的 Pod。在本例中,app-2
可以透過 service-1
服務名稱來存取 app-1
。
假設你的應用程式支持從環境變數讀取目標服務的 DNS 名稱,你可以在 deployment-2
的 Pod 模板中添加一個環境變數,指向 service-1
的 DNS 名稱,例如:
apiVersion: apps/v1
kind: Deployment
metadata:
name: deployment-2
spec:
replicas: 2
selector:
matchLabels:
app: app-2
template:
metadata:
labels:
app: app-2
spec:
containers:
- name: container-2
image: your-image-repo/image-2:latest
ports:
- containerPort: 80
env:
- name: APP_1_SERVICE_URL
value: "http://service-1.default.svc.cluster.local:80"
這裡,我們為 container-2
添加了一個環境變數 APP_1_SERVICE_URL
,其值為 http://service-1.default.svc.cluster.local:80
。這個環境變數將被傳遞給 app-2
的應用程式,讓它可以連接到 app-1
。
在你的 app-2
應用程式中,你需要使用這個環境變數(例如,APP_1_SERVICE_URL
)作為 app-1
服務的基礎 URL 進行連接。根據你的應用程式語言和框架,讀取環境變數的方法可能會有所不同。
例如,如果你的應用程式是用 Python 編寫的,你可以使用以下方式讀取環境變數:
import os
app_1_service_url = os.environ['APP_1_SERVICE_URL']
之後,你可以使用 app_1_service_url
作為 app-1
服務的基礎 URL 進行連接。
我們來做一個測試,嘗試把 container 裡面 console 掛進去看看
先列出 Pod,找到你要的 Pod
$ kubectl get pods -n default
NAME READY STATUS RESTARTS AGE
deployment-1-79c659f4ff-kkvgx 1/1 Running 0 112s
deployment-1-79c659f4ff-wf4kk 1/1 Running 0 112s
deployment-2-76d567869f-cgts7 1/1 Running 0 112s
deployment-2-76d567869f-fpqsm 1/1 Running 0 111s
將 console 掛進去
$ kubectl exec -it -n my-namespace deployment-1-79c659f4ff-kkvgx -- /bin/bash
然後做 curl 瀏覽看看
root@deployment-1-79c659f4ff-kkvgx:/# curl service-2.my-namespace.svc.cluster.local
<!DOCTYPE html>
<html>
<head>
....(後略)
可以成功連線!
關於內部 DNS 名稱
你或許會問: service-1.default.svc.cluster.local
是固定值嗎? 每次 deploy 會不會變更呢?
service-1.default.svc.cluster.local
是一個 Kubernetes 服務的內部 DNS 名稱。這個名稱是根據你的服務名稱和命名空間生成的。在本例中,服務名稱是 service-1
,命名空間是 default
。
DNS 名稱的規則為 <service-name>.<namespace>.svc.cluster.local
。
這個 DNS 名稱在 Kubernetes 集群中是固定的,只要你不更改相應的服務名稱和命名空間。每次部署時,只要保持相同的服務名稱和命名空間,這個 DNS 名稱就不會變更。
在本例中,每次部署時,只要你保持服務名稱為 service-1
和命名空間為 default
,
那這個 service-1.default.svc.cluster.local
的 DNS 名稱就不會變更。
當然,如果你將服務名稱或命名空間更改為其他值,則對應的 DNS 名稱也會相應更改。在這種情況下,你需要在應用程式配置或部署文件中更新相應的 DNS 名稱。
Cleanup
做完實驗了,我們把剛剛建的這些東西都清掉(刪除),避免在雲端服務產生不必要的費用,這一步是很重要的。
$ kubectl delete deployments -n my-namespace deployment-1
deployment.apps "deployment-1" deleted
$ kubectl delete deployments -n my-namespace deployment-2
deployment.apps "deployment-2" deleted
$ kubectl delete services -n my-namespace service-1
service "service-1" deleted
$ kubectl delete services -n my-namespace service-2
service "service-2" deleted
依序把建立出來的 deployment、 service 給刪除
kubectl delete deployments <deployment>
kubectl delete services <services>
kubectl delete pods <pods>
kubectl delete daemonset <daemonset>