K8S-istio

istio

简介

Istio:是一个开源的服务网格(Service Mesh)平台,主要用于连接、管理微服务。无需修改应用代码为分布式应用提供了流量管理、安全、可观测性等功能。

服务网格(Service Mesh):是一种基础设施层,用于处理服务间的通信、监控、安全等,与业务逻辑解耦。

Sidecar模式:Istio通过在每个Pod旁边注入一个Proxy(通常是 Envoy),拦截和管理所有进出该Pod的流量(多为HTTP,也支持TCP和UDP)。

组件

Envoy:代理,负责数据面流量的拦截、转发、加密、限流等。
Pilot:控制面,负责服务发现、流量管理策略的下发。
Citadel:提供服务间的身份认证和密钥管理(安全)。
istiod:Istio 1.5+ 的统一控制面,整合了 Pilot、Citadel、Galley 等功能。
Ingress/Egress Gateway:负责集群流量的统一入口和出口。

istio安装

mkdir -r /k8s/istio && cd /k8s/istio
curl -L https://istio.io/downloadIstio | sh -
echo 'export PATH="$PATH:/k8s/istio/istio-1.28.0/bin"' >> ~/.bashrc && source ~/.bashrc
istioctl x precheck
cd istio-1.28.0
istioctl install

kubectl label namespace default istio-injection=enabled

kubectl set resources deployment/istiod -n istio-system --containers=discovery --requests=cpu=100m,memory=500Mi --limits=cpu=400m,memory=1Gi
kubectl set resources deployment/istio-ingressgateway -n istio-system --containers=istio-proxy --requests=cpu=100m,memory=256Mi --limits=cpu=500m,memory=512Mi

istio-nginx例子

nginx-master.yaml

istio要求调用方同样位于istio环境的容器中,nginx-master.yaml用于调用测试

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-master
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-master
  template:
    metadata:
      labels:
        app: nginx-master
    spec:
      containers:
        - name: nginx-container-master
          image: nginx:latest
          ports:
            - containerPort: 80

nginx-online表示正式环境

# nginx-online.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      isCanary: 'false'
  template:
    metadata:
      labels:
        app: nginx
        isCanary: 'false'
    spec:
      containers:
        - name: nginx-container
          image: nginx:latest
          ports:
            - containerPort: 80
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - sed -i "s/Welcome to nginx\!/Welcome to ${HOSTNAME}\!/g" /usr/share/nginx/html/index.html

nginx-canary-1表示金丝雀1

# nginx-canary-1.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-canary-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      isCanary: 'true'
      canaryId: '1'
  template:
    metadata:
      labels:
        app: nginx
        isCanary: 'true'
        canaryId: '1'
    spec:
      containers:
        - name: nginx-container-canary-1
          image: nginx:latest
          ports:
            - containerPort: 80
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - sed -i "s/Welcome to nginx!/Welcome to ${HOSTNAME}!/g" /usr/share/nginx/html/index.html

nginx-canary-2表示金丝雀2

# nginx-canary-2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-canary-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
      isCanary: 'true'
      canaryId: '2'
  template:
    metadata:
      labels:
        app: nginx
        isCanary: 'true'
        canaryId: '2'
    spec:
      containers:
        - name: nginx-container-canary-2
          image: nginx:latest
          ports:
            - containerPort: 80
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - sed -i "s/Welcome to nginx!/Welcome to ${HOSTNAME}!/g" /usr/share/nginx/html/index.html

nginx的Service,通配nginx匹配

# nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  selector:
    app: nginx
  ports:
    - name: web
      protocol: TCP
      port: 5000
      targetPort: 80
  type: ClusterIP

istio配置

VirtualService负责拦截请求并根据匹配的规则路由到不同的host和subset。

DestinationRule负责定义host的不同subset,并通过标签(labels)将流量映射到具体的服务实例。

VS中优先级同时符合多个条目时,靠上方的优先级更高。

多个不同name的VS文件配置同一个hosts,后应用的VS会覆盖先应用的相同的 VS规则。

# nginx-vs.yaml
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
  name: nginx
