數據主要來源:service官網
一. Services簡述
將運行在一組 Pods 上的應用程序公開為網絡服務的抽象方法。
使用Kubernetes,您無需修改應用程序即可使用不熟悉的服務發現機制。 Kubernetes為Pods提供自己的IP地址和一組Pod的單個DNS名稱,並且可以在它們之間進行負載平衡。
所謂 Service,其實就是 Kubernetes 為 Pod 分配的、固定的、基於 iptables(或者 IPVS)的訪問入口。而這些訪問入口代理的 Pod 信息,則來自於 Etcd,由 kube-proxy 通過控制循環來維護。kube-proxy 負責為 Service 實現了一種 VIP(虛擬 IP)的形式.
Services動機
- Pod 的 IP地址不是固定的,每次通過deploy發布時,Pod都會提供新的IP地址
- 一組 Pod 實例之間需要負載均衡
定義 Service
假定有一組 Pod,它們對外暴露了 9376 端口,同時還被打上 app=MyApp 標簽。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: my-port
protocol: TCP
port: 80
targetPort: 9376
上述配置創建一個名稱為 “my-service” 的 Service 對象,它會將請求代理到使用 TCP 端口 9376,並且具有標簽 "app=MyApp" 的 Pod 上。 Kubernetes 為該服務分配一個 IP 地址,該 IP 地址由服務代理使用。服務選擇器的控制器不斷掃描與其選擇器匹配的 Pod,然后將所有更新發布到也稱為 “my-service” 的Endpoint對象。
服務的默認協議是TCP;
沒有 selector 的 Service
服務最常見的是抽象化對 Kubernetes Pod 的訪問,但是它們也可以抽象化其他種類的后端。 實例:
- 希望在生產環境中使用外部的數據庫集群。
- 希望服務指向另一個 命名空間 中或其它集群中的服務。
- 您正在將工作負載遷移到 Kubernetes。 在評估該方法時,您僅在 Kubernetes 中運行一部分后端。
在任何這些場景中,都能夠定義沒有 selector 的 Service。 實例:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376
由於此服務沒有選擇器,因此 不會 自動創建相應的 Endpoint 對象。 您可以通過手動添加 Endpoint 對象,將服務手動映射到運行該服務的網絡地址和端口:
apiVersion: v1
kind: Endpoints
metadata:
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376
端點 IPs 必須不可以 :
- 環回( IPv4 的 127.0.0.0/8 , IPv6 的 ::1/128 )或本地鏈接(IPv4 的 169.254.0.0/16 和 224.0.0.0/24,IPv6 的 fe80::/64)。
- 端點 IP 地址不能是其他 Kubernetes Services 的群集 IP,因為 kube-proxy 不支持將虛擬 IP 作為目標。
訪問沒有 selector 的 Service,與有 selector 的 Service 的原理相同。
ExternalName Service 是 Service 的特例,它沒有 selector,也沒有使用 DNS 名稱代替。
Endpoint 切片
Endpoint 切片是一種 API 資源,可以為 Endpoint 提供更可擴展的替代方案。
為什么不使用 DNS 輪詢?
- DNS 實現的歷史由來已久,它不遵守記錄 TTL,並且在名稱查找結果到期后對其進行緩存。
- 有些應用程序僅執行一次 DNS 查找,並無限期地緩存結果。
- 即使應用和庫進行了適當的重新解析,DNS 記錄上的 TTL 值低或為零也可能會給 DNS 帶來高負載,從而使管理變得困難
iptables 代理模式
kube-proxy 會監視 Kubernetes 控制節點對 Service 對象和 Endpoints 對象的添加和移除。 對每個 Service,它會安裝 iptables 規則,從而捕獲到達該 Service 的 clusterIP 和端口的請求,進而將請求重定向到 Service 的一組 backend 中的某個上面。 對於每個 Endpoints 對象,它也會安裝 iptables 規則,這個規則會選擇一個 backend 組合。
默認的策略是,kube-proxy 在 iptables 模式下隨機選擇一個 backend。
使用 iptables 處理流量具有較低的系統開銷,因為流量由 Linux netfilter 處理,而無需在用戶空間和內核空間之間切換。 這種方法也可能更可靠。
如果 kube-proxy 在 iptables模式下運行,並且所選的第一個 Pod 沒有響應,則連接失敗。 使用 Pod readiness 探測器 驗證后端 Pod 可以正常工作,以便 iptables 模式下的 kube-proxy 僅看到測試正常的后端。

