Spring Cloud + Kubernetes 微服務框架原理和實踐

早在半年前,公司開始推行容器化部署方案 AppOS,雖然發佈界面過於極客,十分晦澀,不過仔細研究起來真的覺得十分強大,容器化推行後,計算資源(CPU、內存)的利用率可以極大提高,降低服務器數量,從而節約技術成本。

恰巧,若干個朋友所在創業公司最近也在嘗試做微服務、容器化。架構上摒棄 SOA 的 dubbo,加入Spring Cloud陣營;部署方案上從過去的雲服務器直接部署,升級到基於Kubernetes集羣的容器化部署。

Spring Cloud

微服務這個概念從開發者的視角理解和SOA的差異不大,按照業務領域細粒度的拆分系統爲若干服務,服務僅訪問對應的數據庫,按照項目組,服務獨立開發,部署和迭代。服務之間的調用,通過RPC完成。

用幾張圖,直觀、簡明扼要的闡述一下在Spring Cloud中相關的概念~

上圖中,展示了一個簡單的系統,該系統有幾個組件。

  1. 服務提供方,Demo Service,從開發者的視角看,它是一個獨立的項目(或者是子項目),它只提供接口聲明給外部,它運行起來是一個獨立的進程,好像一個 Web Server一樣,在此等候遠端的調用。圖中畫了三個一摸一樣的用來描述它的部署情況,三個服務進程分別位於三臺主機上,高可用(一個掛了,不影響所有),可伸縮(可以增加到十臺),它訪問自己對應的數據庫。
  2. 服務消費方,Demo Consumer,爲了簡化只畫了一個實例,和服務提供方一樣,它也可以高可用,可伸縮。因爲服務提供方和消費方,部署在不同的主機上,所以他們之間的調用使用RPC(遠程服務調用)
  3. 註冊中心,Eureka-Server,既然有服務提供者和服務消費者,而他們都是運行在不同主機上,那如何讓服務消費者發現,並按照相應的協議調用服務提供者呢,這就引入了註冊中心的概念。如果讀者有dubbo使用經驗,很容易想到 zookeeper 集羣對吧,他們提供的功能是類似的。
    當然它的部署也是支持高可用的(多個實例註冊組成集羣),三個核心組件已經浮出水面了,
  4. 路由,Zuul,在圖的最上側。也是整體架構開發在外網的入口。通過 url 規則配置,可以把請求轉發到合適的服務,例如請求 GET api.dummy.com/demo_consumer/user/1 通過Zuul,可以把請求轉發到 demo-consumer:GET /user/1。當然Zuul還可以支持更多,包括通用的鑑權,過濾器等。

核心組件中涉及到服務消費方和服務提供方是通過RPC調用實現的,通過註冊中心,服務消費方發現服務提供方,順其自然就引入了客戶端負載均衡和熔斷相關的概念。消費方手持若干個提供方的實例,最簡單的方式就是輪流調用,這就是客戶端負載均衡了;如果一個服務提供方在過去一段時間內,故障比例達到閾值,那麼可以暫時設置它爲不可用,這就是熔斷了。在Spring Cloud裏提供了相關的內置組件,Ribbon和Hystrix。

當然一切都不絕對,Spring Cloud的一個優勢就是社區裏有很多兼容性良好的備選方案,在 musical.ly 的Spring Cloud架構實踐中提到:團隊對框架本身做了較多的改造,替換了更友好的註冊中心 Consul,採用了 gRPC 作爲遠程調用框架,用 Protobuf 作爲序列化框架,替換了熔斷和限流方案,集成了故障診斷和追蹤功能等等,這些改造對業務是透明的。

微服務的部署

採用Spring Cloud後,不同業務可以拆分成不同的項目,都可以單獨部署。可以使用Jenkins搭建一套簡單的持續集成和持續交付方案。開發人員推代碼到Git倉庫後,會觸發Jenkins的構建動作,進一步的還可以用Jenkins執行不同環境下的發佈腳本,當然腳本還可以執行備份,以及回滾的動作。

執行到這裏,該體系方案就可以支持一個公司走很遠了,那Kubernetes又有什麼勇武之地。

假設公司進一步發展,流量和業務都極具增多,會出現兩個比較常見的問題

  1. 擴容動作依然有些麻煩,可以通過預先準備好的操作系統鏡像(包括各種線上運行環境),把新的實例快速準備好,但是依然需要更新發布腳本。當然如果有強大的運維團隊,是可以做到幾乎自動化的。
  2. 大量的資源浪費,因爲有很多服務,訪問量很小,大量的機器可能CPU使用率不足5%這樣的case時有發生(來自騰訊的同事分享說,他們的優化目標是CPU利用率平均30%),造成技術成本巨高不下。

理想的狀況下,如果把運維手裏的機器,都通過一個入口、統一管理起來,統一掌握集羣的資源使用,需要對集羣擴容或縮容的情況,只要增加或者回收服務器;需要對某個服務擴容縮容,只要簡單的設置一下 replicas 數量。那該多好。(當然Kubernetes遠遠比這個功能多的多)

Kubernetes(K8S)

如果有過Docker的使用經驗,就很容易理解K8S,最初使用docker的用途可能僅僅是用它搭建CI/CD,一條命令啓動Gitlab,再一條命令啓動一個Jenkins,一切超級簡單。很多教程裏,都會把若干微服務,放在一臺服務器中的docker裏運行,你會發現服務註冊,服務發現都很簡單。

但是,當容器運行在不同的服務器上的時候,問題就來了,你甚至發現跨主機都容器之間都不能通信。

