k8s通過service訪問pod(五)--技術流ken
service
每個 Pod 都有自己的 IP 地址。當 controller 用新 Pod 替代發生故障的 Pod 時,新 Pod 會分配到新的 IP 地址。這樣就產生了一個問題:
如果一組 Pod 對外提供服務(比如 HTTP),它們的 IP 很有可能發生變化,那麼客戶端如何找到並訪問這個服務呢?
Kubernetes 給出的解決方案是 Service。
建立 Service
Kubernetes Service 從邏輯上代表了一組 Pod,具體是哪些 Pod 則是由 label 來挑選。Service 有自己 IP,而且這個 IP 是不變的。客戶端只需要訪問 Service 的 IP,Kubernetes 則負責建立和維護 Service 與 Pod 的對映關係。無論後端 Pod 如何變化,對客戶端不會有任何影響,因為 Service 沒有變。
第一步:建立下面的這個 Deployment:
檢視支援的apiversion使用命令kubectl api-versions
第二步:部署並檢視pod
我們啟動了三個 Pod,執行 httpd 映象, label 是 run: httpd , Service 將會用這個 label 來挑選 Pod 。
[root@ken ~]# kubectl apply -f httpd.yml deployment.apps/httpd created [root@ken ~]# kubectl get pod -o wide NAMEREADYSTATUSRESTARTSAGEIPNODENOMINATED NODEREADINESS GATES httpd-8c6c4bd9b-ljvlb1/1Running041s10.244.1.27host1<none><none> httpd-8c6c4bd9b-ngxqv1/1Running041s10.244.1.28host1<none><none> httpd-8c6c4bd9b-wxblj1/1Running041s10.244.2.18host2<none><none>
第三步:叢集內部測試連通性
Pod 分配了各自的 IP,這些 IP 只能被 Kubernetes Cluster 中的容器和節點訪問。
[root@ken ~]# curl 10.244.1.28 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 10.244.1.27 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 10.244.2.18 <html><body><h1>It works!</h1></body></html>
第四步:接下來建立 Service,其配置檔案如下:
① v1 是 Service 的 apiVersion。
② 指明當前資源的型別為 Service。
③ Service 的名字為 httpd-svc。
④ selector 指明挑選那些 label 為 run: httpd 的 Pod 作為 Service 的後端。
⑤ 將 Service 的 8080 埠對映到 Pod 的 80 埠,使用 TCP 協議。
第五步: 執行 kubectl apply 建立 Service httpd-svc 。
[root@ken ~]# kubectl apply -f service.yml service/httpd-svc created [root@ken ~]# kubectl get service NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE httpd-svcClusterIP10.106.64.97<none>8080/TCP17s kubernetesClusterIP10.96.0.1<none>443/TCP11h
httpd-svc 分配到一個 CLUSTER-IP 10.106.64.97。可以通過該 IP 訪問後端的 httpd Pod。
[root@ken ~]# curl 10.106.64.97:8080 <html><body><h1>It works!</h1></body></html>
根據前面的埠對映,這裡要使用 8080 埠。另外,除了我們建立的 httpd-svc ,還有一個 Service kubernetes , Cluster 內部通過這個 Service 訪問 kubernetes API Server 。
通過 kubectl describe 可以檢視 httpd-svc 與 Pod 的對應關係。
[root@ken ~]# kubectl describe service httpd-svc Name:httpd-svc Namespace:default Labels:<none> Annotations:kubectl.kubernetes.io/last-applied-configuration: {"apiVersion":"v1","kind":"Service","metadata":{"annotations":{},"name":"httpd-svc","namespace":"default"},"spec":{"ports":[{"port":8080,"... Selector:run=httpd Type:ClusterIP IP:10.106.64.97 Port:<unset>8080/TCP TargetPort:80/TCP Endpoints:10.244.1.27:80,10.244.1.28:80,10.244.2.18:80 Session Affinity:None Events:<none>
Endpoints 羅列了三個 Pod 的 IP 和埠。
DNS訪問service
在 Cluster 中,除了可以通過 Cluster IP 訪問 Service,Kubernetes 還提供了更為方便的 DNS 訪問。
第一步:檢視coredns
kubeadm 部署時會預設安裝 coredns 元件。
[root@ken ~]# kubectl get deployment --namespace=kube-system NAMEREADYUP-TO-DATEAVAILABLEAGE coredns2/22211h
coredns 是一個 DNS 伺服器。每當有新的 Service 被建立,coredns 會新增該 Service 的 DNS 記錄。Cluster 中的 Pod 可以通過 <SERVICE_NAME>.<NAMESPACE_NAME> 訪問 Service。
第二步:dns訪問
比如可以用 httpd-svc.default 訪問 Service httpd-svc。
[root@ken ~]# kubectl run busybox --rm -it --image=busybox /bin/sh / # wget httpd-svc:8080 Connecting to httpd-svc:8080 (10.106.64.97:8080) index.html100% |*************|450:00:00 ETA / # ls binhomerootusr devindex.htmlsysvar etcproctmp / # cat index.html <html><body><h1>It works!</h1></body></html>
由於這個 Pod 與 httpd-svc 同屬於 default namespace ,可以省略 default 直接用 httpd-svc 訪問 Service 。
第三步:檢視namespace
如果要訪問其他 namespace 中的 Service ,就必須帶上 namesapce 了。 kubectl get namespace 檢視已有的 namespace 。
[root@ken ~]# kubectl get namespace NAMESTATUSAGE defaultActive11h kube-publicActive11h kube-systemActive11h
第 四步:在 kube-public 中部署 Service httpd2-svc
配置如下:
第五步:建立資源
通過 namespace: kube-public 指定資源所屬的 namespace 。多個資源可以在一個 YAML 檔案中定義,用 --- 分割。執行 kubectl apply 建立資源:
[root@ken ~]# kubectl apply -f service.yml deployment.apps/httpd2 created service/httpd2-svc created
第六步:檢視 kube-public 的 Service:
[root@ken ~]# kubectl get service --namespace=kube-public NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE httpd2-svcClusterIP10.111.175.138<none>8080/TCP63s
第七步:在 busybox Pod 中訪問 httpd2-svc:
[root@ken ~]# kubectl run busybox --rm -it --image=busybox /bin/sh / # wget httpd2-svc:8080 wget: bad address 'httpd2-svc:8080' / # wget httpd2-svc.kube-public:8080 Connecting to httpd2-svc.kube-public:8080 (10.111.175.138:8080) index.html100% |*************|450:00:00 ETA
因為屬於不同的 namespace,必須使用 httpd2-svc.kube-public 才能訪問到。
外網訪問service
除了 Cluster 內部可以訪問 Service ,很多情況我們也希望應用的 Service 能夠暴露給 Cluster 外部。 Kubernetes 提供了多種型別的 Service ,預設是 ClusterIP 。
ClusterIP
Service 通過 Cluster 內部的 IP 對外提供服務,只有 Cluster 內的節點和 Pod 可訪問,這是預設的 Service 型別,前面實驗中的 Service 都是 ClusterIP 。
NodePort
Service 通過 Cluster 節點的靜態埠對外提供服務。 Cluster 外部可以通過 <NodeIP>:<NodePort> 訪問 Service 。
LoadBalancer
Service 利用 cloud provider 特有的 load balancer 對外提供服務, cloud provider 負責將 load balancer 的流量導向 Service 。目前支援的 cloud provider 有 GCP 、 AWS 、 Azur 等。
第一步:實踐 NodePort, Service httpd-svc 的配置檔案修改如下:
新增 type: NodePort,重新建立 httpd-svc 。
第二步:建立service
[root@ken ~]# kubectl apply -f service.yml service/httpd-svc created [root@ken ~]# kubectl get service httpd-svc NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE httpd-svcNodePort10.108.118.202<none>8080:31785/TCP12s
Kubernetes 依然會為 httpd-svc 分配一個 ClusterIP,不同的是:
EXTERNAL-IP 為 nodes,表示可通過 Cluster 每個節點自身的 IP 訪問 Service。
PORT(S) 為 8080:31785。8080 是 ClusterIP 監聽的埠(每個節點都有該埠),31785 則是節點上監聽的埠。Kubernetes 會從 30000-32767 中分配一個可用的埠,每個節點都會監聽此埠並將請求轉發給 Service。
[root@ken ~]# ss -tnl | grep 31785 LISTEN0128:::31785:::*
第三步:測試nodeport是否正常工作
[root@ken ~]# curl 172.20.10.2:31785 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.7:31785 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.9:31785 <html><body><h1>It works!</h1></body></html>
通過三個節點 IP + 32312 埠都能夠訪問 httpd-svc 。
第四步:指定特定埠
NodePort 預設是的隨機選擇,不過我們可以用 nodePort
指定某個特定埠。
現在配置檔案中就有三個 Port 了:
nodePort 是節點上監聽的埠。
port 是 ClusterIP 上監聽的埠。
targetPort 是 Pod 監聽的埠。
最終,Node 和 ClusterIP 在各自埠上接收到的請求都會通過 iptables 轉發到 Pod 的 targetPort 。
第四步:應用新的 nodePort 並驗證:
[root@ken ~]# kubectl apply -f service.yml service/httpd-svc configured [root@ken ~]# kubectl get service httpd-svc NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE httpd-svcNodePort10.108.118.202<none>8080:30000/TCP6m8s [root@ken ~]# curl 172.20.10.2:30000 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.7:30000 <html><body><h1>It works!</h1></body></html> [root@ken ~]# curl 172.20.10.9:30000 <html><body><h1>It works!</h1></body></html>
nodePort: 30000 已經生效了。