本文的試驗環境爲CentOS 7.3,Kubernetes集羣爲1.11.2,安裝步驟參見kubeadm安裝kubernetes V1.11.1 集羣
Service 介紹
我們通過Pod、Deployment等可以將應用發佈到Kubernetes平臺中,但是如果我們如何才能訪問我們部署的應用呢?有一個辦法就是通過節點的IP加上節點的端口來訪問這個節點上的容器應用,但是如果我們有多個跨節點的相通應用時該怎麼辦呢?特別是應用發生擴容、縮容時應該如何處理,這時我們就需要利用Service
來實現。
在Kubernetes中,Service是一種資源,提供了我們訪問單個或多個容器應用的能力。每個服務在其生命週期內,都擁有一個固定的IP地址和端口。每個服務對應了後臺的一個或多個Pod,通過這種方式,客戶端就不需要關心Pod所在的位置,方便後端進行方便的Pod擴容、縮容等操作。
可以通過下面的示意圖來理解Service的作用。
由此可知,服務可以被外部的客戶訪問,也可以被內部的客戶訪問。Service通過創建時指定的標籤選擇器來決定用戶的請求轉發到後臺的哪些Pods中。看一下Service創建的具體例子。
apiVersion: v1 kind: Service metadata: name: tomcat-svc spec: sessionAffinity: ClientIP # 該參數可以設置相同IP的請求總是定位到同一個Pod ports: - port: 8080 targetPort: 8080 selector: app: tomcat
這個例子定義了一個Service名爲tomcat-svc,端口爲8080,該服務會將用戶的請求轉發到後臺帶有app=tomcat標籤的Pods上。
kubectl get svc
我們在測試服務是否正常的時候,不能使用ping命令。因爲Kubernetes中實現服務時,並不支持ping。因此不能使用ping的方式檢查服務是否正常,應當使用telnet或者nc等命令進行測試。
Pod中的應用訪問Service有兩種方式,一種是通過向Pod中注入環境變量的方式,這種方式缺點很明顯,必須首先創建Service,Pod內的應用才能通過環境變量訪問;另一種方式是通過DNS的方式,這種方式非常靈活。
Service 與 endpints
Endpoints 是組成Service的一組IP地址和端口資源。
kubectl get endpoints kubia
默認情況下創建Service的時候,會創建一個同名的Endpoints資源,通過kubectl descirbe svc svc-name
可以看到這個服務對應的Endpoints。
如果創建Service的時候,不指定Pod的選擇器,則不會創建Endpoints資源。也可以手工創建Endpoints類型。創建時需要注意名稱要與Service的名稱一致。
apiVersion: v1 kind: Endpoints metadata: name: external-service subsets: - addresses: - ip: 11.11.11.11 - ip: 22.22.22.22 ports: - port: 80
Service 的類型
- NodePort
- Loadbalance
- Ingress
實例解析
1. 創建內部服務
創建用於內部訪問的服務很簡單,創建後服務將在生命週期內擁有固定的IP和端口。
apiVersion: v1 kind: Service metadata: name: tomcat-svc spec: sessionAffinity: ClientIP # 該參數可以設置相同IP的請求總是定位到同一個Pod ports: - port: 8080 targetPort: 8080 selector: app: tomcat
客戶每次鏈接Service時,都會對應到後端的一個Pod上,如果客戶需要鏈接所有的後端Pod呢,可以使用headless service。通過這種方式,Kubernetes內部的DNS服務會將Service對應的所有IP返回。
apiVersion: v1 kind: Service metadata: name: tomcat-headless spec: clusterIP: None ports: - port: 8080 targetPort: 8080 selector: app: tomcat
或者可以在客戶端通過程序調用Kubernetes API的方式獲取地址列表。
2. 爲外部服務創建一個內部別名
容器內部的應用要訪問外部應用時,可以直接在容器內訪問外部服務地址,也可以通過創建一個外部服務的別名進行轉發,這樣相當於將內外部調用關係解耦了,每次外部發生變化的時候,可以不用修改應用的代碼。
apiVersion: v1 kind: Service metadata: name: external-service spec: type: ExternalName externalName: api.baidu.com ports: - port: 80
3. 向外部暴露內部服務
向外部暴露內部服務有三種方式:
- NodePort
- LoadBalancer
- Ingress,7層負載均衡
3.1 NodePort 方式
創建這種方式的Service,內部可以通過ClusterIP進行訪問,外部用戶可以通過NodeIP:NodePort的方式單獨訪問每個Node上的實例。這種方式有很多問題,直接訪問節點的地址和端口需要在客戶端記錄很多信息,Pod發生遷移後這些信息沒辦法動態更新,節點的防火牆及節點所在網絡區域的防火牆策略配置會比較麻煩。
apiVersion: v1 kind: Service metadata: name: nodeport spec: type: NodePort ports: - port: 80 targetPort: 8080 nodePort: 30123 selector: app: httpd
端口如果忽略,Kubernetes會自動生成。
$ kubectl get svc nodeport
獲取一些信息
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type="InternalIP")].address}'
3.2 load balancer 方式
這種方式一般需要雲供應商的支持,或者一些本地資源如F5的支持。這裏不做過多介紹了。
3.3 Ingress 方式
LoadBalancer要求的IP資源較多,每個負載均衡服務都需要一個IP,Ingress可以只用一個IP,通過URL來判斷需要訪問的服務。Ingress工作在HTTP層,配置與使用更加靈活。目前爲止還是一個beta特性。
目前有很多Ingress的支持:
- Kubernetes提供的基於GCE、Nginx的Ingress控制器
- F5提供的控制器
- Kong提供的Kong Ingress Controller for Kubernetes
- Traefik提供收費的解決方案
- NGINX提供的NGINX Ingress Controller for Kubernetes
- 基於HAProxy的HAProxy Ingress Controller for Kubernetes
- Istio提供的Control Ingress Traffic
使用過程中的最佳實踐:
- 總是添加 Readiness 探測
- 不要在探測中使用關閉的邏輯
參考資料
- Kubernetes in action
- JSONPath Support