Istio 之 VirtualService-虛擬服務-

VirtualService(虛擬服務)

VirtualService中文名稱虛擬服務,是 istio 中一個重要的資源, 它定義了一系列針對指定服務的流量路由規則。每個路由規則都針對特定協議的匹配規則。如果流量符合這些特徵,就會根據規則發送到服務註冊表中的目標服務(或者目標服務的子集或版本)。

學習目標

VirtualService 和 k8s service 的區別

如果沒有 Istio virtual service,僅僅使用 k8s service 的話,那麼只能實現最基本的流量負載均衡轉發,但是就不能實現類似按百分比來分配流量等更加複雜、豐富、細粒度的流量控制了。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-namespace
spec:
  hosts:
    - "svc.test.com"
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local   # 代理的SVC

exportTo 生效命名空間

1. 當前名稱空間有效

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-namespace
spec:
  exportTo:
    - "."
  hosts:
    - "svc.test.com"
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local   # 代理的SVC

2. 所有名稱空間有效

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-namespace
spec:
  exportTo:
    - "*"
  hosts:
    - "svc.test.com"
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local   # 代理的SVC

VirtualService + Gateway

Gateway 名稱列表,Sidecar 會據此使用路由。VirtualService 對象可以用於網格中的 Sidecar,也可以用於一個或多個 Gateway。這裏公開的選擇條件可以在協議相關的路由過濾條件中進行覆蓋。保留字 mesh 用來指代網格中的所有 Sidecar。當這一字段被省略時,就會使用缺省值(mesh),也就是針對網格中的所有 Sidecar 生效。如果提供了 gateways 字段,這一規則就只會應用到聲明的 Gateway 之中。要讓規則同時對 Gateway 和網格內服務生效,需要顯式的將 mesh 加入 gateways 列表。

單個 gateway

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: web-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: httpd
        protocol: HTTP
      hosts:
        - "svc.test.com"
    - port:
        number: 80
        name: nginx
        protocol: HTTP
      hosts:
        - "svc.test.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-namespace
spec:
  hosts:
    - "svc.test.com"
  gateways:
    - web-gateway
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local
            port:
              number: 80

測試結果

# while true; do wget -q -O- http://svc.test.com:32623 ;sleep 1; done
<html><body><h1>It works!</h1></body></html>
<h1>Welcome to nginx!</h1>
<h1>Welcome to nginx!</h1>
<html><body><h1>It works!</h1></body></html>

多個 Gateway

---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: web-gateway-nginx
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: nginx
        protocol: HTTP
      hosts:
        - "svc.test.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: web-gateway-httpd
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: httpd
        protocol: HTTP
      hosts:
        - "svc.test.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-namespace
spec:
  hosts:
    - "svc.test.com"
  gateways:
    - web-gateway-nginx
    - web-gateway-httpd
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local
            port:
              number: 80

測試結果

# while true; do wget -q -O- http://svc.test.com:32623 ;sleep 1; done
<html><body><h1>It works!</h1></body></html>
<html><body><h1>It works!</h1></body></html>
<html><body><h1>It works!</h1></body></html>
<h1>Welcome to nginx!</h1>
<html><body><h1>It works!</h1></body></html>
<h1>Welcome to nginx!</h1>
<html><body><h1>It works!</h1></body></html>

不同命名空間下的 Gateway

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: web-gateway-httpd
  namespace: test
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: httpd
        protocol: HTTP
      hosts:
        - "svc.test.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-namespace
spec:
  hosts:
    - "svc.test.com"
  gateways:
    - test/web-gateway-httpd
  http:
    - match:
        - uri:
            prefix: /
      route:
        - destination:
            host: httpd-web-svc.test.svc.cluster.local
            port:
              number: 80

省略 gateways 跨域

---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx-web
  labels:
    app: nginx
    deploy: nginx
    server: web
