云原生環(huán)境網(wǎng)絡(luò)方案2 --- Service和Service Mesh

云原生環(huán)境網(wǎng)絡(luò)方案2 --- Service和Service Mesh

在之前的文章中,我們描述了容器環(huán)境的底層網(wǎng)絡(luò)實(shí)現(xiàn)原理以及一些常見的k8s網(wǎng)絡(luò)插件。這些組件利用Linux系統(tǒng)的內(nèi)核網(wǎng)絡(luò)協(xié)議棧完成容器與容器之間網(wǎng)絡(luò)互連的工作。到了這一步,我們僅僅解決了容器與容器之間互連,在微服務(wù)架構(gòu)中,網(wǎng)絡(luò)層面的互連僅僅是基礎(chǔ),我們還需要從網(wǎng)絡(luò)層面解決:服務(wù)發(fā)現(xiàn),負(fù)載均衡,訪問控制,服務(wù)監(jiān)控,端到端加密等問題。

本文的目的在于描述基于Service的k8s網(wǎng)絡(luò)的原理,首先,我們需要了解以下概念:

  • Pod & Deployment & EndPoints
  • Service
    • ClusterIP
    • Headless
    • NodePort
    • LoadBalancer
    • ExternalName
  • Ingress
  • ServiceMesh

Pod

Pod是K8S的基本調(diào)度單位,我們可以理解其為一組共享命名空間的容器,這一組容器共享PID,IPC,網(wǎng)絡(luò),存儲(chǔ),UTC命名空間,相互之間可以通過localhost訪問,訪問相同的文件等。每個(gè)Pod中的容器會(huì)共用一個(gè)IP地址,該IP地址由K8S底層的網(wǎng)絡(luò)方案決定如何分配。

一般來說,Pod不會(huì)被單獨(dú)使用,一般會(huì)通過Deployment對(duì)象進(jìn)行編排,以實(shí)現(xiàn):

  • 定制Pod的版本,副本數(shù)
  • 通過k8s狀態(tài)控制器恢復(fù)失敗的Pod
  • 通過控制器完成指定的策略控制,如滾動(dòng)更新,重新生成,回滾等
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      run: my-nginx
  replicas: 2
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: nginx
        ports:
        - containerPort: 80

上面是官網(wǎng)上的一個(gè)deployment的例子,其中可以看到,其設(shè)置了一個(gè)名為my-nginx的deployment,副本數(shù)為2,其模板是一個(gè)名為my-nginx的pod。

k8s-Service.png

這里其實(shí)隱含了一個(gè)EndPoint對(duì)象,在k8s中我們一般不會(huì)直接與EndPoint對(duì)象打交道。EndPoint代表的是一組可供訪問的Pod對(duì)象,在創(chuàng)建deployment的過程中,后臺(tái)會(huì)自動(dòng)創(chuàng)建EndPoint對(duì)象。kube-proxy會(huì)監(jiān)控Service和Pod的變化,創(chuàng)建相關(guān)的iptables規(guī)則從網(wǎng)絡(luò)層對(duì)數(shù)據(jù)包以路由的方式做負(fù)載均衡。

Service

Pod是可變且易變,如果像傳統(tǒng)的開發(fā)中使用IP地址來標(biāo)識(shí)是不靠譜的,需要一個(gè)穩(wěn)定的訪問地址來訪問Pod的實(shí)例,在K8S中使用Service對(duì)象來實(shí)現(xiàn)這一點(diǎn)。服務(wù)與服務(wù)之間的訪問,會(huì)通過服務(wù)名進(jìn)行。通過配置Service對(duì)象,K8S會(huì)在內(nèi)部DNS系統(tǒng)(默認(rèn)coreDNS)中添加相關(guān)的PTR與反向PTR。在集群內(nèi)部任何地方,可以通過服務(wù)名來訪問相關(guān)的服務(wù)。

apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx

以上的yaml文件創(chuàng)建了一個(gè)名為my-nginx的service,關(guān)聯(lián)到之前創(chuàng)建的名為my-nginx的pod組。

$ kubectl get svc my-nginx
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
my-nginx   ClusterIP   10.0.162.149   <none>        80/TCP    21s

$ kubectl describe svc my-nginx
Name:                my-nginx
Namespace:           default
Labels:              run=my-nginx
Annotations:         <none>
Selector:            run=my-nginx
Type:                ClusterIP
IP:                  10.0.162.149
Port:                <unset> 80/TCP
Endpoints:           10.244.2.5:80,10.244.3.4:80
Session Affinity:    None
Events:              <none>

以上例子創(chuàng)建了一個(gè)ClusterIP類型的Service,這是Service的默認(rèn)類型,ClusterIP是一個(gè)虛擬IP,kube-proxy在系統(tǒng)層面以iptables 路由規(guī)則或者LVS的形式,將訪問發(fā)送給后端的pod。在K8S環(huán)境中,也是可以顯示指定不生產(chǎn)ClusterIP,這種情況被稱為Headless Service。Service共有有5種類型:

  • Headless
  • ClusterIP
  • NodePort
  • LoadBalance
  • ExternalService

