Skip to content

Commit

Permalink
update istio sections
Browse files Browse the repository at this point in the history
  • Loading branch information
rootsongjc committed Apr 27, 2022
1 parent a3840f2 commit 5a52f0c
Show file tree
Hide file tree
Showing 8 changed files with 443 additions and 80 deletions.
8 changes: 5 additions & 3 deletions SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added images/istio-route-iptables.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
316 changes: 316 additions & 0 deletions usecases/istio-traffic-routing.md
Original file line number Diff line number Diff line change
@@ -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/)
17 changes: 17 additions & 0 deletions usecases/sidecar-pattern.md
Original file line number Diff line number Diff line change
@@ -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 可独立升级,降低应用程序代码和底层平台的耦合度。
Loading

0 comments on commit 5a52f0c

Please sign in to comment.