12.5 故障排除

上一章

12.7 本章小结

下一章

更多图书

12.6 一个请求的完整过程分析

本节展示一个请求从一个服务实例调用另一个服务的过程,分析一下请求是怎么从调用方到服务提供方的。

1.HTTP协议的请求

以service-python服务访问service-lua服务为例。

(1)DNS解析

当service-python服务要访问service-lua服务时,需要先通过kube-dns找到要访问服务的IP地址,也就是service-lua服务的Cluster IP,执行如下的命令可以看到DNS解析得到的service-lua服务的IP地址:


$ kubectl exec $(kubectl get pod -l app=service-python -o jsonpath='{.items[0].metadata.name}') -c service-python -- ping -c 1 service-lua
PING service-lua (10.111.228.100): 56 data bytes
64 bytes from 10.111.228.100: seq=0 ttl=64 time=0.052 ms
--- service-lua ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.052/0.052/0.052 ms

(2)调用方发起请求

当service-python服务拿到service-lua服务的IP地址后,发送请求给服务对应的IP和端口。由于Envoy代理会拦截所有流量,这个请求会最先到达service-python服务实例的Envoy代理上,查看Envoy代理监听了哪些地址和端口:


$ istioctl proxy-config listener $(kubectl get pod -l app=service-python -o jsonpath='{.items[0].metadata.name}')
ADDRESS                           PORT                      TYPE
10.244.1.16                       80                        HTTP
10.99.91.255                      15011                     TCP
10.110.93.233                     443                       TCP
10.99.91.255                      31400                     TCP
10.105.42.173                     443                       TCP
10.96.0.1                         443                       TCP
10.97.150.161                     443                       TCP
10.99.91.255                      8060                      TCP
10.101.237.88                     15011                     TCP
10.96.0.10                        53                        TCP
10.99.91.255                      853                       TCP
10.111.27.172                     42422                     TCP
10.99.91.255                      443                       TCP
0.0.0.0                           15001                     TCP
0.0.0.0                           9093                      HTTP
0.0.0.0                           15010                     HTTP
0.0.0.0                           15031                     HTTP
0.0.0.0                           80                        HTTP
0.0.0.0                           9091                      HTTP
0.0.0.0                           15030                     HTTP
0.0.0.0                           15004                     HTTP
0.0.0.0                           8080                      HTTP
0.0.0.0                           8060                      HTTP

从上面的监听器可以看出,由于service-python服务访问service-lua服务使用的是HTTP协议并且是80端口,所以会匹配到0.0.0.0:80监听器。

查看具体监听器的信息:


$ istioctl proxy-config listener $(kubectl get pod -l app=service-python -o jsonpath='{.items[0].metadata.name}') --type HTTP --port 80 --address 0.0.0.0 -o json
[
    {
        "name": "0.0.0.0_80",
        "address": {
            "socketAddress": {
                "address": "0.0.0.0",
                "portValue": 80
            }
        },
        "filterChains": [
            {
                "filters": [
                    {
                        "name": "envoy.http_connection_manager",
                        "config": {
                            ...
                            "rds": {
                                "config_source": {
                                    "ads": {}
                                },
                                "route_config_name": "80"
                            },
                            ...
                        }
                    }
                ]
            }
        ],
    }
]

从上面的输出信息可以看出,0.0.0.0:80的监听器使用了名为80的路由规则。

查看路由规则信息如下:


$ istioctl proxy-config route $(kubectl get pod -l app=service-python -o jsonpath='{.items[0].metadata.name}') --name=80 -o json
[
    {
        "name": "80",
        "virtualHosts": [
            ...
            {
                "name": "service-lua.default.svc.cluster.local:80",
                "domains": [
                    "service-lua.default.svc.cluster.local",
                    "service-lua.default.svc.cluster.local:80",
                    "service-lua",
                    "service-lua:80",
                    "service-lua.default.svc.cluster",
                    "service-lua.default.svc.cluster:80",
                    "service-lua.default.svc",
                    "service-lua.default.svc:80",
                    "service-lua.default",
                    "service-lua.default:80",
                    "10.109.146.20",
                    "10.109.146.20:80"
                ],
                "routes": [
                    {
                        "match": {
                            "prefix": "/"
                        },
                        "route": {
                            "cluster": "outbound|80||service-lua.default.svc.
                                        cluster.local",
                            "timeout": "0.000s",
                            "maxGrpcTimeout": "0.000s"
                        }
                        ...
                    }
                ]
            },
            ...
        ],
        "validateClusters": false
    }
]

