Service Mesh 現在如此火熱,你瞭解多少?

NO IMAGE

作者:楊章顯,思科高階系統工程師,多年企業級線上會議系統的運維以及軟體釋出、變更管理經驗,熟悉容器技術、容器編排、自動化運維、部署、監控等,目前為思科內部容器 PaaS 主要負責人,負責技術選型、實現、落地等工作。

Service Mesh 是什麼?

關於 Service Mesh,Buoyant 創始人 William Morgan 如是說:

A service mesh is a dedicated infrastructure layer for handling service-to-service communication. It’s responsible for the reliable delivery of requests through the complex topology of services that comprise a modern, cloud native application. In practice, the service mesh is typically implemented as an array of lightweight network proxies that are deployed alongside application code, without the application needing to be aware.

對此,可總結 Service Mesh 為:

  • 專用基礎設施層:獨立的執行單元。
  • 包括資料層和控制層:資料層負責交付應用請求,控制層控制服務如何通訊。
  • 輕量級透明代理:實現形式為輕量級網路代理。
  • 處理服務間通訊:主要目的是實現複雜網路中服務間通訊。
  • 可靠地交付服務請求:提供網路彈性機制,確保可靠交付請求。
  • 與服務部署一起,但服務無需感知:儘管跟應用部署在一起,但對應用是透明的。

Service Mesh 能做什麼?

Service Mesh 作為透明代理,它可以執行在任何基礎設施環境,而且跟應用非常靠近,那麼,Service Mesh 能做什麼呢?

  • 負載均衡:執行環境中微服務例項通常處於動態變化狀態,而且經常可能出現個別例項不能正常提供服務、處理能力減弱、卡頓等現象。但由於所有請求對 Service Mesh 來說是可見的,因此可以通過提供高階負載均衡演算法來實現更加智慧、高效的流量分發,降低延時,提高可靠性。

  • 服務發現:以微服務模式執行的應用變更非常頻繁,應用例項的頻繁增加減少帶來的問題是如何精確地發現新增例項以及避免將請求傳送給已不存在的例項變得更加複雜。Service Mesh 可以提供簡單、統一、平臺無關的多種服務發現機制,如基於 DNS,K/V 鍵值對儲存的服務發現機制。

  • 熔斷:動態的環境中服務例項中斷或者不健康導致服務中斷可能會經常發生,這就要求應用或者其他工具具有快速監測並從負載均衡池中移除不提供服務例項的能力,這種能力也稱熔斷,以此使得應用無需消耗更多不必要的資源不斷地嘗試,而是快速失敗或者降級,甚至這樣可避免一些潛在的關聯性錯誤。而 Service Mesh 可以很容易實現基於請求和連線級別的熔斷機制。

  • 動態路由:隨著服務提供商以提供高穩定性、高可用性以及高 SLA 服務為主要目標,為了實現所述目標,出現各種應用部署策略儘可能從技術手段達到無服務中斷部署,以此避免變更導致服務的中斷和穩定性降低,例如:Blue/Green 部署、Canary 部署,但是實現這些高階部署策略通常非常困難。關於應用部署策略,可參考 Etienne Tremel
    的文章,他對各種部署策略做了詳細的比較。而如果運維人員可以輕鬆的將應用流量從 staging 環境到產線環境,一個版本到另外一個版本,更或者從一個資料中心到另外一個資料中心進行動態切換,甚至可以通過一箇中心控制層控制多少比例的流量被切換。那麼 Service Mesh 提供的動態路由機制和特定的部署策略如 Blue/Green 部署結合起來,實現上述目標更加容易。

  • 安全通訊:無論何時,安全在整個公司、業務系統中都有著舉足輕重的位置,也是非常難以實現和控制的部分。而微服務環境中,不同的服務例項間通訊變得更加複雜,那麼如何保證這些通訊是在安全、授權情況下進行非常重要。通過將安全機制如 TLS 加解密和授權實現在 Service Mesh 上,不僅可以避免在不同應用的重複實現,而且很容易在整個基礎設施層更新安全機制,甚至無需對應用做任何操作。

  • 多語言支援:由於 Service Mesh 作為獨立執行的透明代理,很容易支援多語言。

  • 多協議支援:同多語言支援一樣,實現多協議支援也非常容易。

  • 指標和分散式追蹤:Service Mesh 對整個基礎設施層的可見性使得它不僅可以暴露單個服務的執行指標,而且可以暴露整個叢集的執行指標。

  • 重試和最後期限:Service Mesh 的重試功能避免將其嵌入到業務程式碼,同時最後期限使得應用允許一個請求的最長生命週期,而不是無休止的重試。

