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


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

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

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

Mon Nov 26, 2018

2200 Words|Read in about 5 Min|本文总阅读量
Tags: istio   service mesh   kubernetes  

在上一篇文章 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

这里我们将流量打向了 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. 参考



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

「真诚赞赏,手留余香」

杨传胜

真诚赞赏,手留余香

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

See Also

隐藏