从上面的路由信息可以看出,对于请求service-lua服务的流量使用了名为"outbound|80||service-lua.default.svc.cluster.local"的服务实例集群。

查看服务实例集群的信息:


$ istioctl proxy-config cluster $(kubectl get pod -l app=service-python -o jsonpath='{.items[0].metadata.name}') --fqdn=service-lua.default.svc.cluster.local -o json
[
    {
        "name": "outbound|80||service-lua.default.svc.cluster.local",
        "type": "EDS",
        "edsClusterConfig": {
            "edsConfig": {
                "ads": {}
            },
            "serviceName": "outbound|80||service-lua.default.svc.cluster.local"
        },
        "connectTimeout": "1.000s",
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        }
    }
]

从中可以看出,请求service-lua服务的流量最终都转发到了名为"outbound|80||service-lua.default.svc.cluster.local"的endpoint上。

查看endpoint信息:


$ istioctl proxy-config endpoint $(kubectl get pod -l app=service-python -o jsonpath='{.items[0].metadata.name}') --cluster "outbound|80||service-lua.default.svc.cluster.local"
ENDPOINT    STATUS    CLUSTER
10.244.1.15:80    HEALTHY    outbound|80||service-lua.default.svc.cluster.local
10.244.2.9:80    HEALTHY    outbound|80||service-lua.default.svc.cluster.local

从中可以看出,请求service-lua服务的流量最终到达10.244.1.10:80和10.244.2.8:80地址上,而这两个地址其实就是Kubernetes中service-lua服务实例的Pod IP地址。

查看service-lua服务的Pod IP地址:


$ kubectl get pod -l app=service-lua -o wide
NAME    READY    STATUS    RESTARTS    AGE    IP    NODE    NOMINATED NODE 
service-lua-v1-5c9bcb7778-l6qv2     2/2     Running     0     22m     10.244.1.15  lab3    <none>
service-lua-v2-75cb5cdf8-svn5c     2/2     Running     0     22m     10.244.2.9  lab2    <none>

调用方的Envoy代理在获取到具体的service-lua服务实例地址后,对相应的地址发起请求。

(3)服务方接收请求

由于Envoy代理会拦截所有流量,这个请求会最先到达service-lua服务实例的Envoy代理上,查看Envoy代理监听了哪些地址和端口:


$ istioctl proxy-config listener $(kubectl get pod -l app=service-lua -o jsonpath='{.items[0].metadata.name}')
ADDRESS                           PORT                          TYPE
10.244.1.15                       80                            HTTP
10.99.91.255                      443                           TCP
10.99.91.255                      8060                          TCP
10.111.27.172                     42422                         TCP
10.96.0.1                         443                           TCP
10.110.93.233                     443                           TCP
10.97.150.161                     443                           TCP
10.99.91.255                      31400                         TCP
10.101.237.88                     15011                         TCP
10.96.0.10                        53                            TCP
10.99.91.255                      15011                         TCP
10.99.91.255                      853                           TCP
10.105.42.173                     443                           TCP
0.0.0.0                           9091                          HTTP
0.0.0.0                           8060                          HTTP
0.0.0.0                           15010                         HTTP
0.0.0.0                           8080                          HTTP
0.0.0.0                           80                            HTTP
0.0.0.0                           9093                          HTTP
0.0.0.0                           15030                         HTTP
0.0.0.0                           15031                         HTTP
0.0.0.0                           15004                         HTTP
0.0.0.0                           15001                         TCP

从上面的监听器可以看出,请求service-lua服务实例的流量会匹配到10.244.1.15:80监听器。

查看具体监听器的信息:


$ istioctl proxy-config listener $(kubectl get pod -l app=service-lua -o jsonpath='{.items[0].metadata.name}') --type HTTP --port 80 --address 10.244.1.15 -o json
...
"virtual_hosts": [
    {
        "domains": [
            "*"
        ],
        "name": "inbound|http|80",
        "routes": [
            {
                ...
                "route": {
                    "cluster": "inbound|80||service-lua.default.svc.cluster.local",
                    "max_grpc_timeout": "0.000s",
                    "timeout": "0.000s"
                }
            }
        ]
    }
]
...

