找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

964

积分

0

好友

121

主题
发表于 昨天 16:34 | 查看: 0| 回复: 0

在之前的系列文章中,我们花了大量篇幅,从记录后端Pod真实IP开始,逐步引入Envoy,并解决了诸如配置自动重载、流量劫持、Sidecar自动注入等需求。同时,我们也探索了Envoy的多种能力,包括熔断、流控、分流、透明代理和可观测性等,这些已足以支撑起一个完整的服务治理框架。

而今天要介绍的Istio,正是上述所有功能的集大成者。从本文开始,我们将详细介绍Istio,并与之前手动实现的方案进行对比,为大家在未来选择服务治理工具时提供参考。

Istio架构

我们先来看看Istio的核心架构。Istio采用控制面与数据面分离的设计,这也是现代Service Mesh的典型架构。

           ┌──────────────┐
           │   istiod     │   ← 控制面
           │ (Pilot+CA)   │
           └──────┬───────┘
                  │ xDS (gRPC / TLS)
                  │
┌────────────┐    │    ┌────────────┐
│  Envoy     │◄───┼───►│   Envoy    │  ← 数据面
│ (Sidecar)  │         │ (Sidecar)  │
└─────▲──────┘         └─────▲──────┘
      │ iptables             │
      │                      │
   App Pod                App Pod
  • 数据面就是我们之前深入研究的Envoy代理,它负责处理四层和七层的网络流量,执行熔断、限流、观测等具体策略。简单说,Envoy就是执行控制面下发指令的“士兵”。
  • 控制面主要是istiod组件,它的核心职责是将配置下发到每一个Envoy。在Istio中,配置以Kubernetes自定义资源(CRD)的形式存在,因此istiod需要持续监听Kubernetes API Server,将这些资源的变化“翻译”成Envoy能理解的配置(即xDS协议),并动态下发。

至于Istio的其他资源对象,我们将在后续文章中详细介绍。

安装Istio

理论先行,实践紧随。我们先动手把Istio安装到K8s集群中。

首先,你需要一个可用的Kubernetes集群。然后,下载Istio命令行工具(此步骤可能需要访问外网)。

curl -L https://istio.io/downloadIstio | sh -
cd istio-*
sudo ln -s $PWD/istioctl /usr/local/bin/istioctl

安装前,建议先验证集群兼容性。

istioctl x precheck

验证通过后,使用默认配置进行安装。

istioctl install --set profile=default -y

如果你的网络环境无法直接拉取Docker镜像,需要预先准备或使用镜像加速。本次安装主要需要以下两个镜像:

docker.io/istio/pilot:1.28.2
docker.io/istio/proxyv2:1.28.2

安装完成后,检查istio-system命名空间下的Pod状态。

▶ kubectl -n istio-system get pod
NAME                                   READY   STATUS    RESTARTS   AGE
istio-ingressgateway-865c448856-qs8s2   1/1     Running   0          8s
istiod-86c75775bb-j7qbg                 1/1     Running   0          12s

看到istiodistio-ingressgateway都处于Running状态,说明安装成功。那么,接下来从哪里开始体验呢?

体验Sidecar自动注入

Istio一个非常便捷的功能就是Sidecar的自动注入。只需为命名空间打上标签,该命名空间下新建的Pod就会自动注入Sidecar容器。

kubectl label namespace default istio-injection=enabled

这和之前我们手动管理Envoy Sidecar的方式类似。打上标签后,重启现有的Deployment即可生效。

kubectl rollout restart deploy nginx-test

重启后,Pod中应该已经注入了Sidecar。我们来观察一下Istio究竟做了什么。首先查看Pod的事件记录。

Events:
  Type    Reason     Age   From               Message
  ----    ------     ----  ----               -------
  Normal  Scheduled  8s    default-scheduler  Successfully assigned default/nginx-test-6f855b9bb9-9phsv to wilson
  Normal  Pulled     8s    kubelet            Container image “docker.io/istio/proxyv2:1.28.2“ already present on machine
  Normal  Created    8s    kubelet            Created container: istio-init
  Normal  Started    8s    kubelet            Started container istio-init
  Normal  Pulled     8s    kubelet            Container image “docker.io/istio/proxyv2:1.28.2“ already present on machine
  Normal  Created    8s    kubelet            Created container: istio-proxy
  Normal  Started    8s    kubelet            Started container istio-proxy
  Normal  Pulled     6s    kubelet            Container image “registry.cn-beijing.aliyuncs.com/wilsonchai/nginx:latest“ already present on machine
  Normal  Created    6s    kubelet            Created container: nginx-test
  Normal  Started    5s    kubelet            Started container nginx-test