spec:
  hosts:
    - nginx.default.svc.cluster.local
  http:
    - match:
        - headers:
            canary_id:
              exact: '1'
      route:
        - destination:
            host: nginx.default.svc.cluster.local
            subset: canary-1
            weight: 50 # 配置分流
        - destination:
            host: nginx.default.svc.cluster.local
            subset: canary-11
            weight: 50
    - match:
        - headers:
            canary_id:
              exact: '2'
      route:
        - destination:
            host: nginx.default.svc.cluster.local
            subset: canary-2
    - route:
        - destination:
            host: nginx.default.svc.cluster.local
            subset: online

---
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
  name: nginx
spec:
  host: nginx.default.svc.cluster.local
  subsets:
    - name: online
      labels:
        isCanary: 'false'
    - name: canary-1
      labels:
        isCanary: 'true'
        canaryId: '1'
    - name: canary-2
      labels:
        isCanary: 'true'
        canaryId: '2'
    - name: canary-11
      labels:
        isCanary: 'true'
        canaryId: '11'
  exportTo: # 所有命名空间生效,分命名空间怎么用?
    - '*'

istio-nginx测试

kubectl exec -it nginx-deployment-master-d9dfb9976-x28s5  -- bash

curl -s nginx:5000 -H "canary_id: 1" | grep "Welcome to"
<title>Welcome to nginx-deployment-canary-1-d4d4f9577-9dlvp!</title>
<h1>Welcome to nginx-deployment-canary-1-d4d4f9577-9dlvp!</h1>

curl -s nginx:5000 -H "canary_id: 2" | grep "Welcome to"
<title>Welcome to nginx-deployment-canary-2-558b489475-d6ppx!</title>
<h1>Welcome to nginx-deployment-canary-2-558b489475-d6ppx!</h1>

curl -s nginx:5000 -H "canary_id: 3" | grep "Welcome to"
<title>Welcome to nginx-deployment-748c545c49-mnsnq!</title>
<h1>Welcome to nginx-deployment-748c545c49-mnsnq!</h1>

curl -s nginx:5000  | grep "Welcome to"
<title>Welcome to nginx-deployment-748c545c49-mnsnq!</title>
<h1>Welcome to nginx-deployment-748c545c49-mnsnq!</h1>

配置的1和2都前往了对应的分组,未配置的3和空则走了默认的分组。

原理

  1. 创建一个Service,得到一个域名nginx.default.svc.cluster.local
  2. POD客户端(这个客户端也必须被注入了Istio)访问这个域名
  3. 流量被转发到POD客户端的istio-proxy(Envoy),访问nginx.default.svc.cluster.local不再是直达经过标签选择的pod,而是被转发到Envoy
  4. Envoy查询VS,使用hosts比对得到上面的VS规则,按断http协议的请求是否存在canary_id这个header,都是则继续查找nginx.default.svc.cluster.local的canary这个subset
  5. Envoy查询DestinationRule,判断nginx.default.svc.cluster.local的canary这个subset的规则,得到规则为存在canaryId: ‘1’这个标签
  6. 最终转发到存在canaryId: ‘1’标签的nginx-deployment-canary-1

Istio+Envoy的组合只会拦截TCP和UDP流量,不会拦截ping的ICMP流量

注册和配置收集

  1. pod被注入istio-proxy后,Envoy进程会自动启动
  2. Envoy尝试连接Istio控制面,报告自己的身份
  3. Istio控制面收集VS,DR等配置,计算这个Envoy的相关配置
    1. 默认情况下本命名空间的服务都被认为相关
    2. 所有被注入了Envoy的服务
  4. Istio控制面根据身份信息推送对应的Envoy配置

kiali安装

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.28/samples/addons/kiali.yaml
kubectl -n istio-system get svc kiali
kubectl -n istio-system port-forward svc/kiali 20001:20001 --address="0.0.0.0"

kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.20/samples/addons/prometheus.yaml
kubectl -n istio-system get pod,svc | grep -i prometheus
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
  kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.0/experimental-install.yaml

get vs和get virtualservice和get virtualservice.networking.istio.io

virtualservice.networking.istio.io是CRD的全名

vs一般是默认的注册短名