从上面的路由规则可以看出,10.244.1.15:80监听器会使用名为"inbound|80||service-lua.default.svc.cluster.local"的服务实例集群。

查看服务实例集群的信息:


$ istioctl proxy-config cluster $(kubectl get pod -l app=service-lua -o jsonpath='{.items[0].metadata.name}') --fqdn=service-lua.default.svc.cluster.local --direction inbound -o json
[
    {
        "name": "inbound|80||service-lua.default.svc.cluster.local",
        "connectTimeout": "1.000s",
        "hosts": [
            {
                "socketAddress": {
                    "address": "127.0.0.1",
                    "portValue": 80
                }
            }
        ],
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        }
    }
]

从上面的服务实例集群信息可以看出,请求service-lua服务的流量最终地址为127.0.0.1:80。service-lua服务实例的Envoy代理请求本地址获取响应结果后,返回给服务调用方的Envoy代理,再由调用方的Envoy代理返回数据给服务调用方。

2.TCP协议的请求

以service-go服务访问Redis服务为例。

(1)DNS解析

当service-go服务要访问Redis服务时,需要先通过kube-dns找到要访问服务IP地址,也就是Redis服务的Cluster IP,执行如下的命令可以看到DNS解析得到Redis服务的IP地址:


$ kubectl exec $(kubectl get pod -l app=service-go -o jsonpath='{.items[0].metadata.name}') -c service-go -- ping -c 1 redis
PING redis (10.96.84.23): 56 data bytes
64 bytes from 10.96.84.23: seq=0 ttl=64 time=0.066 ms
--- redis ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 0.066/0.066/0.066 ms

(2)调用方发起请求

当service-go服务拿到Redis服务的IP地址后,发送请求给服务对应的IP和端口。由于Envoy代理会拦截所有流量,这个请求会最先到达service-go服务实例的Envoy代理上,查看Envoy代理监听了哪些地址和端口:


$ istioctl proxy-config listener $(kubectl get pod -l app=service-go -o jsonpath='{.items[0].metadata.name}')
ADDRESS                           PORT                      TYPE
10.244.2.8                        80                        HTTP
10.99.91.255                      443                       TCP
10.96.0.1                         443                       TCP
10.97.150.161                     443                       TCP
10.105.42.173                     443                       TCP
10.99.91.255                      31400                     TCP
10.99.91.255                      8060                      TCP
10.96.0.10                        53                        TCP
10.110.93.233                     443                       TCP
10.111.27.172                     42422                     TCP
10.101.237.88                     15011                     TCP
10.99.91.255                      15011                     TCP
10.99.91.255                      853                       TCP
0.0.0.0                           15030                     HTTP
0.0.0.0                           15010                     HTTP
0.0.0.0                           8080                      HTTP
0.0.0.0                           8060                      HTTP
0.0.0.0                           15031                     HTTP
0.0.0.0                           9091                      HTTP
0.0.0.0                           15004                     HTTP
0.0.0.0                           9093                      HTTP
0.0.0.0                           80                        HTTP
0.0.0.0                           15001                     TCP
10.96.84.23                       6379                      TCP

从上面的监听器可以看出,由于service-go服务访问Redis服务使用的是TCP协议并且是6379端口,所以会匹配到10.96.84.23:6379监听器。

查看具体监听器的信息:


$ istioctl proxy-config listener $(kubectl get pod -l app=service-go -o jsonpath='{.items[0].metadata.name}') --type TCP --port 6379 --address 10.96.84.23 -o json
...
"filters": [
    {
        "name": "envoy.tcp_proxy",
        "config": {
            ...
            "cluster": "outbound|6379||redis.default.svc.cluster.local",
            "stat_prefix": "outbound|6379||redis.default.svc.cluster.local"
        }
    }
]

从上面的监听器信息可以看出,对于请求Redis服务的流量使用了名为"outbound|6379||redis.default.svc.cluster.local"的服务实例集群。

查看服务实例集群的信息:


$ istioctl proxy-config cluster $(kubectl get pod -l app=service-go -o jsonpath='{.items[0].metadata.name}') --fqdn=redis.default.svc.cluster.local -o json
[
    {
        "name": "outbound|6379||redis.default.svc.cluster.local",
        "type": "EDS",
        "edsClusterConfig": {
            "edsConfig": {
                "ads": {}
            },
            "serviceName": "outbound|6379||redis.default.svc.cluster.local"
        },
        "connectTimeout": "1.000s",
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        }
    }
]

从上面的服务实例集群信息可以看出,请求Redis服务的流量最终都转发到了名为"outbound|6379||redis.default.svc.cluster.local"的endpoint上。

查看endpoint信息:


$ istioctl proxy-config endpoint $(kubectl get pod -l app=service-go -o jsonpath='{.items[0].metadata.name}') --cluster "outbound|6379||redis.default.svc.cluster.local"
ENDPOINT            STATUS      CLUSTER
10.244.2.12:6379    HEALTHY     outbound|6379||redis.default.svc.cluster.local

可以看出,请求Redis服务的流量最终到达10.244.2.12:6379地址上,而这个地址其实就是Kubernetes中Redis服务实例的Pod IP地址。

查看Redis服务实例的Pod IP地址:


$ kubectl get pod -l app=redis -o wide
NAME         READY   STATUS    RESTARTS   AGE   IP       NODE   NOMINATED NODE
redis-v1-7d56d758f5-5bx5w  2/2  Running  0  22m   10.244.2.12   lab2   <none>

调用方的Envoy代理在获取到具体的服务实例地址后,对相应的地址发起请求。

(3)服务方接收请求

由于Envoy代理会拦截所有流量,这个请求会最先到达Redis服务实例的Envoy代理上,查看Envoy代理监听了哪些地址和端口:


$ istioctl proxy-config listener $(kubectl get pod -l app=redis -o jsonpath='{.items[0].metadata.name}')
ADDRESS                           PORT                      TYPE
10.244.2.12                       6379                      TCP
10.111.27.172                     42422                     TCP
10.96.0.10                        53                        TCP
10.110.93.233                     443                       TCP
10.99.91.255                      443                       TCP
10.99.91.255                      8060                      TCP
10.97.150.161                     443                       TCP
10.99.91.255                      15011                     TCP
10.96.84.23                       6379                      TCP
10.99.91.255                      31400                     TCP
10.99.91.255                      853                       TCP
10.101.237.88                     15011                     TCP
10.96.0.1                         443                       TCP
10.105.42.173                     443                       TCP
0.0.0.0                           9093                      HTTP
0.0.0.0                           15031                     HTTP
0.0.0.0                           15010                     HTTP
0.0.0.0                           80                        HTTP
0.0.0.0                           15004                     HTTP
0.0.0.0                           8080                      HTTP
0.0.0.0                           8060                      HTTP
0.0.0.0                           15030                     HTTP
0.0.0.0                           9091                      HTTP
0.0.0.0                           15001                     TCP

从上面的监听器可以看出,请求Redis服务实例的流量会匹配到10.244.2.12:6379监听器。

查看具体监听器的信息:


$ istioctl proxy-config listener $(kubectl get pod -l app=redis -o jsonpath='{.items[0].metadata.name}') --type TCP --port 6379 --address 10.244.2.12 -o json
...
{
    "name": "envoy.tcp_proxy",
    "config": {
        ...
        "cluster": "inbound|6379||redis.default.svc.cluster.local",
        "stat_prefix": "inbound|6379||redis.default.svc.cluster.local"
    }
}
...

从中可以看出,10.244.2.12:6379监听器会使用名为"inbound|6379||redis.default.svc.cluster.local"的服务实例集群。

查看服务实例集群的信息:


$ istioctl proxy-config cluster $(kubectl get pod -l app=redis -o jsonpath='{.items[0].metadata.name}') --fqdn=redis.default.svc.cluster.local --direction inbound -o json
[
    {
        "name": "inbound|6379||redis.default.svc.cluster.local",
        "connectTimeout": "1.000s",
        "hosts": [
            {
                "socketAddress": {
                    "address": "127.0.0.1",
                    "portValue": 6379
                }
            }
        ],
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        }
    }
]

从中可以看出,请求Redis服务的流量最终地址为127.0.0.1:6379。Redis服务实例的Envoy代理请求本地地址获取响应结果后,返回给服务调用方的Envoy代理,再由Envoy代理返回数据给服务调用方。