spec:
  selector:
    matchLabels:
      app: nginx
      deploy: nginx
      server: web
  template:
    metadata:
      labels:
        app: nginx
        deploy: nginx
        server: web
    spec:
      containers:
        - name: nginx
          image: alvinos/nginx:v1
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-web-svc
  labels:
    app: nginx-web-svc
    deploy: nginx-web-svc
spec:
  ports:
    - port: 80
      targetPort: 80
      name: nginx
      protocol: TCP
  selector:
    server: web
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: web-gateway-nginx
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: nginx
        protocol: HTTP
      hosts:
        - "svc.test.com"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: allow
spec:
  hosts:
    - "svc.test.com"
  gateways:
    - default/web-gateway-nginx
  http:
    - corsPolicy:
        allowCredentials: true   # 響應頭表示是否可以將對請求的響應暴露給頁面。返回true則可以,其他值均不可以。
        allowOrigins: # 響應頭指定了該響應的資源是否被允許與給定的origin共享。
          - exact: "http://svc.test.com:32623" # 完全匹配
      match:
        - uri:
            prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local
            port:
              number: 80

allowOrigins prefix(前綴匹配)
  http:
    - corsPolicy:
        allowCredentials: true   # 響應頭表示是否可以將對請求的響應暴露給頁面。返回true則可以,其他值均不可以。
        allowOrigins: # 響應頭指定了該響應的資源是否被允許與給定的origin共享。
          - prefix: "http://svc.test.com"

allowOrigins regex(正則匹配)
  http:
    - corsPolicy:
        allowCredentials: true   # 響應頭表示是否可以將對請求的響應暴露給頁面。返回true則可以,其他值均不可以。
        allowOrigins: # 響應頭指定了該響應的資源是否被允許與給定的origin共享。
          - regex: ".*"

設置 exposeHeaders
  http:
    - corsPolicy:
        allowCredentials: true
        allowOrigins:
          - prefix: "http://localhost" 
        exposeHeaders:  # 響應頭指定了該響應首部添加響應header頭
          - test
          - test2

設置 allowHeaders
      allowHeaders:
      - X-Custom-Header
      - content-type
設置 allowMethods
        allowMethods:
          - GET
          - DELETE

長短 fqdn

# 在default名稱空間創建vs
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: my-fqdn
spec:
  hosts:
    - "nginx.default.svc.cluster.local"   # 長fqdn
    - "nginx" # 短fqdn
  http:
    - match:
        - uri:
          prefix: /
      route:
        - destination:
            host: nginx-web-svc.default.svc.cluster.local
            port:
              number: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  ports:
    - port: 80
      name: http
  selector:
    app: nginx

基於 TLS 的 HTTPS 請求

一個有序列表,對應的是透傳 TLS 和 HTTPS 流量。路由過程通常利用 ClientHello 消息中的 SNI 來完成。TLS 路由通常應用在 https-、tls- 前綴的平臺服務端口,或者經 Gateway 透傳的 HTTPS、TLS 協議端口,以及使用 HTTPS 或者 TLS 協議的 ServiceEntry 端口上。注意:沒有關聯 VirtualService 的 https- 或者 tls- 端口流量會被視爲透傳 TCP 流量。

1、創建證書

# 因爲是測試環境,我在這裏手動簽發證書,如果是生產環境最好在雲服務商哪裏購買證書
[root@kubernetes-master-01 cert]# openssl genrsa -out tls.key 2048
[root@kubernetes-master-01 cert]# openssl req -new -x509 -key tls.key -out tls.crt -subj /C=CN/ST=ShangHai/L=ShangHai/O=Ingress/CN=svc.test.com

2、創建 secret

[root@kubernetes-master-01 cert]# kubectl -n default create secret tls nginx-tls --cert=tls.crt --key=tls.key
secret/nginx-tls created
[root@kubernetes-master-01 cert]# kubectl get secrets 
NAME                  TYPE                                  DATA   AGE
nginx-tls             kubernetes.io/tls                     2      19s

3、創建 Nginx 配置文件

---
kind: ConfigMap
apiVersion: v1
metadata:
  name: tls
  labels:
    app: tls
data:
  nginx.conf: |
    worker_processes auto;
    error_log /var/log/nginx/error.log;
    pid /run/nginx.pid;
    events {
        worker_connections 1024;
    }
    http {
         log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                       '$status $body_bytes_sent "$http_referer" '
                       '"$http_user_agent" "$http_x_forwarded_for"';
         access_log  /var/log/nginx/access.log  main;
         include             /etc/nginx/mime.types;
         default_type        application/octet-stream;
         server {
             listen 443 ssl;
             ssl_certificate /etc/nginx-server-certs/tls.crt;
             ssl_certificate_key /etc/nginx-server-certs/tls.key;
             server_name  _;
             location / {
                root         /usr/share/nginx/html;
                index index.html;
             }
         }
    }

4、創建 Deployment 資源

---
kind: Deployment
apiVersion: apps/v1
metadata:
  name: tls
spec:
  selector:
    matchLabels:
      app: tls
  template:
    metadata:
      labels:
        app: tls
    spec:
      containers:
        - name: nginx
          imagePullPolicy: IfNotPresent
          image: nginx 
          volumeMounts:
            - mountPath: /etc/nginx/nginx.conf
              name: nginxconfig
              readOnly: true
              subPath: nginx.conf
            - mountPath: /etc/nginx-server-certs
              name: nginx-tls
              readOnly: true
      volumes:
        - name: nginxconfig
          configMap:
            name: tls
            items:
              - key: nginx.conf
                path: nginx.conf
        - name: nginx-tls
          secret:
            secretName: nginx-tls
---
kind: Service
apiVersion: v1
metadata:
  name: tls
  labels:
    app: tls
spec:
  ports:
    - port: 443
      targetPort: 443
      name: tls
  selector:
    app: tls

5、創建 Gateway 資源

---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: tls
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 443
        name: tls
        protocol: HTTPS
      hosts:
        - "svc.test.com"
      tls:
        mode: PASSTHROUGH

6、創建 VirtualService 資源

---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: nginx
spec:
  hosts:
    - tls.test.com
  gateways:
    - tls
  tls:
    - match:
        - port: 443
          sniHosts:
            - tls.test.com
      route:
        - destination:
            host: tls.default.svc.cluster.local
            port:
              number: 443

基於 TCP 的各種服務

一個針對透傳 TCP 流量的有序路由列表。TCP 路由對所有 HTTP 和 TLS 之外的端口生效。進入流量會使用匹配到的第一條規則。

1、創建基礎服務

---
apiVersion: v1
kind: Service
metadata:
  name: tcp-echo
  labels:
    app: tcp-echo
    service: tcp-echo
spec:
  ports:
    - name: tcp
      port: 3306
  selector:
    app: tcp-echo
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: tcp-echo
spec:
  replicas: 1
  selector:
    matchLabels:
      app: tcp-echo
      version: v1
  template:
    metadata:
      labels:
        app: tcp-echo
        version: v1
    spec:
      containers:
        - name: tcp-echo
          image: mysql:5.7
          imagePullPolicy: IfNotPresent
          env:
            - name: MYSQL_ROOT_PASSWORD
              value: "123456"
          ports:
            - containerPort: 3306

2、創建網關服務

---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: tcp-echo-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 31400
        name: tcp
        protocol: TCP
      hosts:
        - "mysql.test.com"

3、創建 VirtualService 資源

---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: tcp-echo
spec:
  hosts:
    - "mysql.test.com"
  gateways:
    - tcp-echo-gateway
  tcp:
    - match:
        - port: 31400
      route:
        - destination:
            host: tcp-echo
            port:
              number: 3306

4、部署結果

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/4vT7ApEq9X1JiLB45sG4lQ