業界 Service Mesh 產品

當前,業界主要有以下相關產品:

  • Buoyant 的 linkerd,基於 Twitter 的 Fingle,長期的實際產線執行經驗及驗證,支援 Kubernetes,DC/OS 容器管理平臺,CNCF 官方支援的專案之一。

  • Lyft 的 Envoy,7層代理及通訊匯流排,支援7層 HTTP 路由、TLS、gRPC、服務發現以及健康監測等,也是 CNCF 官方支援專案之一。

  • IBM、Google、Lyft 支援的 Istio,一個開源的微服務連線、管理平臺以及給微服務提供安全管理,支援 Kubernetes、Mesos 等容器管理工具,其底層依賴於 Envoy。

我們需要 Service Mesh 嗎?

前面我們已經講述了 Service Mesh 帶來的各種好處,解決各種問題,作為下一代微服務的風口,Service Mesh 可以使得快速轉向微服務或者雲原生應用,以一種自然的機制擴充套件應用負載,解決分散式系統不可避免的部分失敗,捕捉分散式系統動態變化, 完全解耦於應用等等。

我相信 Service Mesh 在微服務或者雲原生應用領域一定別有一番天地,下面我們開始講述 Service Mesh 的一個實現:linkerd。

什麼是 linkerd?

linkerd 是 Buoyant 開發的快速、輕量級、高效能的,每秒以最小的時延及負載處理萬級請求,易於水平擴充套件,經過產線測試及驗證的 Service Mesh 工具,其官方定義為:

linker∙d is a transparent proxy that adds service discovery, routing, failure handling, and visibility to modern software applications.

我們可總結為:

  • 為雲原生應用提供彈性的 Service Mesh。

  • 透明高效能網路代理。

  • 提供服務發現機制、動態路由、錯誤處理機制及應用執行時視覺化。

linkerd 主要功能

這裡寫圖片描述

如圖所示,linkerd 提供;

1、基於感知時延的負載均衡

  • 由於 linkerd 工作於 RPC 層,它能實時觀測到所有 RPC 請求延時、佇列裡即將要處理請求的數量,因此可基於實時效能資料分發請求,相對於傳統啟發式負載均衡演算法如 LRU、TCP 活動情況等,這種分發機制效能更優,可儘可能降低延時,提高穩定性。
  • 提供多種負載均衡演算法如: Power of Two Choices (P2C): Least LoadedPower of Two Choices: Peak EWMAAperture: Least LoadedHeap: Least Loaded以及Round-Robin

2、執行時動態路由

  • 支援基於請求級別路由,可在 HTTP 請求裡嵌入特定包頭,linkerd 將匹配該包頭資訊的請求路由到特定的應用例項。
  • 動態修改路由規則實現流量遷移、Blue/Green 部署、Canary 部署、跨資料中心 failover 等。

3、熔斷機制

  • 快速失敗(Fail Fast):基於連線的熔斷器,如果 linkerd 觀測到訪問某個應用例項時有連線錯誤,linkerd 將該例項從維護的連線池移除,與此同時,linkerd 在後臺不斷嘗試與移除的例項建立連線,一旦確認已經恢復,linkerd 重新將其加入連線池。
  • 失敗計提(Failure Accrual):基於請求的熔斷器,如果 linkerd 觀測到訪問某個應用例項時,指定數目請求連續失敗,linkerd 將該例項標註為死亡狀態,不再接受新的請求嘗試。而在後臺,linkerd 基於設定的退避間隔傳送請求以驗證例項是否恢復,一旦恢復,便可接受新的請求。

