一、 service作用
使用kubernetes集群運行工作負載時,由于Pod經常處于用后即焚狀態,Pod經常被重新生成,因此Pod對應的IP地址也會經常變化,導致無法直接訪問Pod提供的服務,Kubernetes中使用了Service來解決這一問題,即在Pod前面使用Service對Pod進行代理,無論Pod怎樣變化 ,只要有Label,就可以讓Service能夠聯系上Pod,把PodIP地址添加到Service對應的端點列表(Endpoints)實現對Pod IP跟蹤,進而實現通過Service訪問Pod目的。
- 通過service為pod客戶端提供訪問pod方法,即可客戶端訪問pod入口
- 通過標簽動態感知pod IP地址變化等
- 防止pod失聯
- 定義訪問pod訪問策略
- 通過label-selector相關聯
- 通過Service實現Pod的負載均衡(TCP/UDP 4層)
- 底層實現由kube-proxy通過userspace、iptables、ipvs三種代理模式
二、kube-proxy三種代理模式
-
kubernetes集群中有三層網絡,一類是真實存在的,例如Node Network、Pod Network,提供真實IP地址;一類是虛擬的,例如Cluster Network或Service Network,提供虛擬IP地址,不會出現在接口上,僅會出現在Service當中
-
kube-proxy始終watch(監控)kube-apiserver上關于Service相關的資源變動狀態,一旦獲取相關信息kube-proxy都要把相關信息轉化為當前節點之上的,能夠實現Service資源調度到特定Pod之上的規則,進而實現訪問Service就能夠獲取Pod所提供的服務
-
kube-proxy三種代理模式:UserSpace模式、iptables模式、ipvs模式
2.1 UserSpace模式
不再使用了解即可
userspace 模式是 kube-proxy 使用的第一代模式,該模式在 kubernetes v1.0 版本開始支持使用。
userspace 模式的實現原理圖示如下:
kube-proxy 會為每個 Service 隨機監聽一個端口(proxy port),并增加一條 iptables 規則。所以通過 ClusterIP:Port 訪問 Service 的報文都 redirect 到 proxy port,kube-proxy 從它監聽的 proxy port 收到報文以后,走 round robin(默認) 或是 session affinity(會話親和力,即同一 client IP 都走同一鏈路給同一 pod 服務),分發給對應的 pod。
由于 userspace 模式會造成所有報文都走一遍用戶態(也就是 Service 請求會先從用戶空間進入內核 iptables,然后再回到用戶空間,由 kube-proxy 完成后端 Endpoints 的選擇和代理工作),需要在內核空間和用戶空間轉換,流量從用戶空間進出內核會帶來性能損耗,所以這種模式效率低、性能不高,不推薦使用。
2.2 iptables模式
iptables 模式是 kube-proxy 使用的第二代模式,該模式在 kubernetes v1.1 版本開始支持,從 v1.2 版本開始成為 kube-proxy 的默認模式。
iptables 模式的負載均衡模式是通過底層 netfilter/iptables 規則來實現的,通過 Informer 機制 Watch 接口實時跟蹤 Service 和 Endpoint 的變更事件,并觸發對 iptables 規則的同步更新。
iptables 模式的實現原理圖示如下:
通過圖示我們可以發現在 iptables 模式下,kube-proxy 只是作為 controller,而不是 server,真正服務的是內核的 netfilter,體現在用戶態的是 iptables。所以整體的效率會比 userspace 模式高。
2.3 ipvs模式
ipvs 模式被 kube-proxy 采納為第三代模式,模式在 kubernetes v1.8 版本開始引入,在 v1.9 版本中處于 beta 階段,在 v1.11 版本中正式開始使用。
ipvs(IP Virtual Server) 實現了傳輸層負載均衡,也就是 4 層交換,作為 Linux 內核的一部分。ipvs
運行在主機上,在真實服務器前充當負載均衡器。ipvs 可以將基于 TCP 和 UDP 的服務請求轉發到真實服務器上,并使真實服務器上的服務在單個 IP 地址上顯示為虛擬服務。
ipvs 模式的實現原理圖示如下:
ipvs 和 iptables 都是基于 netfilter 的,那么 ipvs 模式有哪些更好的性能呢?
- ipvs 為大型集群提供了更好的可拓展性和性能
- ipvs 支持比 iptables 更復雜的負載均衡算法(包括:最小負載、最少連接、加權等)
- ipvs 支持服務器健康檢查和連接重試等功能
- 可以動態修改 ipset 的集合,即使 iptables 的規則正在使用這個集合
ipvs 依賴于 iptables。ipvs 會使用 iptables 進行包過濾、airpin-masquerade tricks(地址偽裝)、SNAT 等功能,但是使用的是 iptables 的擴展 ipset,并不是直接調用 iptables 來生成規則鏈。通過 ipset 來存儲需要 DROP 或 masquerade 的流量的源或目標地址,用于確保 iptables 規則的數量是恒定的,這樣我們就不需要關心有多少 Service 或是 Pod 了。
使用 ipset 相較于 iptables 有什么優點呢?iptables 是線性的數據結構,而 ipset 引入了帶索引的數據結構,當規則很多的時候,ipset 依然可以很高效的查找和匹配。我們可以將 ipset 簡單理解為一個 IP(段) 的集合,這個集合的內容可以是 IP 地址、IP 網段、端口等,iptables 可以直接添加規則對這個“可變的集合進行操作”,這樣就可以大大減少 iptables 規則的數量,從而減少性能損耗。
舉一個例子,如果我們要禁止成千上萬個 IP 訪問我們的服務器,如果使用 iptables 就需要一條一條的添加規則,這樣會在 iptables 中生成大量的規則;如果用 ipset 就只需要將相關的 IP 地址(網段)加入到 ipset 集合中,然后只需要設置少量的 iptables 規則就可以實現這個目標。
下面的表格是 ipvs 模式下維護的 ipset 表集合:
設置名稱 | 成員 | 用法 |
---|---|---|
KUBE-CLUSTER-IP | 所有服務 IP + 端口 | 在 masquerade-all=true 或 clusterCIDR 指定的情況下對 Service Cluster IP 地址進行偽裝,解決數據包欺騙問題 |
KUBE-LOOP-BACK | 所有服務 IP + 端口 + IP | 解決數據包欺騙問題 |
KUBE-EXTERNAL-IP | 服務外部 IP + 端口 | 將數據包偽裝成 Service 的外部 IP 地址 |
KUBE-LOAD-BALANCER | 負載均衡器入口 IP + 端口 | 將數據包偽裝成 Load Balancer 類型的 Service |
KUBE-LOAD-BALANCER-LOCAL | 負載均衡器入口 IP + 端口 以及externalTrafficPolicy=local | 接受數據包到 Load Balancer externalTrafficPolicy=local |
KUBE-LOAD-BALANCER-FW | 負載均衡器入口 IP + 端口 以及loadBalancerSourceRanges | 使用指定的 loadBalancerSourceRanges 丟棄 Load Balancer 類型 Service 的數據包 |
KUBE-LOAD-BALANCER-SOURCE-CIDR | 負載均衡器入口 IP + 端口 + 源 CIDR | 接受 Load Balancer 類型 Service 的數據包,并指定 loadBalancerSourceRanges |
KUBE-NODE-PORT-TCP | NodePort 類型服務 TCP 端口 | 將數據包偽裝成 NodePort(TCP) |
KUBE-NODE-PORT-LOCAL-TCP | NodePort 類型服務 TCP 端口,帶有externalTrafficPolicy=local | 接受數據包到 NodePort 服務,使用 externalTrafficPolicy=local |
KUBE-NODE-PORT-UDP | NodePort 類型服務 UDP 端口 | 將數據包偽裝成 NodePort(UDP) |
KUBE-NODE-PORT-LOCAL-UDP | NodePort 類型服務 UDP 端口,使用externalTrafficPolicy=local | 接受數據包到 NodePort 服務,使用 externalTrafficPolicy=local |
2.4 iptables與ipvs對比
-
iptables
- 工作在內核空間
- 優點
- 靈活,功能強大(可以在數據包不同階段對包進行操作)
- 缺點
- 表中規則過多時,響應變慢,即規則遍歷匹配和更新,呈線性時延
-
ipvs
- 工作在內核空間
- 優點
- 轉發效率高
- 調度算法豐富:rr,wrr,lc,wlc,ip hash…
- 缺點
- 內核支持不全,低版本內核不能使用,需要升級到4.0或5.0以上。
-
使用iptables與ipvs時機
- 1.10版本之前使用iptables(1.1版本之前使用UserSpace進行轉發)
- 1.11版本之后同時支持iptables與ipvs,默認使用ipvs,如果ipvs模塊沒有加載時,會自動降級至iptables
三、 service類型
Service類型決定了訪問Service的方法
3.1 service類型
-
ClusterIP
- 默認,分配一個集群內部可以訪問的虛擬IP
-
NodePort
- 在每個Node上分配一個端口作為外部訪問入口
- nodePort端口范圍為:30000-32767
-
LoadBalancer
- 工作在特定的Cloud Provider上,例如Google Cloud,AWS,OpenStack
-
ExternalName
- 表示把集群外部的服務引入到集群內部中來,即實現了集群內部pod和集群外部的服務進行通信
3.2 Service參數
-
port 訪問service使用的端口
-
targetPort Pod中容器端口 (容器內的端口,例如nginx,80端口)
-
nodePort 通過Node實現外網用戶訪問k8s集群內service (30000-32767)(適用于nodeport模式)
四、 Service創建
Service的創建在工作中有兩種方式,一是命令行創建,二是通過資源清單文件YAML文件創建。
4.1 ClusterIP類型
ClusterIP根據是否生成ClusterIP又可分為普通Service和Headless Service
Service兩類:
- 普通Service:
為Kubernetes的Service分配一個集群內部可訪問的固定虛擬IP(Cluster IP), 實現集群內的訪問。
- Headless Service:
該服務不會分配Cluster IP, 也不通過kube-proxy做反向代理和負載均衡。而是通過DNS提供穩定的網絡ID來訪問,DNS會將headless service的后端直接解析為pod IP列表。
4.1.1 普通ClusterIP Service創建
4.1.1.1 命令行創建Service
- 創建Deployment類型的應用
[root@master01 ~]# cat 01_create_deployment_app_nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-server1
spec:replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: c1image: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
- 應用資源清單文件
[root@master01 ~]# kubectl apply -f 01_create_deployment_app_nginx.yaml
- 驗證Deployment類型的創建情況
[root@master01 ~]# kubectl get deployment.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-server1 2/2 2 2 13s
- 創建ClusterIP類型service與Deployment類型應用關聯
命令創建service
[root@master01 ~]# kubectl expose deployment.apps nginx-server1 --type=ClusterIP --target-port=80 --port=80
輸出
service/nginx-server1 exposed
說明
expose 創建service
deployment.apps 控制器類型
nginx-server1 應用名稱,也是service名稱
--type=ClusterIP 指定service類型
--target-port=80 指定Pod中容器端口
--port=80 指定service端口
4.1.1.2 通過資源清單文件創建Service
[root@master01 ~]# cat 02_create_deployment_app_nginx_with_service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-server1
spec:replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginx-smartimage: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:name: nginx-svc
spec:type: ClusterIPports:- protocol: TCPport: 80targetPort: 80selector:app: nginx
[root@master01 ~]# kubectl apply -f 02_create_deployment_app_nginx_with_service.yaml
- 驗證
查看service
[root@master01 ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 4d15h
nginx-svc ClusterIP 10.101.153.50 <none> 80/TCP 3s
查看endpoints
[root@master01 ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.122.30:6443 4d15h
nginx-svc 172.16.189.74:80,172.16.235.150:80 8s
查看Pod
[root@master01 ~]# kubectl get pods -l app=nginx
NAME READY STATUS RESTARTS AGE
nginx-server1-77d4c485d8-gsrmq 1/1 Running 0 12s
nginx-server1-77d4c485d8-mmc52 1/1 Running 0 12s
4.1.1.3 訪問
[root@master01 ~]# curl http://10.101.153.50:80
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
4.1.1.4 兩個pod里做成不同的主頁方便測試負載均衡
[root@master01 ~]# kubectl exec -it nginx-server1-77d4c485d8-gsrmq -- /bin/bash
root@deployment-nginx-6fcfb67547-nv7dn:/# cd /usr/share/nginx/html/
root@deployment-nginx-6fcfb67547-nv7dn:/usr/share/nginx/html# echo web1 > index.html
root@deployment-nginx-6fcfb67547-nv7dn:/usr/share/nginx/html# exit
exit
[root@master01 ~]# kubectl exec -it nginx-server1-77d4c485d8-mmc52 -- /bin/bash
root@deployment-nginx-6fcfb67547-rqrcw:/# cd /usr/share/nginx/html/
root@deployment-nginx-6fcfb67547-rqrcw:/usr/share/nginx/html# echo web2 > index.html
root@deployment-nginx-6fcfb67547-rqrcw:/usr/share/nginx/html# exit
exit
4.1.1.5 測試
[root@master01 ~]# curl 10.101.153.50
或
[root@master01 ~]# while true;do curl 10.101.153.50;sleep 1; done
4.1.2 Headless Service
- 普通的ClusterIP service是service name解析為cluster ip,然后cluster ip對應到后面的pod ip
- Headless service是指service name 直接解析為后面的pod ip
4.1.2.1 編寫用于創建Deployment控制器類型的資源清單文件
[root@master01 ~]# cat 03_create_deployment_app_nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-server1
spec:replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginx-smartimage: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
4.1.2.2 通過資源清單文件創建headless Service
編寫YAML文件
命令
[root@master ~]# vim 04_headless-service.yml
apiVersion: v1
kind: Service
metadata:name: headless-servicenamespace: default
spec:type: ClusterIP # ClusterIP類型,也是默認類型clusterIP: None # None就代表是無頭serviceports: # 指定service 端口及容器端口- port: 80 # service ip中的端口protocol: TCPtargetPort: 80 # pod中的端口selector: # 指定后端pod標簽app: nginx # 可通過kubectl get pod -l app=nginx查看哪些pod在使用此標簽
4.1.2.3 應用資源清單文件創建headless Service
命令
[root@master ~]# kubectl apply -f 04_headless_service.yml
輸出
service/headless-service created
4.1.2.4 查看已創建的headless Service
命令
[root@master ~]# kubectl get svc
輸出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
headless-service ClusterIP None <none> 80/TCP 2m18s
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 5d9h
可以看到headless-service沒有CLUSTER-IP,用None表示
4.1.2.5 DNS
DNS服務監視Kubernetes API,為每一個Service創建DNS記錄用于域名解析
headless service需要DNS來解決訪問問題
DNS記錄格式為: ..svc.cluster.local.
4.1.2.5.1 查看kube-dns服務的IP
命令
[root@master1 ~]# kubectl get svc -n kube-system輸出
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.2 <none> 53/UDP,53/TCP,9153/TCP 5d9h
metrics-server ClusterIP 10.105.219.44 <none> 443/TCP 45h
查看到coreDNS的服務地址是10.96.0.2
4.1.2.5.2 在集群主機通過DNS服務地址查找無頭服務的dns解析
命令
[root@master01 ~]# dig -t A headless-service.default.svc.cluster.local. @10.96.0.2輸出
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-16.P2.el7_8.2 <<>> -t A headless-service.default.svc.cluster.local. @10.96.0.2
;; global options: +cmd
;; Got answer:
;; WARNING: .local is reserved for Multicast DNS
;; You are currently testing what happens when an mDNS query is leaked to DNS
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31371
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; WARNING: recursion requested but not available;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;headless-service.default.svc.cluster.local. IN A #被解析域名;; ANSWER SECTION:
headless-service.default.svc.cluster.local. 30 IN A 10.224.235.147 #注意這里IP;; Query time: 0 msec
;; SERVER: 10.96.0.10#53(10.96.0.2)
;; WHEN: Sun May 17 10:58:50 CST 2020
;; MSG SIZE rcvd: 129
4.1.2.5.3 驗證pod的IP
命令
[root@master ~]# kubectl get pod -o wide
輸出
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-56bf6c9c8c-jmk7r 1/1 Running 0 35m 10.224.235.147 worker1 <none> <none>
4.1.2.5.4 在集群中創建一個pod驗證
創建一個鏡像為busyboxplus:curl的pod,pod名稱為bb2,用來解析域名
命令
[root@master01 ~]# kubectl run bbp --image=busyboxplus:curl -it或
[root@master01 ~]# kubectl run bbp --image=1.28 -it輸出
If you don't see a command prompt, try pressing enter.解析域名
nslookup headless-service.default.svc.cluster.local.
訪問命令
[ root@bbp:/ ]$ curl http://headless-service.default.svc.cluster.local.輸出
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
[ root@bbp:/ ]$ exit
Session ended, resume using 'kubectl attach bbp -c bbp -i -t' command when the pod is running
4.2 NodePort類型
- 創建資源清單文件
[root@master01 ~]# cat 05_create_nodeport_service_app.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-applabels:app: nginx-app
spec:replicas: 2selector:matchLabels:app: nginx-apptemplate:metadata:labels:app: nginx-appspec:containers:- name: c1image: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:name: nginx-app
spec:type: NodePortselector:app: nginx-appports:- protocol: TCPnodePort: 30001port: 8060targetPort: 80
- 應用資源清單文件
[root@master01 ~]# kubectl apply -f 05_create_nodeport_service_app.yaml
deployment.apps/nginx-app created
service/nginx-app created
- 驗證service創建
[root@master01 ~]# kubectl get deployment.apps
NAME READY UP-TO-DATE AVAILABLE AGE
nginx-app 2/2 2 2 26s[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d22h
nginx-app NodePort 10.104.157.20 <none> 8060:30001/TCP 36s[root@master01 ~]# kubectl get endpoints
NAME ENDPOINTS AGE
kubernetes 192.168.122.10:6443 2d22h
nginx-app 172.16.1.24:80,172.16.2.20:80 2m10s[root@master01 ~]# ss -anput | grep ":30001"
tcp LISTEN 0 128 :::30001 :::* users:(("kube-proxy",pid=5826,fd=9))[root@worker01 ~]# ss -anput | grep ":30001"
tcp LISTEN 0 128 :::30001 :::* users:(("kube-proxy",pid=4937,fd=11))[root@worker02 ~]# ss -anput | grep ":30001"
tcp LISTEN 0 128 :::30001 :::* users:(("kube-proxy",pid=5253,fd=11))
[root@master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-app-ffd5ccc78-cnwbx 1/1 Running 0 8m59s
nginx-app-ffd5ccc78-mz77g 1/1 Running 0 8m59s[root@master01 ~]# kubectl exec -it nginx-app-ffd5ccc78-cnwbx -- bash
root@nginx-app-ffd5ccc78-cnwbx:/# echo "nginx-app-1" > /usr/share/nginx/html/index.html
root@nginx-app-ffd5ccc78-cnwbx:/# exit
exit
[root@master01 ~]# kubectl exec -it nginx-app-ffd5ccc78-mz77g -- bash
root@nginx-app-ffd5ccc78-mz77g:/# echo "nginx-app-2" > /usr/share/nginx/html/index.html
root@nginx-app-ffd5ccc78-mz77g:/# exit
exit
- 在與kubernetes 節點同一網絡主機中訪問k8s集群內service
[root@bogon ~]# curl http://192.168.10.12:30001
nginx-app-2
[root@bogon ~]# curl http://192.168.10.13:30001
nginx-app-1
[root@bogon ~]# curl http://192.168.10.14:30001
nginx-app-1
[root@bogon ~]# curl http://192.168.10.15:30001
nginx-app-2
4.3 LoadBalancer
負載均衡,類似阿里云的slb
4.3.1 集群外訪問過程
-
用戶
-
域名
-
云服務提供商提供LB服務
-
NodeIP:Port(service IP)
-
Pod IP:端口
4.3.2 自建Kubernetes的LoadBalancer類型服務方案-MetalLB
(如果是kubeadm創建的k8s集群先執行第6部分)
MetalLB可以為kubernetes集群中的Service提供網絡負載均衡功能。
MetalLB兩大功能為:
- 地址分配,類似于DHCP
- 外部通告,一旦MetalLB為服務分配了外部IP地址,它就需要使群集之外的網絡意識到該IP在群集中“存在”。MetalLB使用標準路由協議來實現此目的:ARP,NDP或BGP。
4.3.2.1 參考資料
參考網址: https://metallb.universe.tf/installation/
4.3.2.2 應用資源清單文件
資源清單文件下載:
# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/namespace.yaml
# kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.12.1/manifests/metallb.yaml
4.3.2.3 準備metallb配置文件
[root@nginx metallb]# cat metallb-conf.yaml
apiVersion: v1
kind: ConfigMap
metadata:namespace: metallb-systemname: config
data:config: |address-pools:- name: defaultprotocol: layer2addresses:- 192.168.10.90-192.168.10.100192.168.10.90-192.168.10.100是集群節點服務器IP同一段。
在master01節點應用資源清單文件
[root@master01 ~]# kubectl apply -f metallb-conf.yaml
驗證配置
# kubectl describe configmap config -n metallb-system
Name: config
Namespace: metallb-system
Labels: <none>
Annotations: <none>Data
====
config:
----
address-pools:
- name: defaultprotocol: layer2addresses:- 192.168.10.90-192.168.10.100Events: <none>
4.3.2.4發布Service類型為LoadBalancer的Deployment控制器類型應用
創建Deployment控制器類型應用nginx-metallb及service,service類型為LoadBalancer[root@master01 ~]# vim 02_nginx-metabllb.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-metallb
spec:selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: nginx-metallb1image: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80---
apiVersion: v1
kind: Service
metadata:name: nginx-metallb
spec:ports:- port: 8090protocol: TCPtargetPort: 80selector:app: nginxtype: LoadBalancer[root@master01 ~]# kubectl apply -f nginx.yaml
4.3.2.5 驗證
[root@master01 ~]# kubectl get ns
NAME STATUS AGE
default Active 16d
kube-node-lease Active 16d
kube-public Active 16d
kube-system Active 16d
kubernetes-dashboard Active 13d
metallb-system Active 130m
test1 Active 12d
[root@master01 ~]# kubectl get pods -n metallb-system
NAME READY STATUS RESTARTS AGE
controller-64f8f944d-qdf8m 1/1 Running 0 110m
speaker-cwzq7 1/1 Running 0 110m
speaker-qk5fb 1/1 Running 0 110m
speaker-wsllb 1/1 Running 0 110m
speaker-x4bwt 1/1 Running 0 110m[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16d
nginx-metallb LoadBalancer 10.105.239.69 192.168.10.90 8090:31372/TCP 106m[root@master01 ~]# ping 192.168.10.90
PING 192.168.10.90 (192.168.10.90) 56(84) bytes of data.
64 bytes from 192.168.10.90: icmp_seq=1 ttl=64 time=3.45 ms
64 bytes from 192.168.10.90: icmp_seq=2 ttl=64 time=0.040 ms
4.3.2.6 訪問
[root@master01 ~]# curl http://192.168.122.90:8090
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>body {width: 35em;margin: 0 auto;font-family: Tahoma, Verdana, Arial, sans-serif;}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
注意:使用kubeadm部署kubernetes集群修改方法
如果在IPVS模式下使用kube-proxy,從Kubernetes v1.14.2開始,必須啟用ARP模式。可以通過在當前集群中編輯kube-proxy配置來實現:
# kubectl edit configmap -n kube-system kube-proxy并設置:
apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "ipvs"
ipvs:strictARP: true
4.4 ExternalName
4.4.1 ExternalName作用
- 把集群外部的服務引入到集群內部中來,實現了集群內部pod和集群外部的服務進行通信
- ExternalName 類型的服務適用于外部服務使用域名的方式,缺點是不能指定端口
- 還有一點要注意: 集群內的Pod會繼承Node上的DNS解析規則。所以只要Node可以訪問的服務,Pod中也可以訪問到, 這就實現了集群內服務訪問集群外服務
4.4.2 將公網域名引入
1, 編寫YAML文件
[root@master01 ~]# vim externelname.ymlapiVersion: v1
kind: Service
metadata:name: my-externalnamenamespace: default
spec:type: ExternalNameexternalName: www.baidu.com # 對應的外部域名為www.baidu.com
2, 應用YAML文件
[root@master01 ~]# kubectl apply -f externelname.ymlservice/my-externalname created
3, 查看service
[root@master01 ~]# kubectl get svc |grep extermy-externalname ExternalName <none> www.baidu.com <none> 69s
4, 查看my-service的dns解析
[root@master01 ~]# dig -t A my-externalname.default.svc.cluster.local. @10.96.0.2; <<>> DiG 9.9.4-RedHat-9.9.4-72.el7 <<>> -t A my-externalname.default.svc.cluster.local. @10.2.0.2;; global options: +cmd;; Got answer:;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 31378;; flags: qr aa rd; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1;; WARNING: recursion requested but not available;; OPT PSEUDOSECTION:; EDNS: version: 0, flags:; udp: 4096;; QUESTION SECTION:;my-externalname.default.svc.cluster.local. IN A;; ANSWER SECTION:my-externalname.default.svc.cluster.local. 5 IN CNAME www.baidu.com.www.baidu.com. 5 IN CNAME www.a.shifen.com.www.a.shifen.com. 5 IN A 14.215.177.38 解析的是百度的IPwww.a.shifen.com. 5 IN A 14.215.177.39 解析的是百度的IP;; Query time: 32 msec;; SERVER: 10.2.0.2#53(10.96.0.2);; WHEN: Thu Nov 05 11:23:41 CST 2020;; MSG SIZE rcvd: 245
[root@master01 ~]# kubectl exec -it deploy-nginx-6c9764bb69-86gwj -- /bin/sh/ # nslookup www.baidu.com......Name: www.baidu.comAddress 1: 14.215.177.39Address 2: 14.215.177.38/ # nslookup my-externalname.default.svc.cluster.local ......Name: my-externalname.default.svc.cluster.localAddress 1: 14.215.177.38Address 2: 14.215.177.39
解析此my-externalname.default.svc.cluster.local
域名和解析www.baidu.com
是一樣的結果
4.4.3 不同命名空間訪問
1, 創建ns1命名空間和相關deploy, pod,service
[root@master01 ~]# vim ns1-nginx.yml
apiVersion: v1
kind: Namespace
metadata: name: ns1 # 創建ns1命名空間
---
apiVersion: apps/v1
kind: Deployment
metadata:name: deploy-nginx namespace: ns1 # 屬于ns1命名空間
spec:replicas: 1 selector:matchLabels:app: nginx template: metadata:labels:app: nginx spec:containers: - name: nginximage: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:name: svc1 # 服務名namespace: ns1 # 屬于ns1命名空間
spec:selector:app: nginxclusterIP: None # 無頭serviceports:- port: 80 targetPort: 80
---
kind: Service
apiVersion: v1
metadata:name: external-svc1namespace: ns1 # 屬于ns1命名空間
spec:type: ExternalNameexternalName: svc2.ns2.svc.cluster.local # 將ns2空間的svc2服務引入到ns1命名空間[root@master1 ~]# kubectl apply -f ns1-nginx.ymlnamespace/ns1 createddeployment.apps/deploy-nginx createdservice/svc1 created
2, 創建ns2命名空間和相關deploy, pod,service
[root@master01 ~]# vim ns1-nginx.yml
apiVersion: v1
kind: Namespace
metadata: name: ns2 # 創建ns2命名空間
---
apiVersion: apps/v1
kind: Deployment
metadata:name: deploy-nginx namespace: ns2 # 屬于ns2命名空間
spec:replicas: 1 selector:matchLabels:app: nginx template: metadata:labels:app: nginx spec:containers: - name: nginximage: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:name: svc2 # 服務名namespace: ns2 # 屬于ns2命名空間
spec:selector:app: nginxclusterIP: None # 無頭serviceports:- port: 80 targetPort: 80
---
kind: Service
apiVersion: v1
metadata:name: external-svc1namespace: ns2 # 屬于ns2命名空間
spec:type: ExternalNameexternalName: svc1.ns1.svc.cluster.local # 將ns1空間的svc1服務引入到ns2命名空間
[root@master01 ~]# kubectl apply -f ns2-nginx.ymlnamespace/ns2 createddeployment.apps/deploy-nginx createdservice/svc2 createdservice/external-svc2 created
3, 在ns1命名空間的pod里驗證
[root@master01 ~]# kubectl get pods -n ns1NAME READY STATUS RESTARTS AGEdeploy-nginx-6c9764bb69-g5xl8 1/1 Running 0 8m10s
[root@master01 ~]# kubectl exec -it -n ns1 deploy-nginx-6c9764bb69-g5xl8 -- /bin/sh/ # nslookup svc1......Name: svc1Address 1: 10.3.166.140 deploy-nginx-6c9764bb69-g5xl8 IP與ns1里的podIP一致(見下面的查詢結果)/ # nslookup svc2.ns2.svc.cluster.local.....Name: svc2.ns2.svc.cluster.localAddress 1: 10.3.104.17 10-3-104-17.svc2.ns2.svc.cluster.local IP與ns2里的podIP一致(見下面的查詢結果)/ # exit
[root@master01 ~]# kubectl get pods -o wide -n ns1NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESdeploy-nginx-6c9764bb69-g5xl8 1/1 Running 0 70m 10.3.166.140 192.168.122.13 <none> <none>[root@master01 ~]# kubectl get pods -o wide -n ns2NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READI NESS GATESdeploy-nginx-6c9764bb69-8psxl 1/1 Running 0 68m 10.3.104.17 192.168.122.14 <none> <none>
反之,在ns2命名空間的pod里訪問svc1.ns1.svc.cluster.local
,解析的IP是ns1命名空間里的pod的IP(請自行驗證)
4, 驗證ns2中的pod的IP變化, ns1中的pod仍然可以使用svc2.ns2.svc.cluster.local
訪問
[root@master01 ~]# kubectl get pod -n ns2NAME READY STATUS RESTARTS AGEdeploy-nginx-6c9764bb69-8psxl 1/1 Running 0 81m[root@master01 ~]# kubectl delete pod deploy-nginx-6c9764bb69-8psxl -n ns2pod "deploy-nginx-6c9764bb69-8psxl" deleted 因為有replicas控制器,所以刪除pod會自動拉一個起來[root@master01 ~]# kubectl get pod -o wide -n ns2NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATESdeploy-nginx-6c9764bb69-8qbz2 1/1 Running 0 5m36s 10.3.166.141 192.168.122.13 <none> <none>pod名稱變了,IP也變成了10.3.166.141
回到ns1中的pod驗證
[root@master01 ~]# kubectl exec -it -n ns1 deploy-nginx-6c9764bb69-g5xl8 -- /bin/sh/ # ping svc2.ns2.svc.cluster.local -c 2PING svc2.ns2.svc.cluster.local (10.3.166.141): 56 data bytes 解析的IP就是ns2中pod的新IP64 bytes from 10.3.166.141: seq=0 ttl=63 time=0.181 ms64 bytes from 10.3.166.141: seq=1 ttl=63 time=0.186 ms--- svc2.ns2.svc.cluster.local ping statistics ---2 packets transmitted, 2 packets received, 0% packet lossround-trip min/avg/max = 0.181/0.183/0.186 ms/ # exit
五、sessionAffinity
會話粘貼
設置sessionAffinity為Clientip (類似nginx的ip_hash算法,lvs的sh算法)
[root@nginx ~]# cat 02_create_deployment_app_nginx_with_service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: nginx-server1
spec:replicas: 2selector:matchLabels:app: nginxtemplate:metadata:labels:app: nginxspec:containers:- name: c1image: nginx:1.15-alpineimagePullPolicy: IfNotPresentports:- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:name: nginx-svc
spec:type: ClusterIPports:- protocol: TCPport: 80targetPort: 80selector:app: nginx
[root@master01 ~]# kubectl apply -f 02_create_deployment_app_nginx_with_service.yaml
deployment.apps/nginx-server1 created
service/nginx-svc created
[root@master01 ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
nginx-server1-58845f75f4-9zlnw 1/1 Running 0 2m11s
nginx-server1-58845f75f4-ffqdt 1/1 Running 0 2m11s
[root@master01 ~]# kubectl exec -it nginx-server1-58845f75f4-9zlnw bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
root@nginx-server1-58845f75f4-9zlnw:/# echo web1 > /usr/share/nginx/html/index.html
root@nginx-server1-58845f75f4-9zlnw:/# exit
exit
[root@master01 ~]# kubectl exec -it nginx-server1-58845f75f4-ffqdt bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl kubectl exec [POD] -- [COMMAND] instead.
root@nginx-server1-58845f75f4-ffqdt:/# echo web2 > /usr/share/nginx/html/index.html
root@nginx-server1-58845f75f4-ffqdt:/# exit
exit
[root@master01 ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 16d
nginx-svc ClusterIP 10.100.53.31 <none> 80/TCP 3m53s
[root@master01 ~]# curl http://10.100.53.31
web1
[root@master01 ~]# curl http://10.100.53.31
web2
或
[root@master01 ~]# while true;do curl 10.100.53.31;sleep 1; done
[root@master01 ~]# kubectl patch svc nginx-svc -p '{"spec":{"sessionAffinity":"ClientIP"}}'
service/nginx-svc patched[root@master01 ~]# curl 10.100.53.31
web1
多次訪問,會話粘貼
設置回sessionAffinity為None
[root@master01 ~]# kubectl patch svc nginx-svc -p '{"spec":{"sessionAffinity":"None"}}'
service/my-service patched
測試
[root@master01 ~]# curl 10.100.53.31
web1
多次訪問,回到負載均衡
或
[root@master01 ~]# while true;do curl 10.100.53.31;sleep 1; done
web1
多次訪問,會話粘貼
六、修改為ipvs調度方式(拓展)
部署方式不同,修改方法不一樣。
本次主要介紹使用kubeadm部署集群方式,二進制部署較為簡單。
二進制部署修改:/etc/kubernetes/kube-proxy.yaml文件即可。
從kubernetes1.8版本開始,新增了kube-proxy對ipvs的支持,在kubernetes1.11版本中被納入了GA.
6.1 修改為IPVS調度方式前升級內核
現使用Centos7u6發布版本,默認內核版本為3.10.0,使用kubernetes為1.18.0時,可升級內核版本至4.18.0或5.6.0版本。
在所有節點中安裝,需要重啟操作系統更換內核。以下升級方法供參考。
[root@localhost ~]# yum -y install perl[root@localhost ~]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org[root@localhost ~]# yum -y install https://www.elrepo.org/elrepo-release-7.0-4.el7.elrepo.noarch.rpm[root@localhost ~]# yum --enablerepo="elrepo-kernel" -y install kernel-ml.x86_64
此處升級為5.0以上版本。[root@localhost ~]# grub2-set-default 0[root@localhost ~]# grub2-mkconfig -o /boot/grub2/grub.cfg[root@localhost ~]# reboot
6.2 修改kube-proxy的配置文件
[root@master01 ~]# kubectl edit configmap kube-proxy -n kube-system26 iptables:27 masqueradeAll: false28 masqueradeBit: 1429 minSyncPeriod: 0s30 syncPeriod: 30s31 ipvs:32 excludeCIDRs: null33 minSyncPeriod: 0s34 scheduler: "" # 可以在這里修改ipvs的算法,默認為rr輪循算法35 strictARP: false36 syncPeriod: 30s37 kind: KubeProxyConfiguration38 metricsBindAddress: 127.0.0.1:1024939 mode: "ipvs" # 默認""號里為空,加上ipvs
6.3 查看kube-system的namespace中kube-proxy有關的pod
[root@master01 ~]# kubectl get pods -n kube-system |grep kube-proxy
kube-proxy-69mv6 1/1 Running 6 2d18h
kube-proxy-jpc6c 1/1 Running 4 4d16h
kube-proxy-kq65l 1/1 Running 4 4d16h
kube-proxy-lmphf 1/1 Running 5 4d16h
6.4 驗證kube-proxy-xxx的pod中的信息
[root@master01 ~]# kubectl logs kube-proxy-jpc6c -n kube-system
W0517 00:55:10.914754 1 server_others.go:559] Unknown proxy mode "", assuming iptables proxy
I0517 00:55:10.923228 1 node.go:136] Successfully retrieved node IP: 192.168.122.32
I0517 00:55:10.923264 1 server_others.go:186] Using iptables Proxier.
I0517 00:55:10.923567 1 server.go:583] Version: v1.18.2
I0517 00:55:10.923965 1 conntrack.go:100] Set sysctl 'net/netfilter/nf_conntrack_max' to 131072
I0517 00:55:10.924001 1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0517 00:55:10.924258 1 conntrack.go:83] Setting conntrack hashsize to 32768
I0517 00:55:10.927041 1 conntrack.go:100] Set sysctl 'net/netfilter/nf_conntrack_tcp_timeout_established' to 86400
I0517 00:55:10.927086 1 conntrack.go:100] Set sysctl 'net/netfilter/nf_conntrack_tcp_timeout_close_wait' to 3600
I0517 00:55:10.927540 1 config.go:315] Starting service config controller
I0517 00:55:10.927556 1 shared_informer.go:223] Waiting for caches to sync for service config
I0517 00:55:10.927576 1 config.go:133] Starting endpoints config controller
I0517 00:55:10.927594 1 shared_informer.go:223] Waiting for caches to sync for endpoints config
I0517 00:55:11.027749 1 shared_informer.go:230] Caches are synced for service config
I0517 00:55:11.027858 1 shared_informer.go:230] Caches are synced for endpoints config
6.5 重新啟動kube-proxy
刪除kube-proxy-xxx的所有pod,讓它重新拉取新的kube-proxy-xxx的pod
[root@master01 ~]# kubectl delete pod kube-proxy-69mv6 -n kube-system
pod "kube-proxy-69mv6" deleted[root@master01 ~]# kubectl delete pod kube-proxy-jpc6c -n kube-system
pod "kube-proxy-jpc6c" deleted[root@master01 ~]# kubectl delete pod kube-proxy-kq65l -n kube-system
pod "kube-proxy-kq65l" deleted[root@master01 ~]# kubectl delete pod kube-proxy-lmphf -n kube-system
pod "kube-proxy-lmphf" deleted
[root@master01 ~]# kubectl get pods -n kube-system |grep kube-proxy
kube-proxy-2mk2b 1/1 Running 0 2m23s
kube-proxy-5bj87 1/1 Running 0 30s
kube-proxy-7qq9l 1/1 Running 0 52s
kube-proxy-tjtqf 1/1 Running 0 80s
隨意查看其中1個或3個kube-proxy-xxx的pod,驗證是否為IPVS方式了[root@master1 ~]# kubectl logs kube-proxy-tjtqf -n kube-system
I0517 02:32:26.557696 1 node.go:136] Successfully retrieved node IP: 192.168.122.32
I0517 02:32:26.557745 1 server_others.go:259] Using ipvs Proxier.
W0517 02:32:26.557912 1 proxier.go:429] IPVS scheduler not specified, use rr by default
I0517 02:32:26.560008 1 server.go:583] Version: v1.18.2
I0517 02:32:26.560428 1 conntrack.go:52] Setting nf_conntrack_max to 131072
I0517 02:32:26.561094 1 config.go:315] Starting service config controller
I0517 02:32:26.562251 1 shared_informer.go:223] Waiting for caches to sync for service config
I0517 02:32:26.561579 1 config.go:133] Starting endpoints config controller
I0517 02:32:26.562271 1 shared_informer.go:223] Waiting for caches to sync for endpoints config
I0517 02:32:26.662541 1 shared_informer.go:230] Caches are synced for service config
I0517 02:32:26.662566 1 shared_informer.go:230] Caches are synced for endpoints config