Istio Ingress控制器

此任务将演示如何通过配置Istio将服务公开到service mesh集群外部。在Kubernetes环境中,Kubernetes Ingress Resources 允许用户指定某个服务是否要公开到集群外部。然而,Ingress Resource规范非常精简,只允许用户设置主机,路径,以及后端服务。为了利用Istio的高级路由能力,我们建议组合使用 Ingress Resource和Istio的路由规则。

注意: Istio 不支持在ingress resource规范中使用ingress.kubernetes.io 注解(annotations)。除了kubernetes.io/ingress.class: istio之外的注解都会被忽略。

前提条件

  • 参照文档安装指南中的步骤安装Istio。

  • 确保当前的目录是istio目录。

  • 启动 httpbin 示例, 我们会把这个服务作为目标(destination)服务公开到外部。

    如果你安装了 Istio-Initializer, 请执行:

    1. kubectl apply -f samples/httpbin/httpbin.yaml

    如果没有 Istio-Initializer, 请执行:

    1. kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml)

配置 ingress (HTTP)

  1. 为 httpbin 服务创建一个基本的 Ingress Resource

    1. cat <<EOF | kubectl create -f -
    2. apiVersion: extensions/v1beta1
    3. kind: Ingress
    4. metadata:
    5. name: simple-ingress
    6. annotations:
    7. kubernetes.io/ingress.class: istio
    8. spec:
    9. rules:
    10. - http:
    11. paths:
    12. - path: /.*
    13. backend:
    14. serviceName: httpbin
    15. servicePort: 8000
    16. EOF

    /. 是一个 Istio 特殊表示法,以前缀 / 开始的配置,都表示前缀匹配。上面的配置允许访问 httpbin 服务中的所有 URI。然而,实际上我们只想开放一部分特殊的 URI。我们可以先定义一个默认的 deny all* 路由规则,将所有的请求都拒绝:

    1. cat <<EOF | istioctl create -f -
    2. ## Deny all access from istio-ingress
    3. apiVersion: config.istio.io/v1alpha2
    4. kind: RouteRule
    5. metadata:
    6. name: deny-route
    7. spec:
    8. destination:
    9. name: httpbin
    10. match:
    11. # Limit this rule to istio ingress pods only
    12. source:
    13. name: istio-ingress
    14. labels:
    15. istio: ingress
    16. precedence: 1
    17. route:
    18. - weight: 100
    19. httpFault:
    20. abort:
    21. percent: 100
    22. httpStatus: 403 #Forbidden for all URLs
    23. EOF
  2. 然后,再定义一个高优先级的路由,允许访问 /status/ 前缀的 URI。

    1. cat <<EOF | istioctl create -f -
    2. ## Allow requests to /status prefix
    3. apiVersion: config.istio.io/v1alpha2
    4. kind: RouteRule
    5. metadata:
    6. name: status-route
    7. spec:
    8. destination:
    9. name: httpbin
    10. match:
    11. # Limit this rule to istio ingress pods only
    12. source:
    13. name: istio-ingress
    14. labels:
    15. istio: ingress
    16. request:
    17. headers:
    18. uri:
    19. prefix: /status
    20. precedence: 2 #must be higher precedence than the deny-route
    21. route:
    22. - weight: 100
    23. EOF

    这里也可以用其他的路由功能,比如重定向,重写,正则表达式匹配 HTTP headers,websocket upgrades,超时,重试等。详情请参看 routing rules

验证 ingress

  1. 确定 ingress URL:

    • 如果你的集群运行的环境支持外部的负载均衡器,请使用 ingress 的外部地址:

      1. kubectl get ingress simple-ingress -o wide
      1. NAME HOSTS ADDRESS PORTS AGE
      2. simple-ingress * 130.211.10.121 80 1d
      1. export INGRESS_HOST=130.211.10.121
    • 如果你的环境不支持负载均衡器,使用 ingress 控制器的 pod 的主机 IP:

      1. kubectl get po -l istio=ingress -o jsonpath='{.items[0].status.hostIP}'
      1. 169.47.243.100

      同时也找到 istio-ingress 服务的 80 端口的 nodePort 映射端口:

      1. kubectl get svc istio-ingress
      1. NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
      2. istio-ingress 10.10.10.155 <pending> 80:31486/TCP,443:32254/TCP 32m
      1. export INGRESS_HOST=169.47.243.100:31486
  2. 使用 curl 访问 httpbin 服务:

    1. curl -I http://$INGRESS_HOST/status/200
    1. HTTP/1.1 200 OK
    2. Server: meinheld/0.6.1
    3. Date: Thu, 05 Oct 2017 21:23:17 GMT
    4. Content-Type: text/html; charset=utf-8
    5. Access-Control-Allow-Origin: *
    6. Access-Control-Allow-Credentials: true
    7. X-Powered-By: Flask
    8. X-Processed-Time: 0.00105214118958
    9. Content-Length: 0
    10. Via: 1.1 vegur
    11. Connection: Keep-Alive
  3. 如果访问其他的没有明确公开的 URL,应该收到 HTTP 403 错误

    1. curl -I http://$INGRESS_HOST/headers
    1. HTTP/1.1 403 FORBIDDEN
    2. Server: meinheld/0.6.1
    3. Date: Thu, 05 Oct 2017 21:24:47 GMT
    4. Content-Type: text/html; charset=utf-8
    5. Access-Control-Allow-Origin: *
    6. Access-Control-Allow-Credentials: true
    7. X-Powered-By: Flask
    8. X-Processed-Time: 0.000759840011597
    9. Content-Length: 0
    10. Via: 1.1 vegur
    11. Connection: Keep-Alive

