六, 跨语言微服务框架 - Istio Ingress和Egress详解(解决Istio无法外网访问问题)

六, 跨语言微服务框架 - Istio Ingress和Egress详解(解决Istio无法外网访问问题)

在微服务中另外一个重点就是网关,网关理论包含入口网关和出口网关,传统意义上的网关很难做到出口网络控制,但是对于Istio是一件非常轻松的事情(因为所有的出口流量都会经过Istio),入口网关控制解析路由数据流向,出口网关控制对外访问的限制,在Istio中使用了 Ingress和Egress 来实现网关的功能.

附上:

喵了个咪的博客:w-blog.cn

Istio官方地址:https://preliminary.istio.io/zh

Istio中文文档:https://preliminary.istio.io/zh/docs/

PS : 此处基于当前最新istio版本1.0.3版本进行搭建和演示

一. Ingress(入口网关)

Istio的网关运行配置路由规则以及流量如何进入到集群中,我们使用httpbin来作为实验项目

>kubectl apply -n istio-test -f istio-1.0.3/samples/httpbin/httpbin.yaml
  1. 确定入口 IP 和端口

执行以下命令以确定您的 Kubernetes 集群是否在支持外部负载均衡器的环境中运行。

> kubectl get svc istio-ingressgateway -n istio-system
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)                                                                                                                   AGE
istio-ingressgateway   LoadBalancer   10.43.92.244   172.16.0.203   80:31380/TCP,443:31390/TCP,31400:31400/TCP,15011:30921/TCP,8060:30126/TCP,853:30117/TCP,15030:31865/TCP,15031:30683/TCP   22h

如果环境不再一套内网中使用了负载均衡,需要使用映射的对应的IP和端口,笔者是内网所以对应的访问可以通过其中的任意一个节点即可

  1. 使用 Istio 网关配置 Ingress

Ingress Gateway描述了在网格边缘操作的负载平衡器,用于接收传入的 HTTP/TCP 连接。它配置暴露的端口,协议等,但与 Kubernetes Ingress Resources 不同,它不包括任何流量路由配置。流入流量的流量路由使用 Istio 路由规则进行配置,与内部服务请求完全相同。

让我们看看如何为 Gateway 在 HTTP 80 端口上配置流量。

kubectl apply -n istio-test -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 -n istio-test -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。

该网关列表指定,只有通过我们的要求 httpbin-gateway 是允许的。所有其他外部请求将被拒绝,并返回 404 响应。

请注意,在此配置中,来自网格中其他服务的内部请求不受这些规则约束,而是简单地默认为循环路由。要将这些(或其他规则)应用于内部调用,我们可以将特殊值 mesh 添加到 gateways 的列表中。

  1. 使用 curl 访问 httpbin 服务:
curl -I -HHost:httpbin.example.com http://172.16.0.203:31380/status/200
HTTP/1.1 200 OK
server: envoy
date: Thu, 08 Nov 2018 02:35:52 GMT
content-type: text/html; charset=utf-8
access-control-allow-origin: *
access-control-allow-credentials: true
content-length: 0
x-envoy-upstream-service-time: 46

请注意,这里使用该 -H 标志将 Host HTTP Header 设置为 “httpbin.example.com”。这以操作是必需的,因为上面的 Ingress Gateway 被配置为处理 “httpbin.example.com”,但在测试环境中没有该主机的 DNS 绑定,只是将请求发送到 Ingress IP。

  1. 访问任何未明确公开的其他 URL,应该会看到一个 HTTP 404 错误:
curl -I -HHost:httpbin.example.com http://172.16.0.203:31380/headers
HTTP/1.1 404 Not Found
date: Thu, 08 Nov 2018 02:36:32 GMT
server: envoy
transfer-encoding: chunked

二. Egress(出口网关)

入口网关大家都很好理解不就是一个NGINX域名解析路由控制嘛,你这个出口网关有啥用啊? 在日益精细化运维管理的今天对于出口流量的控制越来越重要, 可以访问什么不可以访问什么对每一个程序来说应该都是确定的,这样的限制可以避免异常流量外部攻击等.

缺省情况下,Istio 服务网格内的 Pod,由于其 iptables 将所有外发流量都透明的转发给了 Sidecar,所以这些集群内的服务无法访问集群之外的 URL,而只能处理集群内部的目标。

这就导致了文章开头所说的问题Istio无法外网访问,如果大家的数据库不在集群内就会发现根本连不上

我们还是使用sleep来作为我们的例子

> kubectl apply -n istio-test -f istio-1.0.3/samples/sleep/sleep.yaml
> export SOURCE_POD=$(kubectl get -n istio-test pod -l app=sleep -o jsonpath={.items..metadata.name})
# 尝试访问(访问任何外部地址都会出现404)
> kubectl exec -n istio-test -it $SOURCE_POD -c sleep bash
crul -I baidu.com
bash: crul: command not found
root@sleep-76df4f989c-9hvvd:/# curl -I baidu.com
HTTP/1.1 404 Not Found
date: Thu, 08 Nov 2018 03:37:16 GMT
server: envoy
transfer-encoding: chunked
  1. 配置外部服务
kubectl apply -n istio-test -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: baidu-ext
spec:
  hosts:
  - baidu.com
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  location: MESH_EXTERNAL
EOF

