邊車容器下的服務網格istio

Istio是一個用於鏈接、管理以及安全化微服務的開放平臺, 提供了一種簡單的方式用於建立微服務網絡,並提供負載均衡/服務間認證/監控等能力,關鍵的是並不須要修改服務自己. 主要提供如下功能: Traffic Management: 控制服務之間調用的流量和API調用; Observability:...

Istio是一個用於鏈接、管理以及安全化微服務的開放平臺, 提供了一種簡單的方式用於建立微服務網絡,並提供負載均衡/服務間認證/監控等能力,關鍵的是並不須要修改服務自己. 主要提供如下功能:html

架構

Istio從架構上看,主要分爲2個部分,即:前端

智能代理Envoy

Envoy 是一個面向服務架構的L7代理和通訊總線而設計的,這個項目誕生是出於如下目標:nginx

對於應用程序而言,網絡應該是透明的,當發生網絡和應用程序故障時,可以很容易定位出問題的根源。算法

Envoy 試圖經過提供如下高級特性:編程

Envoy將做爲一個獨立的sidecar與相關微服務部署在同一個Kubernetes的pod上,並提供一系列的屬性給Mixer。Mixer以此做爲依據執行策略,併發送到監控系統.json

這種sidecar代理模型不須要改變任何服務自己的邏輯,並能增長一系列的功能.後端

Mixer

Mixer負責在服務網格上執行訪問控制和使用策略,並從Envoy代理和其餘服務收集遙測數據。代理提取請求級屬性,發送到Mixer進行評估。Mixer包括一個靈活的插件模型,使其可以接入到各類主機環境和基礎設施後端,從這些細節中抽象出Envoy代理和Istio管理的服務。api

後端的基礎設施經常被設計用於提供創建服務支持的功能,包括訪問控制系統、遙測數據捕獲系統、配額執行系統以及計費系統等。傳統服務會直接和這些後端系統打交道,和後端緊密耦合,並集成其中的個性化語義以及用法。安全

Mixer在應用程序代碼和基礎架構後端之間提供通用中介層。它的設計將策略執行部分移出應用層,而是用運維人員可以控制的配置取而代之。應用程序代碼再也不將應用程序代碼與特定後端集成在一塊兒,而是與Mixer進行至關簡單的集成,而後Mixer負責與後端系統鏈接。bash

Mixer的設計目的是改變層次之間的邊界,以此來下降整體的複雜性。從服務代碼中剔除策略邏輯,改由運維人員進行控制。

Mixer架構

istio_mixer_architecture

Mixer 提供三個核心功能:

這些機制的應用是基於一組 屬性 的,每一個請求都會將這些屬性呈現給Mixer。在Istio中,這些屬性來自於Sidecar代理(Envoy)的每一次請求。

Istio使用 屬性 來控制在服務網格中運行的服務的運行時行爲。屬性是具備名稱和類型的元數據片斷,用以描述入口和出口流量,以及這些流量所屬的環境。Istio屬性攜帶特定信息片斷,例如API請求的錯誤代碼,API請求的延遲或TCP鏈接的原始IP地址. 例如:

request.path: xyz/abc
request.size: 234
request.time: 12:34:56.789 04/17/2017
source.ip: 192.168.0.1
target.service: example複製代碼

基於適配器與模板的配置

mixer_architecture

Mixer是一種屬性處理引擎,請求到達Mixer時帶有一組屬性 ,而且基於這些屬性,Mixer會生成對各類基礎設施後端的調用。該屬性集肯定Mixer爲給定的請求用哪些參數調用哪一個後端。爲了隱藏各個後端的細節,Mixer使用稱爲適配器的模塊。

Mixer的配置有幾個中心職責:

配置基於適配器和模板來完成:

配置使用YAML格式來表示,圍繞幾個核心抽象構建:

Handler

適配器封裝了Mixer和特定外部基礎設施後端進行交互的必要接口,例如Prometheus、New Relic或者Stackdriver。各類適配器都須要參數配置才能工做。例如日誌適配器可能須要IP地址和端口來進行日誌的輸出。

這裏的例子配置了一個類型爲 listchecker 的適配器。listchecker適配器使用一個列表來檢查輸入。若是配置的是白名單模式且輸入值存在於列表之中,就會返回成功的結果。

apiVersion: config.istio.io/v1alpha2
kind: listchecker
metadata:
  name: staticversion
  namespace: istio-system
spec:
  providerUrl: http://white_list_registry/
  blacklist: false複製代碼

{metadata.name}.{kind}.{metadata.namespace} 是Handler的徹底限定名

實例

配置實例將請求中的屬性映射成爲適配器的輸入, 注意Handler配置中須要的全部維度都定義在這一映射之中。

apiVersion: config.istio.io/v1alpha2
kind: metric
metadata:
  name: requestduration
  namespace: istio-system
spec:
  value: response.duration | "0ms"
  dimensions:
    destination_service: destination.service | "unknown"
    destination_version: destination.labels["version"] | "unknown"
    response_code: response.code | 200
  monitored_resource_type: '"UNSPECIFIED"'複製代碼

規則

規則用於指定使用特定實例配置調用某一Handler的時機。好比咱們想要把 service1 服務中,請求頭中帶有 x-user 的請求的 requestduration 指標發送給Prometheus Handler:

apiVersion: config.istio.io/v1alpha2
kind: rule
metadata:
  name: promhttp
  namespace: istio-system
spec:
  match: destination.service == "service1.ns.svc.cluster.local" && request.headers["xuser"] == "user1"
  actions:
  - handler: handler.prometheus
    instances:
    - requestduration.metric.istio-system複製代碼

operator_template_adapter_dev

Pilot (原Istio-Manager)

Pilot負責收集和驗證配置並將其傳播到各類Istio組件。它從Mixer和Envoy中抽取環境特定的實現細節,爲他們提供用戶服務的抽象表示,獨立於底層平臺。此外,流量管理規則(即通用4層規則和7層HTTP/gRPC路由規則)能夠在運行時經過Pilot進行編程。

Service Model服務模型

服務Service自己並非Istio特有或新提出的概念,例如K8s早已經提供了相似的service概念和能力。但在Istio中爲了支持更細粒度的路由能力,針對服務模型提供了版本的機制,例如能夠經過附加label的方式描述版本等。
做爲一個邏輯抽象,每個服務一般會有FQDN全域名及若干個端口,或者也可能會有一個單獨的負載均衡器、虛擬IP地址與之對應。

例如,k8s中,一個服務foo就會有一個域名foo.default.svc.cluster.local hostname,虛擬IP10.0.1.1以及可能的監聽端口。

一個服務每每會對應多個實例,每個實例能夠是一個container,pod或者vm. 每個實例都有一個網絡端點以及暴露的監聽端口。
Istio自己不會提供服務註冊以及發現的能力,而是依賴於底層平臺提供的現成能力來完成服務註冊發現。另外,istio也不會提供DNS能力,也是經過底層平臺提供的dns能力(如kube-dns)解析域名。

基於規則的路由能力則是由Istio proxy sidecar 進程來實現,如envoy, nginx等。這些代理一般會同時提供4層與7層的路由能力。這些規則定義能夠基於label,或者權重,亦或是基於http header、url等。

Configuration Model配置模型

Istio的規則配置提供了一個基於pb的模式定義。這些規則內容存儲在一個KVstore中,pilot訂閱了這些配置信息的變化,以便更新istio其餘組件的配置內容。

apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
  name: reviews-default
spec:
  destination:
    name: reviews
  route:
  - labels:
    version: v1
    weight: 100複製代碼

代理控制器

顯然,代理控制器是pilot的核心模塊,用於控制管理istio中的proxy。當前istio採用了envoy做爲proxy,由於istio提供了一套標準的抽象api來對接proxy,因此未來用戶能夠自定製proxy,不見得必須採用envoy。
針對envoy,istio當前提供了2個組件:

GET /v1/registration/(string: service_name)

請求發現服務返回指定service_name的全部主機, 返回如下JSON格式的響應:
{
  "hosts": []
}

