編者按:雲原生是網易杭州研究院(網易杭研)奉行的核心技術方向之一,開源容器平臺Kubernetes做爲雲原生產業技術標準、雲原生生態基石,在設計上不可避免有其複雜性,Kubernetes系列文章基於網易杭研資深工程師總結,多角度多層次介紹Kubernetes的原理及運用,如何解決生產中的實際需求及規避風險,但願與讀者深刻交流共同進步。html
本文由做者受權發佈,未經許可,請勿轉載。node
做者:李嵐清,網易杭州研究院雲計算技術中心資深工程師nginx
衆所周知,pod的生命週期是不穩定的,可能會朝生夕死,這也就意味着pod的ip是不固定的。web
好比咱們使用三副本的deployment部署了nginx服務,每一個pod都會被分配一個ip,因爲pod的生命週期不穩定,pod可能會被刪除重建,而重建的話pod的ip地址就會改變。也有一種場景,咱們可能會對nginx deployment進行擴縮容,從3副本擴容爲5副本或者縮容爲2副本。當咱們須要訪問上述的nginx服務時,客戶端對於nginx服務的ip地址就很難配置和管理。docker
所以,kubernetes社區就抽象出了service
這個資源對象或者說邏輯概念。後端
service是kubernetes中最核心的資源對象之一,kubernetes中的每一個service其實就是咱們常常提到的「微服務」。api
service定義了一個服務的入口地址,它經過label selector 關聯後端的pod。service會被自動分配一個ClusterIP,service的生命週期是穩定的,它的ClusterIP也不會發生改變,用戶經過訪問service的ClusterIP來訪問後端的pod。因此,無論後端pod如何擴縮容、如何刪除重建,客戶端都不須要關心。網絡
(1)建立一個三副本的nginx deployment:
nginx.yamlsession
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - image: nginx imagePullPolicy: Always name: nginx # kubectl create -f nginx.yaml deployment.extensions/nginx created # kubectl get pods -o wide nginx-5c7588df-5dmmp 1/1 Running 0 57s 10.120.49.230 pubt2-k8s-for-iaas4.dg.163.org <none> <none> nginx-5c7588df-gb2d8 1/1 Running 0 57s 10.120.49.152 pubt2-k8s-for-iaas4.dg.163.org <none> <none> nginx-5c7588df-gdngk 1/1 Running 0 57s 10.120.49.23 pubt2-k8s-for-iaas4.dg.163.org <none> <none>
(2)建立service,經過label selector關聯nginx pod:app
svc.yaml
apiVersion: v1 kind: Service metadata: name: nginx spec: type: ClusterIP selector: app: nginx ports: - port: 80 protocol: TCP targetPort: 80 # kubectl create -f svc.yaml service/nginx created # kubectl get svc nginx -o wide NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR nginx ClusterIP 10.178.4.2 <none> 80/TCP 23s app=nginx
(3)在k8s節點上訪問service地址
# curl 10.178.4.2: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>
service中有幾個關鍵字段:
spec.selector
: 經過該字段關聯屬於該service的podspec.clusterIP
: k8s自動分配的虛擬ip地址spec.ports
: 定義了監聽端口和目的端口。用戶能夠經過訪問clusterip:監聽端口
來訪問後端的pod當用戶建立一個service時,kube-controller-manager會自動建立一個跟service同名的endpoints資源:
# kubectl get endpoints nginx NAME ENDPOINTS AGE nginx 10.120.49.152:80,10.120.49.23:80,10.120.49.230:80 12m
endpoints資源中,保存了該service關聯的pod列表,這個列表是kube-controller-manager自動維護的,當發生pod的增刪時,這個列表會被自動刷新。
好比,咱們刪除了其中的一個pod:
# kubectl delete pods nginx-5c7588df-5dmmp pod "nginx-5c7588df-5dmmp" deleted # kubectl get pods nginx-5c7588df-ctcml 1/1 Running 0 6s nginx-5c7588df-gb2d8 1/1 Running 0 18m nginx-5c7588df-gdngk 1/1 Running 0 18m
能夠看到kube-controller-manager立馬補充了一個新的pod。而後咱們再看一下endpoints資源,後端pod列表也被自動更新了:
# kubectl get endpoints nginx NAME ENDPOINTS AGE nginx 10.120.49.152:80,10.120.49.23:80,10.120.49.73:80 16m
那麼,當用戶去訪問clusterip:port
時,流量是如何負載均衡到後端pod的呢?
k8s在每一個node上運行了一個kube-proxy
組件,kube-proxy
會watch service和endpoints資源,經過配置iptables規則(如今也支持ipvs,不過不在本文章討論範圍以內)來實現service的負載均衡。
能夠在任一個k8s node上看一下上述nginx service的iptables規則:
# iptables -t nat -L PREROUTING Chain PREROUTING (policy ACCEPT) target prot opt source destination KUBE-SERVICES all -- anywhere anywhere /* kubernetes service portals */ # iptables -t nat -L KUBE-SERVICES Chain KUBE-SERVICES (2 references) target prot opt source destination KUBE-SVC-4N57TFCL4MD7ZTDA tcp -- anywhere 10.178.4.2 /* default/nginx: cluster IP */ tcp dpt:http # iptables -t nat -L KUBE-SVC-4N57TFCL4MD7ZTDA Chain KUBE-SVC-4N57TFCL4MD7ZTDA (1 references) target prot opt source destination KUBE-SEP-AHN4ALGUQHWJZNII all -- anywhere anywhere statistic mode random probability 0.33332999982 KUBE-SEP-BDD6UBFFJ4G2PJDO all -- anywhere anywhere statistic mode random probability 0.50000000000 KUBE-SEP-UR2OSKI3P5GEGC2Q all -- anywhere anywhere # iptables -t nat -L KUBE-SEP-AHN4ALGUQHWJZNII Chain KUBE-SEP-AHN4ALGUQHWJZNII (1 references) target prot opt source destination KUBE-MARK-MASQ all -- 10.120.49.152 anywhere DNAT tcp -- anywhere anywhere tcp to:10.120.49.152:80
當用戶訪問clusterip:port
時,iptables會經過iptables DNAT 均衡的負載均衡到後端pod。
service的ClusterIP是一個虛擬ip,它沒有附着在任何的網絡設備上,僅僅存在於iptables規則中,經過dnat實現訪問clusterIP時的負載均衡。
當用戶建立service時,k8s會自動從service網段中分配一個空閒ip設置到.spec.clusterIP
字段。固然,k8s也支持用戶在建立svc時本身指定clusterIP。
service的網段是經過 kube-apiserver的命令行參數--service-cluster-ip-range
配置的,不容許變動。
service網段不能跟機房網絡、docker網段、容器網段衝突,不然可能會致使網絡不通。
service的clusterIP是k8s集羣內的虛擬ip,不一樣的k8s集羣可使用相同的service網段,在k8s集羣外是訪問不通service的clusterIP的。
kubernetes是有本身的域名解析服務的。好比咱們能夠經過訪問域名nginx.default.svc.cluster.local
來訪問上述的nginx服務:
$ curl nginx.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>
域名格式爲: ${ServiceName}.${Namespace}.svc.${ClusterDomain}
. 其中${ClusterDomain}的默認值是cluster.local
,能夠經過kubelet的命令行參數----cluster-domain
進行配置。
當不須要service ip的時候,能夠在建立service的時候指定spec.clusterIP: None
,這種service便是headless service。因爲沒有分配service ip,kube-proxy也不會處理這種service。
DNS對這種service的解析:
service支持多種不一樣的類型,包括ClusterIP
、NodePort
、LoadBalancer
,經過字段spec.type
進行配置。
默認類型。對於ClusterIP service, k8s會自動分配一個只在集羣內可達的虛擬的ClusterIP,在k8s集羣外沒法訪問。
k8s除了會給NodePort service自動分配一個ClusterIP,還會自動分配一個nodeport端口。集羣外的客戶端能夠訪問任一node的ip加nodeport,便可負載均衡到後端pod。
nodeport的端口範圍能夠經過kube-apiserver的命令行參數--service-node-port-range
配置,默認值是30000-32767
,當前咱們的配置是30000-34999
。
可是客戶端訪問哪一個node ip也是須要考慮的一個問題,須要考慮高可用。並且NodePort會致使訪問後端服務時多了一跳,而且可能會作snat看不到源ip。
另外須要注意的是,service-node-port-range
不可以跟幾個端口範圍衝突:
net.ipv4.ip_local_port_range
,能夠配置爲 35000-60999
LoadBalancer service須要對接雲服務提供商的NLB服務。當用戶建立一個LoadBalancer類型的sevice時,cloud-controller-manager
會調用NLB的API自動建立LB實例,而且將service後端的pod掛到LB實例後端。
apiVersion: v1 kind: Service metadata: name: nginx spec: ports: - port: 80 protocol: TCP targetPort: 80 type: LoadBalancer $ kubectl get svc nginx NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE nginx LoadBalancer 10.178.8.216 10.194.73.147 80:32514/TCP 3s
用戶能夠經過配置spec.serviceAffinity=ClientIP
來實現基於客戶端ip的會話保持功能。 該字段默認爲None。
還能夠經過適當設置 service.spec.sessionAffinityConfig.clientIP.timeoutSeconds
來設置最大會話停留時間。 (默認值爲 10800 秒,即 3 小時)
apiVersion: v1 kind: Service metadata: name: nginx spec: ports: - port: 80 protocol: TCP targetPort: 80 type: ClusterIP sessionAffinity: ClientIP
kubernetes
service當咱們部署好一個k8s集羣以後,發現系統自動幫忙在default
namespace下建立了一個name爲kubernetes
的service:
# kubectl get svc kubernetes -o yaml apiVersion: v1 kind: Service metadata: labels: component: apiserver provider: kubernetes name: kubernetes namespace: default spec: clusterIP: 10.178.4.1 ports: - name: https port: 443 protocol: TCP targetPort: 6443 sessionAffinity: None type: ClusterIP status: loadBalancer: {}
能夠看到kubernetes
svc的ip是--service-cluster-ip-range
的第一個ip,而且該service沒有設置spec.selector
。理論上來講,對於沒有設置selector的svc,kube-controller-manager不會自動建立同名的endpoints資源出來。
可是咱們看到是有同名的endpoints存在的,而且多個apiserver的地址也被保存在endpoints資源中:
# kubectl get ep kubernetes NAME ENDPOINTS AGE kubernetes 10.120.0.2:6443,10.120.0.3:6443 137d
具體是如何實現的,感興趣的能夠看下源碼k8s.io/kubernetes/pkg/master/reconcilers
問題一 爲何service clusterip沒法ping通
由於service clusterip是一個k8s集羣內部的虛擬ip,沒有附着在任何網絡設備上,僅僅存在於iptables nat規則中,用來實現負載均衡。
問題二 爲何service的網段不能跟docker網段、容器網段、機房網段衝突
假如service網段跟上述網段衝突,很容易致使容器或者在k8s node上訪問上述網段時發生網絡不通的狀況。
問題三 爲何在k8s集羣外沒法訪問service clusterip
service clusterip是k8s集羣內可達的虛擬ip,集羣外不可達。不一樣的k8s集羣可使用相同的service網段。
或者說,集羣外的機器上沒有本k8s集羣的kube-proxy組件,沒有建立對應的iptables規則,所以集羣外訪問不通service clusterip。
問題四 可否擴容service網段
原則上這個網段是不容許更改的,可是假如由於前期規劃的問題分配的網段太小,實際能夠經過比較hack的運維手段擴容service網段。
問題五 service是否支持七層的負載均衡
service僅支持四層的負載均衡,七層的負載均衡須要使用ingress
做者簡介
李嵐清,網易杭州研究院雲計算技術中心容器編排團隊資深系統開發工程師,具備多年Kubernetes開發、運維經驗,主導實現了容器網絡管理、容器混部等生產級核心系統研發,推進網易集團內部電商、音樂、傳媒、教育等多個業務的容器化。