云原生環境網絡方案2 --- Service和Service Mesh

云原生環境網絡方案2 --- Service和Service Mesh

在之前的文章中,我們描述了容器環境的底層網絡實現原理以及一些常見的k8s網絡插件。這些組件利用Linux系統的內核網絡協議棧完成容器與容器之間網絡互連的工作。到了這一步,我們僅僅解決了容器與容器之間互連,在微服務架構中,網絡層面的互連僅僅是基礎,我們還需要從網絡層面解決:服務發現,負載均衡,訪問控制,服務監控,端到端加密等問題。

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

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

Pod

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

一般來說,Pod不會被單獨使用,一般會通過Deployment對象進行編排,以實現:

  • 定制Pod的版本,副本數
  • 通過k8s狀態控制器恢復失敗的Pod
  • 通過控制器完成指定的策略控制,如滾動更新,重新生成,回滾等
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

上面是官網上的一個deployment的例子,其中可以看到,其設置了一個名為my-nginx的deployment,副本數為2,其模板是一個名為my-nginx的pod。

k8s-Service.png

這里其實隱含了一個EndPoint對象,在k8s中我們一般不會直接與EndPoint對象打交道。EndPoint代表的是一組可供訪問的Pod對象,在創建deployment的過程中,后臺會自動創建EndPoint對象。kube-proxy會監控Service和Pod的變化,創建相關的iptables規則從網絡層對數據包以路由的方式做負載均衡。

Service

Pod是可變且易變,如果像傳統的開發中使用IP地址來標識是不靠譜的,需要一個穩定的訪問地址來訪問Pod的實例,在K8S中使用Service對象來實現這一點。服務與服務之間的訪問,會通過服務名進行。通過配置Service對象,K8S會在內部DNS系統(默認coreDNS)中添加相關的PTR與反向PTR。在集群內部任何地方,可以通過服務名來訪問相關的服務。

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

以上的yaml文件創建了一個名為my-nginx的service,關聯到之前創建的名為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>

以上例子創建了一個ClusterIP類型的Service,這是Service的默認類型,ClusterIP是一個虛擬IP,kube-proxy在系統層面以iptables 路由規則或者LVS的形式,將訪問發送給后端的pod。在K8S環境中,也是可以顯示指定不生產ClusterIP,這種情況被稱為Headless Service。Service共有有5種類型:

  • Headless
  • ClusterIP
  • NodePort
  • LoadBalance
  • ExternalService

ClusterIP的原理上面已經大致提過。Headless Service就是上面提到的不設置ClusterIP的情況。在配置ClusterIP的情況下,CoreDNS中的記錄的Service名指向的是ClusterIP,而在不設置ClusterIP的情況下,CoreDNS中的Service名會直接指向多個后臺Pod的地址。

其中,NodePort是在將某Service暴露到集群外部的情況下使用。在設置Service類型為NodePort的情況下,會在每個Node上設置NAT規則暴露服務。如下圖所示:

k8s-NodePort.png

集群的80端口被映射為ServiceA,那么集群的兩個對外IP:222.111.98.2和222.111.98.3的80端口都可以開啟,可以通過這兩個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

同時,節點也有內部節點,內部的Pod可以從內部節點的80端口來訪問ServiceA。

LoadBalancer指的是外部的Loadbalancer,即云平臺提供者的集群外部負載均衡服務。除了NodePort所做的事情之外,LoadBalancer對象會將集群所有對外接口的IP地址提供給外部Loadbalancer服務。

k8s-LoadBalancer.png

還有一個是ExternalName,主要用來讓內部POD訪問外部服務。

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

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

k8s-External Service.png

通過External-Service,我們可以把一些有狀態的資源如各類數據庫進行外置,集群內部的各Pod可以通過其進行訪問。

Ingress

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

從使用方面來講,Ingress對象是集群級別的metadata,其定義了路由規則,證書等Ingress Controller所需的參數。Ingress Controller是實際執行這些參數的實例。從面向對象的角度來說,Ingress是接口,Ingress Controller是其對應的實現。接口只有一個,實現可以有很多。實際上來講,Ingress Controller的實現有很多,比如Nginx Ingress,Traefik,envoy等。集群內可能存在多個Ingress對象和Ingress Controller,Ingress對象可以指定使用某個Ingress Controller。

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

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

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

總體而言,Ingress的作用是對外部訪問進行細粒度的網絡控制。目前市面上有幾種產品形態,如API Gateway以及ServiceMesh Gateway都可以在這個生態位工作。

ServiceMesh

在原生k8s環境中,kube-proxy組件會通過watch-list接口觀察Service和EndPoints的變化,動態調整iptables/LVS規則,實現端到端的請求路由。在ServiceMesh環境中,會在每個POD中注入一個Sidecar容器來實現流量代理能力。在istio中,往往會使用強大的envoy來完成這件事情。所有的這一切都是通過修改底層基礎設施來完成的,上層應用不感知。

arch-sec.png

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

基于這樣的機制,一方面出入的流量都會經過envoy代理,對比僅僅由內核iptables和LVS進行轉發,envoy代理有更多機會和更強大的能力對流量進行控制。代理與代理之間的通信可以進行加密,解決了以往站內通信都是明文的問題。

同樣,我們可以看到,由于出入都需要代理,天生比僅通過內核轉發多出了兩個用戶態的環節,在性能上必然有一定的衰減。

envoy-sidecar-traffic-interception-20190111.png

借用上面一張圖,我們可以看到,進入POD的請求,首先是在內核態的PREROUTING鏈上進行判斷,符合條件的流量會被導入到ISTIO_INBOUND鏈,然后被重定向到用戶態的Envoy SideCar進行處理,處理完成之后,被重新注回到內核,再被重新定向到用戶態的真實的APP中進行業務上的處理。其返回數據,同樣先進入內核然后被劫持到用戶態SideCar中處理,最后又被重新發回內核態發生出去。

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

小結

本文介紹了k8s環境下,基于服務的網絡原理。并且比較了k8s原生的和基于服務網格的服務架構。兩者對比如下,供參考:

k8s原生 Istio Mesh
服務間路由編排 kube-proxy lstio控制面
負載均衡 iptables/lvs envoy-sidecar
端到端加密 App自己實現 mTLS in envoy-sidecar
證書管理 第三方 istio citadel
從外到內訪問 API-Gateway,Ingress,NodePort,LoadBalancer Istio-gateway
監控及可視化 第三方工具及相關應用打點 通過Sidecar無縫進行

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

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,443評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,530評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,407評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,981評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,759評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,204評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,263評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,415評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,955評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,650評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,892評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,675評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容