4、插入式服務發現

支援各種服務發現機制如:基於檔案(File-based)、DNS 以及基於 KV 鍵值對的 Zookeeper、Consul 及 Kubernetes,可以很方便的接入現有或者新的服務發現工具。

5、指標及分散式追蹤

  • 作為透明代理使 linkerd 第一時間知道從單個到整個叢集應用例項的執行時指標。
  • 除此之外,也可以跟各種分散式追蹤系統整合,如 Zipkin,無需對應用做任何調整。

除此之外,linkerd 支援任意開發語言和多種協議如 HTTP/1.1、HTTP/2、gRPC、Thrift、Mux。

安裝 linkerd

關於如何安裝 linkerd,我們將介紹以兩種不同的方法安裝,一種是傳統安裝方法,即下載 linkerd 的安裝包直接安裝,另一種是通過 Docker 的方式安裝。為了統一實驗環境,在安裝之前需要準備相關環境依賴:

  • Virtualbox 5.2.2
  • CentOS 7.4
  • Vagrant 2.0.1
  • OpenJDK 8
  • Docker Engine 1.12.6
  • 工具集:wget,telnet、tree 和 net-tools

準備實驗環境

  1. 安裝 Virtualbox

    本文 Virualbox 的安裝是在 Mac 系統上進行的,如果你的工作系統是 Windows 或者
    Linux,請到 Virtualbox 官方下載對應的安裝包,並根據安裝指南進行安裝,在這裡就不詳細介紹了。

    在 Mac 上安裝 Virtualbox 相對簡單,開啟終端,然後執行下面的命令即可。

    $ brew cask install virtualbox

    如果沒有任何錯誤,你會看到virtualbox was successfully installed!,表示安裝成功。

  2. 安裝 Vagrant

    同 VirtualBox 一樣,如果你的工作系統是 Windows 或者 Linux,請到 Vagrant 官方下載對應的安裝包進行安裝。同樣在 Mac 終端中執行下面的命令安裝 Vagrant:

    $ brew cask install vagrant

    安裝完成後,執行如下命令驗證 Vagrant 的版本資訊。

    $ vagrant --version
    Vagrant 2.0.1

    接下來我們開始配置 Vagrantfile 檔案,然後從 Vagrant Boxes 下載 CentOS 7.4 映象,最後根據配置的 Vagrantfile 啟動虛機。

    切換你的目錄下初始化 Vagrantfile,比如在我的環境是/Users/zhanyang/vagrant/linkerd/chapter2

    $ cd /Users/zhanyang/vagrant/linkerd/chapter2
    $ vagrant init # 執行完該命令後在/Users/zhanyang/vagrant/linkerd/chapter2目錄會產生預設Vagrantfile檔案

    對於這個預設 Vagrantfile 檔案我們做以下調整:

    • 設定 box 為 centos/7。
    • 配置 linkerd 轉發埠,使得可以從本機訪問執行在 VirtualBox 虛機裡 linkerd 的管理頁面。

    調整後我們移除 Vagrantfile 的其他預設配置,只保留我們需要的,內容如:

    
    # -*- mode: ruby -*-
    # vi: set ft=ruby :
    Vagrant.configure("2") do |config|
    config.vm.box = "centos/7"
    # linkerd router on port 4140
    config.vm.network "forwarded_port", guest: 4140, host: 4140
    # linkerd admin on port 9990
    config.vm.network "forwarded_port", guest: 9990, host: 9990
    end

    執行以下命令啟動虛擬機器,初次啟動可能時間較長,因為需要下載 CentOS 的映象檔案。

    $ vagrant up

    然後登入虛擬機器:

    $ vagrant ssh # 進入CentOS虛擬機器
    $ sudo su -  # 切換到root使用者
    # cat /etc/redhat-release # 驗證CentOS版本資訊
    CentOS Linux release 7.4.1708 (Core)

    安裝工具集:

    yum install -y wget telnet tree net-tools
  3. 安裝 OpenJDK

    進入已準備好的虛擬機器,通過 yum 安裝 OpenJDK,不需要額外配置 yum 源,預設的即可安裝。

    
    # yum install -y java-1.8.0-openjdk
    

    完成後確認 OpenJDK 版本資訊。

    
    # java -version
    openjdk version "1.8.0_151"
    OpenJDK Runtime Environment (build 1.8.0_151-b12)
    OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)
  4. 安裝 Docker Engine

    當前 CentOS 7.4 官方支援的 Docker 版本是1.12.6,如果你需要安裝更新版本的 Docker Engine,請參考 Docker 官方安裝指南

    
    # yum install -y docker
    

    通過docker version可檢視所安裝Docker的詳細資訊:

    
    # docker version
    Client:
    Version:         1.12.6
    API version:     1.24
    Package version: docker-1.12.6-61.git85d7426.el7.centos.x86_64
    Go version:      go1.8.3
    Git commit:      85d7426/1.12.6
    Built:           Tue Oct 24 15:40:21 2017
    OS/Arch:         linux/amd64
    Cannot connect to the Docker daemon. Is the docker daemon running on this host?

    你可發現安裝的 Docker Engine 的確是1.12.6,而且 Docker daemon 還沒有啟動,下面啟動 Docker daemon:

    
    # systemctl enable docker  # 確保系統重啟時Docker自動啟動
    Created symlink from /etc/systemd/system/multi-user.target.wants/docker.service to /usr/lib/systemd/system/docker.service.
    # systemctl start docker
    