iptables的負載均衡分兩種:random / nth,random是隨機模式,--probability p指定了概率,nth是輪巡模式,--every n和--packet p指定了每n個packet中匹配其中的第p個。
IPVS 代理模式
在 ipvs 模式下,kube-proxy監視Kubernetes服務和端點,調用 netlink 接口相應地創建 IPVS 規則, 並定期將 IPVS 規則與 Kubernetes 服務和端點同步。
而相比於 iptables,IPVS 在內核中的實現其實也是基於 Netfilter 的 NAT 模式,所以在轉發這一層上,理論上 IPVS 並沒有顯著的性能提升。但是,IPVS 並不需要在宿主機上為每個 Pod 設置 iptables 規則,而是把對這些“規則”的處理放到了內核態,從而極大地降低了維護這些規則的代價。“將重要操作放入內核態”是提高性能的重要手段。
IPVS 模塊只負責上述的負載均衡和代理功能。而一個完整的 Service 流程正常工作所需要的包過濾、SNAT 等操作,還是要靠 iptables 來實現。只不過,這些輔助性的 iptables 規則數量有限,也不會隨着 Pod 數量的增加而增加。
IPVS代理模式基於類似於 iptables 模式的 netfilter 掛鈎函數,但是使用哈希表作為基礎數據結構,並且在內核空間中工作。 這意味着,與 iptables 模式下的 kube-proxy 相比,IPVS 模式下的 kube-proxy 重定向通信的延遲要短,並且在同步代理規則時具有更好的性能。與其他代理模式相比,IPVS 模式還支持更高的網絡流量吞吐量。
IPVS提供了更多選項來平衡后端Pod的流量。 這些是:
- rr: round-robin
- lc: least connection (smallest number of open connections)
- dh: destination hashing
- sh: source hashing
- sed: shortest expected delay
- nq: never queue
通過 kube-proxy 設置–proxy-mode=ipvs 來開啟這個功能。
要在 IPVS 模式下運行 kube-proxy,必須在啟動 kube-proxy 之前使 IPVS Linux 在節點上可用,否則將退回到以 iptables 代理模式運行。

在這些代理模型中,綁定到服務IP的流量:如果要確保每次都將來自特定客戶端的連接傳遞到同一Pod,則可以通過將 service.spec.sessionAffinity 設置為 “ClientIP” (默認值是 “None”),來基於客戶端的IP地址選擇會話關聯。
您還可以通過適當設置 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds 來設置最大會話停留時間。 (默認值為 10800 秒,即 3 小時)。
二. service的使用
多端口 Service
對於某些服務,您需要公開多個端口。 Kubernetes允許您在Service對象上配置多個端口定義。 為服務使用多個端口時,必須提供所有端口名稱,以使它們無歧義。 例如:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9377
選擇自己的 IP 地址
通過設置 spec.clusterIP 字段來指定自己的集群 IP 地址。替換一個已經已存在的 DNS 條目,或遺留系統已經配置了一個固定的 IP 且很難重新配置。
服務發現
Kubernetes 支持2種基本的服務發現模式 —— 環境變量和 DNS。
環境變量
當 Pod 運行在 Node 上,kubelet 會為每個活躍的 Service 添加一組環境變量。它同時支持 Docker links兼容 變量(查看 makeLinkVariables)、簡單的 {SVCNAME}_SERVICE_HOST 和 {SVCNAME}_SERVICE_PORT 變量,這里 Service 的名稱需大寫,橫線被轉換成下划線。
舉個例子,一個名稱為 "redis-master" 的 Service 暴露了 TCP 端口 6379,同時給它分配了 Cluster IP 地址 10.0.0.11,這個 Service 生成了如下環境變量:
REDIS_MASTER_SERVICE_HOST=10.0.0.11
REDIS_MASTER_SERVICE_PORT=6379
REDIS_MASTER_PORT=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP=tcp://10.0.0.11:6379
REDIS_MASTER_PORT_6379_TCP_PROTO=tcp
REDIS_MASTER_PORT_6379_TCP_PORT=6379
REDIS_MASTER_PORT_6379_TCP_ADDR=10.0.0.11
/ # echo ${REDIS_MASTER_SERVICE_HOST}
10.0.0.11
注意⚠️:
- 當訪問服務的Pod,並且使用環境變量方法將端口和群集IP發布到客戶端Pod時,必須在客戶端Pod出現之前創建服務。
- 如果僅使用DNS查找服務的群集IP,則無需擔心此設定問題。
DNS
使用群集的DNS服務器(例如CoreDNS)監視 Kubernetes API 中的新服務,並為每個服務創建一組 DNS 記錄。 如果在整個群集中都啟用了 DNS,則所有 Pod 都應該能夠通過其 DNS 名稱自動解析服務。
例如,如果您在 Kubernetes 命名空間 "my-ns" 中有一個名為 "my-service" 的服務, 則控制平面和DNS服務共同為 "my-service.my-ns" 創建 DNS 記錄。 "my-ns" 命名空間中的Pod應該能夠通過 my-service 進行名稱查找來找到它( "my-service.my-ns" 也可以工作)。
其他命名空間中的Pod必須將名稱限定為 my-service.my-ns 。 這些名稱將解析為為服務分配的群集IP。
Kubernetes 還支持命名端口的 DNS SRV(服務)記錄。 如果 "my-service.my-ns" 服務具有名為 "http" 的端口,且協議設置為TCP, 則可以對 _http._tcp.my-service.my-ns 執行DNS SRV查詢查詢以發現該端口號, "http"以及IP地址。
Kubernetes DNS 服務器是唯一的一種能夠訪問 ExternalName 類型的 Service 的方式。 更多關於 ExternalName 信息可以查看 DNS Pod 和 Service。
Pod DNS
此外,對於 ClusterIP 模式的 Service 來說,它代理的 Pod 被自動分配的 A 記錄的格式是:..pod.cluster.local。這條記錄指向 Pod 的 IP 地址。
[root@m3 ~]# vim pod-dns.yaml
apiVersion: v1
kind: Service
metadata:
name: default-subdomain
spec:
selector:
name: busybox
clusterIP: None
ports:
- name: foo
port: 1234
targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
name: busybox1
labels:
name: busybox
spec:
hostname: busybox-1
subdomain: default-subdomain
containers:
- image: busybox
command:
- sleep
- "3600"
name: busybox
[root@m3 ~]# kubectl apply -f pod-dns.yaml
在上面這個 Service 和 Pod 被創建之后,你就可以通過 busybox-1.default-subdomain.default.svc.cluster.local 解析到這個 Pod 的 IP 地址了。
看到也有同學問pod DNS,希望能講得更詳細些。我查閱官方文檔及自己實踐后的了解是這兩種pod有DNS記錄:
- statefulset的pod。有人問之前講DNS的是在哪,就是“20 | 深入理解StatefulSet(三):有狀態應用實踐”這一篇。
- pod顯式指定了hostname和subdomain,並且有個headless service的名字和subdomain一樣。
Headless Services
有時不需要或不想要負載均衡,以及單獨的 Service IP,可以通過指定 Cluster IP(spec.clusterIP)的值為 "None" 來創建 Headless Service。
您可以使用 headless Service 與其他服務發現機制進行接口,而不必與 Kubernetes 的實現捆綁在一起。
對這 headless Service 並不會分配 Cluster IP,kube-proxy 不會處理它們,而且平台也不會為它們進行負載均衡和路由。 DNS 如何實現自動配置,依賴於 Service 是否定義了 selector。
配置 Selector
對定義了 selector 的 Headless Service,Endpoint 控制器在 API 中創建了 Endpoints 記錄,並且修改 DNS 配置返回 A 記錄(地址),通過這個地址直接到達 Service 的后端 Pod 上。
不配置 Selector
對沒有定義 selector 的 Headless Service,Endpoint 控制器不會創建 Endpoints 記錄。 然而 DNS 系統會查找和配置,無論是:
- ExternalName 類型 Service 的 CNAME 記錄
- 記錄:與 Service 共享一個名稱的任何 Endpoints,以及所有其它類型
[root@m3 ~]# vim svc-Endpoints.yaml
apiVersion: v1
kind: Endpoints
metadata:
name: mysql-service
subsets:
- addresses:
- ip: 192.168.1.108
hostname: mysql-service
ports:
- port: 3306
---
apiVersion: v1
kind: Service
metadata:
name: mysql-service
spec:
ports:
- port: 3306
三。發布服務 —— 服務類型
Kubernetes ServiceTypes 允許指定一個需要的類型的 Service,默認是 ClusterIP 類型。
Type 的取值以及行為如下:
- ClusterIP:通過集群的內部 IP 暴露服務,選擇該值,服務只能夠在集群內部可以訪問,這也是默認的 ServiceType。
- NodePort:通過每個 Node 上的 IP 和靜態端口(NodePort)暴露服務。NodePort 服務會路由到 ClusterIP 服務,這個 ClusterIP 服務會自動創建。通過請求 <NodeIP>:<NodePort>,可以從集群的外部訪問一個 NodePort 服務。
- LoadBalancer:使用雲提供商的負載局衡器,可以向外部暴露服務。外部的負載均衡器可以路由到 NodePort 服務和 ClusterIP 服務。
- ExternalName:通過返回 CNAME 和它的值,可以將服務映射到 externalName 字段的內容(例如, foo.bar.example.com)。 沒有任何類型代理被創建。
1. NodePort 類型
如果將 type 字段設置為 NodePort,則 Kubernetes 控制平面將在 --service-node-port-range 標志指定的范圍內分配端口(默認值:30000-32767)。 每個節點將那個端口代理到您的服務中。 您的服務在其 .spec.ports[*].nodePort 字段中要求分配的端口。
如果您想指定特定的IP代理端口,則可以將 kube-proxy 中的 --nodeport-addresses 標志設置為特定的IP塊。該標志采用逗號分隔的IP塊列表(例如10.0.0.0/8、192.0.2.0/25)來指定 kube-proxy 應該認為是此節點本地的IP地址范圍。
如果需要特定的端口號,則可以在 nodePort 字段中指定一個值。注意可能發生的端口沖突。 您還必須使用有效的端口號,該端口號在配置用於NodePort的范圍內。
Service 能夠通過 <NodeIP>:spec.ports[*].nodePort 和 spec.clusterIp:spec.ports[*].port 而對外可見。
[root@m3 ~]# vim svc-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
type: NodePort
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30008
selector:
run: my-nginx
[root@m3 ~]# kubectl apply -f svc-nodeport.yaml
service/my-nginx created
[root@s3 ~]# iptables-save | grep "my-nginx"
-A KUBE-EXTERNAL-SERVICES -p tcp -m comment --comment "default/my-nginx:http has no endpoints" -m addrtype --dst-type LOCAL -m tcp --dport 28080 -j REJECT --reject-with icmp-port-unreachable
-A KUBE-SERVICES -d 10.96.154.242/32 -p tcp -m comment --comment "default/my-nginx:http has no endpoints" -m tcp --dport 80 -j REJECT --reject-with icmp-port-unreachable

