update istio sections
parent
a3840f2559
commit
5a52f0c4ee
|
@ -225,11 +225,13 @@
|
|||
* [总结](usecases/service-mesh-conclusion.md)
|
||||
* [Istio](usecases/istio.md)
|
||||
* [使用 Istio 前需要考虑的问题](usecases/before-using-istio.md)
|
||||
* [Istio 中 sidecar 的注入规范及示例](usecases/sidecar-spec-in-istio.md)
|
||||
* [如何参与 Istio 社区及注意事项](usecases/istio-community-tips.md)
|
||||
* [Sidecar 的注入与流量劫持](usecases/understand-sidecar-injection-and-traffic-hijack-in-istio-service-mesh.md)
|
||||
* [Sidecar 模式](usecases/sidecar-pattern.md)
|
||||
* [Istio 中 sidecar 的注入规范](usecases/sidecar-spec-in-istio.md)
|
||||
* [Sidecar 的注入与透明流量劫持](usecases/understand-sidecar-injection-and-traffic-hijack-in-istio-service-mesh.md)
|
||||
* [Istio 中的流量路由过程详解](usecases/istio-traffic-routing.md)
|
||||
* [Istio 如何支持虚拟机](usecases/how-to-integrate-istio-with-vm.md)
|
||||
* [Istio 支持虚拟机的历史](usecases/istio-vm-support.md)
|
||||
* [如何参与 Istio 社区及注意事项](usecases/istio-community-tips.md)
|
||||
* [Envoy](usecases/envoy.md)
|
||||
* [Envoy 的架构与基本术语](usecases/envoy-terminology.md)
|
||||
* [Envoy 作为前端代理](usecases/envoy-front-proxy.md)
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 516 KiB |
Binary file not shown.
After Width: | Height: | Size: 414 KiB |
Binary file not shown.
After Width: | Height: | Size: 116 KiB |
|
@ -0,0 +1,316 @@
|
|||
# Istio 中的流量路由过程详解
|
||||
|
||||
本文以 Istio 官方的 bookinfo 示例来讲解在进入 Pod 的流量被 iptables 转交给 Envoy sidecar 后,Envoy 是如何做路由转发的,详述了 Inbound 和 Outbound 处理过程。
|
||||
|
||||
下面是 Istio 官方提供的 bookinfo 的请求流程图,假设 bookinfo 应用的所有服务中没有配置 DestinationRule。
|
||||
|
||||
![Bookinfo 示例](../images/bookinfo-sample-arch.png)
|
||||
|
||||
我们将要解析的是 `reviews-v1` 这个 Pod 中的 Inbound 和 Outbound 流量。
|
||||
|
||||
### 理解 Inbound Handler
|
||||
|
||||
Inbound Handler 的作用是将 iptables 拦截到的 downstream 的流量转发给 Pod 内的应用程序容器。在我们的实例中,假设其中一个 Pod 的名字是 `reviews-v1-545db77b95-jkgv2`,运行 `istioctl proxy-config listener reviews-v1-545db77b95-jkgv2 --port 15006` 查看该 Pod 中 15006 端口上的监听器情况 ,你将看到下面的输出。
|
||||
|
||||
```ini
|
||||
ADDRESS PORT MATCH DESTINATION
|
||||
0.0.0.0 15006 Addr: *:15006 Non-HTTP/Non-TCP
|
||||
0.0.0.0 15006 Trans: tls; App: istio-http/1.0,istio-http/1.1,istio-h2; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4
|
||||
0.0.0.0 15006 Trans: raw_buffer; App: http/1.1,h2c; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4
|
||||
0.0.0.0 15006 Trans: tls; App: TCP TLS; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4
|
||||
0.0.0.0 15006 Trans: raw_buffer; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4
|
||||
0.0.0.0 15006 Trans: tls; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4
|
||||
0.0.0.0 15006 Trans: tls; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2; Addr: *:9080 Cluster: inbound|9080||
|
||||
0.0.0.0 15006 Trans: raw_buffer; Addr: *:9080 Cluster: inbound|9080||
|
||||
```
|
||||
|
||||
下面列出了以上输出中各字段的含义:
|
||||
|
||||
- ADDRESS:下游地址
|
||||
- PORT:Envoy 监听器监听的端口
|
||||
- MATCH:请求使用的传输协议或匹配的下游地址
|
||||
- DESTINATION:路由目的地
|
||||
|
||||
`reviews` Pod 中的 Iptables 将入站流量劫持到 15006 端口上,从上面的输出我们可以看到 Envoy 的 Inbound Handler 在 15006 端口上监听,对目的地为任何 IP 的 9080 端口的请求将路由到 `inbound|9080||` Cluster 上。
|
||||
|
||||
从该 Pod 的 Listener 列表的最后两行中可以看到,`0.0.0.0:15006/TCP` 的 Listener(其实际名字是 `virtualInbound`)监听所有的 Inbound 流量,其中包含了匹配规则,来自任意 IP 的对 `9080` 端口的访问流量,将会路由到 `inbound|9080||` Cluster,如果你想以 Json 格式查看该 Listener 的详细配置,可以执行 `istioctl proxy-config listeners reviews-v1-545db77b95-jkgv2 --port 15006 -o json` 命令,你将获得类似下面的输出。
|
||||
|
||||
```json
|
||||
[
|
||||
/*省略部分内容*/
|
||||
{
|
||||
"name": "virtualInbound",
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "0.0.0.0",
|
||||
"portValue": 15006
|
||||
}
|
||||
},
|
||||
"filterChains": [
|
||||
/*省略部分内容*/
|
||||
{
|
||||
"filterChainMatch": {
|
||||
"destinationPort": 9080,
|
||||
"transportProtocol": "tls",
|
||||
"applicationProtocols": [
|
||||
"istio",
|
||||
"istio-peer-exchange",
|
||||
"istio-http/1.0",
|
||||
"istio-http/1.1",
|
||||
"istio-h2"
|
||||
]
|
||||
},
|
||||
"filters": [
|
||||
/*省略部分内容*/
|
||||
{
|
||||
"name": "envoy.filters.network.http_connection_manager",
|
||||
"typedConfig": {
|
||||
"@type": "type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager",
|
||||
"statPrefix": "inbound_0.0.0.0_9080",
|
||||
"routeConfig": {
|
||||
"name": "inbound|9080||",
|
||||
"virtualHosts": [
|
||||
{
|
||||
"name": "inbound|http|9080",
|
||||
"domains": [
|
||||
"*"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"name": "default",
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "inbound|9080||",
|
||||
"timeout": "0s",
|
||||
"maxStreamDuration": {
|
||||
"maxStreamDuration": "0s",
|
||||
"grpcTimeoutHeaderMax": "0s"
|
||||
}
|
||||
},
|
||||
"decorator": {
|
||||
"operation": "reviews.default.svc.cluster.local:9080/*"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"validateClusters": false
|
||||
},
|
||||
/*省略部分内容*/
|
||||
}
|
||||
}
|
||||
],
|
||||
/*省略部分内容*/
|
||||
],
|
||||
"listenerFilters": [
|
||||
/*省略部分内容*/
|
||||
],
|
||||
"listenerFiltersTimeout": "0s",
|
||||
"continueOnListenerFiltersTimeout": true,
|
||||
"trafficDirection": "INBOUND"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
既然 Inbound Handler 的流量中将来自任意地址的对该 Pod `9080` 端口的流量路由到 `inbound|9080||` Cluster,那么我们运行 `istioctl pc cluster reviews-v1-545db77b95-jkgv2 --port 9080 --direction inbound -o json` 查看下该 Cluster 配置,你将获得类似下面的输出。
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "inbound|9080||",
|
||||
"type": "ORIGINAL_DST",
|
||||
"connectTimeout": "10s",
|
||||
"lbPolicy": "CLUSTER_PROVIDED",
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295,
|
||||
"maxRetries": 4294967295,
|
||||
"trackRemaining": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"cleanupInterval": "60s",
|
||||
"upstreamBindConfig": {
|
||||
"sourceAddress": {
|
||||
"address": "127.0.0.6",
|
||||
"portValue": 0
|
||||
}
|
||||
},
|
||||
"metadata": {
|
||||
"filterMetadata": {
|
||||
"istio": {
|
||||
"services": [
|
||||
{
|
||||
"host": "reviews.default.svc.cluster.local",
|
||||
"name": "reviews",
|
||||
"namespace": "default"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
我们看其中的 `TYPE` 为 `ORIGINAL_DST`,将流量发送到原始目标地址(Pod IP),因为原始目标地址即当前 Pod,你还应该注意到 `upstreamBindConfig.sourceAddress.address` 的值被改写为了 `127.0.0.6`,而且对于 Pod 内流量是通过 `lo` 网卡发送的,这刚好呼应了上文中的 iptables `ISTIO_OUTPUT` 链中的第一条规则,根据该规则,流量将被透传到 Pod 内的应用容器。
|
||||
|
||||
### 理解 Outbound Handler
|
||||
|
||||
在本示例中 `reviews` 会向 `ratings` 服务发送 HTTP 请求,请求的地址是:`http://ratings.default.svc.cluster.local:9080/`,Outbound Handler 的作用是将 iptables 拦截到的本地应用程序向外发出的流量,经由 Envoy 代理路由到上游。
|
||||
|
||||
Envoy 监听在 15001 端口上监听所有 Outbound 流量,Outbound Handler 处理,然后经过 `virtualOutbound` Listener、`0.0.0.0_9080` Listener,然后通过 Route 9080 找到上游的 cluster,进而通过 EDS 找到 Endpoint 执行路由动作。
|
||||
|
||||
**`ratings.default.svc.cluster.local:9080` 路由**
|
||||
|
||||
运行 `istioctl proxy-config routes reviews-v1-545db77b95-jkgv2 --name 9080 -o json` 查看 route 配置,因为 sidecar 会根据 HTTP header 中的 domains 来匹配 VirtualHost,所以下面只列举了 `ratings.default.svc.cluster.local:9080` 这一个 VirtualHost。
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "9080",
|
||||
"virtualHosts": [
|
||||
{
|
||||
"name": "ratings.default.svc.cluster.local:9080",
|
||||
"domains": [
|
||||
"ratings.default.svc.cluster.local",
|
||||
"ratings.default.svc.cluster.local:9080",
|
||||
"ratings",
|
||||
"ratings:9080",
|
||||
"ratings.default.svc",
|
||||
"ratings.default.svc:9080",
|
||||
"ratings.default",
|
||||
"ratings.default:9080",
|
||||
"10.8.8.106",
|
||||
"10.8.8.106:9080"
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"name": "default",
|
||||
"match": {
|
||||
"prefix": "/"
|
||||
},
|
||||
"route": {
|
||||
"cluster": "outbound|9080||ratings.default.svc.cluster.local",
|
||||
"timeout": "0s",
|
||||
"retryPolicy": {
|
||||
"retryOn": "connect-failure,refused-stream,unavailable,cancelled,retriable-status-codes",
|
||||
"numRetries": 2,
|
||||
"retryHostPredicate": [
|
||||
{
|
||||
"name": "envoy.retry_host_predicates.previous_hosts"
|
||||
}
|
||||
],
|
||||
"hostSelectionRetryMaxAttempts": "5",
|
||||
"retriableStatusCodes": [
|
||||
503
|
||||
]
|
||||
},
|
||||
"maxStreamDuration": {
|
||||
"maxStreamDuration": "0s",
|
||||
"grpcTimeoutHeaderMax": "0s"
|
||||
}
|
||||
},
|
||||
"decorator": {
|
||||
"operation": "ratings.default.svc.cluster.local:9080/*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"includeRequestAttemptCount": true
|
||||
},
|
||||
/*省略部分内容*/
|
||||
],
|
||||
"validateClusters": false
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
从该 Virtual Host 配置中可以看到将流量路由到`outbound|9080||ratings.default.svc.cluster.local` 集群。
|
||||
|
||||
**`outbound|9080||ratings.default.svc.cluster.local` 集群的端点**
|
||||
|
||||
运行 `istioctl proxy-config endpoint reviews-v1-545db77b95-jkgv2 --port 9080 -o json --cluster "outbound|9080||ratings.default.svc.cluster.local"` 查看集群的 Endpoint 配置,结果如下。
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"name": "outbound|9080||ratings.default.svc.cluster.local",
|
||||
"addedViaApi": true,
|
||||
"hostStatuses": [
|
||||
{
|
||||
"address": {
|
||||
"socketAddress": {
|
||||
"address": "10.4.1.12",
|
||||
"portValue": 9080
|
||||
}
|
||||
},
|
||||
"stats": [
|
||||
{
|
||||
"name": "cx_connect_fail"
|
||||
},
|
||||
{
|
||||
"name": "cx_total"
|
||||
},
|
||||
{
|
||||
"name": "rq_error"
|
||||
},
|
||||
{
|
||||
"name": "rq_success"
|
||||
},
|
||||
{
|
||||
"name": "rq_timeout"
|
||||
},
|
||||
{
|
||||
"name": "rq_total"
|
||||
},
|
||||
{
|
||||
"type": "GAUGE",
|
||||
"name": "cx_active"
|
||||
},
|
||||
{
|
||||
"type": "GAUGE",
|
||||
"name": "rq_active"
|
||||
}
|
||||
],
|
||||
"healthStatus": {
|
||||
"edsHealthStatus": "HEALTHY"
|
||||
},
|
||||
"weight": 1,
|
||||
"locality": {
|
||||
"region": "us-west2",
|
||||
"zone": "us-west2-a"
|
||||
}
|
||||
}
|
||||
],
|
||||
"circuitBreakers": {
|
||||
"thresholds": [
|
||||
{
|
||||
"maxConnections": 4294967295,
|
||||
"maxPendingRequests": 4294967295,
|
||||
"maxRequests": 4294967295,
|
||||
"maxRetries": 4294967295
|
||||
},
|
||||
{
|
||||
"priority": "HIGH",
|
||||
"maxConnections": 1024,
|
||||
"maxPendingRequests": 1024,
|
||||
"maxRequests": 1024,
|
||||
"maxRetries": 3
|
||||
}
|
||||
]
|
||||
},
|
||||
"observabilityName": "outbound|9080||ratings.default.svc.cluster.local"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
我们看到端点的地址是 `10.4.1.12`。实际上,Endpoint 可以是一个或多个,sidecar 将根据一定规则选择适当的 Endpoint 来路由。至此 `review` Pod找到了它上游服务 `rating` 的 Endpoint。
|
||||
|
||||
## 参考
|
||||
|
||||
- [Istio 中的 Sidecar 注入、透明流量劫持及流量路由过程详解 - jimmysong.io](https://jimmysong.io/blog/sidecar-injection-iptables-and-traffic-routing/)
|
|
@ -0,0 +1,17 @@
|
|||
# Sidecar 模式
|
||||
|
||||
将应用程序的功能划分为单独的进程运行在同一个最小调度单元中(例如 Kubernetes 中的 Pod)可以被视为 **sidecar 模式**。如下图所示,sidecar 模式允许您在应用程序旁边添加更多功能,而无需额外第三方组件配置或修改应用程序代码。
|
||||
|
||||
![Sidecar 模式示意图](../images/sidecar-pattern.jpg)
|
||||
|
||||
就像连接了 Sidecar 的三轮摩托车一样,在软件架构中, Sidecar 连接到父应用并且为其添加扩展或者增强功能。Sidecar 应用与主应用程序松散耦合。它可以屏蔽不同编程语言的差异,统一实现微服务的可观察性、监控、日志记录、配置、断路器等功能。
|
||||
|
||||
### 使用 Sidecar 模式的优势
|
||||
|
||||
使用 sidecar 模式部署服务网格时,无需在节点上运行代理,但是集群中将运行多个相同的 sidecar 副本。在 sidecar 部署方式中,每个应用的容器旁都会部署一个伴生容器,这个容器称之为 sidecar 容器。Sidecar 接管进出应用容器的所有流量。在 Kubernetes 的 Pod 中,在原有的应用容器旁边注入一个 Sidecar 容器,两个容器共享存储、网络等资源,可以广义的将这个包含了 sidecar 容器的 Pod 理解为一台主机,两个容器共享主机资源。
|
||||
|
||||
因其独特的部署结构,使得 sidecar 模式具有以下优势:
|
||||
|
||||
- 将与应用业务逻辑无关的功能抽象到共同基础设施,降低了微服务代码的复杂度。
|
||||
- 因为不再需要编写相同的第三方组件配置文件和代码,所以能够降低微服务架构中的代码重复度。
|
||||
- Sidecar 可独立升级,降低应用程序代码和底层平台的耦合度。
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
**注意:本文基于 Istio 1.0。**
|
||||
|
||||
我们知道 Istio 通过向 Pod 中注入一个 sidecar 容器来将 Pod 纳入到 Istio service mesh 中的,那么这些 sidecar 容器的注入遵循什么样的规范,需要给每个 Pod 增加哪些配置信息才能纳入 Istio service mesh 中呢?这篇文章将给您答案。
|
||||
我们知道 Istio 通过向 Pod 中注入一个 sidecar 容器来将 Pod 纳入到 Istio 服务网格中的,那么这些 sidecar 容器的注入遵循什么样的规范,需要给每个 Pod 增加哪些配置信息才能纳入 Istio 服务网格中呢?这篇文章将给您答案。
|
||||
|
||||
## Pod Spec 中需满足的条件
|
||||
|
||||
为了成为 Service Mesh 中的一部分,kubernetes 集群中的每个 Pod 都必须满足如下条件,这些规范不是由 Istio 自动注入的,而需要 生成 kubernetes 应用部署的 YAML 文件时需要遵守的:
|
||||
为了成为 服务网格中的一部分,Kubernetes 集群中的每个 Pod 都必须满足如下条件,这些规范不是由 Istio 自动注入的,而需要 生成 Kubernetes 应用部署的 YAML 文件时需要遵守的:
|
||||
|
||||
1. **Service 关联**:每个 pod 都必须只属于某**一个** [Kubernetes Service](https://kubernetes.io/docs/concepts/services-networking/service/) (当前不支持一个 pod 同时属于多个 service)。
|
||||
2. **命名的端口**:Service 的端口必须命名。端口的名字必须遵循如下格式 `<protocol>[-<suffix>]`,可以是 `http`、`http2`、 `grpc`、 `mongo`、 或者 `redis` 作为 `<protocol>` ,这样才能使用 Istio 的路由功能。例如 `name: http2-foo` 和 `name: http` 都是有效的端口名称,而 `name: http2foo` 不是。如果端口的名称是不可识别的前缀或者未命名,那么该端口上的流量就会作为普通的 TCP 流量来处理(除非使用 `Protocol: UDP` 明确声明使用 UDP 端口)。
|
||||
3. **带有 app label 的 Deployment**:我们建议 kubernetes 的`Deploymenet` 资源的配置文件中为 Pod 明确指定 `app`label。每个 Deployment 的配置中都需要有个与其他 Deployment 不同的含有意义的 `app` label。`app` label 用于在分布式追踪中添加上下文信息。
|
||||
4. **Mesh 中的每个 pod 里都有一个 Sidecar**:最后,Mesh 中的每个 pod 都必须运行与 Istio 兼容的 sidecar。以下部分介绍了将 sidecar 注入到 pod 中的两种方法:使用`istioctl` 命令行工具手动注入,或者使用 Istio Initializer 自动注入。注意 sidecar 不涉及到流量,因为它们与容器位于同一个 pod 中。
|
||||
3. **带有 app label 的 Deployment**:我们建议 Kubernetes 的`Deploymenet` 资源的配置文件中为 Pod 明确指定 `app`label。每个 Deployment 的配置中都需要有个与其他 Deployment 不同的含有意义的 `app` label。`app` label 用于在分布式追踪中添加上下文信息。
|
||||
4. **Mesh 中的每个 pod 里都有一个 Sidecar**:最后,网格中的每个 pod 都必须运行与 Istio 兼容的 sidecar。以下部分介绍了将 sidecar 注入到 pod 中的两种方法:使用`istioctl` 命令行工具手动注入,或者使用 Istio Initializer 自动注入。注意 sidecar 不涉及到流量,因为它们与容器位于同一个 pod 中。
|
||||
|
||||
## 将普通应用添加到 Istio service mesh 中
|
||||
## 将普通应用添加到 Istio 服务网格中
|
||||
|
||||
Istio官方的示例[Bookinfo](https://istio.io/docs/guides/bookinfo.html)中并没有讲解如何将服务集成 Istio,只给出了 YAML 配置文件,而其中需要注意哪些地方都没有说明,假如我们自己部署的服务如何使用 Istio 呢?现在我们有如下两个普通应用(代码在 GitHub 上),它们都不具备微服务的高级特性,比如限流和熔断等,通过将它们部署到 kubernetes 并使用 Istio 来管理:
|
||||
Istio官方的示例[Bookinfo](https://istio.io/docs/guides/bookinfo.html)中并没有讲解如何将服务集成 Istio,只给出了 YAML 配置文件,而其中需要注意哪些地方都没有说明,假如我们自己部署的服务如何使用 Istio 呢?现在我们有如下两个普通应用(代码在 GitHub 上),它们都不具备微服务的高级特性,比如限流和熔断等,通过将它们部署到 Kubernetes 并使用 Istio 来管理:
|
||||
|
||||
- [k8s-app-monitor-test](https://github.com/rootsongjc/k8s-app-monitor-test):用来暴露 json 格式的 metrics
|
||||
- [k8s-app-monitor-agent](https://github.com/rootsongjc/k8s-app-monitor-agent):访问上面那个应用暴露的 metrics 并生成监控图
|
||||
|
@ -136,23 +136,23 @@ spec:
|
|||
- `Deployment` 和 `Service` 中的 label 名字必须包含 `app`,zipkin 中的 tracing 需要使用到这个标签才能追踪
|
||||
- `Service` 中的 `ports` 配置和必须包含一个名为 `http` 的 port,这样在 Istio ingress 中才能暴露该服务
|
||||
|
||||
**注意**:该 YAML 文件中 `annotations` 是因为我们一开始使用 `docker-compose` 部署在本地开发测试,后来再使用 [kompose](https://github.com/kubernetes/kompose) 将其转换为 kubernetes 可识别的 YAML 文件。
|
||||
**注意**:该 YAML 文件中 `annotations` 是因为我们一开始使用 `docker-compose` 部署在本地开发测试,后来再使用 [kompose](https://github.com/kubernetes/kompose) 将其转换为 Kubernetes 可识别的 YAML 文件。
|
||||
|
||||
然后执行下面的命令就可以基于以上的 YAML 文件注入 sidecar 配置并部署到 kubernetes 集群中。
|
||||
然后执行下面的命令就可以基于以上的 YAML 文件注入 sidecar 配置并部署到 Kubernetes 集群中。
|
||||
|
||||
```bash
|
||||
kubectl apply -n default -f <(istioctl kube-inject -f manifests/istio/k8s-app-monitor-istio-all-in-one.yaml)
|
||||
```
|
||||
|
||||
如何在本地启动 kubernetes 集群进行测试可以参考 [kubernetes-vagrant-centos-cluster](https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster) 中的说明。
|
||||
如何在本地启动 Kubernetes 集群进行测试可以参考 [kubernetes-vagrant-centos-cluster](https://github.com/rootsongjc/kubernetes-vagrant-centos-cluster) 中的说明。
|
||||
|
||||
## Sidecar 注入说明
|
||||
|
||||
手动注入需要修改控制器的配置文件,如 deployment。通过修改 deployment 文件中的 pod 模板规范可实现该deployment 下创建的所有 pod 都注入 sidecar。添加/更新/删除 sidecar 需要修改整个 deployment。
|
||||
手动注入需要修改控制器的配置文件,如 deployment。通过修改 deployment 文件中的 pod 模板规范可实现该 deployment 下创建的所有 pod 都注入 sidecar。添加/更新/删除 sidecar 需要修改整个 deployment。
|
||||
|
||||
自动注入会在 pod 创建的时候注入 sidecar,无需更改控制器资源。Sidecar 可通过以下方式更新:
|
||||
|
||||
- 选择性地手动删除 pod
|
||||
- 系统得进行 deployment 滚动更新
|
||||
|
||||
手动或者自动注入都使用同样的模板配置。自动注入会从 `istio-system` 命名空间下获取 `istio-inject` 的 ConfigMap。手动注入可以通过本地文件或者 Configmap 。
|
||||
手动或者自动注入都使用同样的模板配置。自动注入会从 `istio-system` 命名空间下获取 `istio-inject` 的 ConfigMap。手动注入可以通过本地文件或者 ConfigMap 。
|
||||
|
|
|
@ -1,31 +1,4 @@
|
|||
# Sidecar 的注入与流量劫持
|
||||
|
||||
本文基于 Istio 1.11 版本,将为大家介绍以下内容:
|
||||
|
||||
- 什么是 sidecar 模式和它的优势在哪里。
|
||||
- Istio 中是如何做 sidecar 注入的?
|
||||
- Sidecar proxy 是如何做透明流量劫持的?
|
||||
- 流量是如何路由到 upstream 的?
|
||||
|
||||
## Sidecar 模式
|
||||
|
||||
将应用程序的功能划分为单独的进程运行在同一个最小调度单元中(例如 Kubernetes 中的 Pod)可以被视为 **sidecar 模式**。如下图所示,sidecar 模式允许您在应用程序旁边添加更多功能,而无需额外第三方组件配置或修改应用程序代码。
|
||||
|
||||
![Sidecar 模式示意图](../images/sidecar-pattern.jpg)
|
||||
|
||||
就像连接了 Sidecar 的三轮摩托车一样,在软件架构中, Sidecar 连接到父应用并且为其添加扩展或者增强功能。Sidecar 应用与主应用程序松散耦合。它可以屏蔽不同编程语言的差异,统一实现微服务的可观察性、监控、日志记录、配置、断路器等功能。
|
||||
|
||||
### 使用 Sidecar 模式的优势
|
||||
|
||||
使用 sidecar 模式部署服务网格时,无需在节点上运行代理,但是集群中将运行多个相同的 sidecar 副本。在 sidecar 部署方式中,每个应用的容器旁都会部署一个伴生容器,这个容器称之为 sidecar 容器。Sidecar 接管进出应用容器的所有流量。在 Kubernetes 的 Pod 中,在原有的应用容器旁边注入一个 Sidecar 容器,两个容器共享存储、网络等资源,可以广义的将这个包含了 sidecar 容器的 Pod 理解为一台主机,两个容器共享主机资源。
|
||||
|
||||
因其独特的部署结构,使得 sidecar 模式具有以下优势:
|
||||
|
||||
- 将与应用业务逻辑无关的功能抽象到共同基础设施,降低了微服务代码的复杂度。
|
||||
- 因为不再需要编写相同的第三方组件配置文件和代码,所以能够降低微服务架构中的代码重复度。
|
||||
- Sidecar 可独立升级,降低应用程序代码和底层平台的耦合度。
|
||||
|
||||
## Istio 中的 sidecar 注入
|
||||
# Sidecar 的注入与透明流量劫持
|
||||
|
||||
Istio 中提供了以下两种 sidecar 注入方式:
|
||||
|
||||
|
@ -48,7 +21,7 @@ istioctl kube-inject -f ${YAML_FILE} | kuebectl apply -f -
|
|||
|
||||
注入完成后您将看到 Istio 为原有 pod template 注入了 `initContainer` 及 sidecar proxy相关的配置。
|
||||
|
||||
### Init 容器
|
||||
## Init 容器
|
||||
|
||||
Init 容器是一种专用容器,它在应用程序容器启动之前运行,用来包含一些应用镜像中不存在的实用工具或安装脚本。
|
||||
|
||||
|
@ -249,7 +222,7 @@ Chain OUTPUT (policy ACCEPT 18M packets, 1916M bytes)
|
|||
|
||||
Init 容器通过向 iptables nat 表中注入转发规则来劫持流量的,下图显示的是三个 reviews 服务示例中的某一个 Pod,其中有 init 容器、应用容器和 sidecar 容器,图中展示了 iptables 流量劫持的详细过程。
|
||||
|
||||
![Sidecar 流量劫持示意图](../images/envoy-sidecar-traffic-interception-jimmysong-blog.png)
|
||||
![Sidecar 流量劫持示意图](../images/envoy-sidecar-traffic-interception-zh-20220424.jpg)
|
||||
|
||||
Init 容器启动时命令行参数中指定了 `REDIRECT` 模式,因此只创建了 NAT 表规则,接下来我们查看下 NAT 表中创建的规则,这是全文中的**重点部分**,前面讲了那么多都是为它做铺垫的。
|
||||
|
||||
|
@ -270,63 +243,117 @@ Istio 向 pod 中自动注入的 sidecar 容器(名为 `istio-proxy`)其中
|
|||
下面是查看 nat 表中的规则,其中链的名字中包含 `ISTIO` 前缀的是由 Init 容器注入的,规则匹配是根据下面显示的顺序来执行的,其中会有多次跳转。
|
||||
|
||||
```bash
|
||||
# 查看 NAT 表中规则配置的详细信息
|
||||
$ iptables -t nat -L -v
|
||||
# PREROUTING 链:用于目标地址转换(DNAT),将所有入站 TCP 流量跳转到 ISTIO_INBOUND 链上
|
||||
Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes)
|
||||
# PREROUTING 链:用于目标地址转换(DNAT),将所有入站 TCP 流量跳转到 ISTIO_INBOUND 链上。
|
||||
Chain PREROUTING (policy ACCEPT 2701 packets, 162K bytes)
|
||||
pkts bytes target prot opt in out source destination
|
||||
2 120 ISTIO_INBOUND tcp -- any any anywhere anywhere
|
||||
2701 162K ISTIO_INBOUND tcp -- any any anywhere anywhere
|
||||
|
||||
# INPUT 链:处理输入数据包,非 TCP 流量将继续 OUTPUT 链
|
||||
Chain INPUT (policy ACCEPT 2 packets, 120 bytes)
|
||||
# INPUT 链:处理输入数据包,非 TCP 流量将继续 OUTPUT 链。
|
||||
Chain INPUT (policy ACCEPT 2701 packets, 162K bytes)
|
||||
pkts bytes target prot opt in out source destination
|
||||
|
||||
# OUTPUT 链:将所有出站数据包跳转到 ISTIO_OUTPUT 链上
|
||||
Chain OUTPUT (policy ACCEPT 41146 packets, 3845K bytes)
|
||||
# OUTPUT 链:将所有出站数据包跳转到 ISTIO_OUTPUT 链上。
|
||||
Chain OUTPUT (policy ACCEPT 79 packets, 6761 bytes)
|
||||
pkts bytes target prot opt in out source destination
|
||||
93 5580 ISTIO_OUTPUT tcp -- any any anywhere anywhere
|
||||
15 900 ISTIO_OUTPUT tcp -- any any anywhere anywhere
|
||||
|
||||
# POSTROUTING 链:所有数据包流出网卡时都要先进入POSTROUTING 链,内核根据数据包目的地判断是否需要转发出去,我们看到此处未做任何处理
|
||||
Chain POSTROUTING (policy ACCEPT 41199 packets, 3848K bytes)
|
||||
# POSTROUTING 链:所有数据包流出网卡时都要先进入 POSTROUTING 链,内核根据数据包目的地判断是否需要转发出去,我们看到此处未做任何处理。
|
||||
Chain POSTROUTING (policy ACCEPT 79 packets, 6761 bytes)
|
||||
pkts bytes target prot opt in out source destination
|
||||
|
||||
# ISTIO_INBOUND 链:将所有目的地为 9080 端口的入站流量重定向到 ISTIO_IN_REDIRECT 链上
|
||||
# ISTIO_INBOUND 链:将所有入站流量重定向到 ISTIO_IN_REDIRECT 链上。目的地为 15090(Prometheus 使用)和 15020(Ingress gateway 使用,用于 Pilot 健康检查)端口的流量除外,发送到以上两个端口的流量将返回 iptables 规则链的调用点,即 PREROUTING 链的后继 POSTROUTING 后直接调用原始目的地。
|
||||
Chain ISTIO_INBOUND (1 references)
|
||||
pkts bytes target prot opt in out source destination
|
||||
2 120 ISTIO_IN_REDIRECT tcp -- any any anywhere anywhere tcp dpt:9080
|
||||
0 0 RETURN tcp -- any any anywhere anywhere tcp dpt:ssh
|
||||
2 120 RETURN tcp -- any any anywhere anywhere tcp dpt:15090
|
||||
2699 162K RETURN tcp -- any any anywhere anywhere tcp dpt:15020
|
||||
0 0 ISTIO_IN_REDIRECT tcp -- any any anywhere anywhere
|
||||
|
||||
# ISTIO_IN_REDIRECT 链:将所有的入站流量跳转到本地的 15006 端口,至此成功的拦截了流量到 Envoy
|
||||
Chain ISTIO_IN_REDIRECT (1 references)
|
||||
# ISTIO_IN_REDIRECT 链:将所有的入站流量跳转到本地的 15006 端口,至此成功的拦截了流量到 sidecar 代理的 Inbound Handler 中。
|
||||
Chain ISTIO_IN_REDIRECT (3 references)
|
||||
pkts bytes target prot opt in out source destination
|
||||
2 120 REDIRECT tcp -- any any anywhere anywhere redir ports 15006
|
||||
0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15006
|
||||
|
||||
# ISTIO_OUTPUT 链:选择需要重定向到 Envoy(即本地) 的出站流量,所有非 localhost 的流量全部转发到 ISTIO_REDIRECT。为了避免流量在该 Pod 中无限循环,所有到 istio-proxy 用户空间的流量都返回到它的调用点中的下一条规则,本例中即 OUTPUT 链,因为跳出 ISTIO_OUTPUT 规则之后就进入下一条链 POSTROUTING。如果目的地非 localhost 就跳转到 ISTIO_REDIRECT;如果流量是来自 istio-proxy 用户空间的,那么就跳出该链,返回它的调用链继续执行下一条规则(OUPT 的下一条规则,无需对流量进行处理);所有的非 istio-proxy 用户空间的目的地是 localhost 的流量就跳转到 ISTIO_REDIRECT
|
||||
# ISTIO_OUTPUT 链:规则比较复杂,将在下文解释
|
||||
Chain ISTIO_OUTPUT (1 references)
|
||||
pkts bytes target prot opt in out source destination
|
||||
0 0 ISTIO_REDIRECT all -- any lo anywhere !localhost
|
||||
40 2400 RETURN all -- any any anywhere anywhere owner UID match istio-proxy
|
||||
0 0 RETURN all -- any any anywhere anywhere owner GID match istio-proxy
|
||||
0 0 RETURN all -- any any anywhere localhost
|
||||
53 3180 ISTIO_REDIRECT all -- any any anywhere anywhere
|
||||
0 0 RETURN all -- any lo 127.0.0.6 anywhere #规则1
|
||||
0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !localhost owner UID match 1337 #规则2
|
||||
0 0 RETURN all -- any lo anywhere anywhere ! owner UID match 1337 #规则3
|
||||
15 900 RETURN all -- any any anywhere anywhere owner UID match 1337 #规则4
|
||||
0 0 ISTIO_IN_REDIRECT all -- any lo anywhere !localhost owner GID match 1337 #规则5
|
||||
0 0 RETURN all -- any lo anywhere anywhere ! owner GID match 1337 #规则6
|
||||
0 0 RETURN all -- any any anywhere anywhere owner GID match 1337 #规则7
|
||||
0 0 RETURN all -- any any anywhere localhost #规则8
|
||||
0 0 ISTIO_REDIRECT all -- any any anywhere anywhere #规则9
|
||||
|
||||
# ISTIO_REDIRECT 链:将所有流量重定向到 Envoy(即本地) 的 15001 端口
|
||||
Chain ISTIO_REDIRECT (2 references)
|
||||
# ISTIO_REDIRECT 链:将所有流量重定向到 Envoy 代理的 15001 端口。
|
||||
Chain ISTIO_REDIRECT (1 references)
|
||||
pkts bytes target prot opt in out source destination
|
||||
53 3180 REDIRECT tcp -- any any anywhere anywhere redir ports 15001
|
||||
0 0 REDIRECT tcp -- any any anywhere anywhere redir ports 15001
|
||||
```
|
||||
|
||||
`iptables` 显示的链的顺序,即流量规则匹配的顺序。其中要特别注意 `ISTIO_OUTPUT` 链中的规则配置。为了避免流量一直在 Pod 中无限循环,所有到 istio-proxy 用户空间的流量都返回到它的调用点中的下一条规则,本例中即 OUTPUT 链,因为跳出 `ISTIO_OUTPUT` 规则之后就进入下一条链 `POSTROUTING`。
|
||||
这里着重需要解释的是 `ISTIO_OUTPUT` 链中的 9 条规则,为了便于阅读,我将以上规则中的部分内容使用表格的形式来展示如下:
|
||||
|
||||
`ISTIO_OUTPUT` 链规则匹配的详细过程如下:
|
||||
| **规则** | **target** | **in** | **out** | **source** | **destination** |
|
||||
| -------- | ----------------- | ------ | ------- | ---------- | ------------------------------- |
|
||||
| 1 | RETURN | any | lo | 127.0.0.6 | anywhere |
|
||||
| 2 | ISTIO_IN_REDIRECT | any | lo | anywhere | !localhost owner UID match 1337 |
|
||||
| 3 | RETURN | any | lo | anywhere | anywhere !owner UID match 1337 |
|
||||
| 4 | RETURN | any | any | anywhere | anywhere owner UID match 1337 |
|
||||
| 5 | ISTIO_IN_REDIRECT | any | lo | anywhere | !localhost owner GID match 1337 |
|
||||
| 6 | RETURN | any | lo | anywhere | anywhere !owner GID match 1337 |
|
||||
| 7 | RETURN | any | any | anywhere | anywhere owner GID match 1337 |
|
||||
| 8 | RETURN | any | any | anywhere | localhost |
|
||||
| 9 | ISTIO_REDIRECT | any | any | anywhere | anywhere |
|
||||
|
||||
- 如果目的地非 localhost 就跳转到 ISTIO_REDIRECT 链
|
||||
- 所有来自 istio-proxy 用户空间的非 localhost 流量跳转到它的调用点 `OUTPUT` 继续执行 `OUTPUT` 链的下一条规则,因为 `OUTPUT` 链中没有下一条规则了,所以会继续执行 `POSTROUTING` 链然后跳出 iptables,直接访问目的地
|
||||
- 如果流量不是来自 istio-proxy 用户空间,又是对 localhost 的访问,那么就跳出 iptables,直接访问目的地
|
||||
- 其它所有情况都跳转到 `ISTIO_REDIRECT` 链
|
||||
下图展示了 `ISTIO_ROUTE` 规则的详细流程。
|
||||
|
||||
其实在最后这条规则前还可以增加 IP 地址过滤,让某些 IP 地址段不通过 Envoy 代理。
|
||||
![ISTIO_ROUTE iptables 规则流程图](../images/istio-route-iptables.jpg)
|
||||
|
||||
以上 iptables 规则都是 Init 容器启动的时使用 [istio-iptables](https://github.com/istio/istio/tree/master/tools/istio-iptables) 命令生成的,详细过程可以查看该命令行程序。
|
||||
我将按照规则的出现顺序来解释每条规则的目的、对应文章开头图示中的步骤及详情。其中规则 5、6、7 是分别对规则 2、3、4 的应用范围扩大(从 UID 扩大为 GID),作用是类似的,将合并解释。注意,其中的规则是按顺序执行的,也就是说排序越靠后的规则将作为默认值。出站网卡(out)为 `lo` (本地回环地址,loopback 接口)时,表示流量的目的地是本地 Pod,对于 Pod 向外部发送的流量就不会经过这个接口。所有 `review` Pod 的出站流量只适用于规则 4、7、8、9。
|
||||
|
||||
**规则 1**
|
||||
|
||||
- 目的:**透传** Envoy 代理发送到本地应用容器的流量,使其绕过 Envoy 代理,直达应用容器。
|
||||
- 对应图示中的步骤:6 到 7。
|
||||
- 详情:该规则使得所有来自 `127.0.0.6`(该 IP 地址将在下文解释) 的请求,跳出该链,返回 iptables 的调用点(即 `OUTPUT`)后继续执行其余路由规则,即 `POSTROUTING` 规则,把流量发送到任意目的地址,如本地 Pod 内的应用容器。如果没有这条规则,由 Pod 内 Envoy 代理发出的对 Pod 内容器访问的流量将会执行下一条规则,即规则 2,流量将再次进入到了 Inbound Handler 中,从而形成了死循环。将这条规则放在第一位可以避免流量在 Inbound Handler 中死循环的问题。
|
||||
|
||||
**规则 2、5**
|
||||
|
||||
- 目的:处理 Envoy 代理发出的站内流量(Pod 内部的流量),但不是对 localhost 的请求,通过后续规则将其转发给 Envoy 代理的 Inbound Handler。该规则适用于 Pod 对自身 IP 地址调用的场景。
|
||||
- 对应图示中的步骤:6 到 7。
|
||||
- 详情:如果流量的目的地非 localhost,且数据包是由 1337 UID(即 `istio-proxy` 用户,Envoy 代理)发出的,流量将被经过 `ISTIO_IN_REDIRECT` 最终转发到 Envoy 的 Inbound Handler。
|
||||
|
||||
**规则 3、6**
|
||||
|
||||
- 目的:**透传** Pod 内的应用容器的站内流量。适用于在应用容器中发出的对本地 Pod 的流量。
|
||||
- 详情:如果流量不是由 Envoy 用户发出的,那么就跳出该链,返回 `OUTPUT` 调用 `POSTROUTING`,直达目的地。
|
||||
|
||||
**规则 4、7**
|
||||
|
||||
- 目的:**透传** Envoy 代理发出的出站请求。
|
||||
- 对应图示中的步骤:14 到 15。
|
||||
- 详情:如果请求是由 Envoy 代理发出的,则返回 `OUTPUT` 继续调用 `POSTROUTING` 规则,最终直接访问目的地。
|
||||
|
||||
**规则 8**
|
||||
|
||||
- 目的:**透传** Pod 内部对 localhost 的请求。
|
||||
- 详情:如果请求的目的地是 localhost,则返回 OUTPUT 调用 `POSTROUTING`,直接访问 localhost。
|
||||
|
||||
**规则 9**
|
||||
|
||||
- 目的:所有其他的流量将被转发到 `ISTIO_REDIRECT` 后,最终达到 Envoy 代理的 Outbound Handler。
|
||||
|
||||
以上规则避免了 Envoy 代理到应用程序的路由在 iptables 规则中的死循环,保障了流量可以被正确的路由到 Envoy 代理上,也可以发出真正的出站请求。
|
||||
|
||||
**关于 RETURN target**
|
||||
|
||||
你可能留意到上述规则中有很多 RETURN target,它的意思是,指定到这条规则时,跳出该规则链,返回 iptables 的调用点(在我们的例子中即 `OUTPUT`)后继续执行其余路由规则,在我们的例子中即 `POSTROUTING` 规则,把流量发送到任意目的地址,你可以把它直观的理解为**透传**。
|
||||
|
||||
**关于 127.0.0.6 IP 地址**
|
||||
|
||||
127.0.0.6 这个 IP 是 Istio 中默认的 `InboundPassthroughClusterIpv4`,在 Istio 的代码中指定。即流量在进入 Envoy 代理后被绑定的 IP 地址,作用是让 Outbound 流量重新发送到 Pod 中的应用容器,即 **Passthought(透传),绕过 Outbound Handler**。该流量是对 Pod 自身的访问,而不是真正的对外流量。至于为什么选择这个 IP 作为流量透传,请参考 [Istio Issue-29603](https://github.com/istio/istio/issues/29603)。
|
||||
|
||||
## 使用 iptables 做流量劫持时存在的问题
|
||||
|
||||
|
@ -368,4 +395,5 @@ tproxy 可以用于 inbound 流量的重定向,且无需改变报文中的目
|
|||
|
||||
- [Debugging Envoy and Istiod - istio.io](https://istio.io/docs/ops/diagnostic-tools/proxy-cmd/)
|
||||
- [揭开 Istio Sidecar 注入模型的神秘面纱 - istio.io](https://istio.io/latest/zh/blog/2019/data-plane-setup/)
|
||||
- [MOSN 作为 Sidecar 使用时的流量劫持方案 - mosn.io](https://mosn.io/docs/concept/traffic-hijack/)
|
||||
- [MOSN 作为 Sidecar 使用时的流量劫持方案 - mosn.io](https://mosn.io/docs/concept/traffic-hijack/)
|
||||
- [Istio 中的 Sidecar 注入、透明流量劫持及流量路由过程详解 - jimmysong.io](https://jimmysong.io/blog/sidecar-injection-iptables-and-traffic-routing/)
|
Loading…
Reference in New Issue