从事件可以看出,Pod内现在有三个容器:一个istio-init初始化容器,一个业务容器nginx-test,以及Sidecar容器istio-proxy

其中,istio-init容器的配置尤为关键:

Init Containers:
  istio-init:
    Container ID:  containerd://2bf56cd37703d82a2a43e94e8c8d683ed66b0afe22bf7148a597d67b89a727a8
    Image:         docker.io/istio/proxyv2:1.28.2
    Image ID:      docker.m.daocloud.io/istio/proxyv2@sha256:39065152d6bd3e7fbf6bb04be43c7a8bbd16b5c7181c84e3d78fa164a945ae7f
    Port:          <none>
    Host Port:     <none>
    Args:
      istio-iptables
      -p
      15001
      -z
      15006
      -u
      1337
      -m
      REDIRECT
      -i
      *
      -x

      -b
      *
      -d
      15090,15021,15020
      --log_output_level=default:info
...

可以看到,Istio和我们之前手动操作时一样,也是利用istio-iptables命令在Pod网络空间内设置iptables规则,从而将进出业务容器的流量劫持并转发到Sidecar代理(Envoy)中处理。

现在,尝试访问这个已经注入Sidecar的服务。

▶ curl 10.22.12.178:30785/test
i am backend in backend-6d76f54494-g6srz

访问成功!但查看Sidecar的日志,却发现是空的。为了方便调试,我们开启访问日志并输出到标准输出。

kubectl -n istio-system edit cm istio

在ConfigMap中找到mesh配置,添加或修改accessLogFile字段:

apiVersion: v1
data:
  mesh: |-
    accessLogFile: /dev/stdout
  ...

至此,我们完成了对Istio第一个核心功能——Sidecar自动注入及流量劫持的探索。

遭遇“Upgrade Required” (HTTP 426) 错误

接下来,我们要模拟一个更真实的微服务场景:让一个注入了Sidecar的Nginx,去访问另一个同样注入了Sidecar的后端服务(Backend)。当前的架构是下图左侧,目标是演进到右侧的架构。

Istio Sidecar双注入流量路径架构图

其实很简单,为后端服务所在的命名空间打上注入标签后,重启Backend的Deployment即可。

▶ kubectl get pod -owide
NAME                          READY   STATUS        RESTARTS   AGE     IP            NODE     NOMINATED NODE   READINESS GATES
backend-5d4d7b598c-f7852      2/2     Running       0          13s     10.244.0.49   wilson   <none>           <none>
nginx-test-6f855b9bb9-9phsv   2/2     Running       0          58m     10.244.0.48   wilson   <none>           <none>

注入完成后,再次进行测试。

▶ curl 10.22.12.178:30785/test
Upgrade Required

请求失败了,返回了 HTTP 426 (Upgrade Required) 状态码。查看Nginx Sidecar的日志:

▶ kubectl logs -f -l app=nginx-test -c istio-proxy
[2026-01-26T07:54:42.977Z] “GET /test HTTP/1.1” 426 - upstream=10.244.0.48:80 duration=6ms route=default
[2026-01-26T07:54:42.978Z] “- - -” 0 - upstream=10.105.148.194:10000 duration=9ms route=-

问题分析:当只有Nginx注入Sidecar而Backend没有时,请求是正常的。一旦双方都注入了Sidecar,就出现了426错误。这是为什么呢?

这涉及到Istio/Envoy的智能协议检测与优化机制:

  1. 单Sidecar场景:Nginx的Sidecar发现目标(Backend)是一个普通的HTTP服务(无Sidecar),它会退回到“透明代理”模式,简单地将Nginx发出的原始流量透传出去,因此不会出错。
  2. 双Sidecar场景:Nginx的Sidecar发现目标服务也有Sidecar,它会尝试建立一个高度优化的、基于mTLS的隧道进行通信(mTLS后续文章会详述)。此时,如果Nginx发出的原始请求不符合Envoy对这种隧道协议的预期,就可能触发问题。

一个常见的原因在于 HTTP协议版本。Nginx的proxy_pass指令默认使用HTTP/1.0,而Istio的Sidecar间隧道强烈依赖HTTP/1.1的特性(如持久连接)。当使用HTTP/1.0发起请求时,可能因为缺少必要的头部(如Host)或协议特性不支持,导致通信失败,从而产生426错误。

解决方案

有两种主流方法可以解决这个426错误。

方法一:改造Nginx配置(推荐)