[root@s3 ~]# iptables-save | grep "KUBE-POSTROUTING"
:KUBE-POSTROUTING - [0:0]
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE
可以看到,這條規則設置在 POSTROUTING 檢查點,也就是說,它給即將離開這台主機的 IP 包,進行了一次 SNAT 操作,將這個 IP 包的源地址替換成了這台宿主機上的 CNI 網橋地址,或者宿主機本身的 IP 地址(如果 CNI 網橋不存在的話)。當然,這個 SNAT 操作只需要對 Servicespec.externalTrafficPolicy 轉發出來的 IP 包進行(否則普通的 IP 包就被影響了)。而 iptables 做這個判斷的依據,就是查看該 IP 包是否有一個“0x4000”的“標志”。你應該還記得,這個標志正是在 IP 包被執行 DNAT 操作之前被打上去的。
可以將 Service 的 spec.externalTrafficPolicy 字段設置為 local,這就保證了所有 Pod 通過 Service 收到請求之后,一定可以看到真正的、外部 client 的源地址。
這時候,一台宿主機上的 iptables 規則,會設置為只將 IP 包轉發給運行在這台宿主機上的 Pod。
client
^ / \
/ / \
/ v X
node 1 node 2
^ |
| |
| v
endpoint
2. LoadBalancer 類型
使用支持外部負載均衡器的雲提供商的服務,設置 type 的值為 "LoadBalancer",將為 Service 提供負載均衡器。 負載均衡器是異步創建的,關於被提供的負載均衡器的信息將會通過 Service 的 status.loadBalancer 字段被發布出去。 實例:
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 80
targetPort: 9376
clusterIP: 10.0.171.239
loadBalancerIP: 78.11.24.19
type: LoadBalancer
status:
loadBalancer:
ingress:
- ip: 146.148.47.155
來自外部負載均衡器的流量將直接打到 backend Pod 上,不過實際它們是如何工作的,這要依賴於雲提供商。
在這些情況下,將根據用戶設置的 loadBalancerIP 來創建負載均衡器。 某些雲提供商允許設置 loadBalancerIP。如果沒有設置 loadBalancerIP,將會給負載均衡器指派一個臨時 IP。 如果設置了 loadBalancerIP,但雲提供商並不支持這種特性,那么設置的 loadBalancerIP 值將會被忽略掉。
3. 類型ExternalName
類型為 ExternalName 的服務將服務映射到 DNS 名稱,而不是典型的選擇器,例如 my-service 或者 cassandra。 您可以使用 spec.externalName 參數指定這些服務。
例如,以下 Service 定義將 prod 名稱空間中的 my-service 服務映射到 my.database.example.com:
apiVersion: v1
kind: Service
metadata:
name: my-service
namespace: prod
spec:
type: ExternalName
externalName: my.database.example.com
4. 外部 IP
如果外部的 IP 路由到集群中一個或多個 Node 上,Kubernetes Service 會被暴露給這些 externalIPs。 通過外部 IP(作為目的 IP 地址)進入到集群,打到 Service 的端口上的流量,將會被路由到 Service 的 Endpoint 上。 externalIPs 不會被 Kubernetes 管理,它屬於集群管理員的職責范疇。
根據 Service 的規定,externalIPs 可以同任意的 ServiceType 來一起指定。 在上面的例子中,my-service 可以在 “80.11.12.10:80”(externalIP:port) 上被客戶端訪問。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- name: http
protocol: TCP
port: 80
targetPort: 9376
externalIPs:
- 80.11.12.10
四. service服務實現過程
1. 創建svc和pod
最典型的 Service 定義。使用了 selector 字段來聲明這個 Service 只代理攜帶了 app=hostnames 標簽的 Pod。 Service 的 80 端口,代理的是 Pod 的 9376 端口。
[root@m3 ~]# vim svc-test.yaml
apiVersion: v1
kind: Service
metadata:
name: hostnames
spec:
selector:
app: hostnames
ports:
- name: default
protocol: TCP
port: 80 #監聽的端口號
targetPort: 9376
[root@m3 ~]# kubectl apply -f svc-test.yaml
service/hostnames created
[root@m3 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostnames ClusterIP 10.96.132.245 <none> 80/TCP 4s
設置pod后端
[root@m3 ~]# vim deploy-test.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hostnames
spec:
selector:
matchLabels:
app: hostnames
replicas: 2
template:
metadata:
labels:
app: hostnames
spec:
containers:
- name: hostnames
image: k8s.gcr.io/serve_hostname
ports:
- containerPort: 9376
protocol: TCP
[root@m3 ~]# kubectl apply -f deploy-test.yaml
deployment.apps/hostnames created
2. 查看后端服務
被 selector 選中的 Pod,就稱為 Service 的 Endpoints,可以使用 kubectl get ep 命令看到它們
[root@m3 ~]# kubectl get endpoints hostnames
NAME ENDPOINTS AGE
hostnames 10.100.130.119:9376,10.100.152.254:9376 42m
Service 提供的是 Round Robin 方式的負載均衡,我們稱為:ClusterIP 模式的 Service。
[root@m3 ~]# kubectl get svc hostnames
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hostnames ClusterIP 10.96.132.245 <none> 80/TCP 43m
[root@m3 ~]# curl 10.96.132.245:80
hostnames-78f4d6f547-6tc6q
[root@m3 ~]# curl 10.96.132.245:80
hostnames-78f4d6f547-tp7fc
3. iptables過濾請求到svc的規則
對於我們前面創建的名叫 hostnames 的 Service 來說,一旦它被提交給 Kubernetes,那么 kube-proxy 就可以通過 Service 的 Informer 感知到這樣一個 Service 對象的添加。而作為對這個事件的響應,它就會在宿主機上創建這樣一條 iptables 規則(你可以通過 iptables-save 看到它),如下所示:
[root@s3 ~]# iptables-save | grep hostnames
-A KUBE-SERVICES ! -s 10.100.0.0/16 -d 10.96.132.245/32 -p tcp -m comment --comment "default/hostnames:default cluster IP" -m tcp --dport 80 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.132.245/32 -p tcp -m comment --comment "default/hostnames:default cluster IP" -m tcp --dport 80 -j KUBE-SVC-ODX2UBAZM7RQWOIU

4. iptables將請求轉發到后端
這條 iptables 規則的含義是:凡是目的地址是 10.96.132.245、目的端口是 80 的 IP 包,都應該跳轉到另外一條名叫 KUBE-SVC-ODX2UBAZM7RQWOIU 的 iptables 鏈進行處理。
10.96.132.245 正是這個 Service 的 VIP。所以這一條規則,就為這個 Service 設置了一個固定的入口地址。並且,由於 10.96.132.245 只是一條 iptables 規則上的配置,並沒有真正的網絡設備,所以你 ping 這個地址,是不會有任何響應的。
[root@s3 ~]# iptables-save | grep KUBE-SVC-ODX2UBAZM7RQWOIU
:KUBE-SVC-ODX2UBAZM7RQWOIU - [0:0]
-A KUBE-SERVICES -d 10.96.132.245/32 -p tcp -m comment --comment "default/hostnames:default cluster IP" -m tcp --dport 80 -j KUBE-SVC-ODX2UBAZM7RQWOIU
-A KUBE-SVC-ODX2UBAZM7RQWOIU -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-JVSOCRZ6JY75X2W7
-A KUBE-SVC-ODX2UBAZM7RQWOIU -j KUBE-SEP-3WIYSPFWPJKZWGOR

這一組規則,實際上是一組隨機模式(–mode random)的 iptables 鏈。
而隨機轉發的目的地,分別是KUBE-SEP-JVSOCRZ6JY75X2W7,KUBE-SEP-3WIYSPFWPJKZWGOR
[root@s3 ~]# iptables-save | grep KUBE-SEP-JVSOCRZ6JY75X2W7
:KUBE-SEP-JVSOCRZ6JY75X2W7 - [0:0]
-A KUBE-SEP-JVSOCRZ6JY75X2W7 -s 10.100.130.119/32 -j KUBE-MARK-MASQ
-A KUBE-SEP-JVSOCRZ6JY75X2W7 -p tcp -m tcp -j DNAT --to-destination 10.100.130.119:9376
-A KUBE-SVC-ODX2UBAZM7RQWOIU -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-JVSOCRZ6JY75X2W7

需要注意的是,iptables 規則的匹配是從上到下逐條進行的,所以為了保證上述三條規則每條被選中的概率都相同,我們應該將它們的 probability 字段的值分別設置為 1/2和1。但在 DNAT 規則之前,iptables 對流入的 IP 包還設置了一個“標志”(–set-xmark)。
而 DNAT 規則的作用,就是在 PREROUTING 檢查點之前,也就是在路由之前,將流入 IP 包的目的地址和端口,改成–to-destination 所指定的新的目的地址和端口。可以看到,這個目的地址和端口,正是被代理 Pod 的 IP 地址和端口。這樣,訪問 Service VIP 的 IP 包經過上述 iptables 處理之后,就已經變成了訪問具體某一個后端 Pod 的 IP 包了。
5. service使用iptables總結
如果 kube-proxy 一切正常,你就應該仔細查看宿主機上的 iptables 了。而一個 iptables 模式的 Service 對應的規則,我在上一篇以及這一篇文章里已經全部介紹到了,它們包括:
- KUBE-SERVICES 或者 KUBE-NODEPORTS 規則對應的 Service 的入口鏈,這個規則應該與 VIP 和 Service 端口一一對應;
- KUBE-SEP-(hash) 規則對應的 DNAT 鏈,這些規則應該與 Endpoints 一一對應;
- KUBE-SVC-(hash) 規則對應的負載均衡鏈,這些規則的數目應該與 Endpoints 數目一致;
- 如果是 NodePort 模式的話,還有 POSTROUTING 處的 SNAT 鏈。
Service異常排查思路
當你的 Service 沒辦法通過 DNS 訪問到的時候。
- 集群的 DNS是否正常
- Service 本身的配置問題
- Service 是否有 Endpoints:
- 通過kube-proxy日志確認 kube-proxy 是否在正確運行
- 查看iptables規則
- Pod 沒辦法通過 Service 訪問到自己