更多相关文章
个人博客 :pyenv.cc
一、Istio环境中运行Pod的要求
- Service association (服务关联)
- Pod 必须从属于某个Service,哪怕Pod不需要暴露任何端口,否则此Pod就是一个无Service的Pod,会导致网格内部无法于相关的Sidecar进行关联;
- 同时从属于多个Service时,这些Service不能为该类Pod的同一个端口标识使用不同的协议;
- Application UIDs
- UID 1337 预留给了Sidecar Proxy 使用,业务应用不能以这一 UID 运行;
- NET_ADMIN and NET_RAW capabilities
- 强制启用了PSP(Pod Security Policy)的Kubernetes环境中,必须允许在网格内的Pod上使用NET_ADMIN和 NET_RAW这两个Capability,以确保Sidecar Envoy依赖的初始化Pod能够正常运行;
- 未启用PSP,或者启用了PSP但使用了专用的Istio CNI Plugin的场景,可以不用;
- Pods with app and version labels (部署在网格内的每一个应用程序都需要明确使用以下两个标签)
- 显式地为 Pod使用app和version标签;
- app标签用于为分布式追踪生成context,而label则用于指示应用的版本化;
- Named Service Ports (为网格内的每一个Service的端口定义一个名称及明确其协议)
- Service Port 应该明确指定使用的协议;
- 命名格式 :
- [-] 如 http-8080
- Kubernetes v1.18 及之后的版本中,可以直接使用 appProtocol 字段进行标识;
# 网格会依赖对应方式指定的协议标识作为协议,即应用程序对外提供服务协议的特定标识;这也是Envoy在代理应用程序基于的协议类型;
1、<protocol>[-<suffix>] 格式
apiVersion: v1
kind: Service
metadata:
name: demoappv10
spec:
ports:
- name: http-8080
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: demoapp
version: v1.0
type: ClusterIP
2、Kubernetes v1.18 及之后的版本中,可以直接使用 appProtocol 字段进行标识
apiVersion: v1
kind: Service
metadata:
name: demoappv10
spec:
appProtocol: http
ports:
- name: http
port: 8080
protocol: TCP
targetPort: 8080
selector:
app: demoapp
version: v1.0
type: ClusterIP
二、协议选择 (Protocol Selection)
- Istio 支持代理的协议
- 支持代理任何类型的TCP流量,包括HTTP、HTTPS、gRPC及原始TCP(raw tcp) 协议;
- 但为了提供额外的能力,比如路由和更加丰富的指标,Istio需要确定更加具体的协议( 应用层协议) ;
- Istio不会代理任何UDP协议;
- 协议选择
- istio 能够自动检测并识别 HTTP和HTTP/2 的流量,未检测出的协议类型将一律视为普通的TCP流量;
- 也支持由用户手动指定;
- 手动指定协议
- Service Port 应该明确指定使用的协议;
- 命名格式 :
- [-]
- Kubernetes v1.18 及之后的版本中,可以直接使用 appProtocol 字段进行标识;
- Istio支持的协议类型如下 :
https://istio.io/latest/docs/ops/configuration/traffic-management/protocol-selection/
三、Sidecar代理方式简介
- Kubernetes平台上,Envoy Sidecar容器与application容器于同一个Pod中共存,它们共享 NETWORK、UTS和IPC等名称空间,因此也共用同一个网络协议栈;
- Envoy Sidecar 基于 init 容器 (需要使用 NET_ADMIN 和 NET_RAW Capability 于 Pod启动时设置的 iptables规则以实现流量拦截)
- 入站流量由iptables拦截后转发给Envoy;
- Envoy根据配置完成入栈流量代理;
- 后端应用程序生成的出站流量依然由iptables拦截并转发给Envoy;
- Envoy 根据配置完成出站流量代理;
- Envoy Sidecar 基于 init 容器 (需要使用 NET_ADMIN 和 NET_RAW Capability 于 Pod启动时设置的 iptables规则以实现流量拦截)
- 流量拦截模式
- REDIRECT : 重定向模式
- TPROXY : 透明代理模式
- 具体操作参考 : https://github.com/istio/istio/blob/master/tools/packaging/common/istio-start.sh
四、Sidecar Envoy 流量劫持
- 流量的透明劫持
- 流量的透明劫持,用于确保让应用无需事先改造即可获得服务治理和观测能力;
- 开启透明劫持功能后,出入应用的业务流量将会被Sidecar Envoy自动拦截;
五、Istio注入的Envoy Sidecar
- Istio 基于 Kubernetes Admission Controller Webhook 完成sidecar自动注入,它会为每个微服务分别添加两个相关的容器;
- istio-init : 隶属于 Init Containers ,即初始化容器,负责在微服务相关的Pod中 生成iptables规则 以进行流量拦截并向Envoy Proxy进行转发,运行完成后退出;
- istio-proxy : 隶属于Contianers, 即Pod中的正常容器,程序为 Envoy Proxy;
# 查看每个Pod上是如何指定注入的 istio init container
# Init Containers 生成流量劫持规则
Init Containers:
istio-init:
Container ID: docker://943e9512f4e3ec0f64b25b26ac334c70800169416389abc58b723413481c787f
Image: docker.io/istio/proxyv2:1.12.1
Image ID: docker-pullable://istio/proxyv2@sha256:4704f04f399ae24d99e65170d1846dc83d7973f186656a03ba70d47bd1aba88f
Port: <none>
Host Port: <none>
Args:
istio-iptables
# 入向流量生成规则的重定向端口
-p
15001
# 出向流量端口
-z
15006
# 使用用户UID
-u
1337
-m
REDIRECT
-i
*
-x
-b
*
-d
15090,15021,15020
...
Containers:
demoapp:
Container ID: docker://563a48b833baf3ab46ef35f147012e124ba3c114323377c261f2cb5c8729a34f
Image: ikubernetes/demoapp:v1.0
Image ID: docker-pullable://ikubernetes/demoapp@sha256:6698b205eb18fb0171398927f3a35fe27676c6bf5757ef57a35a4b055badf2c3
Port: <none>
Host Port: <none>
...
# istio-proxy: 真正意义上流量治理 sidecar envoy
istio-proxy:
Container ID: docker://bab78cd8993123c8a24a236a02a0b572a3acbc0e013559a24dd57959d9c7430c
Image: docker.io/istio/proxyv2:1.12.1
Image ID: docker-pullable://istio/proxyv2@sha256:4704f04f399ae24d99e65170d1846dc83d7973f186656a03ba70d47bd1aba88f
Port: 15090/TCP
Host Port: 0/TCP
Args:
proxy
sidecar # sidecar 模式运行
--domain
$(POD_NAMESPACE).svc.cluster.local
--proxyLogLevel=warning
--proxyComponentLogLevel=misc:error
--log_output_level=default:info
--concurrency
2
5.1、istio-init初始化容器
- istio-init初始化容器基于 istio/proxyv2 镜像启动,它运行 istio-iptables 程序以生成流量拦截规则
- 拦截的流量将转发至两个相关的端口
- 15006 :由 -z 选项定义,指定用于接收拦截所有发往当前Pod/VM的入栈流量的目标端口,该配置仅用于 REDIRECT 转发模式;
- 15001 :由 -p 选项定义,指定用于接收拦截的所有TCP流量的目标端口;
- 流量拦截模式由 -m 选项指定,目前支持 REDIRECT 和 TPROXY 两种模式;
- 流量拦截时要包含的目标端口列表使用 -b 选项指定,而要排除的目标端口列表则使用 -d 选项指定;
- 流量拦截时要包含的目标 CIDR 地址列表可使用 -i 选项指定,而要排除的目标 CIDR 格式的地址列表则使用 -x 选项指定;
- 拦截的流量将转发至两个相关的端口
5.1.1、istio中用于流量拦截的iptables规则
- nsenter 命令于宿主机上可直接于目标容器的网络名称空间中运行 iptables 命令,例如,假设 productpage 相关的 Pod被调度运行于 k8s-node02 节点之上,且其内部的 envoy 进程的 pid在宿主机为 5607 ;
# 对应运行envoy sidecar 节点上运行命令
root@native:~# ps aux | grep envoy | grep 1337
root@native:~# nsenter -t 8134 -n iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N ISTIO_INBOUND
-N ISTIO_IN_REDIRECT
-N ISTIO_OUTPUT
-N ISTIO_REDIRECT
-A PREROUTING -p tcp -j ISTIO_INBOUND
-A OUTPUT -p tcp -j ISTIO_OUTPUT
-A ISTIO_INBOUND -p tcp -m tcp --dport 15008 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 22 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15090 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15021 -j RETURN
-A ISTIO_INBOUND -p tcp -m tcp --dport 15020 -j RETURN
-A ISTIO_INBOUND -p tcp -j ISTIO_IN_REDIRECT
-A ISTIO_IN_REDIRECT -p tcp -j REDIRECT --to-ports 15006
-A ISTIO_OUTPUT -s 127.0.0.6/32 -o lo -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --uid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --uid-owner 1337 -j RETURN
-A ISTIO_OUTPUT ! -d 127.0.0.1/32 -o lo -m owner --gid-owner 1337 -j ISTIO_IN_REDIRECT
-A ISTIO_OUTPUT -o lo -m owner ! --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -m owner --gid-owner 1337 -j RETURN
-A ISTIO_OUTPUT -d 127.0.0.1/32 -j RETURN
-A ISTIO_OUTPUT -j ISTIO_REDIRECT
-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001
5.1.2、流量拦截的处理机制
- 入向流量拦截 : PREROUTING -> ISTIO_INBOUND
- RETURN
- 目标端口为 TCP 协议的 22、15020、15021、15008、15090 时将报文返回至上级 PREROUTING 链,即不予拦截;
- ISTIO_IN_REDIRECT : 其它目标端口的请求报文则由该自定链中的规则 (-A ISTIO_IN_REDIRECT -p tcp -j RESIRECT --to-ports 15006) 将请求重定向至 15006 端口;
- 15006 端口时为 envoy 的 VirtualInboundListener 绑定的端口,于是请求则转交由Envoy处理;
- Envoy 根据报文目标地址匹配入向 (Inbound) 方向的Listener,若存在,则根据该 Listener的配置决定如何将请求转发至 Envoy后端的应用程序;
- RETURN
- 出向流量拦截 : OUTPUT -> ISTIO_OUTPUT
- RETURN
- 从 LO 接口流出,且源地址为 127.0.0.6/32 的出向流量
- UID Owner 为 1337 的出向流量
- GID Owner 为 1337 的出向流量
- 从 LO 接口流出,且UID Owner 非为 1337 的出向流量
- 从 LO 接口流出,且GID Owner 非为 1337 的出向流量
- 目标地址是 127.0.0.1/32 的出向流量
- ISTIO_IN_REDIRECT : Sidecar Envoy 同其反向代理的本地后端应用程序间的通信
- 从 LO 接口流出,目标地址非为 127.0.0.1/32,且UID Owner 为 1337 的出向流量
- 从 LO 接口流出,目标地址非为 127.0.0.1/32,且GID Owner 为 1337 的出向流量
- RETURN
- ISTIO_REDIRECT:其它报文则由该自定义链中的规则(-A ISTIO_REDIRECT -p tcp -j REDIRECT --to-ports 15001)重定向至15001端口;
- Envoy根据报文目标地址匹配出向(Outbound)方向的Listener,若存在,则 根据该Listener的配置决定如何将报文向外转发;
5.2、istio-proxy 容器
- istio-proxy即所谓的sidecar容器,它运行两个进程:
- pilot-agent
- 基于k8s api server 为 envoy 初始化出可用的 bootstrap 配置文件并启动envoy;
- 监控并管理envoy的运行状态,包括envoy出错时重启envoy,以及envoy配置变更后将其重载等;
- envoy
- envoy 由pilot-agent 进程基于生成bootstrap 配置进行启动,而后根据配置中指定的 pilot 地址,通过xDS API获取动态配置信息;
- Sidecar形式的Envoy通过流量拦截机制为应用程序实现入站和出站代理功能;
# kubectl exec demoappv10-6ff964cbff-84kqr -it -c istio-proxy -- ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
istio-p+ 1 0.1 0.4 747732 47700 ? Ssl Feb19 0:50 /usr/local/bin/pilot-agent proxy sidecar --domain default.svc.cluster.local --proxyLogLevel=
istio-p+ 28 0.4 0.5 185544 58488 ? Sl Feb19 3:16 /usr/local/bin/envoy -c etc/istio/proxy/envoy-rev0.json --restart-epoch 0 --drain-time-s 45
istio-p+ 42 0.0 0.0 5900 2804 pts/0 Rs+ 03:45 0:00 ps aux
5.2.1、istio-proxy 容器及其Listener
- Envoy 的bootstrap 配合文件由pilot-agent 负责生成,而生效的大多配置则定义了dynamic resources ,并通过xDS静态集群指向的Plot 获取;
- 而这些配置则来自于 VirtaulService、DestinationRule、Gateway和ServiceEntry资源对象等提供的配置;
- 由Pilot基于Kubernetes发现的网格内的各Service均会被自动转换为下发给各Sidecar实例的Listener、Route、Cluster和Endpoint的相关配置;
- 为了能够让Sidecar Envoy代理网格内进出各Service的流量,所以需要进行流量劫持,而后由Envoy根据请求特征,将流量派发至合适的Listener,进而完成后续的流量路由、负载均衡等;
5.2.2、istio-proxy Listener (几个重要侦听器的作用)
- Envoy Listener 支持绑定于 IP Socket 或 Unix Domain Socket 之上,也可以不予绑定,而是接收由其他的Listener转发来的数据;
- VirtualOutboundListener
- 通过一个端口接收所有的出口流量,而后再按照请求的端口分别转发给相应的 Listener进行处理;
- VirtualInboundListner
- 功能相似,但主要用于处理入向流量;
VirtualOutboundListener(不过多展开)
- iptables 将其所在的Pod中的外发流量拦截后转发至监听于15001的Listener,而该Listener通过在配置中将 use_origin_dest 参数设置为true,从而实现将接收到的请求转发给同请求原目标地址关联的Listener之上;
- 若不存在可接收转发报文的Listener,则Envoy将根据Istio的全局配置选项 outboundTrafficPolicy 参数的值决定如何进行处理 :
- ALLOW_ANY :
- 允许外发至任何服务的请求,无论目标服务是否存在于Pilot的注册表中;此时,没有匹配的目标Listener的流量将由该侦听器上 tcp_proxy 过滤器指向的 Passthrouh Cluster 进行透传;
- REGISTRY_ONLY :
- 仅允许外发请求至注册与Polit 中的服务;此时,没有匹配的目标Listener的流量将由该侦听器上 tcp_proxy 过滤器指向的 BlackHoleCluster 将流量直接丢弃;
- 出向流量劫持方式总结 :
- 所有应用的出向流量会被重定向给 Envoy Sidecar 的 15001 侦听器,而后 15001 侦听器将流量根据其请求的原有的目标IP和目标端口来在Envoy中找出真正于该流量相关的出站侦听器,再把其请求转交至其真正相关的出站侦听器;若其没有找到对应条件的出站侦听器则根据出站流量策略 outboundTrafficPolicy 确定是交给 Passthrouh Cluster 进行透传还是 BlackHoleCluster 进行丢弃;
VirtualInbound Listener(不过多展开)
- 入向流量劫持
- 较早版本的 Istio 基于同一个 VirtualListener 在 15001 端口上同时处理入站和出站流量;
- 自 1.4 版本起,Istio引入了 REDIRECT 代理模式,它通过监听于 15006 端口 的专用 VirtualInboundListener 处理入向流量代理以避免潜在的死循环问题;
- 入向流量处理
- 对于进入到 侦听器 “0.0.0.0:15006” 的流量,VirtaulInboundListener 会在 filterChains 中,通过一系列的 filter_chain_match 对流量进行匹配检测,以确定应该由哪个或哪些过滤器进行流量处理;
- 入向流量劫持方式总结
- istio proxy 对所有相关入向流量的处理方式是所有请求的无论目标 APP服务监听的端口,Envoy 都不会为其在前端创建入站侦听器而是所有流量交给 15006 ,在15006入站侦听器内部 都会通过 filter_chain_match 进行处理,然后通过其内部 filterChainMatch 进行匹配,而后又经过filter进行流量转发;
5.3、istio-proxy Clusters
- Istio 网格中的Cluster分别由 static_resources 提供的静态配置集群以及 dynamic_resources 提供的动态配置集群组成;
- 静态集群由 envoy_rev0.json 的初始化配置中的 prometheus_stats 、xDS server 和zipkin server等组成;
- 动态集群则是由通过xDS API 从Pilot获取的配置信息生成;
- 对于网格内的一个特定Pod来说,其集群可主要分为两类 :
- inbound : 该Pod内的Sidecar Envoy直接代理的服务;
- outbound : 网格内的所有服务;
- 动态集群类型
- Inbound Cluster :Sidecar Envoy直接代理的应用,同一Pod中,由Sidecar Envoy反向代理的应用容器;
- Outbound Cluster :网格中的所有服务,包括当前Sidecar Envoy直接代理的服务,该类Cluster占了Envoy可用集 群中的绝大多数;
- PassthroughCluster和InboundPassthroughClusterIpv4 :发往此类集群的请求报文会被直接透传至其请求中的原 始目标地址,Envoy不会进行重新路由;
- BlackHoleCluster :Envoy的一个特殊集群,它没有任何可用的endpoint,接收到的请求会被直接丢弃;未能正 确匹配到目标服务的请求通常会被发往此Cluster;