在Nginx的location配置块中,显式指定使用HTTP/1.1协议并添加必要的Host头部。

        location /test {
            proxy_http_version 1.1; # 必须添加这一行
            proxy_set_header Host $host; # 这一行也是必须的
            proxy_pass http://backend_ups;
        }
  • 原理:强制Nginx使用HTTP/1.1协议与上游通信,并补全Host头部,使其符合现代HTTP协议及Istio Sidecar的预期。
  • 优点:保持服务为HTTP类型,可以继续享受Istio提供的HTTP层监控、追踪和高级路由功能。
  • 缺点:需要有权修改应用的Nginx配置。

方法二:将Backend Service声明为TCP服务

如果无法修改Nginx配置,可以尝试修改Backend服务的定义,将其端口协议从HTTP改为TCP

apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: default
spec:
  ports:
  - name: tcp-80 # 关键修改:原为 http-80 改为 tcp-80
    port: 10000
    protocol: TCP
    targetPort: 10000
  selector:
    app: backend
  • 原理:Istio只有在识别到流量是HTTP时才会进行深度的七层协议分析和处理。将服务声明为TCP,Istio会将其视为原始的字节流进行透传,不再关心其内容是HTTP/1.0还是1.1,从而绕过协议冲突。
  • 优点:无需改动应用配置,一劳永逸。
  • 缺点:你会失去Istio针对该服务的所有七层能力,包括基于HTTP路径/头部的路由、HTTP指标监控(如QPS、4xx/5xx错误率)以及HTTP层的分布式追踪。

深入理解:HTTP/1.0 与 HTTP/1.1

为什么Istio如此“挑剔”HTTP协议版本?我们简单回顾一下两者的核心区别,这能帮助我们更好地理解各类网络中间件的兼容性问题。

  • 连接管理(最显著的区别)

    • HTTP 1.0:默认短连接。每个请求完成后,TCP连接立即关闭。如果页面有多个资源,就需要多次握手,效率低下。
    • HTTP 1.1:默认持久连接(Keep-Alive)。一个TCP连接可被多个请求复用,极大提升了性能。
    • 在Istio中:Envoy Sidecar之间需要维持高性能的持久连接隧道。HTTP 1.0的频繁连接断开行为会被视为非标准或低效,可能引发问题。
  • Host头部(虚拟主机的基石)

    • HTTP 1.0:设计时认为一个IP对应一个网站,因此请求头中不需要携带域名信息。
    • HTTP 1.1:为支持虚拟主机(一个IP托管多个网站),强制要求请求必须包含Host头部。
    • 在K8s/Istio中:服务发现和路由决策严重依赖Host头。如果Nginx用HTTP/1.0转发且不补全Host头,后端服务很可能无法正确响应。

除了上述两点,两者还有其他重要差异:

特性 HTTP 1.0 HTTP 1.1
连接模型 默认短连接,每次请求新开TCP 默认持久连接 (Keep-Alive),复用TCP
Host 头部 可选 (导致无法支持虚拟主机) 必须 (支持一IP多域名)
流水线 (Pipelining) 不支持 支持 (但在实际应用中受限)
断点续传 不支持 支持 (通过 Range 头部)
缓存控制 简单 (Expires) 复杂且强大 (Cache-Control, ETag)
常见场景 许多旧软件(如 Nginx proxy_pass)的默认值 现代Web应用和中间件的标准

小结与后记

本章作为Istio的入门实践,我们成功安装了Istio,体验了其Sidecar自动注入能力,并诊断和修复了一个典型的双Sidecar环境下的HTTP 426协议错误。这仅仅是开始,后续我们将把之前在Envoy中实现的熔断、限流、观测等最佳实践,逐步迁移到Istio的配置模型中来。

附加提示:如果整个命名空间已启用Sidecar注入(istio-injection=enabled),但某个特定的Deployment不希望被注入,可以使用以下命令为其添加注解来禁用注入。

kubectl patch deployment nginx -p '{"spec":{"template":{"metadata":{"annotations":{"sidecar.istio.io/inject":"false"}}}}}'

希望这篇结合实战踩坑经验的介绍,能帮助你更顺畅地迈出使用Istio的第一步。如果你对云原生技术有更多兴趣,欢迎到云栈社区与更多开发者交流探讨。




上一篇:物联网设备OTA技术全解析:架构方案与核心流程详解
下一篇:从内核视角剖析Docker底层核心:Namespace、cgroups与UnionFS
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-1 00:15 , Processed in 1.300497 second(s), 47 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表