使用 Istio 出口网关代理遗留服务
独立部署多个 Istio 出口网关,以便对网格中的出口通信进行细粒度控制。
在 德国电信 Pan-Net,我们已经将 Istio 作为覆盖我们服务的保护伞。不幸的是,有些服务尚未迁移到 Kubernetes,或者无法迁移。
我们可以将 Istio 设置为这些上游服务的代理服务。这使我们能够受益于授权/身份验证、可追溯性和可观察性等功能,即使遗留服务保持原样。
在本文的最后,有一个动手练习,您可以模拟这种情况。在练习中,托管在 https://httpbin.org 的上游服务将由 Istio 出口网关代理。
如果您熟悉 Istio,则连接到上游服务的方法之一是通过 出口网关。
您可以部署一个来控制所有上游流量,或者您可以部署多个来进行细粒度控制并满足 单一职责原则,如下图所示
使用此模型,一个出口网关负责一个上游服务。
虽然 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 插件)。
如您所见,您的可能性增加了,Istio 变得非常可扩展。
让我们看看如何实现此模式。
解决方案
有多种方法可以执行此任务,但在这里您将找到如何定义多个 Operator 并部署生成的资源。
在下一节中,您将部署一个出口网关以连接到上游服务:httpbin
(https://httpbin.org/)
最后,您将拥有
动手操作
先决条件
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
部署出口网关
此任务的步骤假设
- 服务安装在命名空间:
httpbin
下。 - 服务名称为:
http-egress
。
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 证书
您需要一个证书才能从集群外部到您的出口服务建立安全连接。
创建并应用一个证书,以便在本文的最后从集群外部访问服务(<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 及其可能性,请联系我们中的任何一位