使用 Istio 出口网关代理遗留服务

独立部署多个 Istio 出口网关,以便对网格中的出口通信进行细粒度控制。

2020 年 12 月 16 日 | 作者:Antonio Berben - 德意志电信 - PAN-NET

德国电信 Pan-Net,我们已经将 Istio 作为覆盖我们服务的保护伞。不幸的是,有些服务尚未迁移到 Kubernetes,或者无法迁移。

我们可以将 Istio 设置为这些上游服务的代理服务。这使我们能够受益于授权/身份验证、可追溯性和可观察性等功能,即使遗留服务保持原样。

在本文的最后,有一个动手练习,您可以模拟这种情况。在练习中,托管在 https://httpbin.org 的上游服务将由 Istio 出口网关代理。

如果您熟悉 Istio,则连接到上游服务的方法之一是通过 出口网关

您可以部署一个来控制所有上游流量,或者您可以部署多个来进行细粒度控制并满足 单一职责原则,如下图所示

Overview multiple Egress Gateways
多个出口网关概述

使用此模型,一个出口网关负责一个上游服务。

虽然 Operator 规范允许您部署多个出口网关,但清单可能变得难以管理

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
[...]
spec:
    egressGateways:
    - name: egressgateway-1
      enabled: true
    - name: egressgateway-2
      enabled: true
    [egressgateway-3, egressgateway-4, ...]
    - name: egressgateway-N
      enabled: true
[...]

将出口网关与 Operator 清单分离的好处是,您可以设置自定义就绪探测,使两个服务(网关和上游服务)保持一致。

您还可以将 OPA 作为 sidecar 注入 pod 中,以使用复杂规则执行授权(OPA envoy 插件)。

Authorization with OPA and `healthcheck` to upstream service
使用 OPA 和外部 `healthcheck` 进行授权

如您所见,您的可能性增加了,Istio 变得非常可扩展。

让我们看看如何实现此模式。

解决方案

有多种方法可以执行此任务,但在这里您将找到如何定义多个 Operator 并部署生成的资源。

在下一节中,您将部署一个出口网关以连接到上游服务:httpbinhttps://httpbin.org/

最后,您将拥有

Communication
通信

动手操作

先决条件

Kind

将此保存为 config.yaml

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
kubeadmConfigPatches:
  - |
    apiVersion: kubeadm.k8s.io/v1beta2
    kind: ClusterConfiguration
    metadata:
      name: config
    apiServer:
      extraArgs:
        "service-account-issuer": "kubernetes.default.svc"
        "service-account-signing-key-file": "/etc/kubernetes/pki/sa.key"
$ kind create cluster --name <my-cluster-name> --config config.yaml

其中 <my-cluster-name> 是集群的名称。

使用 Istioctl 的 Istio Operator

安装 Operator

$ istioctl operator init --watchedNamespaces=istio-operator
$ kubectl create ns istio-system

将此保存为 operator.yaml

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
metadata:
  name: istio-operator
  namespace: istio-operator
spec:
  profile: default
  tag: 1.8.0
  meshConfig:
    accessLogFile: /dev/stdout
    outboundTrafficPolicy:
      mode: REGISTRY_ONLY
$ kubectl apply -f operator.yaml

部署出口网关

此任务的步骤假设

Istio 1.8 引入了应用覆盖配置的可能性,以便对创建的资源进行细粒度控制。

将此保存为 egress.yaml

apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  profile: empty
  tag: 1.8.0
  namespace: httpbin
  components:
    egressGateways:
    - name: httpbin-egress
      enabled: true
      label:
        app: istio-egressgateway
        istio: egressgateway
        custom-egress: httpbin-egress
      k8s:
        overlays:
        - kind: Deployment
          name: httpbin-egress
          patches:
          - path: spec.template.spec.containers[0].readinessProbe
            value:
              failureThreshold: 30
              exec:
                command:
                  - /bin/sh
                  - -c
                  - curl http://localhost:15021/healthz/ready && curl https://httpbin.org/status/200
              initialDelaySeconds: 1
              periodSeconds: 2
              successThreshold: 1
              timeoutSeconds: 1
  values:
    gateways:
      istio-egressgateway:
        runAsRoot: true

创建您将在其中安装出口网关的命名空间

$ kubectl create ns httpbin

文档中所述,您可以部署多个 Operator 资源。但是,它们必须先进行预解析,然后才能应用到集群中。

$ istioctl manifest generate -f egress.yaml | kubectl apply -f -

Istio 配置

现在,您将配置 Istio 以允许连接到 https://httpbin.org 上的上游服务。

TLS 证书

您需要一个证书才能从集群外部到您的出口服务建立安全连接。

Istio 入口文档中解释了如何生成证书