ClusterIP的原理上面已經(jīng)大致提過。Headless Service就是上面提到的不設(shè)置ClusterIP的情況。在配置ClusterIP的情況下,CoreDNS中的記錄的Service名指向的是ClusterIP,而在不設(shè)置ClusterIP的情況下,CoreDNS中的Service名會(huì)直接指向多個(gè)后臺(tái)Pod的地址。

其中,NodePort是在將某Service暴露到集群外部的情況下使用。在設(shè)置Service類型為NodePort的情況下,會(huì)在每個(gè)Node上設(shè)置NAT規(guī)則暴露服務(wù)。如下圖所示:

k8s-NodePort.png

集群的80端口被映射為ServiceA,那么集群的兩個(gè)對(duì)外IP:222.111.98.2和222.111.98.3的80端口都可以開啟,可以通過這兩個(gè)IP:port在集群外訪問ServiceA。

apiVersion: v1
kind: Service
metadata:
  name: ServiceA
spec:
  ports:
  - port: 3000
    protocol: TCP
    targetPort: 443
    nodePort: 80
  selector:
    run: PodA
  type: NodePort

同時(shí),節(jié)點(diǎn)也有內(nèi)部節(jié)點(diǎn),內(nèi)部的Pod可以從內(nèi)部節(jié)點(diǎn)的80端口來訪問ServiceA。

LoadBalancer指的是外部的Loadbalancer,即云平臺(tái)提供者的集群外部負(fù)載均衡服務(wù)。除了NodePort所做的事情之外,LoadBalancer對(duì)象會(huì)將集群所有對(duì)外接口的IP地址提供給外部Loadbalancer服務(wù)。

k8s-LoadBalancer.png

還有一個(gè)是ExternalName,主要用來讓內(nèi)部POD訪問外部服務(wù)。

apiVersion: v1
kind: Service
metadata:
  name: External-ElasticSearch
  namespace: prod
spec:
  type: ExternalName
  externalName: my.elasticsearch.org:9200

上面是官方文檔中的一個(gè)例子,該例子里面,將一個(gè)外部的服務(wù) my.database.example.com映射為一個(gè)內(nèi)部的服務(wù)名my-service,內(nèi)部POD可以用my-service來訪問該外部服務(wù)。

k8s-External Service.png

通過External-Service,我們可以把一些有狀態(tài)的資源如各類數(shù)據(jù)庫進(jìn)行外置,集群內(nèi)部的各Pod可以通過其進(jìn)行訪問。

Ingress

以上NodePort以及LoadBalancer實(shí)現(xiàn)的是從外部對(duì)機(jī)器內(nèi)部進(jìn)行L4網(wǎng)絡(luò)訪問。在公有云環(huán)境中,LoadBalancer需要配合云服務(wù)提供商的負(fù)載均衡器使用,每個(gè)暴露出去的服務(wù)需要搭配一個(gè)負(fù)載均衡器,代價(jià)比較昂貴。那有沒有比較便宜一些的解決方案呢?答案是Ingress對(duì)象。在集群內(nèi)部搭建Ingress服務(wù),提供反向代理能力,實(shí)現(xiàn)負(fù)載均衡能力,一方面對(duì)基于http的流量可以進(jìn)行更細(xì)粒度的控制,此外,不需要額外的LoadBalancer設(shè)備的費(fèi)用。Ingress對(duì)象需要配合Ingress controller來進(jìn)行使用。

從使用方面來講,Ingress對(duì)象是集群級(jí)別的metadata,其定義了路由規(guī)則,證書等Ingress Controller所需的參數(shù)。Ingress Controller是實(shí)際執(zhí)行這些參數(shù)的實(shí)例。從面向?qū)ο蟮慕嵌葋碚f,Ingress是接口,Ingress Controller是其對(duì)應(yīng)的實(shí)現(xiàn)。接口只有一個(gè),實(shí)現(xiàn)可以有很多。實(shí)際上來講,Ingress Controller的實(shí)現(xiàn)有很多,比如Nginx Ingress,Traefik,envoy等。集群內(nèi)可能存在多個(gè)Ingress對(duì)象和Ingress Controller,Ingress對(duì)象可以指定使用某個(gè)Ingress Controller。

從功能上來看,Ingress一般會(huì)具有以下能力:對(duì)外提供訪問入口,TLS卸載,http路由,負(fù)載均衡,身份認(rèn)證,限流等,這些基本上都是針對(duì)http協(xié)議提供的。這些能力會(huì)通過Ingress Controller提供。

