這篇文章主要來介紹 Service 元件
內容會談到 Service 的功能與不同種 Services 類型與適合使用時機
在 k8s 叢集裡, 每個 Pod 都有自己的內部 IP
內部 IP 是在 Pod 啟動時動態給予的
因此, 當 Pod 重起之後內部 IP 的就會跟原本的不同
所以使用內部 IP 來存取 Pod 不是個好方法
Service 元件提供了一個固定 IP 給 Pod
所以一旦讓 Pod 綁定 Service
就可以透過這個固定 IP 來存取 Pod
除了提供固定 IP 外, 當多個 Pod 掛載在同一個 Service 上時
Service 會對傳入的 request 做負載平衡
由於 Pod 綁定 Service 所以對於其他相關的服務只依賴於 Service 這個抽象層
所以對 Pod 間做到了鬆耦合
而對於 Pod 來說對內與對外的接口都是透過 Service 來做處理
在 k8s 叢集中, Service 類別分為好幾種, 以下就分別來介紹幾種比較常見的
k8s 叢集, 預設的 Service 就是 ClusterIP Service
設定檔範例如下:
apiVersion: v1
kind: Service
metadata:
name: microservice-one-service
spec:
selector:
app: microservice-one
ports:
- protocol: TCP
port: 3200
targetPort: 3000
上面設定檔, 如果 kind 部份直接設定成 Service
則預設就是給 ClusterIP Service
而實際上關於 ClusterIP Service 給的固定 IP 值是看 Pode 所處的結點 IP 範圍
如下圖所示:
如果想要查詢 Pod 所對應的 IP 可以使用以下指令
kubectl get pod -o wide
假設給定 Ingress, Service 與 Deployment 設定
ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: name-virtual-host-ingress-no-third-host
spec:
rules:
- host: analytics.myapp.com
http:
paths:
- path: "/"
path: Prefix
backend:
service:
name: microservice-one-service
port:
number: 3200
service-clusterIP.yaml 如下
apiVersion: v1
kind: Service:
metadata:
name: microservice-one-service
spec:
selector:
app: microservice-one
ports:
- portocol: TCP
port: 3200
targetPort: 3000
deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: v
spec:
replicas: 2
...
template:
metadata:
labels:
app: microservice-one
則 ingress 透過 Service 的 name 找到到相對應的路由
而 Service 透過 selector 比對 Deployment 中的 Pod 對應的 labels 找到對應的 Pod
舉例來說: 上面的 Service selector 為 app: microservice
而 Deployment 中 template 內的 metadata labels 也是相同的值
而 Service 要路由到 Pod 的 Port 則定義在 Service 的 TargetPort
封包路由如下圖:
當 k8s 叢集建立 Service, 同時也會建立 Endpoint 物件
EndPoint 物件會跟 Service 具有相同名稱, 除了 IP 資訊外多了一個 Port 資訊
下面是 k8s 官網 EndPoint 物件的設定檔範例
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
EndPoint 物件用來紀錄哪個 Pod 屬於哪一個 Service
Service 定義的對外的 Port 可以由使用者定義
targetPort 則必須要開放到 Pod 內 Container 實際服務的 Port
這類的 Service 代表同時會開放對應到兩個 Port
舉例來說: 假設 mongodb 除了本身外還有一個 mongodb-exporter 需要開發給 Prometheus app 存取, 設定檔如下
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- name: mongodb
protocol: 27017
targetPort: 27017
- name: mongodb-exporter
protocol: 9216
targetPort: 9216
注意的是, 如果同時服務兩個以上的 Port 就必須要設定名稱
Headless 只會連接一個 Pod
設定檔範例如下:
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
當不需要做平衡負載時, 就可以使用 Headless Service
使用時機當想要只跟某個一特定的 Pod 做通訊
這種 Service 會使用在有狀態的應用, 比如 資料庫(mysql, mongodb), elastic search
因為有狀態的應用每個 Pod 並非相同, 像是資料庫只有 master Pod 才能做寫入
這類 Service 的 IP 會直接回應 Pod IP address
當要建立 Service 時, type 有三種值可以設定 ClusterIP, NodePort, LoadBalancer
是 type 的預設值, 不需要特別設定
屬於內部 Service, 外面讀取不到
範例如下:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP
就是對於每個結點 IP 開一個固定的 Port 來做端點
這類 Service 可以從外部經過以下形式的路徑來存取:
NodeIP: 開啟的 Port
設定檔範例如下:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
假設一個 NodePort Service 定義如下:
apiVersion: v1
kind: Service
metadata:
name: ms-service-nodeport
spec:
type: NodePort
selector:
app: microservice-one
ports:
- protocol: TCP
port: 3200
targetPort: 3000
nodePort: 30008
則代表這個 microservice-one 服務的 Port 在定義的 nodePort 30008
而這個 NodePort 有一個範圍限定 30000-32767
然而, NodePort Service 因為直接開放結點的 Port 給外部來連接
因此不是一個安全的作法
LoadBalancer 類別讓 Service 可以透過 cloud provider 的負載平衡器來對外開放服務
其結構概念如下:
由結構圖可以發現當 type 為 LoadBalancer Service 建立時, 相對應的 NodePort Service 與 ClusterIP Service 也會自動建立
設定檔範例如下
apiVersion: v1
kind: Service
metadata:
name: my-service-loadbalancer
spec:
type: LoadBalancer
selector:
app: microservice-one
ports:
- protocol: TCP
port: 3200
targetPort: 3000
nodePort: 30010
注意的是, 這邊的 nodePort 只開放給 loadBalancer 存取
loadBalancer 透過 nodePort 傳遞封包到結點然後透過對應的 ClusterIP Service 傳遞到對應的 Pod
一般而言, 實務上不太會使用 NodePort Service 在正式環境上, NodePort Service 只會在測試階段拿來驗證服務
在 k8s 叢集, 用來把應用對外開放的元件一般來說是使用 loadBalancer 類型的 Service 外面對接 cloud provider 提供的 loadbalancer 或是直接使用 Ingress 對接 ClusterIP Service 來使用