kubesphere安装Istio

KubeSphere 服务网格基于Istio,将微服务治理和流量管理可视化。它拥有强大的工具包,包括熔断机制、蓝绿部署、金丝雀发布、流量镜像、链路追踪、可观测性和流量控制等。KubeSphere 服务网格支持代码无侵入的微服务治理,帮助开发者快速上手,Istio 的学习曲线也极大降低。KubeSphere 服务网格的所有功能都旨在满足用户的业务需求。

kubesphere安装Istio

登录控制台

kubesphere已经更新到4.2.0版本,Istio已经解耦出来,作为扩展组件安装,可以直接从控制台扩展中心安装,可参考官方文档:安装扩展组件 - KubeSphere,这里不再赘述。

由于我的kubesphere是3.3.2版本,此版本集成了Istio,在控制台修改一下参数就可以自动安装:

登录控制台,左上角点击平台管理-集群管理:

在定制资源定义,搜索clusterconf

点进这个ClusterConfiguration之后:

末尾servicemesh修改参数:

1
2
3
4
5
6
7
8
9
servicemesh:
enabled: true # 将“false”更改为“true”。
istio: # Customizing the istio installation configuration, refer to https://istio.io/latest/docs/setup/additional-setup/customize-installation/
components:
ingressGateways:
- name: istio-ingressgateway # 将服务暴露至服务网格之外。默认不开启。
enabled: false
cni:
enabled: false # 启用后,会在 Kubernetes pod 生命周期的网络设置阶段完成 Istio 网格的 pod 流量转发设置工作。

确定保存后,即可检查Istio组件的安装过程:

1
kubectl logs -n kubesphere-system $(kubectl get pod -n kubesphere-system -l 'app in (ks-install, ks-installer)' -o jsonpath='{.items[0].metadata.name}') -f

安装完成后,日志输出如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Waiting for all tasks to be completed ...
task network status is successful (1/5)
task openpitrix status is successful (2/5)
task multicluster status is successful (3/5)
task monitoring status is successful (4/5)
task servicemesh status is successful (5/5)
**************************************************
Collecting installation results ...
#####################################################
### Welcome to KubeSphere! ###
#####################################################

Console: http://192.168.88.20:30880
Account: admin
Password: P@88w0rd

NOTES:
1. After you log into the console, please check the
monitoring status of service components in
"Cluster Management". If any service is not
ready, please wait patiently until all components
are up and running.
2. Please change the default password after login.

这就代表完成了Istio的安装及初始化。不需要理会这个原始密码,重新登录还是要用已经更改的密码

验证Istio组件安装情况

在WebUI可以看到:

系统组件中已经出现了Istio组件。但是点进去发现:

此时不但Istio异常,连带之前正常的Prometheus也一并异常了:

等待一段时间,等他拉取镜像,启动容器即可。

我的是能直接启动成功,有人反馈说容易一直卡在容器创建中,一直启动不了

看pod的events如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 43m default-scheduler Successfully assigned istio-system/istiod-1-11-2-54dd699c87-99krn to zhiyong-ksp1
Warning FailedCreatePodSandBox 43m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "5d0a3bdb6dea937aa5b118bbd00305a1542111c97af84a3cbdd8f188b1681687": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 43m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "ff84de82acfd944be7f3804c96f39ab976ae4d6810b7e0364c90560a4b4070e7": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 42m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "6337bea6f7c16cd9adcff0d2b75238beb4365dc4b880d4c8e4f4535885d59d30": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 42m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "42e08603d4d7e7d1713eecbb21af258022e3fb50c6f5611808b3e2755d50d980": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 42m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "51a6b5b8ea5a63f4be828a0c855802e42640324c440fcc3487c535123d7b3372": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 42m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "dada948b2a416a0ec925b7f67a101b8fd48fdad9fb20d6c41eaf1bbad0a18e57": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 41m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "df3487e020c1e7eb527cc0fce1fe990873bd20f46cbf04de99005e0da5896abe": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 41m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "92e739549a96aa03ea864188abc1b91c9a45394dae28ad97234fa1caf4d52240": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 41m kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "bc5d1999a2d5ad4d7cf5c1e1c3c7c1a80dee02b806d0be2e15c326e2d82f4af5": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized
Warning FailedCreatePodSandBox 3m2s (x176 over 41m) kubelet (combined from similar events): Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox "be41a317c2e14b4096f2f8f0d4bfaa8a80572f7365ab3d92c20be75fe97304f4": plugin type="calico" failed (add): error getting ClusterInformation: connection is unauthorized: Unauthorized