const std::string Json::Schema::SDS_SCHEMA(R"EOF( { "$schema": "http://json-schema.org/schema#",
    "definitions" : {
      "host" : {
        "type" : "object",
        "properties" : {
          "ip_address" : {"type" : "string"},
          "port" : {"type" : "integer"},
          "tags" : {
            "type" : "object",
            "properties" : {
              "az" : {"type" : "string"},
              "canary" : {"type" : "boolean"},
              "load_balancing_weight": {
                "type" : "integer",
                "minimum" : 1,
                "maximum" : 100
              }
            }
          }
        },
        "required" : ["ip_address", "port"]
      }
    },
    "type" : "object",
    "properties" : {
      "hosts" : {
        "type" : "array",
        "items" : {"$ref" : "#/definitions/host"}
      }
    },
    "required" : ["hosts"]
  }
  )EOF");複製代碼

這兒不得不提的是pilot的 proxy injection 能力,你可能已經想到了它是基於iptable規則來實現的。這樣全部的服務交互都會被pilot捕獲並從新轉發。

Istio-Auth

提供服務間以及用戶之間的認證,確保不須要修改服務code的前提下加強服務之間的安全性. 主要包括如下3個組件:

分佈式跟蹤

Istio的分佈式跟蹤是基於Twitter開源的zipkin分佈式跟蹤系統,理論模型來自於Google Dapper 論文.

啓動zipkin

安裝Istio時會啓動zipkin addon,固然也可使用以下命令啓動:

kubectl apply -f install/kubernetes/addons/zipkin.yaml複製代碼

訪問zipkin

訪問zipkin dashboard: http://localhost:9411

kubectl port-forward $(kubectl get pod -l app=zipkin -o jsonpath='{.items[0].metadata.name}') 9411:9411複製代碼

在服務中enable trace

服務自己實現須要作必定的改動,即從最初始的HTTP請求中獲取如下header並傳遞給其餘的請求:

x-request-id
x-b3-traceid
x-b3-spanid
x-b3-parentspanid
x-b3-sampled
x-b3-flags
x-ot-span-context複製代碼

啓用Ingress

在Kubernetes環境下, Istio使用了內置的Ingress來暴露服務,目前支持HTTP和HTTPS兩種方式. 具體的Ingress,參見Kubernetes Ingress.

配置HTTP服務

cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: simple-ingress
  annotations:
    kubernetes.io/ingress.class: istio
spec:
  rules:
  - http:
      paths:
      - path: /headers
        backend:
          serviceName: httpbin
          servicePort: 8000
      - path: /delay/.*
        backend:
          serviceName: httpbin
          servicePort: 8000
EOF複製代碼

配置HTTPS服務

cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: secured-ingress
  annotations:
    kubernetes.io/ingress.class: istio
spec:
  tls:
    - secretName: ingress-secret
  rules:
  - http:
      paths:
      - path: /ip
        backend:
          serviceName: httpbin
          servicePort: 8000
EOF複製代碼

啓用Egress

默認狀況下,Istio中的服務是沒法訪問cluster以外的服務的,這是由於pod中的iptable設置爲全部的對外請求都指向sidecar proxy. 而爲了能訪問外部服務, Istio提供了兩種方式來解決這個問題.

配置外部服務

註冊一個HTTP和HTTPS服務, 以下:

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
 name: externalbin
spec:
 type: ExternalName
 externalName: httpbin.org
 ports:
 - port: 80
   # important to set protocol name
   name: http
EOF複製代碼

或者

cat <<EOF | kubectl create -f -
apiVersion: v1
kind: Service
metadata:
 name: securegoogle
spec:
 type: ExternalName
 externalName: www.google.com
 ports:
 - port: 443
   # important to set protocol name
   name: https
EOF複製代碼

其中, metadata.name 就是內部服務所須要訪問的外部服務的名稱, spec.externalName則是外部服務的DNS名稱.

執行以下命令能夠嘗試訪問外部服務,

export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
kubectl exec -it $SOURCE_POD -c sleep bash
curl http://externalbin/headers
curl http://securegoogle:443複製代碼

直接訪問外部服務

Istio Egress目前只支持訪問HTTP/HTTPS請求,而爲了支持其餘協議請求(如mqtt, mongo等), 就須要配置服務的Envoy sidecar來避免截取外部請求.
最簡單的方式是使用參數--includeIPRanges來指定內部cluster服務所使用的IP範圍.

Note: 不一樣的cloud provider所支持的IP範圍和獲取方式不盡相同.

例如, minikube支持以下:

kubectl apply -f <(istioctl kube-inject -f samples/apps/sleep/sleep.yaml --includeIPRanges=10.0.0.1/24)複製代碼