傳統安裝 linkerd

現在所有環境依賴已經準備好,我們可以在 CentOS 虛機裡安裝 linkerd,首先建立一個安裝目錄,比如/root/linkerd/local,然後下載 linkerd 安裝包。

# mkdir /root/linkerd/local
# cd /root/linkerd/local
# wget https://github.com/linkerd/linkerd/releases/download/1.3.3/linkerd-1.3.3.tgz

然後解壓 linkerd 安裝包並啟動 linkerd。

# tar xzf linkerd-1.3.3.tgz
# tree linkerd-1.3.3
linkerd-1.3.3
├── config
│   └── linkerd.yaml
├── disco
│   ├── thrift-buffered
│   ├── thrift-framed
│   └── web
├── docs
│   ├── CHANGES.md
│   └── README.md
├── linkerd-1.3.3-exec
└── logs

linkerd 安裝包包括:

  • config 目錄:linkerd 配置檔案存放於該目錄
  • disco 目錄:如果採用基於檔案方式進行服務發現,相關配置資訊存於該目錄
  • logs 目錄:預設 linkerd 將所產生的日誌寫於該目錄
  • linkerd 可執行檔案
  • docs 文件目錄

首先預覽 config 目錄下的預設配置 linkerd.yaml,然後將標籤為/host/thrift-framed/host/thrift-buffered部分配置刪除,新的配置為:

# cd linkerd-1.3.3 # 進入linkerd-1.3.3目錄
# more config/linkerd.yaml # 預覽linkerd.yml
admin:
port: 9990
# 預設admin使用loopback地址,這裡需手動修改成0.0.0.0,為了能從宿主機訪問linkerd管理頁面
ip: 0.0.0.0
namers:
- kind: io.l5d.fs
rootDir: disco
routers:
- protocol: http
dtab: |
/svc => /#/io.l5d.fs;
httpAccessLog: logs/access.log
label: int
servers:
- port: 4140
ip: 0.0.0.0