出现了网络没有认证通过的问题,根据报错日志,基本上确定是Calico的问题。

解决:把/etc/cni/net.d/目录下的calico-kubeconfig10-calico.conflist文件移到另一个目录下备份,然后重启机器刷新Calico的配置。

等待容器启动完成即可。

自制应用

kubesphere的服务网格功能是在企业空间的自制应用里使用的,所以我们先要创建企业空间,然后把项目加到企业空间里,再创建一个自制应用,这个自制应用就是我们部署的整个系统,然后再把各个微服务通过标签加入到这个自制应用里。

创建自制应用

基本信息

要启用应用治理才可以使用Istio功能

服务设置

无状态服务为deployment,有状态服务为StatefulSet

填名称,镜像,存储等等信息

路由设置

注:这里只创建了Ingress,这样是访问不了集群内部的,需要启用网关,用nodeport的方式会有一个nodeport

把这个域名解析到任意一个k8s节点,然后就可以域名+nodeport的方式访问:

或者如果部署了ingress-nginx-controller的话:

在ingress里加入ingressClassName: nginx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: bookinfo-ingress
namespace: bookinfo
labels:
app.kubernetes.io/name: bookinfo
app.kubernetes.io/version: v1
annotations:
kubesphere.io/creator: admin
nginx.ingress.kubernetes.io/upstream-vhost: productpage.bookinfo.svc.cluster.local
spec:
ingressClassName: nginx #加入这个参数
rules:
- host: bookinfo.keyfel.com
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: productpage
port:
number: 9080

就可以直接用域名访问

创建完成

这样创建的deploy,svc,StatefulSet,Ingress等等都会自动加入匹配此自制应用的标签以及注解:

Ingress:

svc:

deploy:

已运行资源加入自制应用

但是由于我们之前已经在运行的微服务,并没有这些标签,所以我们需要在已经存在的资源加入这些标签及注解,以加入此自制应用来使用Istio功能。

  1. Deployment 有 app version 这两个 label;Service 有 app Label;且 Deploy 与 service 的 App Label 一致,等于 Service Name(Istio 需要)
  2. 在一个应用内,所有资源需要有这两个标签 app.kubernetes.io/name=, app.kubernetes.io/version=(Application 需要)
  3. Deployment Name 为 Service Name 后面加 v1;如 Serevice 为 nginx, deployment 为 nginx-v1
  4. Deployment Template 中有相应 Annotation (Istio Sidecar 自动注入需要)
1
2
3
4
template:
metadata:
annotations:
sidecar.istio.io/inject: "true"

​ 5.Service/Deployment 有相应 Annotation (KubeSphere CRD Controller 会自动将 Service 同步为 Virtual Service/DestinationRules,CRD controller 需要)

1
2
3
4
5
6
7
8
9
10
11
# Service
kind: Service
metadata:
annotations:
servicemesh.kubesphere.io/enabled: "true"

# Deployment
kind: Deployment
metadata:
annotations:
servicemesh.kubesphere.io/enabled: "true"

(注:已创建的deployment的selector修改不了,只能把yaml文件复制出来,把原deployment删了再根据yaml创建新的)

即:

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
47
48
49
deployment需要加:
metadata:
labels:
app: $SERVICE
app.kubernetes.io/name: $自制应用名称
app.kubernetes.io/version: v1
version: v1
annotations:
servicemesh.kubesphere.io/enabled: 'true'
spec:
selector:
matchLabels:
app: $SERVICE
app.kubernetes.io/name: $自制应用名称
app.kubernetes.io/version: v1
version: v1
template:
metadata:
labels:
app: $SERVICE
app.kubernetes.io/name: $自制应用名称
app.kubernetes.io/version: v1
version: v1
annotations:
sidecar.istio.io/inject: 'true'

svc需要加:
metadata:
labels:
app: $SERVICE
app.kubernetes.io/name: $自制应用名称
app.kubernetes.io/version: v1
annotations:
servicemesh.kubesphere.io/enabled: "true"
spec:
ports:
- name: http-web
selector:
app: $SERVICE
app.kubernetes.io/name: $自制应用名称
app.kubernetes.io/version: v1