配置請求路由

默認配置下,Istio會將全部的請求路由到同一個服務的全部版本上.此外,Istio提供了根據請求內容的路由規則,以下規則描述了全部的請求都會指向服務的版本v1:

type: route-rule
name: ratings-default
namespace: default
spec:
  destination: ratings.default.svc.cluster.local
  precedence: 1
  route:
  - tags:
      version: v1
    weight: 100
---
type: route-rule
name: reviews-default
namespace: default
spec:
  destination: reviews.default.svc.cluster.local
  precedence: 1
  route:
  - tags:
      version: v1
    weight: 100
---
type: route-rule
name: details-default
namespace: default
spec:
  destination: details.default.svc.cluster.local
  precedence: 1
  route:
  - tags:
      version: v1
    weight: 100
---
type: route-rule
name: productpage-default
namespace: default
spec:
  destination: productpage.default.svc.cluster.local
  precedence: 1
  route:
  - tags:
      version: v1
    weight: 100
---複製代碼

若是須要將某些請求指向其餘版本的服務,如根據請求的cookie進行路由:

destination: reviews.default.svc.cluster.local
match:
  httpHeaders:
    cookie:
      regex: ^(.*?;)?(user=jason)(;.*)?$
precedence: 2
route:
- tags:
    version: v2複製代碼

其餘具體的規則,參見: https://istio.io/docs/reference/config/traffic-rules/routing-rules.html#routerule

錯誤注入

Istio提供2種錯誤類型能夠注入到請求中:

故障注入規則描述以下例子所述:

destination: ratings.default.svc.cluster.local
httpFault:
  delay:
    fixedDelay: 7s
    percent: 100
match:
  httpHeaders:
    cookie:
      regex: "^(.*?;)?(user=jason)(;.*)?$"
precedence: 2
route:
 - tags:
    version: v1複製代碼

設置請求超時

HTTP請求超時能夠經過在路由規則中設置字段httpReqTimeout實現.
具體例子以下:

cat <<EOF | istioctl replace
type: route-rule
name: reviews-default
spec:
  destination: reviews.default.svc.cluster.local
  route:
  - tags:
      version: v2
  httpReqTimeout:
    simpleTimeout:
      timeout: 1s
EOF複製代碼

限流

在Istio的mixer中配置限流規則,以下ratelimit.yaml:

rules:
- selector: source.labels["app"]=="reviews" && source.labels["version"] == "v3"  
- aspects:
  - kind: quotas
    params:
      quotas:
      - descriptorName: RequestCount
        maxAmount: 5000
        expiration: 5s
        labels:
          label1: target.service複製代碼

若是target.service=rating, 那麼計數器的key則爲:

$aspect_id;RequestCount;maxAmount=5000;expiration=5s;label1=ratings複製代碼

執行以下命令可使得rating服務的請求控制在每5秒5000次(限定在reviews v3服務在調用時生效):

istioctl mixer rule create global ratings.default.svc.cluster.local -f ratelimit.yaml複製代碼

簡單的訪問控制

Istio能夠經過設置規則實現簡單的訪問控制.

使用denials屬性

例如:

rules:
- aspects:
  - kind: denials
  selector: source.labels["app"]=="reviews" && source.labels["version"] == "v3"複製代碼

執行以下命令可使rating服務拒絕來自reviews v3服務的任何請求.

istioctl mixer rule create global ratings.default.svc.cluster.local -f samples/apps/bookinfo/mixer-rule-ratings-denial.yaml

使用黑白名單

使用黑白名單以前須要先定義一個adapter,以下:

- name: versionList
  impl: genericListChecker
  params:
    listEntries: ["v1", "v2"]複製代碼

啓用白名單時blacklist設置爲false,反之爲true.

rules:
  aspects:
  - kind: lists
    adapter: versionList
    params:
      blacklist: false
      checkExpression: source.labels["version"]複製代碼

結論

本文主要從概念、架構到每一個組件原理介紹了istio,做爲一個用於鏈接、管理以及安全化微服務的開放平臺, istio的確提供了一種簡單的方式用於建立微服務網絡。後續將會提供一系列的文章分別介紹具體的案例以及涉及的核心技術。

原文連接

相關文章
相關標籤/搜索