---
title: Kubernetes Egress流量服务
description: 展示如何配置 Istio Kubernetes 外部服务。
keywords: [traffic-management,egress]
weight: 60
---

Kubernetes [ExternalName](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) 服务和带 [Endpoints](https://kubernetes.io/docs/concepts/services-networking/service/#services-without-selectors) 的 Kubernetes 服务使你可以创建一个外部服务的本地 DNS 别名。这个 DNS 别名与本地服务的 DNS 条目具有相同的形式，即 `<service name>.<namespace name>.svc.cluster.local`。DNS 别名为您的工作负载提供“位置透明性”：工作负载可以以相同的方式调用本地和外部服务。如果您决定在某个时间在集群内部部署外部服务，您只需更新其 Kubernetes 服务以引用本地版本即可。工作负载将继续运行，而不会有任何变化。

这部分内容表明这些访问外部服务的 Kubernetes 机制在 Istio 中依然有效。您只需要配置使用 TLS 模式即可，并不需要 Istio 的[双向 TLS](/zh/docs/concepts/security/#mutual-TLS-authentication)。因为外部服务不是 Istio 服务网格的一部分，所以它们无法执行 Istio 的双向 TLS。您在配置TLS 模式时，一要按照外部服务的 TLS 模式的要求、二要遵从您的工作负载访问外部服务的方式。当您的工作负载发起的是 HTTP 请求但是外部服务需要 TLS，你可以通过 Istio 发起 TLS。当您的工作负载已经使用 TLS 来加密流量，您可以禁用 Istio 的双向 TLS。

虽然此部分的示例使用 HTTP 协议，但是用于引导出口流量的 Kubernetes 服务也可以与其他协议一起使用。

{{< boilerplate before-you-begin-egress >}}

*  为没有 Istio 控制的源 pod 创建一个命名空间：

    {{< text bash >}}
    $ kubectl create namespace without-istio
    {{< /text >}}

*  启动 [sleep]({{< github_tree >}}/samples/sleep) 在命名空间 `without-istio` 中的事例。

    {{< text bash >}}
    $ kubectl apply -f @samples/sleep/sleep.yaml@ -n without-istio
    {{< /text >}}

*  发送请求，创建环境变量 `SOURCE_POD_WITHOUT_ISTIO` 来保存源 pod 的名称：

    {{< text bash >}}
    $ export SOURCE_POD_WITHOUT_ISTIO=$(kubectl get pod -n without-istio -l app=sleep -o jsonpath={.items..metadata.name})
    {{< /text >}}

*   验证是否未注入 Istio Sidecar，即 pod 中有一个容器：

    {{< text bash >}}
    $ kubectl get pod $SOURCE_POD_WITHOUT_ISTIO -n without-istio
    NAME                     READY   STATUS    RESTARTS   AGE
    sleep-66c8d79ff5-8tqrl   1/1     Running   0          32s
    {{< /text >}}

## Kubernetes ExternalName 服务访问外部服务{#ks-external-name-service-to-access-an-external-service}

1.  为 `httpbin.org` 创建一个 Kubernetes [ExternalName](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) 服务：

    {{< text bash >}}
    $ kubectl apply -f - <<EOF
    kind: Service
    apiVersion: v1
    metadata:
      name: my-httpbin
    spec:
      type: ExternalName
      externalName: httpbin.org
      ports:
      - name: http
        protocol: TCP
        port: 80
    EOF
    {{< /text >}}

1.  观察您的服务。注意它没有集群 IP。

    {{< text bash >}}
    $ kubectl get svc my-httpbin
    NAME         TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
    my-httpbin   ExternalName   <none>       httpbin.org   80/TCP    4s
    {{< /text >}}

1.  从没有 Istio Sidecar 的源 pod 通过 Kubernetes 服务的主机名访问 `httpbin.org`：

    {{< text bash >}}
    $ kubectl exec -it $SOURCE_POD_WITHOUT_ISTIO -n without-istio -c sleep -- curl my-httpbin.default.svc.cluster.local/headers
    {
      "headers": {
        "Accept": "*/*",
        "Host": "my-httpbin.default.svc.cluster.local",
        "User-Agent": "curl/7.55.0"
      }
    }
    {{< /text >}}

1.  在这个例子中，未加密的 HTTP 请求被发送到 `httpbin.org`。仅出于示例目的，您禁用 TLS 模式，并允许外部服务的未加密流量。在现实生活中，我们建议
    由 Istio 执行 [Egress TLS 起源](/zh/docs/tasks/traffic-management/egress/egress-tls-origination)。

    {{< text bash >}}
    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: my-httpbin
    spec:
      host: my-httpbin.default.svc.cluster.local
      trafficPolicy:
        tls:
          mode: DISABLE
    EOF
    {{< /text >}}

1.  通过带有 Istio Sidecar 的源 pod 通过 Kubernetes 服务的主机名访问 `httpbin.org`。注意 Istio Sidecar 添加的 headers，例如， `X-Istio-Attributes` 和 `X-Envoy-Decorator-Operation`。另请注意 `Host` header 等于您的服务的主机名。

    {{< text bash >}}
    $ kubectl exec -it $SOURCE_POD -c sleep -- curl my-httpbin.default.svc.cluster.local/headers
    {
      "headers": {
        "Accept": "*/*",
        "Host": "my-httpbin.default.svc.cluster.local",
        "User-Agent": "curl/7.55.0",
        "X-B3-Sampled": "0",
        "X-B3-Spanid": "5b68b3f953945a08",
        "X-B3-Traceid": "0847ba2513aa0ffc5b68b3f953945a08",
        "X-Envoy-Decorator-Operation": "my-httpbin.default.svc.cluster.local:80/*",
        "X-Istio-Attributes": "CigKGGRlc3RpbmF0aW9uLnNlcnZpY2UubmFtZRIMEgpteS1odHRwYmluCioKHWRlc3RpbmF0aW9uLnNlcnZpY2UubmFtZXNwYWNlEgkSB2RlZmF1bHQKOwoKc291cmNlLnVpZBItEitrdWJlcm5ldGVzOi8vc2xlZXAtNjZjOGQ3OWZmNS04aG1neC5kZWZhdWx0CkAKF2Rlc3RpbmF0aW9uLnNlcnZpY2UudWlkEiUSI2lzdGlvOi8vZGVmYXVsdC9zZXJ2aWNlcy9teS1odHRwYmluCkIKGGRlc3RpbmF0aW9uLnNlcnZpY2UuaG9zdBImEiRteS1odHRwYmluLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWw="
      }
    }
    {{< /text >}}

### 清理 Kubernetes ExternalName 服务{#cleanup-of-ks-external-name-service}

{{< text bash >}}
$ kubectl delete destinationrule my-httpbin
$ kubectl delete service my-httpbin
{{< /text >}}

## 使用带 endpoints 的 Kubernetes 服务来访问外部服务{#use-a-ks-service-with-endpoints-to-access-an-external-service}

1.  为 Wikipedia 创建没有 selector 的 Kubernetes 服务：

    {{< text bash >}}
    $ kubectl apply -f - <<EOF
    kind: Service
    apiVersion: v1
    metadata:
      name: my-wikipedia
    spec:
      ports:
      - protocol: TCP
        port: 443
        name: tls
    EOF
    {{< /text >}}

1.  为您的服务创建 endpoints。从 [Wikipedia 范围列表](https://www.mediawiki.org/wiki/Wikipedia_Zero/IP_Addresses)中选择几个 IP。

    {{< text bash >}}
    $ kubectl apply -f - <<EOF
    kind: Endpoints
    apiVersion: v1
    metadata:
      name: my-wikipedia
    subsets:
      - addresses:
          - ip: 91.198.174.192
          - ip: 198.35.26.96
        ports:
          - port: 443
            name: tls
    EOF
    {{< /text >}}

1.  观察您的服务。请注意，它具有一个群集 IP，您可以使用它访问 `wikipedia.org`。

    {{< text bash >}}
    $ kubectl get svc my-wikipedia
    NAME           TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
    my-wikipedia   ClusterIP   172.21.156.230   <none>        443/TCP   21h
    {{< /text >}}

1.  从没有 Istio sidecar 的源 Pod 通过您的向您的 Kubernetes 服务集群 IP 来发送 HTTPS 请求到 `wikipedia.org`。使用 `curl` 的 `--resolve` 选项通过集群 IP 访问 `wikipedia.org`：

    {{< text bash >}}
    $ kubectl exec -it $SOURCE_POD_WITHOUT_ISTIO -n without-istio -c sleep -- curl -s --resolve en.wikipedia.org:443:$(kubectl get service my-wikipedia -o jsonpath='{.spec.clusterIP}') https://en.wikipedia.org/wiki/Main_Page | grep -o "<title>.*</title>"
    <title>Wikipedia, the free encyclopedia</title>
    {{< /text >}}

1.  在这种情况下，工作负载将 HTTPS 请求（开放 TLS 连接）发送到 `wikipedia.org`。流量已经通过工作负载加密，因此您可以安全地禁用 Istio 的双向 TLS：

    {{< text bash >}}
    $ kubectl apply -f - <<EOF
    apiVersion: networking.istio.io/v1alpha3
    kind: DestinationRule
    metadata:
      name: my-wikipedia
    spec:
      host: my-wikipedia.default.svc.cluster.local
      trafficPolicy:
        tls:
          mode: DISABLE
    EOF
    {{< /text >}}

1.  使用 Istio Sidecar 从源 Pod 中通过 Kubernetes 服务的群集 IP 访问 `wikipedia.org`：

    {{< text bash >}}
    $ kubectl exec -it $SOURCE_POD -c sleep -- curl -s --resolve en.wikipedia.org:443:$(kubectl get service my-wikipedia -o jsonpath='{.spec.clusterIP}') https://en.wikipedia.org/wiki/Main_Page | grep -o "<title>.*</title>"
    <title>Wikipedia, the free encyclopedia</title>
    {{< /text >}}

1.  检查访问是否确实由群集 IP 完成。在 `curl -v` 的输出中注意这句话 `Connected to en.wikipedia.org   (172.21.156.230)`，其中提到了在您的服务输出中作为群集 IP 打印的 IP。

    {{< text bash >}}
    $ kubectl exec -it $SOURCE_POD -c sleep -- curl -v --resolve en.wikipedia.org:443:$(kubectl get service my-wikipedia -o jsonpath='{.spec.clusterIP}') https://en.wikipedia.org/wiki/Main_Page -o /dev/null
    * Added en.wikipedia.org:443:172.21.156.230 to DNS cache
    * Hostname en.wikipedia.org was found in DNS cache
    *   Trying 172.21.156.230...
    * TCP_NODELAY set
    * Connected to en.wikipedia.org (172.21.156.230) port 443 (#0)
    ...
    {{< /text >}}

### 清理没有 endpoints 的 Kubernetes 服务{#cleanup-of-ks-service-with-endpoints}

{{< text bash >}}
$ kubectl delete destinationrule my-wikipedia
$ kubectl delete endpoints my-wikipedia
$ kubectl delete service my-wikipedia
{{< /text >}}

## 清理{#cleanup}

1.  停止服务 [sleep]({{< github_tree >}}/samples/sleep)：

    {{< text bash >}}
    $ kubectl delete -f @samples/sleep/sleep.yaml@
    {{< /text >}}

1.  停止命名空间 `without-istio` 中的服务 [sleep]({{< github_tree >}}/samples/sleep)：

    {{< text bash >}}
    $ kubectl delete -f @samples/sleep/sleep.yaml@ -n without-istio
    {{< /text >}}

1.  删除命名空间 `without-istio`：

    {{< text bash >}}
    $ kubectl delete namespace without-istio
    {{< /text >}}

1. 注销环境变量：

    {{< text bash >}}
    $ unset SOURCE_POD SOURCE_POD_WITHOUT_ISTIO
    {{< /text >}}

