K8S入門到實(shí)戰(zhàn)--跨服務(wù)調(diào)用
背景
在做傳統(tǒng)業(yè)務(wù)開發(fā)的時(shí)候,當(dāng)我們的服務(wù)提供方有多個(gè)實(shí)例時(shí),往往我們需要將對(duì)方的服務(wù)列表保存在本地,然后采用一定的算法進(jìn)行調(diào)用;當(dāng)服務(wù)提供方的列表變化時(shí)還得及時(shí)通知調(diào)用方。
student:  
   url:     
   - 192.168.1.1:8081     
   - 192.168.1.2:8081這樣自然是對(duì)雙方都帶來不少的負(fù)擔(dān),所以后續(xù)推出的服務(wù)調(diào)用框架都會(huì)想辦法解決這個(gè)問題。
以 spring cloud 為例:
圖片
服務(wù)提供方會(huì)向一個(gè)服務(wù)注冊(cè)中心注冊(cè)自己的服務(wù)(名稱、IP等信息),客戶端每次調(diào)用的時(shí)候會(huì)向服務(wù)注冊(cè)中心獲取一個(gè)節(jié)點(diǎn)信息,然后發(fā)起調(diào)用。
但當(dāng)我們切換到 k8s 后,這些基礎(chǔ)設(shè)施都交給了 k8s 處理了,所以 k8s 自然得有一個(gè)組件來解決服務(wù)注冊(cè)和調(diào)用的問題。
也就是我們今天重點(diǎn)介紹的 service。
service
在介紹 service 之前我先調(diào)整了源碼:
func main() {  
   http.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {  
      name, _ := os.Hostname()  
      log.Printf("%s ping", name)  
      fmt.Fprint(w, "pong")  
   })  
   http.HandleFunc("/service", func(w http.ResponseWriter, r *http.Request) {  
      resp, err := http.Get("http://k8s-combat-service:8081/ping")  
      if err != nil {  
         log.Println(err)  
         fmt.Fprint(w, err)  
         return  
      }  
      fmt.Fprint(w, resp.Status)  
   })  
  
   http.ListenAndServe(":8081", nil)  
}新增了一個(gè) /service 的接口,這個(gè)接口會(huì)通過 service 的方式調(diào)用服務(wù)提供者的服務(wù),然后重新打包。
make docker同時(shí)也新增了一個(gè) deployment-service.yaml:
apiVersion: apps/v1  
kind: Deployment  
metadata:  
  labels:  
    app: k8s-combat-service # 通過標(biāo)簽選擇關(guān)聯(lián)  
  name: k8s-combat-service  
spec:  
  replicas: 1  
  selector:  
    matchLabels:  
      app: k8s-combat-service  
  template:  
    metadata:  
      labels:  
        app: k8s-combat-service  
    spec:  
      containers:  
        - name: k8s-combat-service  
          image: crossoverjie/k8s-combat:v1  
          imagePullPolicy: Always  
          resources:  
            limits:  
              cpu: "1"  
              memory: 100Mi  
            requests:  
              cpu: "0.1"  
              memory: 10Mi  
---  
apiVersion: v1  
kind: Service  
metadata:  
  name: k8s-combat-service  
spec:  
  selector:  
    app: k8s-combat-service # 通過標(biāo)簽選擇關(guān)聯(lián)  
  type: ClusterIP  
  ports:  
    - port: 8081        # 本 Service 的端口  
      targetPort: 8081  # 容器端口  
      name: app使用相同的鏡像部署一個(gè)新的 deployment,名稱為 k8s-combat-service,重點(diǎn)是新增了一個(gè)kind: Service 的對(duì)象。
這個(gè)就是用于聲明 service 的組件,在這個(gè)組件中也是使用 selector 標(biāo)簽和 deployment 進(jìn)行了關(guān)聯(lián)。
也就是說這個(gè) service 用于服務(wù)于名稱等于 k8s-combat-service 的 deployment。
下面的兩個(gè)端口也很好理解,一個(gè)是代理的端口, 另一個(gè)是 service 自身提供出去的端口。
至于 type: ClusterIP 是用于聲明不同類型的 service,除此之外的類型還有:
- NodePort
 - LoadBalancer
 - ExternalName等類型,默認(rèn)是 ClusterIP,現(xiàn)在不用糾結(jié)這幾種類型的作用,后續(xù)我們?cè)谥v到 Ingress 的時(shí)候會(huì)具體介紹。
 
負(fù)載測(cè)試
我們先分別將這兩個(gè) deployment 部署好:
k apply -f deployment/deployment.yaml
k apply -f deployment/deployment-service.yaml
? k get pod
NAME                                  READY   STATUS    RESTARTS   AGE
k8s-combat-7867bfb596-67p5m           1/1     Running   0          3h22m
k8s-combat-service-5b77f59bf7-zpqwt   1/1     Running   0          3h22m由于我新增了一個(gè) /service 的接口,用于在 k8s-combat 中通過 service 調(diào)用 k8s-combat-service 的接口。
resp, err := http.Get("http://k8s-combat-service:8081/ping")其中 k8s-combat-service 服務(wù)的域名就是他的服務(wù)名稱。
如果是跨 namespace 調(diào)用時(shí),需要指定一個(gè)完整名稱,在后續(xù)的章節(jié)會(huì)演示。
我們整個(gè)的調(diào)用流程如下:
圖片
相信大家也看得出來相對(duì)于 spring cloud 這類微服務(wù)框架提供的客戶端負(fù)載方式,service 是一種服務(wù)端負(fù)載,有點(diǎn)類似于 Nginx 的反向代理。
為了更直觀的驗(yàn)證這個(gè)流程,此時(shí)我將 k8s-combat-service 的副本數(shù)增加到 2:
spec:  
  replicas: 2只需要再次執(zhí)行:
? k apply -f deployment/deployment-service.yaml
deployment.apps/k8s-combat-service configured
service/k8s-combat-service unchanged
圖片
不管我們對(duì) deployment 的做了什么變更,都只需要 apply 這個(gè) yaml 文件即可, k8s 會(huì)自動(dòng)將當(dāng)前的 deployment 調(diào)整為我們預(yù)期的狀態(tài)(比如這里的副本數(shù)量增加為 2);這也就是 k8s 中常說的聲明式 API。
可以看到此時(shí) k8s-combat-service 的副本數(shù)已經(jīng)變?yōu)閮蓚€(gè)了。如果我們此時(shí)查看這個(gè) service 的描述時(shí):
? k describe svc k8s-combat-service |grep Endpoints
Endpoints:         192.168.130.133:8081,192.168.130.29:8081會(huì)發(fā)現(xiàn)它已經(jīng)代理了這兩個(gè) Pod 的 IP。
圖片
此時(shí),我進(jìn)入了 k8s-combat-7867bfb596-67p5m 的容器:
k exec -it k8s-combat-7867bfb596-67p5m bash
curl http://127.0.0.1:8081/service并執(zhí)行兩次 /service 接口,發(fā)現(xiàn)請(qǐng)求會(huì)輪訓(xùn)進(jìn)入 k8s-combat-service 的代理的 IP 中。
由于 k8s service 是基于 TCP/UDP 的四層負(fù)載,所以在 http1.1 中是可以做到請(qǐng)求級(jí)的負(fù)載均衡,但如果是類似于 gRPC 這類長鏈接就無法做到請(qǐng)求級(jí)的負(fù)載均衡。
換句話說 service 只支持連接級(jí)別的負(fù)載。
如果要支持 gRPC,就得使用 Istio 這類服務(wù)網(wǎng)格,相關(guān)內(nèi)容會(huì)在后續(xù)章節(jié)詳解。
總結(jié)
總的來說, k8s service 提供了簡易的服務(wù)注冊(cè)發(fā)現(xiàn)和負(fù)載均衡功能,當(dāng)我們只提供 http 服務(wù)時(shí)是完全夠用的。















 
 
 

















 
 
 
 