配置安全ingress(HTTPS)

  1. 生成必要的secretOpenSSL创建测试私钥和证书

    1. openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /tmp/tls.key -out /tmp/tls.crt -subj "/CN=foo.bar.com"
  2. kubectl更新secret

    1. kubectl create -n istio-system secret tls istio-ingress-certs --key /tmp/tls.key --cert /tmp/tls.crt

3.为 httpbin服务创建Ingress Resource

  1. cat <<EOF | kubectl create -f -
  2. apiVersion: extensions/v1beta1
  3. kind: Ingress
  4. metadata:
  5. name: secure-ingress
  6. annotations:
  7. kubernetes.io/ingress.class: istio
  8. spec:
  9. tls:
  10. - secretName: istio-ingress-certs # currently ignored
  11. rules:
  12. - http:
  13. paths:
  14. - path: /.*
  15. backend:
  16. serviceName: httpbin
  17. servicePort: 8000
  18. EOF

创建 deny rule ,同时为 /status 前缀创建前文相同的规则。同时,像前面一样,设置 INGRESS_HOST 指向 ingress 服务的 IP 和端口。

注意: Envoy 当前只允许一个 TLS ingress 密钥,因为 SNI 尚未支持。也就是说看,ingress 中的 secretName 字段并没有用,secret 必须叫做 istio-ingress-certs 并且在 istio-system 命名空间(namespace)。

  1. curl访问安全httpbin服务

    1. curl -I -k https://$INGRESS_HOST/status/200

为gRPC配置ingress

Ingress控制器的path字段当前并不支持. 字符。这会导致使用命名空间的gRPC服务出问题(译注:因为gRPC用 . 分割命名空间和服务名)。为了解决这个问题,可以先把流量导向到一个虚拟(dummy)服务,然后设置路由规则去拦截流量并重定向到期望的服务。

  1. 创建一个虚拟ingress服务:

    1. cat <<EOF | kubectl create -f -
    2. apiVersion: v1
    3. kind: Service
    4. metadata:
    5. name: ingress-dummy-service
    6. spec:
    7. ports:
    8. - name: grpc
    9. port: 1337
    10. EOF
  2. 创建一个拦截所有流量的ingress指向虚拟服务:

    1. cat <<EOF | kubectl create -f -
    2. apiVersion: extensions/v1beta1
    3. kind: Ingress
    4. metadata:
    5. name: all-istio-ingress
    6. annotations:
    7. kubernetes.io/ingress.class: istio
    8. spec:
    9. rules:
    10. - http:
    11. paths:
    12. - backend:
    13. serviceName: ingress-dummy-service
    14. servicePort: grpc
    15. EOF
  3. 给每个服务创建一个RouteRule ,将流量从虚拟服务重定向到正确的gRPC服务:

    1. cat <<EOF | istioctl create -f -
    2. apiVersion: config.istio.io/v1alpha2
    3. kind: RouteRule
    4. metadata:
    5. name: foo-service-route
    6. spec:
    7. destination:
    8. name: ingress-dummy-service
    9. match:
    10. request:
    11. headers:
    12. uri:
    13. prefix: "/foo.FooService"
    14. precedence: 1
    15. route:
    16. - weight: 100
    17. destination:
    18. name: foo-service
    19. ---
    20. apiVersion: config.istio.io/v1alpha2
    21. kind: RouteRule
    22. metadata:
    23. name: bar-service-route
    24. spec:
    25. destination:
    26. name: ingress-dummy-service
    27. match:
    28. request:
    29. headers:
    30. uri:
    31. prefix: "/bar.BarService"
    32. precedence: 1
    33. route:
    34. - weight: 100
    35. destination:
    36. name: bar-service
    37. EOF

理解ingress原理

Ingress为外部流量进入Istio service mesh提供一个网关,并使Istio的流量管理和策略功能可用于边缘服务。

在前面的步骤中,我们在Istio service mesh中创建了一个服务,并展示了如何将服务的HTTP和HTTPS公开给外部流量,同时还展示了如何使用Istio路由规则来控制入口流量。

清理

  1. 删除密钥,Ingress Resource定义以及Istio规则。

    1. istioctl delete routerule deny-route status-route
    2. kubectl delete ingress simple-ingress secure-ingress
    3. kubectl delete -n istio-system secret istio-ingress-certs
  2. 删除httpbin服务。

    1. kubectl delete -f samples/httpbin/httpbin.yaml

进阶阅读