通過配置我們可知:

  • admin模組:設定 linkerd 管理介面,預設監聽 loopback 地址的9990埠
  • namers模組:設定如何通過服務發現工具進行服務發現,預設配置為基於檔案的服務發現,服務相關資訊儲存在 disco 目錄的檔案中,每個檔案以服務名字命名,包括一系列主機名字/埠對。
  • routers模組: 設定服務間通訊協議,預設為http;dtab 路由規則,其根據訪問資訊如請求主機頭從namers指定的服務發現工具中找到需要訪問的服務,預設配置指定從namers模組配置的檔案目錄讀取資訊;日誌寫入路徑,預設 logs 目錄下的access.log 檔案;配置 router 伺服器的監聽地址和埠,預設為0.0.0.0的4140埠;其他配置如 router的標籤為 int。

然後通過 linkerd.yaml 啟動 linkerd 程序:

# ./linkerd-1.3.3-exec config/linkerd.yaml &
...
I 1204 01:19:23.776 UTC THREAD1: linkerd 1.3.3 (rev=9c47a744e433ae35d3a413e3f4448814a744f1a6) built at 20171201-150042
I 1204 01:19:24.453 UTC THREAD1: Finagle version 7.1.0 (rev=37212517b530319f4ba08cc7473c8cd8c4b83479) built at 20170906-132024
I 1204 01:19:27.031 UTC THREAD1: Tracer: com.twitter.finagle.zipkin.thrift.ScribeZipkinTracer
I 1204 01:19:27.067 UTC THREAD1: connecting to usageData proxy at Set(Inet(stats.buoyant.io/104.28.22.233:443,Map()))
I 1204 01:19:27.404 UTC THREAD1: serving http admin on /0.0.0.0:9990
I 1204 01:19:27.439 UTC THREAD1: serving int on /0.0.0.0:4140
I 1204 01:19:27.530 UTC THREAD1: serving /host/thrift-framed on /0.0.0.0:4141
I 1204 01:19:27.561 UTC THREAD1: serving /host/thrift-buffered on /0.0.0.0:4142
I 1204 01:19:27.570 UTC THREAD1: initialized

從輸出資訊我們知道:

  • linkerd 的版本是1.3.3
  • Finagle 的版本是7.1.0(linkerd 底層依賴Finagle)
  • linkerd admin 執行於9990埠,可通過0.0.0.0:9990進行訪問
  • linkerd 的 http router 執行於4140口,通過0.0.0.0:4140訪問

在宿主機的瀏覽器訪問 http://127.0.0.1:9990 可進入 linkerd 的管理介面,從上面你可以看到一些資訊如請求數量、成功率、失敗率等,除此之外,還可以從頁面進行 dtab(Delegation Table)調測以及調整日誌列印級別。

enter image description here

基於 Docker 的安裝

相對傳統安裝,使用 Docker 安裝 linkerd 更加方便,首先我們建立另一個目錄/root/linkerd/docker存放相關配置檔案,

mkdir /root/linkerd/docker
cd /root/linkerd/docker  # 進入Docker安裝目錄
cp -R /root/linkerd/local/linkerd-1.3.3/{config,disco} . 複製傳統安裝的配置資訊以便使用

在啟動之前需要對 config 目錄下 linkerd.yaml 的namers模組做調整使 Docker 從/disco讀取服務資訊,調整後如:

namers:
- kind: io.l5d.fs
rootDir: /disco

然後我們啟動 linkerd 的容器,使用 host 網路模式是為了方便從宿主機瀏覽器開啟管理頁面或者訪問通過 linkerd 代理的服務。

# docker run -d --name linkerd --network host -v `pwd`/disco:/disco -v `pwd`/config/linkerd.yaml:/linkerd.yaml  buoyantio/linkerd:1.3.3 /linkerd.yaml

為了避免埠衝突,在啟動 linkerd 容器之前須停止已啟動的 linkerd 程序。

跟傳統安裝一樣,你可以在宿主機的瀏覽器通過 http://127.0.0.1:9990 開啟 linkerd 管理頁面。

一個簡單的應用程式