Ingress Controller需要部署為Service或者DaemonSet,一般來說有三種部署方式:

  1. 以Deployment方式部署為LoadBalancer類型的Service,一般在公有云場(chǎng)景下使用,相應(yīng)云服務(wù)商會(huì)提供外置的LoadBalancer將流量分發(fā)到Ingress Controller的地址上。
  2. 以Deployment方式部署為NodePort類型的Service,這種情況下會(huì)在所有節(jié)點(diǎn)上打開相應(yīng)端口,這種情況,外部訪問會(huì)比較麻煩,一般來說需要外置一個(gè)Ngnix或者其他類型的負(fù)載均衡器來分發(fā)請(qǐng)求,這樣其實(shí)就變得和情況1類似,但是可能會(huì)更麻煩。
  3. 以DaemonSet的形式部署在指定的幾個(gè)對(duì)外節(jié)點(diǎn)上。在部署過程中,可以給邊緣節(jié)點(diǎn)打上相應(yīng)的tag,在配置是使用NodeSelector來指定邊緣節(jié)點(diǎn),在每個(gè)邊緣節(jié)點(diǎn)上部署Ingress Controller。外界可以通過這些邊緣節(jié)點(diǎn)上的Ingress Controller來訪問集群內(nèi)部的服務(wù)。

總體而言,Ingress的作用是對(duì)外部訪問進(jìn)行細(xì)粒度的網(wǎng)絡(luò)控制。目前市面上有幾種產(chǎn)品形態(tài),如API Gateway以及ServiceMesh Gateway都可以在這個(gè)生態(tài)位工作。

ServiceMesh

在原生k8s環(huán)境中,kube-proxy組件會(huì)通過watch-list接口觀察Service和EndPoints的變化,動(dòng)態(tài)調(diào)整iptables/LVS規(guī)則,實(shí)現(xiàn)端到端的請(qǐng)求路由。在ServiceMesh環(huán)境中,會(huì)在每個(gè)POD中注入一個(gè)Sidecar容器來實(shí)現(xiàn)流量代理能力。在istio中,往往會(huì)使用強(qiáng)大的envoy來完成這件事情。所有的這一切都是通過修改底層基礎(chǔ)設(shè)施來完成的,上層應(yīng)用不感知。

arch-sec.png

上圖是istio mesh的架構(gòu)圖,在基于istio的service mesh中,底層的Service和EndPoints還是基于k8s的原生機(jī)制,但是編排能力由istio進(jìn)行了接管,原生的基于kube-proxy的網(wǎng)絡(luò)層編排,變成了有istio控制面進(jìn)行編排,各個(gè)Pod中的SideCar代理同步配置。POD與POD之間的流量由SideCar代理進(jìn)行了劫持,變成了代理與代理之間端到端的流量通訊。

基于這樣的機(jī)制,一方面出入的流量都會(huì)經(jīng)過envoy代理,對(duì)比僅僅由內(nèi)核iptables和LVS進(jìn)行轉(zhuǎn)發(fā),envoy代理有更多機(jī)會(huì)和更強(qiáng)大的能力對(duì)流量進(jìn)行控制。代理與代理之間的通信可以進(jìn)行加密,解決了以往站內(nèi)通信都是明文的問題。

同樣,我們可以看到,由于出入都需要代理,天生比僅通過內(nèi)核轉(zhuǎn)發(fā)多出了兩個(gè)用戶態(tài)的環(huán)節(jié),在性能上必然有一定的衰減。

envoy-sidecar-traffic-interception-20190111.png

借用上面一張圖,我們可以看到,進(jìn)入POD的請(qǐng)求,首先是在內(nèi)核態(tài)的PREROUTING鏈上進(jìn)行判斷,符合條件的流量會(huì)被導(dǎo)入到ISTIO_INBOUND鏈,然后被重定向到用戶態(tài)的Envoy SideCar進(jìn)行處理,處理完成之后,被重新注回到內(nèi)核,再被重新定向到用戶態(tài)的真實(shí)的APP中進(jìn)行業(yè)務(wù)上的處理。其返回?cái)?shù)據(jù),同樣先進(jìn)入內(nèi)核然后被劫持到用戶態(tài)SideCar中處理,最后又被重新發(fā)回內(nèi)核態(tài)發(fā)生出去。

在兩個(gè)被SideCar代理托管的服務(wù)之間通信,需要被SideCar代理處理4次,數(shù)據(jù)包有8次內(nèi)核態(tài)與用戶態(tài)之間的切換,其帶來的延時(shí)可想而知。安全從來都不是沒有代價(jià)的,需要從性能,部署復(fù)雜度等方面進(jìn)行取舍。

小結(jié)

本文介紹了k8s環(huán)境下,基于服務(wù)的網(wǎng)絡(luò)原理。并且比較了k8s原生的和基于服務(wù)網(wǎng)格的服務(wù)架構(gòu)。兩者對(duì)比如下,供參考:

k8s原生 Istio Mesh
服務(wù)間路由編排 kube-proxy lstio控制面
負(fù)載均衡 iptables/lvs envoy-sidecar
端到端加密 App自己實(shí)現(xiàn) mTLS in envoy-sidecar
證書管理 第三方 istio citadel
從外到內(nèi)訪問 API-Gateway,Ingress,NodePort,LoadBalancer Istio-gateway
監(jiān)控及可視化 第三方工具及相關(guān)應(yīng)用打點(diǎn) 通過Sidecar無縫進(jìn)行

ServiceMesh在給我們帶來很多便利的同時(shí),也帶來了性能上的降低,以及ServiceMesh本身的復(fù)雜性。在選型過程中,需要注意結(jié)合自身情況作出選擇。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容