Istio 的高级边缘流量控制(一)


通过 Egress Gateway 引导 Istio 的出口 HTTP 流量

Istio 的高级边缘流量控制(一)

通过 Egress Gateway 引导 Istio 的出口 HTTP 流量

搬瓦工

在上一篇文章 Istio 出口流量的 TLS 中,我演示了如何在网格内部直接通过 HTTP 协议访问外部加密服务,并揭示了其背后 Envoy 的配置逻辑。

本文将会通过 Egress Gateway 来引导 Istio 的出口流量,与 Istio 出口流量的 TLS 任务中描述的功能的相同,唯一的区别就是,这里会使用 Egress Gateway 来完成这一任务。

Istio 0.8 引入了 ingress 和 Egress gateway 的概念。 Ingress Gateway 允许定义进入服务网格的流量入口,所有入站流量都通过该入口;Egress Gateway 与之相对,它定义了网格的流量出口。 Egress Gateway 允许将 Istio 的流量治理功能(例如,监控和路由规则)应用于 Egress 流量。

1. 用例


设想一个具有严格安全要求的组织。根据这些要求,服务网格的所有出口流量必须流经一组专用节点。这些节点与运行其他应用的节点分开,通过策略来控制出口流量。相比其他节点而言,对这些专用节点的监控也更加详细。

另一个用例是设想一个集群,它的应用程序所在的节点没有外网 IP,因此在其上运行的网格内服务无法访问外网服务。通过定义 Egress Gateway,并将公共 IP 分配给 Egress Gateway 节点,然后通过它引导所有出口流量,就可以控制网格内服务访问外网服务了。

2. 前提条件


  • 按照安装指南中的说明设置 Istio 。
  • 启动 sleep 示例,它将作为外部调用的测试源。

如果您已启用自动注入 sidecar, 请按如下命令部署 sleep 应用程序:

$ kubectl apply -f samples/sleep/sleep.yaml

否则,您必须在部署 sleep 应用程序之前手动注入 sidecar:

$ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml)

请注意,任何可以 execcurl 的 pod 都可以执行以下步骤。

  • 创建一个 shell 变量来保存源 pod 的名称,以便将请求发送到外部服务, 如果您使用 sleep 示例,请按如下命令运行:

    $ export SOURCE_POD=$(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name})
    

3. 定义 Egress Gateway 来引导 Istio 的出口 HTTP 流量


首先创建一个 ServiceEntry 以允许网格内服务访问外部服务。

1.edition.cnn.com 定义一个 ServiceEntry

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: cnn
spec:
  hosts:
  - edition.cnn.com
  ports:
  - number: 80
    name: http-port
    protocol: HTTP
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
EOF

2. 验证 ServiceEntry 是否生效。发送 HTTPS 请求到 http://edition.cnn.com/politics

$ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics

HTTP/1.1 301 Moved Permanently
...
location: https://edition.cnn.com/politics
...

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...
Content-Length: 151654
...

此处的返回结果应该与 Istio 出口流量的 TLS 中没有配置 TLS 发起的情况下的返回结果相同。

3.edition.cnn.com 的 80 端口创建一个 Egress Gateway(假设没有启用双向 TLS 认证)。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector:
    istio: egressgateway
  servers:
  - port:
      number: 80
      name: http
      protocol: HTTP
    hosts:
    - edition.cnn.com
EOF

此处 Istio 会将 Gateway 翻译成 Egress Gateway 所在的 Pod 的 Listener。具体配置如下:

$ istioctl -n istio-system pc listeners istio-egressgateway-f8b6469db-4csb2 -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": "http.80"
                            },
                            ...

可以看到流量经过该 Listener 之后被转交给 RDS http.80,由于此时我们还没有创建 VirtualService,所以 RDS http.80 中不会包含任何有意义的路由,它会直接返回 404 状态码。

$ istioctl -n istio-system pc route istio-egressgateway-f8b6469db-4csb2 -o json
[
    {
        "name": "http.80",
        "virtualHosts": [
            {
                "name": "blackhole:80",
                "domains": [
                    "*"
                ],
                "routes": [
                    {
                        "match": {
                            "prefix": "/"
                        },
                        "directResponse": {
                            "status": 404
                        },
                        "perFilterConfig": {
                            "mixer": {}
                        }
                    }
                ]
            }
        ],
        "validateClusters": false
    }
]

此处的 validateClusters 用来决定集群管理器是否对路由中指向的 Cluster 进行验证。如果该参数设置为 true 且路由指向了不存在的集群,则不会加载该路由;如果该参数设置为 false 且路由指向了不存在的集群,则会继续加载该路由,最后找不到路由会返回 404。如果通过静态配置文件 route_config 定义路由,则该选项默认值为 true;如果通过 RDS 接口动态加载路由,则该选项默认值为 false

如果你启用了双向 TLS 认证,需要加上额外的 TLS 配置,这里我不展开详述,可以参考官方文档

4. 创建一个 DestinationRuleVirtualService 来引导流量通过 Egress Gateway 与外部服务通信。

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: direct-cnn-through-egress-gateway
spec:
  hosts:
  - edition.cnn.com
  gateways:
  - istio-egressgateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh    ①
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        subset: cnn
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - istio-egressgateway    ②
      port: 80
    route:
    - destination:
        host: edition.cnn.com
        port:
          number: 80
      weight: 100