在完成前面的安裝後(無論是傳統安裝還是基於 Docker 的安裝),我們使用一個簡單的應用程式 helloworld 來驗證是否 linkerd 將應用請求轉發給後端例項,helloworld 程式在後面的文章也將被使用到。

  1. 部署應用 helloworld

    helloworld 是一個參考 linkerd 官方helloworld
    例子改寫的簡單程式,可以返回服務名字和 IP 地址以及服務監聽埠。你可以通過執行下面命令檢視幫助:

    
    # docker run --rm docker.io/zhanyang/helloworld:1.0 --help
    Usage of /usr/local/bin/helloworld:
    -addr string
    address to serve on (default ":7777")
    -failure-rate float
    rate of error responses to return
    -json
    return JSON instead of plaintext responses (HTTP only)
    -latency duration
    time to sleep before processing request
    -protocol string
    API protocol: http or grpc (default "http")
    -target string
    target service to call before returning
    -text string
    service to serve (default "HelloWorld")

    通過addr選項或者環境變數 PORT 可以覆蓋預設執行埠7777,而環境變數 IP 指定容器執行的宿主機 IP 地址,text選項指定服務名稱,預設 HelloWorld。如果target選項指定之後,在服務返回之前先呼叫target指定的服務,將target服務返回的資訊和當前服務返回的資訊連線後輸出。

    下面我們啟動2個 helloworld 例項:

    
    # docker run -d --network host --env IP=0.0.0.0 docker.io/zhanyang/helloworld:1.0 # 第一個例項,以預設7777埠啟動
    # curl localhost:7777 # 在虛機上執行,返回helloworld執行的IP地址和埠資訊
    HelloWorld (0.0.0.0:7777)!
    # docker run -d --network host --env IP=0.0.0.0 --env PORT=8888 docker.io/zhanyang/helloworld:1.0 # 第二個例項,以8888埠啟動
    
  2. 配置 linkerd 使其代理應用請求

    在 dico 目錄下新建一檔案helloworld,無論是 /root/linkerd/docker 還是 /root/linkerd/local 目錄的 dico 都可以,然後在helloworld檔案新增兩條記錄,即正在執行的2個 helloworld 例項 IP 地址和埠:

    0.0.0.0 7777
    0.0.0.0 8888

    由於 linkerd 在發現 disco 目錄更新後自動載入新的配置,因此無須重啟 linkerd 服務便生效,如果你使用 Docker 的方式安裝 linkerd,最好重啟下 linkerd 服務,如 issue 所描述一樣,可能有些時候新的記錄不能被載入,因此建議不要在產線使用基於檔案的服務發現方式。然後通過 curl 命令驗證 linkerd 是否可以成功地將應用求轉發到實際後端例項。

    
    # curl -H "Host: helloworld" localhost:4140 # 跟直接訪問helloworld返回一樣的資訊,該命令可以在宿主機和虛機執行都可以,因為我們在Vagrantfile已配置轉發規則
    

    基於檔案的服務發現還可能導致消耗大量的CPU,不建議產線使用這種方式。

    執行該命令後可能得到輸出HelloWorld (0.0.0.0:8888)!或者HelloWorld (0.0.0.0:7777)!,不同的結果是linkerd基於它的負載均衡演算法導致的。

由執行結果可知 linkerd 很好的將應用請求代理到後端例項,並且其基於檔案的服務發現能將請求代理到不同的執行例項。

總結

這一節我們主要介紹了 linkerd 的基本功能、如何以不同的方式安裝 linkerd,最後通過一個簡單的應用程式驗證 linkerd 將應用請求轉發給後端執行例項。

來自 思科高階系統研發工程師 · 楊章顯 的達人課:《Service Mesh 入門實戰》

這裡寫圖片描述

Service Mesh 入門實戰 中,我將以例項為基礎詳細介紹 Service Mesh 的主要實現 linkerd。

其中包括:

  • 深入瞭解 linkerd 的主要功能;
  • 如何安裝部署 linkerd;
  • 深入理解 linkerd 配置,dtab 路由規則;
  • linkerd 工作機制、資料訪問流、部署模型等。

認真學習完這個課程之後,你將獲得或者具備以下能力:從0到1理解 linkerd 的工作機制;具備通過 linkerd 構建彈性的、高可見性的、可追蹤的雲原生應用。