安装

需要先准备一个 kubernetes 集群,这里是用的 k3s 安装 的 1.25 版本。

下载 istio

先下载 istio 安装文件,执行:

1
curl -L https://istio.io/downloadIstio | sh -

如果测试机器下载不了,可以手动下载脚本和安装文件传到测试机器,然后将 istioctl 文件路径加到系统环境变量中:

1
2
vim ~/.bashrc   # 最后增加 export PATH={/xxx/istio-1.16.1}/bin:$PATH
source ~/.bashrc

安装 istio

执行istioctl install --set profile=demo -y安装官方的 demo 配置组合,在 k3s 集群中,执行出现了以下问题:

1
2
root@alton:~# istioctl install --set profile=demo -y
Error: Get "https://127.0.0.1:6443/api?timeout=32s": x509: certificate signed by unknown authority

后面才发现问题可能是,以前将 k3s 的 config 进行过复制操作,但是最近认证已经变更了,而~/.kube/config 还是老的,istioctl默认是从~/.kube/config获取集群信息的,导致无法识别,解决办法:重新再执行以下命令复制即可。最好是在安装时,设置环境变量KUBECONFIG="/root/.kube/config",因为k3s的kubectl命令默认使用的配置路径是/etc/rancher/k3s/k3s.yaml。

1
cp /etc/rancher/k3s/k3s.yaml ~/.kube/config

给命名空间添加标签,指示 Istio 在部署应用的时候,自动注入 Envoy 边车代理:

1
kubectl label namespace default istio-injection=enabled

部署测试

部署 Bookinfo

使用官网示例,部署 Bookinfo 应用:

1
2
cd istio-1.16.1
kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml

等待部署完成,查看 pods:

1
2
3
4
5
6
7
8
root@alton:~/zt/istio/istio-1.16.1# kubectl get pods
NAME                             READY   STATUS    RESTARTS   AGE
reviews-v1-569db879f5-6qgmk      2/2     Running   0          20s
ratings-v1-5f9699cfdf-nk8fn      2/2     Running   0          20s
details-v1-5ffd6b64f7-wdwb7      2/2     Running   0          20s
reviews-v2-65c4dc6fdc-9bbjj      2/2     Running   0          20s
productpage-v1-979d4d9fc-fknt8   2/2     Running   0          20s
reviews-v3-c9c4fb987-nfvrq       2/2     Running   0          20s

通过kubectl get pods reviews-v1-569db879f5-6qgmk -oyaml 查看 pods 描述,可以看到注入了一个名为“istio-proxy”的 sidecar pods,如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  - args:
    - proxy
    - sidecar
    - --domain
    - $(POD_NAMESPACE).svc.cluster.local
    - --proxyLogLevel=warning
    - --proxyComponentLogLevel=misc:error
    - --log_output_level=default:info
    - --concurrency
    - "2"
    image: docker.io/istio/proxyv2:1.16.1
    imagePullPolicy: IfNotPresent
    name: istio-proxy
    ports:
    - containerPort: 15090
      name: http-envoy-prom
      protocol: TCP
    readinessProbe:
      failureThreshold: 30
      httpGet:
        path: /healthz/ready
        port: 15021
        scheme: HTTP
      initialDelaySeconds: 1
      periodSeconds: 2
      successThreshold: 1
      timeoutSeconds: 3
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    # 部分省略

验证是否部署成功:

1
2
root@alton:~/zt/istio/istio-1.16.1# kubectl exec "$(kubectl get pod -l app=ratings -o jsonpath='{.items[0].metadata.name}')" -c ratings -- curl -sS productpage:9080/productpage | grep -o "<title>.*</title>"
<title>Simple Bookstore App</title>

在集群外部访问

要开放访问,您需要创建 Istio 入站网关, 它会在网格边缘把一个路径映射到路由:

1
kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml

如果是通过 Minikube 安装的集群,可以使用 Minikube 隧道提供一个外部负载均衡器。这里使用的 k3s,只能通过机器的 ip 和 nodePort 来访问。

1
2
root@alton:~/zt/istio/istio-1.16.1# kubectl -n istio-system get service istio-ingressgateway -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}'
31779

查看这个 svc,主要是向集群外部暴露访问地址和端口,上面是获取的 http 的 nodePort 端口。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
apiVersion: v1
kind: Service
metadata:
  name: istio-ingressgateway
  namespace: istio-system
spec:
  allocateLoadBalancerNodePorts: true
  clusterIP: 10.43.157.67
  clusterIPs:
  - 10.43.157.67
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ipFamilies:
  - IPv4
  ipFamilyPolicy: SingleStack
  ports:
  - name: status-port
    nodePort: 30587
    port: 15021
    protocol: TCP
    targetPort: 15021
  - name: http2
    nodePort: 31779
    port: 80
    protocol: TCP
    targetPort: 8080
  - name: https
    nodePort: 30159
    port: 443
    protocol: TCP
    targetPort: 8443
  - name: tcp
    nodePort: 31621
    port: 31400
    protocol: TCP
    targetPort: 31400
  - name: tls
    nodePort: 31129
    port: 15443
    protocol: TCP
    targetPort: 15443
  selector:
    app: istio-ingressgateway
    istio: ingressgateway
  sessionAffinity: None
  type: LoadBalancer