EOF

这里其实创建了两条路由,我们一个一个来看:

  • ① : gateway 选择了 mesh,表示该路由创建在网格内的应用中:

    $ istioctl pc route sleep-5bc866558c-5nl8k --name 80 -o json|grep "edition.cnn.com" -A 11 -B 1
    
    {
    "name": "edition.cnn.com:80",
    "domains": [
        "edition.cnn.com",
        "edition.cnn.com:80"
    ],
    "routes": [
        {
            "match": {
                "prefix": "/"
            },
            "route": {
                "cluster": "outbound|80|cnn|istio-egressgateway.istio-system.svc.cluster.local",
                "timeout": "0.000s",
                "maxGrpcTimeout": "0.000s"
            },
    

如果不指定 gateway,gateway 默认值就是 mesh

该 VirtualService 的作用就是将目的地址是 edition.cnn.com:80 的流量重定向到 Egress Gateway

https://hugo-picture.oss-cn-beijing.aliyuncs.com/images/1543226447622-e17653c5-4dd3-4768-9b8f-cb1f3b0ef6a5.svg

这里我们将流量打向了 subset 为 cnn 的 Cluster,但现在不存在这个 Cluster,所以还需要通过 DestinationRule 定义一个 Cluster:

$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: egressgateway-for-cnn
spec:
  host: istio-egressgateway.istio-system.svc.cluster.local
  subsets:
  - name: cnn
EOF

查看创建好的 Cluster:

$ istioctl pc cluster sleep-5bc866558c-5nl8k --fqdn istio-egressgateway.istio-system.svc.cluster.local --subset cnn --port 80 -o json
[
    {
        "name": "outbound|80|cnn|istio-egressgateway.istio-system.svc.cluster.local",
        "type": "EDS",
        "edsClusterConfig": {
            "edsConfig": {
                "ads": {}
            },
            "serviceName": "outbound|80|cnn|istio-egressgateway.istio-system.svc.cluster.local"
        },
        "connectTimeout": "1.000s",
        "circuitBreakers": {
            "thresholds": [
                {}
            ]
        },
        "http2ProtocolOptions": {
            "maxConcurrentStreams": 1073741824
        }
    }
]
  • ② : gateway 选择了 istio-egressgateway,表示该路由创建在 Egress Gateway 中:

    $ istioctl -n istio-system pc route istio-egressgateway-f8b6469db-fj6zr -o json
    
    [
    {
        "name": "http.80",
        "virtualHosts": [
            {
                "name": "edition.cnn.com:80",
                "domains": [
                    "edition.cnn.com",
                    "edition.cnn.com:80"
                ],
                "routes": [
                    {
                        "match": {
                            "prefix": "/"
                        },
                        "route": {
                            "cluster": "outbound|80||edition.cnn.com",
                            "timeout": "0.000s",
                            "maxGrpcTimeout": "0.000s"
                        },
                        ...
    

该 VirtualService 的作用是通过 Egress Gateway 访问目的地址 edition.cnn.com:80这里 Egress Gateway 将流量路由到 Cluster outbound|80||edition.cnn.com,最后将流量转发到服务 edition.cnn.com:80。完整的流量转发流程如下图所示:

通过 Egress Gateway 引导 Istio 的出口 HTTP 流量

5. 重新发送 HTTP 请求到 http://edition.cnn.com/politics

$ kubectl exec -it $SOURCE_POD -c sleep -- curl -sL -o /dev/null -D - http://edition.cnn.com/politics

HTTP/1.1 301 Moved Permanently
...
location: https://edition.cnn.com/politics
...

HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
...
Content-Length: 151654
...

输出应与步骤 2 中的输出相同。

6. 查看 istio-egressgateway pod 中与我们的请求相对应的日志。

$ kubectl logs $(kubectl get pod -l istio=egressgateway -n istio-system -o jsonpath='{.items[0].metadata.name}') istio-proxy -n istio-system | tail

你会在输出结果中看到与请求相关的日志:

[2018-06-14T11:46:23.596Z] "GET /politics HTTP/1.1" 301 - 0 0 3 1 "172.30.146.87" "curl/7.35.0" "ab7be694-e367-94c5-83d1-086eca996dae" "edition.cnn.com" "151.101.193.67:80"

这里我们只将到 80 端口的 HTTP 流量重定向到 Egress Gateway,到 443 端口的 HTTPS 流量直接转到 edition.cnn.com

4. 清理


删除 Gateway、VirtualService、DestinationRule 和 ServiceEntry。

$ kubectl delete gateway istio-egressgateway
$ kubectl delete serviceentry cnn
$ kubectl delete virtualservice direct-cnn-through-egress-gateway
$ kubectl delete destinationrule egressgateway-for-cnn

5. 参考



-------他日江湖相逢 再当杯酒言欢-------

「真诚赞赏,手留余香」

米开朗基杨

真诚赞赏,手留余香

使用微信扫描二维码完成支付


相关推荐