Kubernetes(k8s)kube-proxy、Service詳解
一、kube-proxy簡介
kube-proxy負責為Service提供cluster內部的服務發現和負載均衡,它執行在每個Node計算節點上,負責Pod網路代理, 它會定時從etcd服務獲取到service資訊來做相應的策略,維護網路規則和四層負載均衡工作。在K8s叢集中微服務的負載均衡是由Kube-proxy實現的,它是K8s叢集內部的負載均衡器,也是一個分散式代理伺服器,在K8s的每個節點上都有一個,這一設計體現了它的伸縮性優勢,需要訪問服務的節點越多,提供負載均衡能力的Kube-proxy就越多,高可用節點也隨之增多。
service是一組pod的服務抽象,相當於一組pod的LB,負責將請求分發給對應的pod。service會為這個LB提供一個IP,一般稱為cluster IP。kube-proxy的作用主要是負責service的實現,具體來說,就是實現了內部從pod到service和外部的從node port向service的訪問。
簡單來說: - kube-proxy其實就是管理service的訪問入口,包括叢集內Pod到Service的訪問和叢集外訪問service。 - kube-proxy管理sevice的Endpoints,該service對外暴露一個Virtual IP,也成為Cluster IP, 叢集內通過訪問這個Cluster IP:Port就能訪問到叢集內對應的serivce下的Pod。 - service是通過Selector選擇的一組Pods的服務抽象,其實就是一個微服務,提供了服務的LB和反向代理的能力,而kube-proxy的主要作用就是負責service的實現。 - service另外一個重要作用是,一個服務後端的Pods可能會隨著生存滅亡而發生IP的改變,service的出現,給服務提供了一個固定的IP,而無視後端Endpoint的變化。
二、Service 簡介
Kubernetes Service定義了這樣一種抽象: Service是一種可以訪問 Pod邏輯分組的策略, Service通常是通過 Label Selector訪問 Pod組。
Service能夠提供負載均衡的能力,但是在使用上有以下限制:只提供 4 層負載均衡能力,而沒有 7 層功能,但有時我們可能需要更多的匹配規則來轉發請求,這點上 4 層負載均衡是不支援的。
三、Service 型別
Service在 K8s中有以下四種類型:
1)ClusterIp(叢集內部使用)
預設型別,自動分配一個僅Cluster內部可以訪問的虛擬IP(VIP)。
2)NodePort(對外暴露應用)
在ClusterIP基礎上為Service在每臺機器上繫結一個埠,這樣就可以通過NodeIP:NodePort訪問來訪問該服務。 埠範圍:30000~32767
3)LoadBalancer(對外暴露應用,適用於公有云)
在NodePort的基礎上,藉助Cloud Provider建立一個外部負載均衡器,並將請求轉發到NodePort。
4)ExternalName
建立一個dns別名指到service name上,主要是防止service name發生變化,要配合dns外掛使用。通過返回 CNAME 和它的值,可以將服務對映到 externalName 欄位的內容。這隻有 Kubernetes 1.7或更高版本的kube-dns才支援(我這裡是Kubernetes 1.22.1版本)。
四、Service 工作流程
- 客戶端訪問節點時通過 iptables實現的
- iptables規則是通過 kube-proxy寫入的
- apiserver通過監控 kube-proxy去進行對服務和端點的監控
- kube-proxy通過 pod的標籤( lables)去判斷這個斷點資訊是否寫入到 Endpoints裡
五、Endpoints簡介
endpoint是k8s叢集中的一個資源物件,儲存在etcd中,用來記錄一個service對應的所有pod的訪問地址。service配置selector,endpoint controller才會自動建立對應的endpoint物件;否則,不會生成endpoint物件。
【例如】k8s叢集中建立一個名為hello的service,就會生成一個同名的endpoint物件,ENDPOINTS就是service關聯的pod的ip地址和埠。
1)工作流程
一個 Service 由一組 backend Pod 組成。這些 Pod 通過 endpoints 暴露出來。 Service Selector 將持續評估,結果被 POST 到一個名稱為 Service-hello 的 Endpoint 物件上。 當 Pod 終止後,它會自動從 Endpoint 中移除,新的能夠匹配上 Service Selector 的 Pod 將自動地被新增到 Endpoint 中。 檢查該 Endpoint,注意到 IP 地址與建立的 Pod 是相同的。現在,能夠從叢集中任意節點上使用 curl 命令請求 hello Service \
:\ 。
2)示例
1、deployment-hello.yaml
bash
$ cat << EOF > deployment-hello.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello
spec:
replicas: 3
selector:
matchLabels:
run: hello
template:
metadata:
labels:
run: hello
spec:
containers:
- name: nginx
image: nginx:1.17.1
EOF
2、service-hello.yaml
bash
$ cat << EOF > service-hello.yaml
apiVersion: v1
kind: Service
metadata:
name: service-hello
labels:
name: service-hello
spec:
type: NodePort #這裡代表是NodePort型別的,另外還有ingress,LoadBalancer
ports:
- port: 80
targetPort: 8080
protocol: TCP
nodePort: 31111 # 所有的節點都會開放此埠30000--32767,此埠供外部呼叫。
selector:
run: hello
EOF
3、檢視驗證
```bash $ kubectl apply -f deployment-hello.yaml $ kubectl apply -f service-hello.yaml
檢視pod,如果本地沒有映象,可能等待的時候比較長,一定要等到所有pod都在執行中才行。
$ kubectl get pod -o wide|grep hello-*
檢視service
$ kubectl get service service-hello -o wide
檢視service詳情
$ kubectl describe service service-hello
檢視pointer
$ kubectl get endpoints service-hello ```
六、Service, Endpoints與Pod的關係
Kube-proxy程序獲取每個Service的Endpoints,實現Service的負載均衡功能。
Service的負載均衡轉發規則
訪問Service的請求,不論是Cluster IP+TargetPort的方式;還是用Node節點IP+NodePort的方式,都被Node節點的Iptables規則重定向到Kube-proxy監聽Service服務代理埠。kube-proxy接收到Service的訪問請求後,根據負載策略,轉發到後端的Pod。
七、Service的資源清單檔案詳解
```yaml apiVersion: v1 kind: Service metadata:
元資料
name: string
#Service名稱
namespace: string
#名稱空間,不指定時預設為default名稱空間
labels:
#自定義標籤屬性列表
- name: string
annotations:
#自定義註解屬性列表
- name: string
spec:
詳細描述
selector: []
#這裡選擇器一定要選擇容器的標籤,也就是pod的標籤
#selector:
# app: web
#Label Selector配置,選擇具有指定label標籤的pod作為管理範圍
type: string
#service的型別,指定service的訪問方式,預設ClusterIP
#ClusterIP:虛擬的服務ip地址,用於k8s叢集內部的pod訪問,在Node上kube-porxy通過設定的iptables規則進行轉發
#NodePort:使用宿主機埠,能夠訪問各Node的外部客戶端通過Node的IP和埠就能訪問伺服器
#LoadBalancer:使用外部負載均衡器完成到伺服器的負載分發,
#需要在spec.status.loadBalancer欄位指定外部負載均衡伺服器的IP,並同時定義nodePort和clusterIP用於公有云環境。
clusterIP: string
#虛擬服務IP地址,當type=ClusterIP時,如不指定,則系統會自動進行分配,也可以手動指定。當type=loadBalancer,需要指定
sessionAffinity: string
#是否支援session,可選值為ClietIP,預設值為空
#ClientIP表示將同一個客戶端(根據客戶端IP地址決定)的訪問請求都轉發到同一個後端Pod
ports:
#service需要暴露的埠列表
- name: string
#埠名稱
protocol: string
#埠協議,支援TCP或UDP,預設TCP
port: int
#服務監聽的埠號
targetPort: int
#需要轉發到後端的埠號
nodePort: int
#當type=NodePort時,指定對映到物理機的埠號
status:
#當type=LoadBalancer時,設定外部負載均衡的地址,用於公有云環境
loadBalancer:
#外部負載均衡器
ingress:
#外部負載均衡器
ip: string
#外部負載均衡器的IP地址
hostname: string
#外部負載均衡器的機主機
```
八、kubernetes中的四種port
1)nodePort
nodePort是外部訪問k8s叢集中service的埠,通過nodeIP: nodePort可以從外部訪問到某個service。
2)port
port是k8s叢集內部訪問service的埠,即通過clusterIP: port可以訪問到某個service。
3)targetPort
targetPort是pod的埠,從port和nodePort來的流量經過kube-proxy流入到後端pod的targetPort上,最後進入容器。
4)containerPort
containerPort是pod內部容器的埠,targetPort對映到containerPort。
九、kubernetes服務發現
Kubernetes提供了兩種方式進行服務發現, 即環境變數和DNS, 簡單說明如下:
1)環境變數
當你建立一個Pod的時候,kubelet會在該Pod中注入叢集內所有Service的相關環境變數。
【注意】要想一個Pod中注入某個Service的環境變數,則必須Service要比該Pod先建立。這一點,幾乎使得這種方式進行服務發現不可用。比如,一個ServiceName為redis-master的Service,對應的ClusterIP:Port為172.16.50.11:6379,則其對應的環境變數為:
REDIS_MASTER_SERVICE_HOST=172.16.50.11 REDIS_MASTER_SERVICE_PORT=6379 REDIS_MASTER_PORT=tcp://172.16.50.11:6379 REDIS_MASTER_PORT_6379_TCP=tcp://172.16.50.11:6379 REDIS_MASTER_PORT_6379_TCP_PROTO=tcp REDIS_MASTER_PORT_6379_TCP_PORT=6379 REDIS_MASTER_PORT_6379_TCP_ADDR=172.16.50.11
2) DNS
這是k8s官方強烈推薦的方式!!! 可以通過cluster add-on方式輕鬆的建立KubeDNS來對叢集內的Service進行服務發現。
十、Service代理模式
k8s群集中的每個節點都執行一個kube-proxy的元件,kube-proxy其實是一個代理層負責實現service。
Kubernetes v1.2之前預設是userspace,v1.2之後預設是iptables模式,iptables模式效能和可靠性更好,但是iptables模式依賴健康檢查,在沒有健康檢查的情況下如果一個pod不響應,iptables模式不會切換另一個pod上。
1)userspace模式
客戶端訪問ServiceIP(clusterIP)請求會先從使用者空間到核心中的iptables,然後回到使用者空間kube-proxy,kube-proxy負責代理工作。
缺點:
可見,userspace這種mode最大的問題是,service的請求會先從使用者空間進入核心iptables,然後再回到使用者空間,由kube-proxy完成後端Endpoints的選擇和代理工作,這樣流量從使用者空間進出核心帶來的效能損耗是不可接受的。這也是k8s v1.0及之前版本中對kube-proxy質疑最大的一點,因此社群就開始研究iptables mode。
詳細工作流程:
userspace這種模式下,kube-proxy 持續監聽 Service 以及 Endpoints 物件的變化;對每個 Service,它都為其在本地節點開放一個埠,作為其服務代理埠;發往該埠的請求會採用一定的策略轉發給與該服務對應的後端 Pod 實體。kube-proxy 同時會在本地節點設定 iptables 規則,配置一個 Virtual IP,把發往 Virtual IP 的請求重定向到與該 Virtual IP 對應的服務代理埠上。其工作流程大體如下:
【分析】該模式請求在到達 iptables 進行處理時就會進入核心,而 kube-proxy 監聽則是在使用者態, 請求就形成了從使用者態到核心態再返回到使用者態的傳遞過程, 一定程度降低了服務效能。
2)iptables模式(預設模式)
該模式完全利用核心iptables來實現service的代理和LB, 這是K8s在v1.2及之後版本預設模式. 工作原理如下:
iptables mode因為使用iptable NAT來完成轉發,也存在不可忽視的效能損耗。另外,如果叢集中存在上萬的Service/Endpoint,那麼Node上的iptables rules將會非常龐大,效能還會再打折扣。這也導致目前大部分企業用k8s上生產時,都不會直接用kube-proxy作為服務代理,而是通過自己開發或者通過Ingress Controller來整合HAProxy, Nginx來代替kube-proxy。
詳細工作流程:
iptables 模式與 userspace 相同,kube-proxy 持續監聽 Service 以及 Endpoints 物件的變化;但它並不在本地節點開啟反向代理服務,而是把反向代理全部交給 iptables 來實現;即 iptables 直接將對 VIP 的請求轉發給後端 Pod,通過 iptables 設定轉發策略。其工作流程大體如下:
【分析】 該模式相比 userspace 模式,克服了請求在使用者態-核心態反覆傳遞的問題,效能上有所提升,但使用 iptables NAT 來完成轉發,存在不可忽視的效能損耗,而且在大規模場景下,iptables 規則的條目會十分巨大,效能上還要再打折扣。
示例:
bash
cat << EOF > mysql-service.yaml
apiVersion: v1
kind: Service
metadata:
labels:
name: mysql
role: service
name: mysql-service
spec:
ports:
- port: 3306
targetPort: 3306
nodePort: 30964
type: NodePort
selector:
mysql-service: "true"
name: mysql
EOF
$ kubectl apply -f mysql-service.yaml
$ kubectl get svc
3)ipvs模型
在kubernetes 1.8以上的版本中,對於kube-proxy元件增加了除iptables模式和使用者模式之外還支援ipvs模式。kube-proxy ipvs 是基於 NAT 實現的,通過ipvs的NAT模式,對訪問k8s service的請求進行虛IP到POD IP的轉發。當建立一個 service 後,kubernetes 會在每個節點上建立一個網絡卡,同時幫你將 Service IP(VIP) 繫結上,此時相當於每個 Node 都是一個 ds,而其他任何 Node 上的 Pod,甚至是宿主機服務(比如 kube-apiserver 的 6443)都可能成為 rs;
詳細工作流程:
與iptables、userspace 模式一樣,kube-proxy 依然監聽Service以及Endpoints物件的變化, 不過它並不建立反向代理, 也不建立大量的 iptables 規則, 而是通過netlink 建立ipvs規則,並使用k8s Service與Endpoints資訊,對所在節點的ipvs規則進行定期同步; netlink 與 iptables 底層都是基於 netfilter 鉤子,但是 netlink 由於採用了 hash table 而且直接工作在核心態,在效能上比 iptables 更優。其工作流程大體如下:
【分析】ipvs 是目前 kube-proxy 所支援的最新代理模式,相比使用 iptables,使用 ipvs 具有更高的效能。
4)kube-proxy配置 ipvs模式(所有節點)
1、載入ip_vs相關核心模組
bash
$ modprobe -- ip_vs
$ modprobe -- ip_vs_sh
$ modprobe -- ip_vs_rr
$ modprobe -- ip_vs_wrr
$ modprobe -- nf_conntrack_ipv4
所有節點驗證開啟了ipvs:
bash
$ lsmod |grep ip_vs
2、安裝ipvsadm工具
bash
$ yum install ipset ipvsadm -y
3、編輯kube-proxy配置檔案,mode修改成ipvs
bash
$ kubectl edit configmap -n kube-system kube-proxy
4、重啟kube-proxy
先檢視之前的kube-proxy
bash
$ kubectl get pod -n kube-system | grep kube-proxy
刪掉上面三個kube-proxy,重新拉起新的服務
bash
$ kubectl get pod -n kube-system | grep kube-proxy |awk '{system("kubectl delete pod "$1" -n kube-system")}'
再檢視
bash
$ kubectl get pod -n kube-system | grep kube-proxy
5、檢視
bash
$ ipvsadm -Ln
關於Kubernetes(k8s)kube-proxy、Service的介紹,就先到這裡了,有疑問的小夥伴,歡迎給我留言哦~
- 企業級日誌系統架構——ELK(Elasticsearch、Filebeat、Kafka、Logstash、Kibana)
- Kubernetes(k8s)許可權管理RBAC詳解
- Kubernetes(k8s)kube-proxy、Service詳解
- Linux防火牆——Firewalld原理與實戰操作
- Kubernetes(k8s)基礎概念介紹
- 大資料Hadoop之——資料分析引擎Apache Pig
- 大資料Hadoop之——任務排程器Azkaban(Azkaban環境部署)
- 大資料Hadoop之——Flink的狀態管理和容錯機制(checkpoint)
- 大資料Hadoop之——實時計算流計算引擎Flink(Flink環境部署)
- Jave-Maven詳解
- 大資料Hadoop之——Spark Streaming原理
- 大資料Hadoop生態系統介紹
- 導航軟體怎麼知道這條路堵車?老司機硬核吹牛科普知識
- 太強大!發現一個老司機專用資料分析神器!
- 網易資料分析高階總監:10年資料分析老司機的深度思考
- 導航軟體怎麼知道這條路堵車?老司機硬核吹牛科普知識