Ingress需要加:
metadata:
labels:
app.kubernetes.io/name: $自制应用名称
app.kubernetes.io/version: v1
annotations:
nginx.ingress.kubernetes.io/upstream-vhost: $SERVICE.$NAMESPACE.svc.cluster.local

Ingres-nginx注入sidecar容器

Istio有自己的网关ingress-gateway,但是配置Gateway在Kubesphere没法可视化,所以还是使用可以可视化的ingress-nginx,需要在ingress-nginx的deployment注入sidecar容器以及在Ingress的注解加上nginx.ingress.kubernetes.io/upstream-vhost: $SERVICE.$NAMESPACE.svc.cluster.local,然后通过Ingress来访问就可以正常使用Istio的功能。

注入sidecar:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spec:
replicas: 2
selector:
matchLabels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
template:
metadata:
creationTimestamp: null
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.8.1
pod-template-hash: 6646d898d8
annotations:
kubesphere.io/restartedAt: '2025-07-08T02:17:42.455Z'
sidecar.istio.io/inject: 'true' #加入这个注解

需要注意一下,可能本地集群部署,没有负载均衡,所以ingress-nginx开启了hostNetwork: true和hostPort: 80,直接在node节点上监听了80端口,但是如果开启了hostNetwork: true,sidecar容器是注入不了的。

所以这里可以把hostNetwork: true去掉,只保留hostPort: 80,这样iptables会创建规则,把流量转到k8s集群内的pod,还是可以使用node节点的80端口,又可以注入sidecar容器。

或者使用metallb负载均衡器,这里不再赘述,metallb部署可以看我另一篇文章

使用Istio

部署实例应用bookinfo演示Istio功能

部署完成:

可以点击流量监控查看拓扑图:

以及链路追踪:

熔断功能:

点击具体的微服务,右边有个流量管理,我们点击启用熔断器,确认就可以使用熔断功能,会自动更新相关的VirtualService和DestinationRule:

查看DestinationRule:

金丝雀发布

点击灰度发布,选择金丝雀发布:

选择哪个微服务需要灰度发布:

配置要灰度的版本信息:

配置灰度策略:

按比例或者指定参数:

创建完发布任务之后就灰度版本就发布了,VirtualService和DestinationRule会自动调整,可以查看验证一下:

VirtualService:

DestinationRule:

验证灰度结果:

刷新可以看到两个版本在切换:

v1版本:

gray版本:

回到控制台,点击发布任务:

可以点进去查看当前版本和灰度版本的流量,请求成功率等等信息,以及调整灰度比例:

灰度版本有问题的话可以回滚到v1版本,灰度版本验证没问题需要全量发布的话可以选择gray版本接管流量。

因为直接用灰度版本去接管全部流量的话,每次灰度发布都会创建不同的deploy,因为我的CI/CD用jenkins发布,写死了deploy的名称,所以我的做法是验证完灰度版本没问题,先把gray版本接管所有流量,然后把v1版本的镜像改为灰度版本的镜像,等容器启动完之后,现在两个版本都是gray版本的镜像,再把v1版本接管所有流量,然后再把发布任务删除(必须要有一个版本接管全部流量之后才删的了灰度发布任务),这样也就相当于是gray版本全量发布了。

删除灰度发布任务后,非接管流量的那个版本对应的deploy等等资源也会自动删除。

按需注入sidecar

如果微服务很多,每个微服务都注入sidecar容器的话,会有性能损耗,有延迟,需要的服务器资源也多,所以我们可以按需注入sidecar容器,需要灰度发布的微服务就注入sidecar,不需要的就不注入,不注入的话请求就是k8s默认的负载均衡。

但是要注意不能使用mTLS,使用mTLS的话,没注入sidecar容器的都会通信失败,且如果被调用方需要灰度,那么被调用方和调用方都需要注入sidecar,规则才能生效。

即:

所以如果你的微服务相互间都有调用的话,那还是需要都注入sidecar,规则才能生效。

使用Istio遇到的问题

nacos注册

微服务注入Sidecar后服务注册到Nacos不稳定,时有时无。

解决办法:

nacos的headless svc的9848,9849端口加上appProtocol: tcp

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
kind: Service
apiVersion: v1
metadata:
name: nacos-headless
namespace: qifu
labels:
app: nacos
spec:
ports:
- name: server
protocol: TCP
port: 8848
targetPort: 8848
- name: client-rpc
protocol: TCP
appProtocol: tcp #添加这个
port: 9848
targetPort: 9848
- name: raft-rpc
protocol: TCP
appProtocol: tcp #添加这个
port: 9849
targetPort: 9849
- name: old-raft-rpc
protocol: TCP
port: 7848
targetPort: 7848
selector:
app: nacos
clusterIP: None
clusterIPs:
- None
type: ClusterIP
sessionAffinity: None
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
internalTrafficPolicy: Cluster

参考文档资料:

1.https://github.com/alibaba/nacos/issues/10141
2.https://github.com/nacos-group/nacos-k8s/issues/221
3.https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/

tcp探针健康检查失败

Istio官网提出:

TCP 探针检查需要特殊处理,因为lstio 将所有传入的流量重定向到 Sidecar,所以所有TCP 端口都显示为开放。kubelet仅检查某个进程是否正在监听指定的端口,因此只要 Sidecar 正在运行,该探针就总会成功。

所以即使pod主容器还没启动完成,pod也会显示就绪。

解决办法是使用httpget探针检测,我用的Istio版本是1.11.2,此问题在1.12.0已修复,或者升级Istio。

nacos访问失败

nacos服务也是k8s集群里跑的,通过Ingress访问,Ingress-nginx注入sidecar后,发现访问nacos报404.

解决办法:

nacos的Ingress添加注解nginx.ingress.kubernetes.io/upstream-vhost: $svc.$ns.svc.cluster.local

但是又出现新的问题:

Ingress添加注解nginx.ingress.kubernetes.io/upstream-vhost后,浏览器访问不是根路径时,重写路径会
导致浏览器访问会跳转到k8s集群内部的值: $svc.$ns.svc.cluster.local,从而访问失败。

解决办法:

方法一:

下游服务是nginx时,可修改nginx配置:

1
2
3
location /system {
return 301 $http_x_forwarded_proto://$http_x_forwarded_host/system/;
}

方法二:

修改Ingress,加上以下注解,重定向:

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
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: nacos
namespace: qifu
annotations:
nginx.ingress.kubernetes.io/proxy-redirect-from: 'http://nacos-headless.qifu.svc.cluster.local/'
nginx.ingress.kubernetes.io/proxy-redirect-to: '$scheme://$host/'
nginx.ingress.kubernetes.io/server-snippet: |
proxy_set_header Host $host;
nginx.ingress.kubernetes.io/upstream-vhost: nacos-headless.qifu.svc.cluster.local
nginx.ingress.kubernetes.io/use-regex: 'true'
spec:
ingressClassName: nginx
tls:
- hosts:
- nacos.keyfel.com
secretName: keyfel-secrect
rules:
- host: nacos.keyfel.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nacos-headless
port:
number: 8848

一个Ingress多个路径怎么配置注解

可能有一个Ingress不同路径路由到不同服务的情况,但是ingress-nginx注入sidecar之后,需要添加注解nginx.ingress.kubernetes.io/upstream-vhost,只有一个值匹配不了多个服务,所以我们需要把多个服务拆解为多个ingress管理。

Ingress-nginx获取不到真实IP

Ingress-nginx注入sidecar后日志获取到的来源IP全部为127.0.0.6,无法获取真实IP(前端传入的X-Forwarded-For中的IP被覆盖)。

解决办法:

1.ingress controller的cm中加上如下data:

1
use-forwarded-headers: "true"

然后重启这个pod,发送测试请求:

1
curl -H "X-Forwarded-For: 1.2.3.4" http://your-domain.com

查看ingress-nginx日志:

1
kubectl logs -f deployment/ingress-nginx-controller -n ingress-nginx

发现已经可以透传Header中的外部IP。

然后添加以下envoyfilter:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: preserve-client-ip
namespace: istio-system
spec:
configPatches:
- applyTo: NETWORK_FILTER
match:
context: ANY
listener:
filterChain:
filter:
name: "envoy.filters.network.http_connection_manager"
patch:
operation: MERGE
value:
typed_config:
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager"
skip_xff_append: false
xff_num_trusted_hops: 1
use_remote_address: true
EOF

发现可以正常获取真实IP了:

Thank you for your accept. mua!
-------------本文结束感谢您的阅读-------------