Istio 学习笔记
常用的 Istio 资源类型
VirtualService
控制请求路由到网格中哪个服务。
- hosts: 可以配置为带通配符前缀的 DNS 名称,或者 IP 地址。如果采用缩写,比如 reviews,那么在 Kubernetes 平台上就会根据 VirtualService 所在的命名空间,解释为 reviews.default.svc.cluster.local
- gateways: 该 VirtualService 应用于哪些 Gateway 和 Sidecar。如果没有配置,那么默认值为 mesh。mesh 表示所有的 Sidecar。
- http HTTPRoute[]: 匹配 HTTP 流量的有序路由规则列表,按顺序应用于 Kubernetes service.spec.ports.name(以 http-/http2-/grpc-* 命名)、gateway.spec.servers.port.protocol(HTTP/HTTP2/GRPC/ TLS-terminated-HTTPS)、serviceentry.spec.ports.protocol(HTTP/HTTP2/GRPC),第一条匹配路由规则将被使用。
- name: 路由的名字,会被记录到 access log 中
- match HTTPMatchRequest[]: 单个 HTTPMatchRequest 中的条件是 AND 语义,多个 HTTPMatchRequest 之间是 OR 语义
- name: 与路由的名字拼接后记录到 access log 中
- uri StringMatch: 匹配 URI,支持三种匹配规则:
- exact: 精确匹配
- prefix:前缀匹配
- regex:正则匹配
- ignoreUriCase: 不区分大小写匹配 URI
- scheme StringMatch: 匹配 URI Scheme
- method StringMatch: 匹配 HTTP method
- authority StringMatch: 匹配 HTTP Authority
- headers map<string, StringMatch>: 必须小写,并且采用连字符作为分隔符,例如 x-request-id。如果值为空,则判断请求头是否存在。注意 key 如果为 uri、scheme、method、authority 将被忽略
- port: 端口
- sourceLabels: 限制部分 Sidecar 应用此匹配规则,gateways 必须包含 mesh 值
- gateways: 覆盖顶层的 gateways
- queryParams map<string, StringMatch>: 匹配查询参数,不支持前缀匹配
- withoutHeaders map<string, StringMatch>: 与 headers 参数相反的含义
- sourceNamespace: 限制应用此匹配规则的命名空间,gateways 必须包含 mesh 值
- route HTTPRouteDestination[]: 流量被转发到哪个服务上
- destination Destination:
- host: 必须存在于 service registry(Kubernetes services)中,或来自 ServiceEntry 中定义的 hosts
- subset: DestinationRule 中定义的 subset 名字
- port: 转发的端口
- weight: 权重
- headers Headers: 对请求头或响应头进行修改
- destination Destination:
- redirect: 配置重定向
- delegate: route 和 redirect 为空时才可以设置,用于指定把流量转交给其他 VirtualService 处理
- rewrite: 转发请求之前对 uri 和 Authority/Host 请求头进行重写
- timeout: 请求超时
- retries: 重试策略
- fault: 故障注入
- mirror Destination: 流量镜像
- mirrorPercentage: 流量镜像百分比,默认 100%
- corsPolicy:CORS 策略
- headers Headers:对请求头或响应头进行修改
- tls: 匹配 HTTPS 流量的有序路由规则列表。
- tcp: 匹配 TCP 流量的有序路由规则列表。
- exportTo string[]: 控制 VirtualService 能否被其他命名空间的 Gateway 和 Sidecar 使用,如果不设置值,默认导出到所有命名空间
- . 表示当前命名空间
- * 表示所有命名空间
Destination Rule
控制流量转发给服务的哪些 Workload。
- host: 必须存在于 service registry(Kubernetes services)中,或来自 ServiceEntry 中定义的 hosts
- trafficPolicy TrafficPolicy:
- loadBalancer LoadBalancerSettings
- simple:
- ROUND_ROBIN: 默认
- LEAST_CONN: 最少请求
- RANDOM: 随机
- PASSTHROUGH: 透传
- consistentHash: 根据请求信息做哈希
- localityLbSetting LocalityLoadBalancerSetting: 用于控制地域上的就近访问
- simple:
- connectionPool ConnectionPoolSettings: 关于上游的连接池设置
- tcp TCPSettings:
- maxConnections: 最大连接数,默认 2^32-1
- connectTimeout: TCP 连接超时时间,默认 10s
- tcpKeepalive:
- probes: 最大发送多少个探测包,没有收到响应,则认为连接已关闭,默认使用操作系统的配置(Linux 默认为 9)
- time: 探测包发送之前的空闲时间,默认使用操作系统的设置(Linux 默认为 7200s)
- interval:探测包的发送间隔,默认使用操作系统的设置(Linux 默认为 75s)
- http HTTPSettings:
- http1MaxPendingRequests:upstream cluster 最大等待请求数(没有空闲链接可以处理的请求会排队),默认 2^32-1
- http2MaxRequests:upstream cluster 最大允许并行的请求数,默认 2^32-1
- 并非只用于 http2,http1 同样受限制
- maxRequestsPerConnection:一个连接上最多可以发送多少个请求,默认 0,无限制
- 如果配置了 2,那么一个连接在处理完 2 个请求后就会被关闭
- maxRetries:upstream cluster 最大允许并行的重试请求数,默认 2^32-1
- idleTimeout:默认 1h,连接最长空闲时间,到期关闭,应用于 HTTP1.1 和 HTTP2 的连接
- h2UpgradePolicy:指定 HTTP1.1 连接是否升级到 HTTP2
- useClientProtocol bool: 是否使用客户端的协议,如果为 true,那么 h2UpgradePolicy 将无效
- tcp TCPSettings:
- outlierDetection OutlierDetection: 对于上游 HTTP/TCP 服务中的每个端点的熔断配置。对于 HTTP 服务,如果端点连续返回 5xx 错误,将会从连接池移除一段时间。对于 TCP 服务,如果端点出现连续的连接超时或连接失败,也会从连接池移除一段时间。
- consecutiveGatewayErrors: 连续错误阈值,HTTP 502、503、504 和 TCP 连接超时、连接错误/失败都会认为是 gateway error,默认 0。
- consecutive5xxErrors: 连续错误阈值,HTTP 5xx 和 TCP 连接超时、连接错误/失败都会认为是 5xx error,默认 5。
- interval: 驱逐分析间隔,默认 10s
- baseEjectionTime: 最小驱逐时间基数,实际驱逐时间会根据驱逐次数不断增大,默认 30s
- maxEjectionPercent: 上游端点最大驱逐百分比,默认 10%
- minHealthPercent: outlierDetection 启用的最低健康度。如果连接池中的健康端点百分比低于该值,outlierDetection 将会禁用,并且请求会负载均衡到所有端点,默认 0%。
- tls ClientTLSSettings: 配置与上游连接的 SSL/TLS
- mode:
- DISABLE: 不启用 TLS 连接
- SIMPLE: 建立仅服务端认证的 TLS 连接
- MUTUAL: 通过指定客户端证书建立 mTLS 连接
- ISTIO_MUTUAL: 使用 Istio 自动生成的客户端证书建立 mTLS 连接,ClientTLSSettings 其他字段值需为空
- clientCertificate
- privateKey
- caCertificates
- credentialName
- subjectAltNames
- sni
- mode:
- portLevelSettings PortTrafficPolicy[]: 为单独的端口配置流控,覆盖顶层配置
- port: 指定端口号
- loadBalancer LoadBalancerSettings
- connectionPool ConnectionPoolSettings
- outlierDetection OutlierDetection
- tls ClientTLSSettings
- loadBalancer LoadBalancerSettings
- subsets: 服务端点的子集
- name: 子集的名字,会用于 VirtualService 的路由中
- labels: 根据标签过滤出子集
- trafficPolicy TrafficPolicy: 可以覆盖顶层的设置
- exportTo string[]: 控制 VirtualService 能否被其他命名空间的 Gateway 和 Sidecar 使用,如果不设置值,默认导出到所有命名空间
- . 表示当前命名空间
- * 表示所有命名空间
Gateway
网格边缘的负载均衡,负责接收和发送流量。
- servers Server[]:
- port Port: 监听的端口
- hosts string[]: 在 port 上暴露的服务
- tls ServerTLSSettings: 控制是否所有的 http 请求都重定向为 https 和 TLS 模式
- name: 服务的名称
- selector: 应用到哪些 gateway 实例上
Service Entry
将网格外的服务添加进来。
- hosts: 用于匹配 VirtualService 和 DestinationRule。
- 对于 HTTP 流量,Host/Authority 请求头会用来与 hosts 匹配
- 对于 HTTPs/TLS 流量,SNI 会用来与 hosts 匹配
- 当 resolution=DNS 时,并且 endpoints 为空,那么 hosts 字段会被作为端点的 DNS 进行路由
- addresses: 服务的虚拟 IP,可以是 CIDR。
- ports Port[]: 服务的端口
- location Location: 服务是否应该被认为是网格内的,还是网格外的
- resolution Resolution:
- NONE: 到来的连接已经解析过了
- STATIC: 使用 endpoints 中指定的静态 IP 地址作为服务的端点
- DNS: 如果 endpoints 为空,则使用不带通配符的 hosts 解析出端点地址;如果 endpoints 不为空,DNS 类型的地址会用于解析出服务的端点
- endpoints WorkloadEntry[]:
- address: IP 或 DNS
- ports: 端口映射
- labels: 标签
- network: Istio mesh 跨越多个集群时使用
- locality
- weight
- serviceAccount
- workloadSelector WorkloadSelector: 与 endpoints 互斥,location 必须为 MESH_INTERNAL
- exportTo string[]
- subjectAltNames: 如果指定,sidecar 会校验服务器证书的 SAN 是否匹配一个指定的值
流量管理
协议选择
1、自动协议选择
Istio 能自动检测 HTTP 和 HTTP/2 的流量,其他流量会被当作 TCP 的字节流。
2、显式协议选择
有两种配置方式:
- Kubernetes service 端口的名字:
<protocol>[-<suffix>]
- 在 Kubernetes 1.18+,通过 Service 字段 appProtocol:
支持如下协议:
- http
- http2
- https
- tcp
- tls
- grpc
- grpc-web
- mongo
- mysql*
- redis*
- udp (UDP will not be proxied, but the port can be explicitly declared as UDP)
带 * 的协议默认是不启用的,如果需要,可通过 Pilot 的环境变量配置。
局部负载均衡
理解 TLS 配置
- External inbound traffic: 来自外部客户端被 sidecar 拦截的入流量。mTLS 模式默认为 PERMISSIVE,即 sidecar 同时接收 mTLS 和 non-mTLS 的流量。如果设置为 STRICT 模式,则 sidecar 只接收 mTLS 的流量;如果设置为 DISABLE 模式,则 sidecar 只接收 non-mTLS 的流量。
- Local inbound traffic: sidecar 转发给应用的流量
- Local outbound traffic: 应用发出的被 sidecar 拦截的流量
- External outbound traffic: 来自应用的流量可能被直接透传,也可能通过 TLS 连接传输,由 DestinationRule 中的 trafficPolicy 控制
关键点:
- PeerAuthentication: 配置什么类型的 mTLS 流量被 sidecar 接收
- DestinationRule: 配置什么类型的 TLS 流量被 sidecar 发送
- Port names 或 automatic protocol selection 决定 sidecar 会把流量解析为何种协议
自动 mTLS: 如果 DestinationRule 没有 TLS 相关的配置,sidecar 会自动判断是否发送 ISTIO_MUTUAL。
Istio 资源与 Envoy 配置对应关系
bootstrap 配置
通过命令istioctl proxy-config bootstrap ${POD_NAME}
可查看 envoy 的启动时的配置,即 istio-proxy 容器中 envoy 启动时使用的配置文件 /etc/istio/proxy/envoy-rev0.json,详细配置参考 envoy Bootstrap 文档。
- node: 节点信息
- admin: 本地管理 HTTP 服务信息
- layeredRuntime: 运行时动态配置相关
- statsConfig: 内部统计配置
- tracing: 外部跟踪服务配置
- staticResources: 静态配置的资源
- clusters
- prometheus_stats: 端口 15000
- agent: 端口 15020
- sds-grpc: 管道 /etc/istio/proxy/SDS
- xds-grpc: istiod.istio-system.svc:15012
- zipkin: zipkin.istio-system.svc:9411
- listeners
- prometheus 数据采集
- 端口:15090
- 路径:/stats/prometheus
- cluster: prometheus_stats
- 健康检查
- 端口:15021
- 路径:/healthz/ready
- cluster: agent
- prometheus 数据采集
- clusters
- dynamicResources: 动态资源配置
- adsConfig: xds-grpc 将 CDS、EDS、RDS 合并在单一的流中下发,避免分布式同步问题
- cdsConfig: ads
- ldsConfig: ads
listener 配置
通过命令istioctl proxy-config listener proxy-c6c5cdf6b-ftznk
可查看 envoy 当前监听的端口,除了 15001(拦截出口流量)和 15006(拦截入口流量),其他端口的监听都是来自 bootstrap 静态配置的 listener 和 kubernetes service。
如下 Kubernetes service:
apiVersion: v1
kind: Service
metadata:
name: proxy
namespace: default
spec:
clusterIP: 10.200.1.132
externalTrafficPolicy: Cluster
ports:
- name: web
nodePort: 30007
port: 80
protocol: TCP
targetPort: 3030
selector:
app: proxy
sessionAffinity: None
type: LoadBalancer
查看 sidecar envoy 在 80 端口上都有哪些 listener,istioctl proxy-config listener --port 80 webserverv1-84596579c6-wqmx4
:
- ADDRESS: 监听的地址
- PORT: 监听的端口
- MATCH: 匹配条件
- ALL 是兜底的规则,会直接走 envoy.filters.network.tcp_proxy
- App: HTTP 表示匹配应用层的 http/1.0、http/1.1、h2c 协议
- DESTINATION: 表示匹配后下一阶段处理
- Route: 80 跟名称为 80 的路由规则进行匹配
- PassthroughCluster: 直接将请求转发给所请求的目的地址,即不会经过 envoy 进行负载均衡
- Cluster: outbound|80||proxy.default.svc.cluster.local 表示将请求转发给 outbound|80||proxy.default.svc.cluster.local 的后端
ADDRESS PORT MATCH DESTINATION
0.0.0.0 80 App: HTTP Route: 80
0.0.0.0 80 ALL PassthroughCluster
10.200.1.132 80 App: HTTP Route: proxy.default.svc.cluster.local:80
10.200.1.132 80 ALL Cluster: outbound|80||proxy.default.svc.cluster.local
route 配置
通过 VirtualService 配置,查看 sidecar envoy 的路由,istioctl proxy-config route webserverv1-84596579c6-xxvhd
:
- NAME: 路由名称
- DOMAINS: 路由匹配的域名,如果域名是 Kubernetes service,则都是缩写的形式 {SVC_NAME}-{NS}
- MATCH: 匹配条件
- VIRTUAL SERVICE: 来自哪个命名空间下的 VirtualService
NAME DOMAINS MATCH VIRTUAL SERVICE
istio-ingressgateway.istio-system.svc.cluster.local:15021 istio-ingressgateway.istio-system /*
15010 istiod.istio-system /*
15014 istiod.istio-system /*
kube-dns.kube-system.svc.cluster.local:9153 kube-dns.kube-system /*
proxy.default.svc.cluster.local:80 proxy /*
istiod.istio-system.svc.cluster.local:853 istiod.istio-system /*
80 istio-egressgateway.istio-system /*
80 istio-ingressgateway.istio-system /*
80 proxy /*
80 webserver regex google_re2:{max_program_size:{value:1024}} regex:".*" webserver.default
80 webserverv1 /*
80 webserverv2 /*
inbound|80|http-web|webserver.default.svc.cluster.local * /*
InboundPassthroughClusterIpv4 * /*
InboundPassthroughClusterIpv4 * /*
* /healthz/ready*
inbound|80|http-web|webserver.default.svc.cluster.local * /*
* /stats/prometheus*
cluster 配置
通过 DestinationRule 配置,查看 sidecar envoy 的 cluster,istioctl proxy-config cluster webserverv1-84596579c6-xxvhd
:
- SERVICE FQDN: 服务端点集合名称
- PORT: 服务监听的端口
- SUBSET: 所属的子集
- DIRECTION: 流量方向
- outbound: 出流量
- inbound: 入流量
- TYPE: 类型
- STATIC: 来自 bootstrap 中的配置
- ORIGINAL_DST: 直接把流量转发给所请求的地址
- EDS: 通过服务发现获取服务端点,选择一个服务端点转发流量
- STRICT_DNS: 通过持续异步地解析域名,获取服务端点
- DESTINATION RULE: 来自哪个命名空间下的 DestinationRule
SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE
BlackHoleCluster - - - STATIC
InboundPassthroughClusterIpv4 - - - ORIGINAL_DST
PassthroughCluster - - - ORIGINAL_DST
agent - - - STATIC
istio-egressgateway.istio-system.svc.cluster.local 80 - outbound EDS
istio-egressgateway.istio-system.svc.cluster.local 443 - outbound EDS
istio-egressgateway.istio-system.svc.cluster.local 15443 - outbound EDS
istio-ingressgateway.istio-system.svc.cluster.local 80 - outbound EDS
istio-ingressgateway.istio-system.svc.cluster.local 443 - outbound EDS
istio-ingressgateway.istio-system.svc.cluster.local 15021 - outbound EDS
istio-ingressgateway.istio-system.svc.cluster.local 15443 - outbound EDS
istio-ingressgateway.istio-system.svc.cluster.local 31400 - outbound EDS
istiod.istio-system.svc.cluster.local 443 - outbound EDS
istiod.istio-system.svc.cluster.local 853 - outbound EDS
istiod.istio-system.svc.cluster.local 15010 - outbound EDS
istiod.istio-system.svc.cluster.local 15012 - outbound EDS
istiod.istio-system.svc.cluster.local 15014 - outbound EDS
kube-dns.kube-system.svc.cluster.local 53 - outbound EDS
kube-dns.kube-system.svc.cluster.local 9153 - outbound EDS
kubernetes.default.svc.cluster.local 443 - outbound EDS
prometheus_stats - - - STATIC
proxy.default.svc.cluster.local 80 - outbound EDS
raw.githubusercontent.com 443 - outbound STRICT_DNS github.default
sds-grpc - - - STATIC
webserver.default.svc.cluster.local 80 - outbound EDS webserver.default
webserver.default.svc.cluster.local 80 http-web inbound STATIC
webserver.default.svc.cluster.local 80 v1 outbound EDS webserver.default
webserver.default.svc.cluster.local 80 v2 outbound EDS webserver.default
webserverv1.default.svc.cluster.local 80 - outbound EDS
webserverv1.default.svc.cluster.local 80 http-web inbound STATIC
webserverv2.default.svc.cluster.local 80 - outbound EDS
xds-grpc - - - STRICT_DNS
zipkin - - - STRICT_DNS
endpoint 配置
通过 Kubernetes service 的 label 获取到服务都哪些 endpoints,查看 sidecar envoy 的 endpoint,istioctl proxy-config endpoint webserverv1-84596579c6-xxvhd
:
- ENDPOINT: Pod 的 IP 地址和监听的端口
- STATUS: 健康状态
- OUTLIER CHECK: 被动健康检查,在 cluster 中配置,但需要一些过滤器(http router, tcp proxy, redis proxy)支持
- CLUSTER: 所属的端点集合
ENDPOINT STATUS OUTLIER CHECK CLUSTER
10.200.0.135:3030 HEALTHY OK outbound|80|v1|webserver.default.svc.cluster.local
10.200.0.135:3030 HEALTHY OK outbound|80||webserver.default.svc.cluster.local
10.200.0.135:3030 HEALTHY OK outbound|80||webserverv1.default.svc.cluster.local
10.200.0.137:15010 HEALTHY OK outbound|15010||istiod.istio-system.svc.cluster.local
10.200.0.137:15012 HEALTHY OK outbound|15012||istiod.istio-system.svc.cluster.local
10.200.0.137:15014 HEALTHY OK outbound|15014||istiod.istio-system.svc.cluster.local
10.200.0.137:15017 HEALTHY OK outbound|443||istiod.istio-system.svc.cluster.local
10.200.0.137:15053 HEALTHY OK outbound|853||istiod.istio-system.svc.cluster.local
10.200.0.139:53 HEALTHY OK outbound|53||kube-dns.kube-system.svc.cluster.local
10.200.0.139:9153 HEALTHY OK outbound|9153||kube-dns.kube-system.svc.cluster.local
10.200.0.141:8080 HEALTHY OK outbound|80||istio-egressgateway.istio-system.svc.cluster.local
10.200.0.141:8443 HEALTHY OK outbound|443||istio-egressgateway.istio-system.svc.cluster.local
10.200.0.141:15443 HEALTHY OK outbound|15443||istio-egressgateway.istio-system.svc.cluster.local
10.200.0.146:8080 HEALTHY OK outbound|80||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.146:8443 HEALTHY OK outbound|443||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.146:15021 HEALTHY OK outbound|15021||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.146:15443 HEALTHY OK outbound|15443||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.146:31400 HEALTHY OK outbound|31400||istio-ingressgateway.istio-system.svc.cluster.local
10.200.0.148:3030 HEALTHY OK outbound|80|v2|webserver.default.svc.cluster.local
10.200.0.148:3030 HEALTHY OK outbound|80||webserver.default.svc.cluster.local
10.200.0.148:3030 HEALTHY OK outbound|80||webserverv2.default.svc.cluster.local
10.200.0.158:3030 HEALTHY OK outbound|80||proxy.default.svc.cluster.local
10.200.1.62:15012 HEALTHY OK xds-grpc
127.0.0.1:3030 HEALTHY OK inbound|80|http-web|webserver.default.svc.cluster.local
127.0.0.1:3030 HEALTHY OK inbound|80|http-web|webserverv1.default.svc.cluster.local
127.0.0.1:15000 HEALTHY OK prometheus_stats
127.0.0.1:15020 HEALTHY OK agent
151.101.228.133:443 HEALTHY OK outbound|443||raw.githubusercontent.com
9.134.236.162:6443 HEALTHY OK outbound|443||kubernetes.default.svc.cluster.local
unix://./etc/istio/proxy/SDS HEALTHY OK sds-grpc
sidecar 对流量的劫持
由 Pod 的 initContainers istio-init 启动命令为:istio-iptables -p "15001" -z "15006" -u "1337" -m REDIRECT -i '*' -x "" -b '*' -d 15090,15021,15020
设置 Pod 的容器网络 iptables 规则:
- -p “15001”: 将所有出站流量重定向到 15001
- -z “15006”: 将所有入站流量重定向到 15006 端口
- -u “1337”: 使用 istio-proxy 用户身份运行
- -m REDIRECT: 将入流量转发给 Envoy 的模式,可以为 REDIRECT 或 TPROXY
- REDIRECT 模式下,借助 conntrack 模块进行连接跟踪,Envoy 会通过 getsockopt(SO_ORIGINAL_DST) 获取原始的目的端口
- TPROXY 模式下,方便应用程序获取源 IP 地址,不会执行连接跟踪
- -i ‘*’: 将所有 IP 地址的出站流量进行重定向
- -x “”: 哪些 IP 地址的出站流量不进行重定向
- -b ‘*’: 将所有 IP 地址的入站流量进行重定向
- -d 15090,15021,15020: 这 3 个端口的入站流量不进行重定向
查看 Pod 的容器网络 iptables 规则:
- 登陆 Pod 所在宿主机
- 获取到 Pod pause 容器在宿主机上的 PID:docker top
docker ps|grep "k8s_POD_webserverv1" | cut -d " " -f1
- 进入 PID 的网络命名空间:nsenter -n –target 28045
- 查看 nat 表的规则:iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 81387 packets, 4883K bytes)
pkts bytes target prot opt in out source destination
81387 4883K ISTIO_INBOUND tcp -- any any anywhere anywhere
Chain INPUT (policy ACCEPT 81387 packets, 4883K bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 76333 packets, 7134K bytes)
pkts bytes target prot opt in out source destination
186 11160 ISTIO_OUTPUT tcp -- any any anywhere anywhere
Chain POSTROUTING (policy ACCEPT 76333 packets, 7134K bytes)
pkts bytes target prot opt in out source destination
Chain ISTIO_INBOUND (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:15008
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:ssh
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:15090
81387 4883K RETURN tcp -- any any anywhere anywhere tcp dpt:15021
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:15020
0 0 ISTIO_IN_REDIRECT tcp -- any any anywhere anywhere
Chain ISTIO_IN_REDIRECT (3 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15006
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- any lo 127.0.0.6 anywhere
0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !127.0.0.1 owner UID match 1337
0 0 RETURN all -- any lo anywhere anywhere ! owner UID match 1337
186 11160 RETURN all -- any any anywhere anywhere owner UID match 1337
0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !127.0.0.1 owner GID match 1337
0 0 RETURN all -- any lo anywhere anywhere ! owner GID match 1337
0 0 RETURN all -- any any anywhere anywhere owner GID match 1337
0 0 RETURN all -- any any anywhere 127.0.0.1
0 0 ISTIO_REDIRECT all -- any any anywhere anywhere
Chain ISTIO_REDIRECT (1 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15001
入站流量分析
流量经过 nat 表链的顺序:PREROUTING -> INPUT -> 用户程序
。
先看 PREROUTING 链的规则:所有的 TCP 流量都执行 ISTIO_INBOUND 动作。
Chain PREROUTING (policy ACCEPT 81387 packets, 4883K bytes)
pkts bytes target prot opt in out source destination
81387 4883K ISTIO_INBOUND tcp -- any any anywhere anywhere
查看 ISTIO_INBOUND 链的规则:
- 对于 TCP 流量目的端口为 15008、ssh、15090、15021、15020 的流量执行 RETURN 动作,即进入 INPUT 链。
- 其他的 TCP 流量执行 ISTIO_IN_REDIRECT 动作。
Chain ISTIO_INBOUND (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:15008
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:ssh
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:15090
81387 4883K RETURN tcp -- any any anywhere anywhere tcp dpt:15021
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:15020
0 0 ISTIO_IN_REDIRECT tcp -- any any anywhere anywhere
查看 ISTIO_IN_REDIRECT 链的规则:所有 TCP 流量都转发到 15006 端口。
Chain ISTIO_IN_REDIRECT (3 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15006
出站流量分析
流量经过 nat 表链的顺序:OUTPUT -> POSTROUTING -> 网卡
。
OUTPUT 链的规则:所有的 TCP 流量都执行 ISTIO_OUTPUT 动作。
Chain OUTPUT (policy ACCEPT 76333 packets, 7134K bytes)
pkts bytes target prot opt in out source destination
186 11160 ISTIO_OUTPUT tcp -- any any anywhere anywhere
查看 ISTIO_OUTPUT 链的规则:
- 规则一,来自 127.0.0.6 的流量,通过 lo 网卡发出的报文执行 RETURN 动作
- https://github.com/istio/istio/issues/29603
- 访问 k8s svc 未声明的 容器端口(targetPort),流量被 sidecar 劫持后,会以 127.0.0.6 的源 IP 转发给监听未声明的端口的进程
- 容器中(宿主机也一样),进程发往其他进程监听的地址和端口的数据包都是只走虚拟设备 loopback device
- 规则二,通过 lo 网卡发出的报文,且目的地址不是 127.0.0.1,数据包来自用户 1337,执行 ISTIO_IN_REDIRECT 动作
- 暂不清楚什么样的场景会有这类流量
- 规则三,通过 lo 网卡发出的报文,数据包不是来自用户 1337,执行 RETURN 动作
- 本地进程间的网络通信流量
- 规则四,数据包来自用户 1337 执行 RETURN 动作
- sidecar 发送的流量
- 规则五、六、七,跟二、三、四重复了
- 规则八,目的地址是 127.0.0.1,执行 RETURN 动作
- 规则九,对所有流量,执行 ISTIO_REDIRECT 动作
Chain ISTIO_OUTPUT (1 references)
pkts bytes target prot opt in out source destination
0 0 RETURN all -- any lo 127.0.0.6 anywhere
0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !127.0.0.1 owner UID match 1337
0 0 RETURN all -- any lo anywhere anywhere ! owner UID match 1337
186 11160 RETURN all -- any any anywhere anywhere owner UID match 1337
0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !127.0.0.1 owner GID match 1337
0 0 RETURN all -- any lo anywhere anywhere ! owner GID match 1337
0 0 RETURN all -- any any anywhere anywhere owner GID match 1337
0 0 RETURN all -- any any anywhere 127.0.0.1
0 0 ISTIO_REDIRECT all -- any any anywhere anywhere
查看 ISTIO_REDIRECT 链的规则:所有流量都重定向到 15001 端口
Chain ISTIO_REDIRECT (1 references)
pkts bytes target prot opt in out source destination
0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15001
流量流向关系图解
流程:Pod A 作为客户端向 Pod B 发送请求,Pod B 接收到来自 Pod A 的请求后,需要调用 Pod C 之后,才响应 Pod A。
1、Pod A 发送请求给 Pod B
- Pod A 与 Pod B 建立 TCP 连接:10.200.0.190:38568 <-> 10.200.0.188.80
- Pod B 中的 iptables 规则拦截了 Pod A 发过来的数据包,转发到 sidecar 监听的 15006 端口
- sidecar 与 APP 建立 TCP 连接:127.0.0.1.60180 <-> 127.0.0.1.80,将 Pod A 的请求转发给 APP
由于 tcpdump 抓包的实现原理是通过读取网络设备上的数据链路层的帧。
上面的 1、3 步骤就可以被 tcpdump 抓包看到,而 2 步骤是 netfilter 处理的,tcpdump 抓包是看不到的
2、Pod B 调用 Pod C
- Pod B 通过 Pod C 的 k8s svc IP 调用,发出的请求会被 iptables 规则拦截,转发到 sidecar 监听的 15001 端口
- APP 会认为自己与 k8s svc IP 建立了一条 TCP 连接:10.200.0.188:38783 <-> 10.200.1.246:80,通过 netstat 命令可观察到
- sidecar 会在 k8s svc 关联的 endpoint 中,选择 Pod C 建立连接:10.200.0.188:58649 <-> 10.200.0.189:3030,然后将 Pod B 的请求转发给 Pod C
- Pod C 在收到 Pod B 的请求后,处理完成,响应 Pod B
- 来自 Pod C 的响应会被 iptables 规则拦截,转发到 sidecar 监听的 15006 端口
- sidecar 再将 Pod C 的响应转发给 APP
1 步骤中可以通过 tcpdump 在 lo 上观察到请求被转发到 15001,因为是在 netfilter 中先做了 REDIRECT 后,数据包才出现在 lo 上,才被 tcpdump 观察到
2、3 步骤 tcpdump 在 eth0 上可以管擦到
5 步骤通过 tcpdump 在 lo 上可以观察到
3、Pod B 响应 Pod A
- APP 响应 sidecar
- sidecar 将 APP 的响应转发给 Pod A
istio-proxy 的访问日志
即 Envoy 的访问日志,字段解释可查看文档:Access logs。
访问日志分为两类:
- inbound: 入流量的访问日志
- outbound: 出流量的访问日志
ingress gateway 没有 inbound 的 访问日志
出流量的访问日志例子:
{
"upstream_local_address": "10.200.0.131:42288", // envoy 向服务端发起的 TCP 连接的源地址和端口
"duration": "1", // 对于 HTTP 请求,表示请求开始到响应的最后一个字节发送出去的时间,单位毫秒
"upstream_transport_failure_reason": "-", // 对于 HTTP 请求,连接建立失败的原因
"route_name": "default", // 匹配的路由名称
"downstream_local_address": "10.200.1.246:80", // envoy 劫持客户端发起连接的目的端点
"user_agent": "Go-http-client/1.1",
"response_code": "200", // HTTP 响应状态码
"response_flags": "-",
"start_time": "2021-02-18T07:08:50.031Z", // 对于 HTTP 请求,请求开始的时间,单位毫秒
"method": "GET",
"request_id": "967f8e1a-dbc0-4cce-ad53-ce6a27d5e54c",
"upstream_host": "10.200.0.189:3030", // 被 envoy 选中作为服务端的端点
"x_forwarded_for": "-",
"requested_server_name": "-", // SSL 连接的 SNI
"bytes_received": "0", // 对于 HTTP 请求,客户端发送的请求 body 字节数
"istio_policy_status": "-",
"bytes_sent": "75", // 对于 HTTP 请求,服务端响应的 body 字节数
"upstream_cluster": "outbound|80||webserverv1.default.svc.cluster.local", // 服务端集合
"downstream_remote_address": "10.200.0.131:44728", // 客户端端点
"authority": "webserverv1.default.svc",
"path": "/",
"protocol": "HTTP/1.1",
"upstream_service_time": "0"
}
对 downstream 和 upstream 的图解: