本指南演示了在服务网格内的客户端通过使用 OSM 的 Egress 策略 API 访问在网格外的目标。

先决条件

  • Kubernetes 集群运行版本 v1.22.9 或者更高。
  • 已安装 OSM。
  • 已安装 kubectl 与 API server 交互。
  • 已安装 osm 命令行工具,用于管理服务网格。

演示

  1. 如果没有启用出口策略,将其开启。

    1. export osm_namespace=osm-system # Replace osm-system with the namespace where OSM is installed
    2. kubectl patch meshconfig osm-mesh-config -n "$osm_namespace" -p '{"spec":{"featureFlags":{"enableEgressPolicy":true}}}' --type=merge
  2. curl 命名空间下部署 curl 客户端,并将该命名空间纳入网格中。

    1. # Create the curl namespace
    2. kubectl create namespace curl
    3. # Add the namespace to the mesh
    4. osm namespace add curl
    5. # Deploy curl client in the curl namespace
    6. kubectl apply -f https://raw.githubusercontent.com/openservicemesh/osm-docs/release-v1.2/manifests/samples/curl/curl.yaml -n curl

    确认 curl 客户端 pod 启动并运行。

    1. $ kubectl get pods -n curl
    2. NAME READY STATUS RESTARTS AGE
    3. curl-54ccc6954c-9rlvp 2/2 Running 0 20s

HTTP 出口

  1. 确认 curl 客户端无法发送请求 http://httpbin.org:80/get 到网站 httpbin.org80 端口。

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/get
    2. command terminated with exit code 7
  2. 配置出口策略允许 curl 客户端的 ServiceAccount 通过 http 协议访问 80 端口的网站 httpbin.org

    1. kubectl apply -f - <<EOF
    2. kind: Egress
    3. apiVersion: policy.openservicemesh.io/v1alpha1
    4. metadata:
    5. name: httpbin-80
    6. namespace: curl
    7. spec:
    8. sources:
    9. - kind: ServiceAccount
    10. name: curl
    11. namespace: curl
    12. hosts:
    13. - httpbin.org
    14. ports:
    15. - number: 80
    16. protocol: http
    17. EOF
  3. 确认 curl 客户端可以成功发送 HTTP 请求 http://httpbin.org:80/get

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/get
    2. HTTP/1.1 200 OK
    3. date: Thu, 13 May 2021 21:49:35 GMT
    4. content-type: application/json
    5. content-length: 335
    6. server: envoy
    7. access-control-allow-origin: *
    8. access-control-allow-credentials: true
    9. x-envoy-upstream-service-time: 168
  4. 确认删除上面的策略之后,curl 客户端无法再发送 HTTP 请求到 http://httpbin.org:80/get

    1. kubectl delete egress httpbin-80 -n curl
    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/get
    2. command terminated with exit code 7

HTTPS 出口

由于 HTTPS 流量使用 TLS 加密,OSM 通过将基于 HTTPS 的流量作为 TCP 流代理到其原始目的地来路由。在 TSL 握手时,HTTPS 客户端应用的服务器名称标识(SNI)与出口策略中指定的域名匹配。

  1. 确认 curl 客户端无法发送 HTTPS 请求 http://httpbin.org:80/get 到网站 httpbin.org443 端口。

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI https://httpbin.org:443/get
    2. command terminated with exit code 7
  2. 配置出口策略允许 curl 客户端的 ServiceAccount 通过 https 协议访问运行在 443 端口的 httpbin.org

    1. kubectl apply -f - <<EOF
    2. kind: Egress
    3. apiVersion: policy.openservicemesh.io/v1alpha1
    4. metadata:
    5. name: httpbin-443
    6. namespace: curl
    7. spec:
    8. sources:
    9. - kind: ServiceAccount
    10. name: curl
    11. namespace: curl
    12. hosts:
    13. - httpbin.org
    14. ports:
    15. - number: 443
    16. protocol: https
    17. EOF
  3. 确认 curl 客户端可以成功发送 HTTPS 请求 https://httpbin.org:443/get

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI https://httpbin.org:443/get
    2. HTTP/2 200
    3. date: Thu, 13 May 2021 22:09:36 GMT
    4. content-type: application/json
    5. content-length: 260
    6. server: gunicorn/19.9.0
    7. access-control-allow-origin: *
    8. access-control-allow-credentials: true
  4. 确认删除上面的策略之后,curl 客户端无法再发送 HTTPS 请求到 https://httpbin.org:443/get

    1. kubectl delete egress httpbin-443 -n curl
    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI https://httpbin.org:443/get
    2. command terminated with exit code 7

TCP 出口

基于 TCP 的出口流量是通过出口策略中指定的目的端口和 IP 地址范围进行匹配。如果未指定 IP 地址范围,流量只会基于目的端口进行匹配。

  1. 确认 curl 客户端无法发送 HTTPS 请求 https://openservicemesh.io:443 到运行在 443 端口的 openservicemesh.io。由于 HTTPS 使用 TCP 作为底层传输协议,基于 TCP 的路由应隐式启用对指定端口上的任何 HTTP(s) 主机的访问。

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI https://openservicemesh.io:443
    2. command terminated with exit code 7
  2. 配置出口策略,允许 curl 客户端的 ServiceAccount 可以通过 tcp 协议访问任何目的地的 443 端口。

    1. kubectl apply -f - <<EOF
    2. kind: Egress
    3. apiVersion: policy.openservicemesh.io/v1alpha1
    4. metadata:
    5. name: tcp-443
    6. namespace: curl
    7. spec:
    8. sources:
    9. - kind: ServiceAccount
    10. name: curl
    11. namespace: curl
    12. ports:
    13. - number: 443
    14. protocol: tcp
    15. EOF

    注意:对于像 MySQLPostgresSQL 这样的 server-first 协议,服务器发起客户端和服务器之间的数据的第一个字节,协议必须设置为 tcp-server-first 指示 OSM 无需在端口上进行协议检测。协议检测依赖于检测连接的第一个字节,与 server-first 协议不兼容。当端口的协议设置为 tcp-server-first 时,会跳过该端口的协议检测。同样需要注意的 server-first 端口号不得用于需要执行协议检测的其他应用程序端口,这就意味着使用 server-first 协议的端口不得使用其他需要执行协议检测的如 HTTPTCP 的协议。

  3. 确认 curl 客户端可以成功发送 HTTPS 请求 https://openservicemesh.io:443

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI https://openservicemesh.io:443
    2. HTTP/2 200
    3. cache-control: public, max-age=0, must-revalidate
    4. content-length: 0
    5. content-type: text/html; charset=UTF-8
    6. date: Thu, 13 May 2021 22:35:07 GMT
    7. etag: "353ebaaf9573718bd1df6b817a472e47-ssl"
    8. strict-transport-security: max-age=31536000
    9. age: 0
    10. server: Netlify
    11. x-nf-request-id: 35a4f2dc-5356-45dc-9208-63e6fa162e0f-3350874
  4. 确认删除上面的策略之后,curl 客户端无法再发送 HTTPS 请求到 https://openservicemesh.io:443

    1. kubectl delete egress tcp-443 -n curl
    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI https://openservicemesh.io:443
    2. command terminated with exit code 7

与 SMI 路由匹配的 HTTP 出口

HTTP Egress 策略可以为基于 HTTP 方法、请求头和路径的细粒度流量控制指定 SMI HTTPRouteGroup 匹配。

  1. 确认 curl 客户端无法发送 HTTP 请求 http://httpbin.org:80/gethttp://httpbin.org:80/status/200 到网站 httpbin.org80 端口。

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/get
    2. command terminated with exit code 7
    3. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/status/200
    4. command terminated with exit code 7
  2. 配置 SMI HTTPRouteGroup 资源来允许访问 HTTP 路径 /get,应用出口策略来访问匹配 SMI HTTPRouteGroup 的 80 端口的 httpbin.org

    1. kubectl apply -f - <<EOF
    2. apiVersion: specs.smi-spec.io/v1alpha4
    3. kind: HTTPRouteGroup
    4. metadata:
    5. name: egress-http-route
    6. namespace: curl
    7. spec:
    8. matches:
    9. - name: get
    10. pathRegex: /get
    11. ---
    12. kind: Egress
    13. apiVersion: policy.openservicemesh.io/v1alpha1
    14. metadata:
    15. name: httpbin-80
    16. namespace: curl
    17. spec:
    18. sources:
    19. - kind: ServiceAccount
    20. name: curl
    21. namespace: curl
    22. hosts:
    23. - httpbin.org
    24. ports:
    25. - number: 80
    26. protocol: http
    27. matches:
    28. - apiGroup: specs.smi-spec.io/v1alpha4
    29. kind: HTTPRouteGroup
    30. name: egress-http-route
    31. EOF
  3. 确认 curl 客户端可以成功发送 HTTP 请求到 http://httpbin.org:80/get

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/get
    2. HTTP/1.1 200 OK
    3. date: Thu, 13 May 2021 21:49:35 GMT
    4. content-type: application/json
    5. content-length: 335
    6. server: envoy
    7. access-control-allow-origin: *
    8. access-control-allow-credentials: true
    9. x-envoy-upstream-service-time: 168
  4. 确认 curl 客户端无法发送 HTTP 请求到 http://httpbin.org:80/status/200

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/status/200
    2. HTTP/1.1 404 Not Found
    3. date: Fri, 14 May 2021 17:08:48 GMT
    4. server: envoy
    5. transfer-encoding: chunked
  5. 更新 SMI HTTPRouteGroup 资源允许请求匹配正则 /status* 的 HTTP 路径。

    1. kubectl apply -f - <<EOF
    2. apiVersion: specs.smi-spec.io/v1alpha4
    3. kind: HTTPRouteGroup
    4. metadata:
    5. name: egress-http-route
    6. namespace: curl
    7. spec:
    8. matches:
    9. - name: get
    10. pathRegex: /get
    11. - name: status
    12. pathRegex: /status.*
    13. EOF
  6. 确认 curl 客户端此时可以成功发送 HTTP 请求到 http://httpbin.org:80/status/200

    1. $ kubectl exec $(kubectl get pod -n curl -l app=curl -o jsonpath='{.items..metadata.name}') -n curl -c curl -- curl -sI http://httpbin.org:80/status/200
    2. HTTP/1.1 200 OK
    3. date: Fri, 14 May 2021 17:10:48 GMT
    4. content-type: text/html; charset=utf-8
    5. content-length: 0
    6. server: envoy
    7. access-control-allow-origin: *
    8. access-control-allow-credentials: true
    9. x-envoy-upstream-service-time: 188