创建一个 ServiceEntry 以及 VirtualService,允许访问外部 HTTPS 服务。注意:包括 HTTPS 在内的 TLS 协议,在 ServiceEntry 之外,还需要创建 TLS VirtualService

> kubectl exec -n istio-test -it $SOURCE_POD -c sleep bash
> curl -I baidu.com
HTTP/1.1 200 OK
date: Thu, 08 Nov 2018 03:37:57 GMT
server: envoy
last-modified: Tue, 12 Jan 2010 13:48:00 GMT
etag: "51-47cf7e6ee8400"
accept-ranges: bytes
content-length: 81
cache-control: max-age=86400
expires: Fri, 09 Nov 2018 03:37:57 GMT
content-type: text/html
x-envoy-upstream-service-time: 67
  1. 配置外部 HTTPS 服务

上面只配置了http可以访问,如果使用https会出现以下情况

> curl -I https://baidu.com
curl: (35) Unknown SSL protocol error in connection to baidu.com:443 

创建一个 ServiceEntry 和一个 VirtualService 以允许访问外部 HTTPS 服务。请注意, 对于 TLS 协议(包括 HTTPS),除了 ServiceEntry 之外,还需要 VirtualService。 VirtualService 必须在 match 子句中包含 tls 规则和 sni_hosts 以启用 SNI 路由。

kubectl apply -n istio-test -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: baidu
spec:
  hosts:
  - baidu.com
  ports:
  - number: 443
    name: https
    protocol: HTTPS
  resolution: DNS
  location: MESH_EXTERNAL
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: baidu
spec:
  hosts:
  - baidu.com
  tls:
  - match:
    - port: 443
      sni_hosts:
      - baidu.com
    route:
    - destination:
        host: baidu.com
        port:
          number: 443
      weight: 100
EOF
> curl -I https://baidu.com
HTTP/1.1 302 Moved Temporarily
Server: bfe/1.0.8.18
Date: Thu, 08 Nov 2018 03:41:05 GMT
Content-Type: text/html
Content-Length: 161
Connection: keep-alive
Location: http://www.baidu.com/
  1. 为外部服务设置路由规则

通过 ServiceEntry 访问外部服务的流量,和网格内流量类似,都可以进行 Istio 路由规则 的配置。下面我们使用 istioctl 为 httpbin.org 服务设置一个超时规则。

在测试 Pod 内部,使用 curl 调用 httpbin.org 这一外部服务的 /delay 端点:

> kubectl apply -n istio-test -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: ServiceEntry
metadata:
  name: httpbin-ext
spec:
  hosts:
  - httpbin.org
  ports:
  - number: 80
    name: http
    protocol: HTTP
  resolution: DNS
  location: MESH_EXTERNAL
EOF
> time curl -o /dev/null -s -w "%{http_code}\n" http://httpbin.org/delay/5
200

real    0m5.752s
user    0m0.013s
sys     0m0.022s

使用 kubectl 为 httpbin.org 外部服务的访问设置一个 3 秒钟的超时

kubectl apply -n istio-test -f - <<EOF
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: httpbin-ext
spec:
  hosts:
    - httpbin.org
  http:
  - timeout: 3s
    route:
      - destination:
          host: httpbin.org
        weight: 100
EOF

这一次会在 3 秒钟之后收到一个内容为 504 (Gateway Timeout) 的响应。虽然 httpbin.org 还在等待他的 5 秒钟,Istio 却在 3 秒钟的时候切断了请求。

time curl -o /dev/null -s -w "%{http_code}\n" http
504

real    0m3.046s
user    0m0.010s
sys     0m0.010s
  1. 直接调用外部服务

确认限制可以带来更多的控制避免出错,但是很多时候还是会带来很多麻烦,那么如果不希望Istio进行限制可以随意的访问需要怎么办呢?当然是可以的,可以配置Istio对于哪些范围,首先我们需要确定内部集群IP范围:

  • 使用minikube 范围是 10.0.0.1/24
  • 使用rancher 范围是 10.43.0.1/24

方案一(HELM):

注意这里应该使用和之前部署 Istio 的时候同样的 Helm 命令,尤其是 --namespace 参数。在安装 Istio 原有命令的基础之上,加入 --set global.proxy.includeIPRanges="10.0.0.1/24" -x templates/sidecar-injector-configmap.yaml 即可。

helm template install/kubernetes/helm/istio <安装 Istio 时所使用的参数> --set global.proxy.includeIPRanges="10.0.0.1/24" -x templates/sidecar-injector-configmap.yaml | kubectl apply -f -

然后重新部署sleep就可以了

方案二(改编排):

应为笔者不是使用helm进行的istio安装,直接使用的官方demo来安装的,我们可以先找到includeOutboundIPRanges然后修改后面的*改成对应的IP段

PS : 不同版本生成的yaml是不一样的需要注意

istio-1.0.1版本

- "[[ index .ObjectMeta.Annotations "traffic.sidecar.istio.io/includeOutboundIPRanges"  ]]"
[[ else -]]
- "10.43.0.1/24"
[[ end -]]

istio1.0.3版本

- "[[ annotation .ObjectMeta `traffic.sidecar.istio.io/includeOutboundIPRanges`  "10.43.0.1/24"  ]]"

然后重新部署Istio就可以没有任何访问限制了

上一篇:电子邮件地址策略


下一篇:三, 跨语言微服务框架 - Istio官方示例(自动注入.请求路由.流量控制.故障注入)