创建并应用一个证书,以便在本文的最后从集群外部访问服务(<my-proxied-service-hostname>

$ kubectl create -n istio-system secret tls <my-secret-name> --key=<key> --cert=<cert>

其中 <my-secret-name> 是稍后用于 Gateway 资源的名称。<key><cert> 是证书的文件。<cert>

入口网关

创建一个 Gateway 资源以操作入口网关以接受请求。

示例

apiVersion: networking.istio.io/v1beta1
kind: Gateway
metadata:
  name: my-ingressgateway
  namespace: istio-system
spec:
  selector:
    istio: ingressgateway
  servers:
  - hosts:
    - "<my-proxied-service-hostname>"
    port:
      name: http
      number: 80
      protocol: HTTP
    tls:
     httpsRedirect: true
  - port:
      number: 443
      name: https
      protocol: https
    hosts:
    - "<my-proxied-service-hostname>"
    tls:
      mode: SIMPLE
      credentialName: <my-secret-name>

其中 <my-proxied-service-hostname> 是通过 my-ingressgateway 访问服务的主机名,而 <my-secret-name> 是包含证书的密钥。

出口网关

创建另一个 Gateway 对象,但这次用于操作您已安装的出口网关

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: "httpbin-egress"
  namespace: "httpbin"
spec:
  selector:
    istio: egressgateway
    service.istio.io/canonical-name: "httpbin-egress"
  servers:
  - hosts:
    - "<my-proxied-service-hostname>"
    port:
      number: 80
      name: http
      protocol: HTTP

其中 <my-proxied-service-hostname> 是通过 my-ingressgateway 访问的主机名。

虚拟服务

为三种用例创建 VirtualService

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: "httpbin-egress"
  namespace: "httpbin"
spec:
  hosts:
  - "<my-proxied-service-hostname>"
  gateways:
  - mesh
  - "istio-system/my-ingressgateway"
  - "httpbin/httpbin-egress"
  http:
  - match:
    - gateways:
      - "istio-system/my-ingressgateway"
      - mesh
      uri:
        prefix: "/"
    route:
    - destination:
        host: "httpbin-egress.httpbin.svc.cluster.local"
        port:
          number: 80
  - match:
    - gateways:
      - "httpbin/httpbin-egress"
      uri:
        prefix: "/"
    route:
    - destination:
        host: "httpbin.org"
        subset: "http-egress-subset"
        port:
          number: 443

其中 <my-proxied-service-hostname> 是通过 my-ingressgateway 访问的主机名。

服务入口

创建一个 ServiceEntry 以允许与上游服务的通信

apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: "httpbin-egress"
  namespace: "httpbin"
spec:
  hosts:
  - "httpbin.org"
  location: MESH_EXTERNAL
  ports:
  - number: 443
    name: https
    protocol: TLS
  resolution: DNS

目标规则

创建一个 DestinationRule 以允许出口流量的 TLS 发起,如 文档中所述

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: "httpbin-egress"
  namespace: "httpbin"
spec:
  host: "httpbin.org"
  subsets:
  - name: "http-egress-subset"
    trafficPolicy:
      loadBalancer:
        simple: ROUND_ROBIN
      portLevelSettings:
      - port:
          number: 443
        tls:
          mode: SIMPLE

对等身份验证

为了保护服务之间的通信,您需要强制执行 mTLS

apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
  name: "httpbin-egress"
  namespace: "httpbin"
spec:
  mtls:
    mode: STRICT

测试

验证您是否已正确指定所有对象

$ istioctl analyze --all-namespaces

外部访问

从集群外部测试出口网关,转发 ingressgateway 服务的端口并调用服务

$ kubectl -n istio-system port-forward svc/istio-ingressgateway 15443:443
$ curl -vvv -k -HHost:<my-proxied-service-hostname> --resolve "<my-proxied-service-hostname>:15443:127.0.0.1" --cacert <cert> "https://<my-proxied-service-hostname>:15443/status/200"

其中 <my-proxied-service-hostname> 是通过 my-ingressgateway 访问的主机名,而 <cert> 是为 ingressgateway 对象定义的证书。这是由于 tls.mode: SIMPLE 不会终止 TLS

服务间访问

通过部署 sleep 服务从集群内部测试出口网关。这在您设计故障转移时很有用。

$ kubectl label namespace httpbin istio-injection=enabled --overwrite
$ kubectl apply -n httpbin -f  https://raw.githubusercontent.com/istio/istio/release-1.24/samples/sleep/sleep.yaml
$ kubectl -n httpbin "$(kubectl get pod -n httpbin -l app=sleep -o jsonpath={.items..metadata.name})" -- curl -vvv http://<my-proxied-service-hostname>/status/200

其中 <my-proxied-service-hostname> 是通过 my-ingressgateway 访问的主机名。

现在是时候创建第二个、第三个和第四个出口网关,它们指向其他上游服务。

最终思考

Istio 的配置可能看起来很复杂。但它绝对值得,因为它为您的服务带来了大量好处(对于 Kiali,额外赠送一个Olé!)。

Istio 的开发方式使我们能够以最小的努力满足像本文中提出的那种不常见的需求。

最后,我只想指出,Istio 作为一项优秀的云原生技术,不需要大型团队来维护。例如,我们目前的团队由 3 名工程师组成。

要进一步讨论 Istio 及其可能性,请联系我们中的任何一位

分享此文章