K8S來源於谷歌,高富帥的出身,決定了剛出道就自帶各種光環。市場佔有率已經超過70%,已經成爲了容器管理的主流工具。在實踐中,因爲大量的資料實踐的背景都是GPE上的K8S集羣,網絡、存儲等基礎設施都由平臺提供,一切都覺得好簡單,但是一旦嘗試自建私有的K8S集羣,卻發現世界卻充滿敵意,甚至基本的網絡插件都需要自己安裝。

谷歌雖好,並且提供 $300 的代金券和一年試用期很厚道,但是谷歌不是你想訪問就可以訪問。幸好阿里雲也提供了Kubernetes集羣服務,雖然價格比買ECS貴,不過相比一個運維團隊的開銷和各種不斷踩坑感覺也是划算的。

本文先介紹一些基礎概念,然後介紹如果在阿里雲的K8S集羣上,部署Spring Cloud的微服務的實踐。

集羣

集羣是一組節點,這些節點可以是物理服務器或者虛擬機,之上安裝了Kubernetes平臺。下圖展示這樣的集羣。注意該圖爲了強調核心概念有所簡化。這裏可以看到一個典型的Kubernetes架構圖。

Pod

K8S中最基礎的調度單位是Pod,它有網絡,有存儲。Pod裏面運行着一個或者若干個docker容器。同一個Pod裏的容器共享同一個網絡命名空間,可以使用localhost互相通信。可以理解成Pod就是一臺主機,docker容器是運行在主機上的進程。

Replication Controller

我們一般不會手動自己創建Pod,這樣很難管理。利用Replication Controller,可以定義Pod運行內容,副本的個數等信息,它的升級版本是 ReplicaSet。現在已經創建了Pod的一些副本,那麼在這些副本上如何均衡負載呢?我們需要的是Service。

Service

可以把一組Pod組成服務 Service,Service有一個虛擬的ClusterIP,服務訪問可以通過ClusterIP作爲統一請求入口,因爲一個 Service 對應一組Pod,所以可以做到負載均衡。服務可以通過 NodePort,LoadBalancer的方式暴露對外服務。注意 type = LoadBalancer需要雲服務平臺提供基礎的服務,自建的K8S集羣默認是沒有這個東西的。如果在阿里雲上定義服務 type = LoadBalancer 後,你會發現,在管理後臺的負載均衡頁面,會增加一個負載均衡器

kubectl get service 執行結果,注意External-IP
自動創建負載均衡器對外提供統一入口,backend對應容器Pod

實踐

爲了降低成本,筆者從阿里雲採購了最低配置的K8S集羣,包含 3個Master節點,還有2個Node節點。基本都是最低配置,每天成本30塊錢。預先準備好了一份手腳架代碼,包括幾個基本項目

  • demo-service 服務提供方
  • demo-provider 服務消費方
  • eureka-server 註冊中心
  • api-gateway 網關

需要首先部署註冊中心 eureka-server, 然後部署服務提供方 demo-service 和 消費方demo-provider,最後部署 api-gateway。

那麼手裏是代碼,對面是K8S集羣,怎麼部署上去呢,答案是 鏡像服務。
阿里的鏡像服務是一個選擇,當然也可以選擇其它的,可以通過CI/CD方案,自動把構建後的鏡像,Tag後,推到鏡像服務提供的Registry中,然後就可以使用了。

例如在鏡像倉庫中,有如下鏡像:registry.cn-beijing.aliyuncs.com,通過書寫YAML文件,定義RC

apiVersion: v1
kind: ReplicationController
metadata:
 name: demo-service
spec:
 replicas: 2
 selector:
  app: demo-service
 template:
  metadata:
   labels:
    app: demo-service
  spec:
   containers:
    - name: demo-service
      image: registry.cn-beijing.aliyuncs.com/tianming/demo-service:latest
      ports:
      - containerPort: 8081

黑色字體部分,將要發佈服務的鏡像了,這裏設置了副本數是 2,通過執行下面的bash命令就可以創建 RC了

kubectl create -f demo-service-rc.yaml

然後可以執行

kubectl get pods

查看容器是否被正確創建,如果pod有狀態異常,例如 Error 等可以通過describe命令查看創建失敗的原因,這個命令很有用,可以幫我們搞定很多問題。

kubectl describe pod demo-service-xxx

當然這還不夠,我們還需要定義服務,以及服務暴露的接口:

apiVersion: v1
kind: Service
metadata:
 name: demo-service
spec:
 type: LoadBalancer
 ports:
  - port: 8081
 selector:
  app: demo-service

這樣服務就建立好,因爲設置了 LoadBalancer,所以可以通過 external ip 在外部網絡訪問到。在 Prod 環境中,我們不會這樣做,一般只有 api-gateway 項目纔會對外暴露訪問端口。

按照這樣的方式,依次部署其它服務,如果有一套可行的 CI/CD 方案,那麼後續的發佈,擴容縮容,都將易如反掌。

碎碎念

如果你是一個初創公司的CTO,沒什麼人手搭建集羣,自己也沒有精力學習K8S。在架構選型上,可以只用Spring Cloud的微服務組件,然後在雲主機上部署;如果你有能力學習K8S,但是沒有精力和人力搭建自建K8S集羣,可以購買雲廠商的集羣服務,有了這套東西,至少再也不用擔心未來擴容的痛苦了,並且作爲架構的發展的相對終極形態,短期內也不會有重構的需求,等未來有人力財力,再遷回自建的K8S集羣,也是易如反掌。

總體來講,文章還是有些淺了,實踐中的坑和優化的選項,還是要遠遠大於此文的。

傳送門:https://zhuanlan.zhihu.com/p/31670782

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章