envoy 啟動流程
數(shù)據(jù)面組件啟動流程
- initContainer執(zhí)行初始化腳本偏螺,為Pod添加iptables規(guī)則
- Pilot-agent根據(jù)啟動參數(shù)和K8S API Server中的配置信息生成Envoy的初始配置文件envoy-rev0.json唧躲,該文件告訴Envoy從xDS server中獲取動態(tài)配置信息,并配置了xDS server的地址信息帖渠,即控制面的Pilot艺谆。
- Pilot-agent使用envoy-rev0.json啟動Envoy進程棵红。
- Envoy根據(jù)初始配置獲得Pilot地址胀糜,采用xDS接口從Pilot獲取到Listener,Cluster缕坎,Route等d動態(tài)配置信息怖侦。
- Envoy根據(jù)獲取到的動態(tài)配置啟動Listener,并根據(jù)Listener的配置谜叹,結(jié)合Route和Cluster對攔截到的流量進行處理匾寝。
istio劫持流量總流程
進流量:
downstream -> iptables -> envoy Inbound -> app
出流量:
app -> iptables -> envoy Outbound -> upstream
1. IPTABLES 劫持轉(zhuǎn)發(fā)流量的過程
- 啟動流程中有講到第一步是envoy執(zhí)行初始化腳本,添加iptables規(guī)則
參考文章
istio sidecar中創(chuàng)建iptables流程
2. envoy Inbound 和 Outbound 流程
istio 的監(jiān)聽端口
$ nsenter -t 20533 -n netstat -nl
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:15090 0.0.0.0:* LISTEN
tcp 0 0 127.0.0.1:15000 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:15001 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:15006 0.0.0.0:* LISTEN
tcp6 0 0 :::15020 :::* LISTEN
tcp6 0 0 :::8889 :::* LISTEN
由上圖可以看到envoy有多個端口在監(jiān)聽
15000 端口: 管理端口
15001 端口:Outbound流量重定向端口
15006 端口:Inbound流量重定向端口
15020 端口:健康檢查端口
15090 端口: 普羅米修斯端口
應(yīng)用端口:8899
envoy Inbound處理流量過程
Inbound 流程是將 iptables 攔截到的 downstream 的流量轉(zhuǎn)交給 localhost荷腊,與 Pod 內(nèi)的應(yīng)用程序容器建立連接旗吁。
下面以進入calculator的流量為例子,來分析一下Inbound流程
calculator Inbound流程圖
Inbound配置
- listener
- virtual listener
{
"version_info": "2020-02-11T01:52:11Z/5",
"listener": {
"name": "virtualInbound",
"address": {
"socket_address": {
"address": "0.0.0.0",
"port_value": 15006
}
},
"filter_chains": [{
"filter_chain_match": {
"prefix_ranges": [{
"address_prefix": "::0",
"prefix_len": 0
}]
},
"filters": [{
"name": "envoy.tcp_proxy",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
"stat_prefix": "InboundPassthroughClusterIpv6",
"cluster": "InboundPassthroughClusterIpv6"
}
}],
"filter_chain_match": {
"prefix_ranges": [{
"address_prefix": "192.xxx.xx.163",
"prefix_len": 32
}],
"destination_port": 8888
},
"filters": [{
"name": "envoy.http_connection_manager",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",
"stat_prefix": "inbound_192.xxx.xx.163_8888",
"http_filters": [{
"name": "istio_authn"
},
{
"name": "mixer"
},
{
"name": "envoy.router"
}
],
"route_config": {
"name": "inbound|8888|grpc|calculator.vm.svc.cluster.local",
"virtual_hosts": [{
"name": "inbound|http|8888",
"domains": [
"*"
],
"routes": [{
"match": {
"prefix": "/"
},
"decorator": {
"operation": "calculator.vm.svc.cluster.local:8888/*"
},
"name": "default",
"route": {
"timeout": "0s",
"max_grpc_timeout": "0s",
"cluster": "inbound|8888|grpc|calculator.vm.svc.cluster.local"
}
}]
}],
"validate_clusters": false
}
}
}]
}]
}
}
- 該Listener中第三個filterchain用于處理Calculator服務(wù)的入向請求停局。
- 該filterchain的匹配條件為Calculator服務(wù)的 IP和8888端口很钓,配置了一個http_connection_manager filter
- http_connection_manager 中又嵌入了istio_auth,Mixer董栽,envoy.router等http filter码倦,經(jīng)過這些filter進行處理后,請求最終將被轉(zhuǎn)發(fā)給""cluster": "inbound|8888|grpc|calculator.vm.svc.cluster.local""
接下來我們看下inbound cluster的配置
- cluster
- inbound cluster
{
"version_info": "2020-02-10T08:16:25Z/3",
"cluster": {
"name": "inbound|8888|grpc|calculator.vm.svc.cluster.local",
"type": "STATIC",
"connect_timeout": "10s",
"circuit_breakers": {
"thresholds": [
{
"max_connections": 4294967295,
"max_pending_requests": 4294967295,
"max_requests": 4294967295,
"max_retries": 4294967295
}
]
},
"http2_protocol_options": {
"max_concurrent_streams": 1073741824
},
"load_assignment": {
"cluster_name": "inbound|8888|grpc|calculator.vm.svc.cluster.local",
"endpoints": [
{
"lb_endpoints": [
{
"endpoint": {
"address": {
"socket_address": {
"address": "127.0.0.1",
"port_value": 8888
}
}
}
}
]
}
]
}
}
}
- 這個Inbound Cluster锭碳,由于該Inbound Cluster中配置的Upstream為127.0.0.1:8888(?如何查看其upstream)
- 由于iptable設(shè)置中127.0.0.1不會被攔截,該請求將發(fā)送到Calculator服務(wù)的8888端口上進行業(yè)務(wù)處理袁稽。
Inbound流程代碼
//todo
envoy Outbound處理流量過程
webapp outbound流程圖
outbound配置
- virtual outbound istener
{
"listener": {
"name": "virtualOutbound",
"address": {
"socket_address": {
"address": "0.0.0.0",
"port_value": 15001
}
},
"filter_chains": [{
"filter_chain_match": {
"prefix_ranges": [{
"address_prefix": "10.244.2.73",
"prefix_len": 32
}]
},
"filters": [{
"name": "envoy.tcp_proxy",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
"stat_prefix": "BlackHoleCluster",
"cluster": "BlackHoleCluster"
}
}]
},
{
"filters": [{
"name": "mixer"
},
{
"name": "envoy.tcp_proxy",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
"stat_prefix": "PassthroughCluster",
"cluster": "PassthroughCluster"
}
}
]
}
],
"use_original_dst": true
}
}
改listener的最后一行有個配置 "use_original_dst": true,表示用原有地址擒抛,不進行路由推汽,這樣就會轉(zhuǎn)發(fā)到0.0.0.0_8888這個監(jiān)聽器上
下面看一下0.0.0.0_8888這個監(jiān)聽器的配置
- outbound listener
{
"listener": {
"name": "0.0.0.0_8888",
"address": {
"socket_address": {
"address": "0.0.0.0",
"port_value": 8888
}
},
"filter_chains": [{
"filter_chain_match": {
"prefix_ranges": [{
"address_prefix": "10.244.2.73",
"prefix_len": 32
}]
},
"filters": [{
"name": "envoy.tcp_proxy",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy",
"stat_prefix": "BlackHoleCluster",
"cluster": "BlackHoleCluster"
}
}]
},
{
"filters": [{
"name": "envoy.http_connection_manager",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager",
"stat_prefix": "outbound_0.0.0.0_8888",
"http_filters": [{
"name": "mixer"
},
{
"name": "envoy.cors"
},
{
"name": "envoy.fault"
},
{
"name": "envoy.router"
}
],
"use_remote_address": false,
"generate_request_id": true,
"upgrade_configs": [{
"upgrade_type": "websocket"
}],
"stream_idle_timeout": "0s",
"normalize_path": true,
"rds": {
"config_source": {
"ads": {}
},
"route_config_name": "8888"
}
}
}]
}
],
"deprecated_v1": {
"bind_to_port": false
},
"listener_filters_timeout": "0.100s",
"traffic_direction": "OUTBOUND",
"continue_on_listener_filters_timeout": true
}
}
同理最后有一個envoy.router filter,這個filter中route_config_name:"8888"歧沪,所以我們可以找到名為"8888"的Route配置
- outbound Route
{
"route_config": {
"name": "8888",
"virtual_hosts": [{
"name": "calculator.vm.svc.cluster.local:8888",
"domains": [
"calculator.vm.svc.cluster.local",
"calculator.vm.svc.cluster.local:8888",
"calculator.vm",
"calculator.vm:8888",
"calculator.vm.svc.cluster",
"calculator.vm.svc.cluster:8888",
"calculator.vm.svc",
"calculator.vm.svc:8888",
"10.106.45.142",
"10.106.45.142:8888"
],
"routes": [{
"match": {
"prefix": "/"
},
"route": {
"weighted_clusters": {
"clusters": [{
"name": "outbound|8888|v1|calculator.vm.svc.cluster.local",
"weight": 50
},
{
"name": "outbound|8888|v2|calculator.vm.svc.cluster.local",
"weight": 50
}
]
}
},
"decorator": {
"operation": "calculator:8888/*"
}
}]
},
{
"name": "allow_any",
"domains": [
"*"
],
"routes": [{
"match": {
"prefix": "/"
},
"route": {
"cluster": "PassthroughCluster"
}
}]
}
],
"validate_clusters": false
}
}
根據(jù)Route 8888的配置可以找到outbound cluster
- outbound cluster
{
"version_info": "2020-02-07T10:38:17Z/1847",
"cluster": {
"name": "outbound|8888|v2|calculator.vm.svc.cluster.local",
"type": "EDS",
"eds_cluster_config": {
"eds_config": {
"ads": {}
},
"service_name": "outbound|8888|v2|calculator.vm.svc.cluster.local"
},
"connect_timeout": "10s",
"circuit_breakers": {
"thresholds": [
{
"max_connections": 4294967295,
"max_pending_requests": 4294967295,
"max_requests": 4294967295,
"max_retries": 4294967295
}
]
},
"http2_protocol_options": {
"max_concurrent_streams": 1073741824
},
"metadata": {
"filter_metadata": {
"istio": {
"config": "/apis/networking/v1alpha3/namespaces/vm/destination-rule/calculator"
}
}
}
},
"last_updated": "2020-02-07T10:38:17.893Z"
}
我們會發(fā)現(xiàn)這個cluster沒有直接的endpoint,因為這是動態(tài)資源歹撒,
可以通過Pilot的調(diào)試接口獲取該Cluster的endpoint:
$ curl http://xxx.xxx.xxx.xxx.108:15014/debug/edsz > pilot_eds_dump
最后發(fā)現(xiàn)endpoint為192.xxx.xxx.163:8888
然后就把流量轉(zhuǎn)發(fā)到calculator服務(wù)了
- BlackHoleCluster
這是一個特殊的Cluster,并沒有配置后端處理請求的Host诊胞。如其名字所暗示的一樣暖夭,請求進入后將被直接丟棄掉。如果一個請求沒有找到其對的目的服務(wù)撵孤,則被發(fā)到cluste迈着。
{
"cluster": {
"name": "BlackHoleCluster",
"type": "STATIC",
"connect_timeout": "1s"
}
}
*PassthroughCluster
和BlackHoleCluter相反,發(fā)向PassthroughCluster的請求會被直接發(fā)送到其請求中要求的原始目地的邪码,Envoy不會對請求進行重新路由裕菠。
{
"cluster": {
"name": "PassthroughCluster",
"type": "ORIGINAL_DST",
"connect_timeout": "1s",
"lb_policy": "CLUSTER_PROVIDED",
"circuit_breakers": {
"thresholds": [
{
"max_connections": 4294967295,
"max_pending_requests": 4294967295,
"max_requests": 4294967295,
"max_retries": 4294967295
}
]
}
}
}
outbound流程代碼
//todo