在浏览器中,使用http://nodeip:31779/productpage就可以访问集群了,nodeip 即 k3s 集群 node 的外网 ip 地址。

测试 istio 特性

配置请求路由

需要使用目标规则来定义 subset,使用虚拟服务来配置请求路由到哪个子集。

1
2
3
4
// 配置虚拟服务
kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml
// 配置目标规则
kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml

使用http://nodeip:31779/productpage再次访问集群,多次刷新页面,评论部分不会显示评级星标。

基于用户身份的路由

可以将特定用户的所有流量路由到特定服务版本,可用于线上发布后进行回归测试。下面的例子设置用户 Jason 的所有流量将被路由到服务 reviews:v2,因此在页面上用测用户登陆时,评论处会显示星级。

1
2
3
4
// 应用路由设置
kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v2.yaml
// 查看虚拟服务路由设置
kubectl get virtualservice reviews -o yaml

访问外部服务

默认情况下,来自 Istio-enable Pod 的所有出站流量都会重定向到其 Sidecar 代理,集群外部 URL 的可访问性取决于代理的配置。默认情况下,Istio 将 Envoy 代理配置为允许传递未知服务的请求。 先使用遥测 API 来开启 enovy 访问日志:

1
2
3
4
5
6
7
8
9
apiVersion: telemetry.istio.io/v1alpha1
kind: Telemetry
metadata:
  name: mesh-default
  namespace: istio-system
spec:
  accessLogging:
    - providers:
      - name: envoy

因为前面已经开启了自动注入 sidecar,直接部署示例应用,并设置环境变量为 pod 的名称:

1
2
kubectl apply -f samples/sleep/sleep.yaml
export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})

查看 global.outboundTrafficPolicy.mode 安装选项配置,它配置 sidecar 对外部服务的处理方式,其值有:

  • ALLOW_ANY:默认值,允许调用未知的服务。
  • REGISTRY_ONLY:阻止任何没有在网格中定义的 HTTP 服务或 service entry 的主机。

查看是否配置为 ALLOW_ANY:

1
2
# 输出为 ALLOW_ANY 或者为空,即为允许调用未知的服务
kubectl get istiooperator installed-state -n istio-system -o jsonpath='{.spec.meshConfig.outboundTrafficPolicy.mode}'

向外部发送请求进行测试:

1
2
3
root@alton:~/zt/istio# kubectl exec "$SOURCE_POD" -c sleep -- curl -sSI https://www.baidu.com | grep  "HTTP/"; kubectl exec "$SOURCE_POD" -c sleep -- curl -sI https://kubernetes.io/ | grep "HTTP/"
HTTP/1.1 200 OK
HTTP/2 200

重新设置为 REGISTRY_ONLY 再进行测试,这里用istioctl install进行安装的,重新设置需执行:

1
2
3
root@alton:~/zt/istio# istioctl install --set profile=demo -y --set meshConfig.outboundTrafficPolicy.mode=REGISTRY_ONLY
root@alton:~/zt/istio# kubectl get istiooperator installed-state -n istio-system -o jsonpath='{.spec.meshConfig.outboundTrafficPolicy.mode}'
REGISTRY_ONLY

再次进行测试,请求已经被阻止了:

1
2
3
root@alton:~/zt/istio# kubectl exec "$SOURCE_POD" -c sleep -- curl -sSI https://www.baidu.com | grep  "HTTP/"; kubectl exec "$SOURCE_POD" -c sleep -- curl -sI https://kubernetes.io/ | grep "HTTP/"
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to www.baidu.com:443
command terminated with exit code 35

测试在服务网格中创建一个 ServiceEntry,以允许访问一个外部的 HTTP 服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-ext
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  location: MESH_EXTERNAL
EOF

测试向外部 HTTP 发送请求:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
root@alton:~/zt/istio#  kubectl exec -it $SOURCE_POD -c sleep -- curl http://httpbin.org/headers
{
  "headers": {
    "Accept": "*/*",
    "Host": "httpbin.org",
    "User-Agent": "curl/7.87.0-DEV",
    "X-Amzn-Trace-Id": "Root=1-63bb815d-1a017d290b48c2fa34471a3f",
    "X-B3-Sampled": "1",
    "X-B3-Spanid": "7aeb48cca0f822ad",
    "X-B3-Traceid": "3ece8f6c652caa017aeb48cca0f822ad",
    "X-Envoy-Decorator-Operation": "httpbin.org:80/*",
    #...
  }
}

查看 sidecar 里面的日志:

1
2
root@alton:~/zt/istio# kubectl logs $SOURCE_POD -c istio-proxy | tail
[2023-01-09T02:52:13.283Z] "GET /headers HTTP/1.1" 200 - via_upstream - "-" 0 1189 510 509 "-" "curl/7.87.0-DEV" "535009a8-12ed-9358-8b0a-c54713f83e09" "httpbin.org" "3.229.200.44:80" outbound|80||httpbin.org 10.42.0.30:47352 3.220.55.57:80 10.42.0.30:58464 - default