入口网关
除了支持 Kubernetes Ingress,Istio还提供了另一种配置模式,Istio Gateway。与 Ingress
相比,Gateway
提供了更广泛的自定义和灵活性,并允许将 Istio 功能(例如监控和路由规则)应用于进入集群的流量。
本任务描述了如何配置 Istio,以使用 Istio Gateway
来将服务暴露至服务网格之外。
开始之前
遵照安装指南中的文档说明,安装 Istio。
启动 httpbin 样例,用作入口流量的目标服务:
$ kubectl apply -f @samples/httpbin/httpbin.yaml@
请注意本文旨在展示如何使用网关控制到“Kubernetes 集群”中的入口流量,无论是否启用 Sidecar 注入都可以启动
httpbin
服务(即目标服务可以在 Istio 网格内,也可以在 Istio 网格外)。
使用网关配置 Ingress
Ingress Gateway
描述在网格边界运作的负载均衡器,用于接收传入的 HTTP/TCP 连接。
它会配置暴露的端口、协议等,但与 Kubernetes Ingress 资源不同,不会包括任何流量路由配置。
转而使用路由规则来配置入口流量的流量路由,这与内部服务请求所用的方式相同。
现在看看如何为 HTTP 流量在 80 端口上配置 Gateway
。
创建 Istio Gateway:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "httpbin.example.com"
EOF
通过 Gateway
为进入的流量配置路由:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "httpbin.example.com"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /status
- uri:
prefix: /delay
route:
- destination:
port:
number: 8000
host: httpbin
EOF
已为 httpbin
服务创建了虚拟服务配置,包含两个路由规则,允许流量流向路径 /status
和 /delay
。
Gateways 列表指定了哪些请求允许通 httpbin-gateway
网关。
所有其他外部请求均被拒绝并返回 404 响应。
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: httpbin-gateway
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: "httpbin.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
EOF
因为创建 Kubernetes Gateway
资源也将部署关联的代理服务,运行以下命令等待 Gateway 就绪:
$ kubectl wait --for=condition=programmed gtw httpbin-gateway
通过 Gateway
为进入的流量配置路由:
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- name: httpbin-gateway
hostnames: ["httpbin.example.com"]
rules:
- matches:
- path:
type: PathPrefix
value: /status
- path:
type: PathPrefix
value: /delay
backendRefs:
- name: httpbin
port: 8000
EOF
您现在已为 httpbin
服务创建了HTTP 路由配置,包含两个路由规则,允许流量流向路径 /status
和 /delay
。
确定 Ingress IP 和端口
每个 Gateway
由类型为 LoadBalancer 的服务支撑。
该服务的外部负载均衡器 IP 和端口用于访问 Gateway。
大多数云平台上运行的集群默认支持类型为 LoadBalancer
的 Kubernetes 服务,
但在某些环境(例如测试环境)中,您可能需要执行如下操作:
minikube
- 在另一个终端中运行以下命令,启动一个外部负载均衡器:$ minikube tunnel
kind
- 遵循 MetalLB 设置指南使得类型为LoadBalancer
的服务能够工作。其他平台 - 您可以使用 MetalLB 获取
EXTERNAL-IP
用于LoadBalancer
服务。
为了方便演示,我们将 Ingress IP 和端口存储到环境变量中,在后续的教程中使用。
根据以下指示设置 INGRESS_HOST
和 INGRESS_PORT
环境变量:
将以下环境变量设置到您集群中 Istio Ingress Gateway 所用的名称及其所在的命名空间:
$ export INGRESS_NAME=istio-ingressgateway
$ export INGRESS_NS=istio-system
执行如下指令,确定您的 Kubernetes 集群是否运行在支持外部负载均衡器的环境中:
$ kubectl get svc "$INGRESS_NAME" -n "$INGRESS_NS"
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
istio-ingressgateway LoadBalancer 172.21.109.129 130.211.10.121 ... 17h
如果 EXTERNAL-IP
值已设置,说明环境正在使用外部负载均衡器,可以用其为 Ingress Gateway 提供服务。
如果 EXTERNAL-IP
值为 <none>
(或持续显示 <pending>
),说明环境没有为 Ingress Gateway 提供外部负载均衡器,无法使用 Ingress Gateway。
如果您的环境不支持外部负载均衡器,您可以尝试使用 Node Port 访问 Ingress Gateway。 否则,使用以下命令设置 Ingress IP 和端口:
$ export INGRESS_HOST=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
$ export INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="http2")].port}')
$ export SECURE_INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="https")].port}')
$ export TCP_INGRESS_PORT=$(kubectl -n "$INGRESS_NS" get service "$INGRESS_NAME" -o jsonpath='{.spec.ports[?(@.name=="tcp")].port}')
从 httpbin 网关资源获取网关地址和端口:
$ export INGRESS_HOST=$(kubectl get gtw httpbin-gateway -o jsonpath='{.status.addresses[0].value}')
$ export INGRESS_PORT=$(kubectl get gtw httpbin-gateway -o jsonpath='{.spec.listeners[?(@.name=="http")].port}')
访问 Ingress 服务
使用 curl 访问 httpbin 服务:
$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/status/200" HTTP/1.1 200 OK server: istio-envoy ...
注意上文命令使用
-H
标识将 HTTP 头部参数 Host 设置为 “httpbin.example.com”。 该操作是必需的,因为 IngressGateway
已被配置用来处理 “httpbin.example.com” 的服务请求, 而在测试环境中并没有为该主机绑定 DNS,而是简单直接地向 Ingress IP 发送请求。访问其他没有被显式暴露的 URL 时,将看到 HTTP 404 错误:
$ curl -s -I -HHost:httpbin.example.com "http://$INGRESS_HOST:$INGRESS_PORT/headers" HTTP/1.1 404 Not Found ...
通过浏览器访问 Ingress 服务
在浏览器中输入 httpbin
服务的 URL 不能获得有效的响应,因为无法像 curl
那样,将请求头部参数 Host 传给浏览器。
在现实场景中,这并不是问题,因为您需要合理配置被请求的主机及可解析的 DNS,从而在 URL 中使用主机的域名,
例如 https://httpbin.example.com/status/200
。
您可以在简单的测试和演示中按下述方法绕过这个问题:
在 Gateway
和 VirtualService
配置中使用通配符 *
。例如如下修改 Ingress 配置:
$ kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
name: httpbin-gateway
spec:
selector:
istio: ingressgateway # use Istio default gateway implementation
servers:
- port:
number: 80
name: http
protocol: HTTP
hosts:
- "*"
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: httpbin
spec:
hosts:
- "*"
gateways:
- httpbin-gateway
http:
- match:
- uri:
prefix: /headers
route:
- destination:
port:
number: 8000
host: httpbin
EOF
如果您从 Gateway
和 HTTPRoute
配置中移除主机名,则将应用到任何请求。
例如,如下修改 Ingress 配置:
$ kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
name: httpbin-gateway
spec:
gatewayClassName: istio
listeners:
- name: http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Same
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
name: httpbin
spec:
parentRefs:
- name: httpbin-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /headers
backendRefs:
- name: httpbin
port: 8000
EOF
此时,便可以在浏览器中输入包含 $INGRESS_HOST:$INGRESS_PORT
的 URL。
譬如,输入 http://$INGRESS_HOST:$INGRESS_PORT/headers
,将显示浏览器发送的所有 Header 信息。
理解原理
Gateway
配置资源允许外部流量进入 Istio 服务网格,并对边界服务实施流量管理和 Istio 可用的策略特性。
在前面的步骤中,在服务网格中创建一个服务并向外部流量暴露该服务的 HTTP 端点。
使用 Ingress Gateway 服务的 Node Port
如果您的环境不支持外部负载均衡器,则您仍然可以使用 istio-ingressgateway
服务的 Node Port来实验某些 Istio 特性。
设置 Ingress 端口:
$ export INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="http2")].nodePort}')
$ export SECURE_INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')
$ export TCP_INGRESS_PORT=$(kubectl -n "${INGRESS_NS}" get service "${INGRESS_NAME}" -o jsonpath='{.spec.ports[?(@.name=="tcp")].nodePort}')
根据集群提供商来设置 Ingress IP:
GKE:
$ export INGRESS_HOST=worker-node-address
您需要创建防火墙规则以允许 TCP 流量到达 ingressgateway 服务的端口。 运行以下命令以允许到 HTTP 和/或 HTTPS 端口的流量:
$ gcloud compute firewall-rules create allow-gateway-http --allow "tcp:$INGRESS_PORT" $ gcloud compute firewall-rules create allow-gateway-https --allow "tcp:$SECURE_INGRESS_PORT"
IBM Cloud Kubernetes Service:
$ ibmcloud ks workers --cluster cluster-name-or-id $ export INGRESS_HOST=public-IP-of-one-of-the-worker-nodes
Docker For Desktop:
$ export INGRESS_HOST=127.0.0.1
其他环境:
$ export INGRESS_HOST=$(kubectl get po -l istio=ingressgateway -n "${INGRESS_NS}" -o jsonpath='{.items[0].status.hostIP}')
问题排查
检查环境变量
INGRESS_HOST
andINGRESS_PORT
。确保环境变量的值有效,命令如下:$ kubectl get svc -n istio-system $ echo "INGRESS_HOST=$INGRESS_HOST, INGRESS_PORT=$INGRESS_PORT"
检查没有在相同的端口上定义其它 Istio Ingress Gateway:
$ kubectl get gateway --all-namespaces
检查没有在相同的 IP 和端口上定义 Kubernetes Ingress 资源:
$ kubectl get ingress --all-namespaces
如果使用了外部负载均衡器,该外部负载均衡器无法正常工作,尝试通过 Node Port 访问 Gateway。
清除
删除 Gateway
和 VirtualService
配置,并关闭 httpbin 服务:
$ kubectl delete gateway httpbin-gateway
$ kubectl delete virtualservice httpbin
$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@
删除 Gateway
和 HTTPRoute
配置,并关闭 httpbin 服务:
$ kubectl delete gtw httpbin-gateway
$ kubectl delete httproute httpbin
$ kubectl delete --ignore-not-found=true -f @samples/httpbin/httpbin.yaml@