• 【Kubernetes】K8s笔记(十一):Ingress 集群进出流量总管


    0. Ingress 解决了什么问题

    上一篇笔记中讲解了 Service 的功能和运行机制。Service 本质上是一个由 kube-proxy 控制的四层负载均衡,在 TCP/IP 协议栈上转发流量。然而四层负载均衡能做的很有限,现在绝大多应用运行在应用层(五层/ OSI 七层)的 HTTP/HTTPS 协议之上,有更多的高级路由条件,而这些在传输层是不可见的。

    Service 比较适合代理集群内部的服务。如果想要把服务暴露到集群外部,就只能使用 NodePort 或者 LoadBalancer 这两种方式,而它们都缺乏足够的灵活性,难以管控。

    Kubernetes 为了解决这个问题,引入了一个新的 API 对象做七层负载均衡。除了七层负载均衡,这个对象还承担了更多的职责——作为流量的总入口,管理进出集群的数据(南北向流量),让外部用户能够安全便捷地访问集群内部的服务。这个 API 对象被命名为 Ingress,意思就是集群内外边界上的入口。

    *图示是一个将所有流量都发送到同一 Service 的简单 Ingress 示例 图片来源

    1. Ingress Controller

    Service 本身是没有服务能力的,它只是一些 iptables 规则,真正配置、应用这些规则的实际上是节点里的 kube-proxy 组件。如果没有 kube-proxy,Service 定义得再完善也没有用。

    同样的,Ingress 也只是一些 HTTP 路由规则的集合,相当于一份静态的描述文件,真正要把这些规则在集群里实施运行,还需要有另外一个东西,这就是 Ingress Controller,它的作用就相当于 Service 的 kube-proxy,能够读取、应用 Ingress 规则,处理、调度流量。

    为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。Ingress 控制器不是随集群自动启动的,用户可以选择最适合集群的 Ingress 控制器实现。Kubernetes 目前支持和维护 AWS、 GCE 和 Nginx Ingress 控制器。还有很多控制器可供选择。

    从 Ingress Controller 的描述上我们也可以看到,HTTP 层面的流量管理、安全控制等功能其实就是经典的反向代理,而 Nginx 则是其中稳定性最好、性能最高的产品,所以它也理所当然成为了 Kubernetes 里应用得最广泛的 Ingress Controller。不过,因为 Nginx 是开源的,谁都可以基于源码做二次开发,所以它又有很多的变种。这里我们选取 Nginx 对 Ingress Controller 的开发实现 NGINX Ingress Controller

    *图示展示了 Ingress Controller 在集群中的位置 图片来源

    2. 指定 Ingress Class 使用多个 Ingress Controller

    IngressClass Docs

    起初,Kubernetes 集群内只有一个 Ingress Controller,这样的用法会带来一些问题:

    • 由于某些原因,项目组需要引入不同的 Ingress Controller,但 Kubernetes 不允许这样做

    • Ingress 规则太多,都交给一个 Ingress Controller 处理会让它不堪重负

    • 多个 Ingress 对象没有很好的逻辑分组方式,管理和维护成本很高

    • 集群里有不同的租户,他们对 Ingress 的需求差异很大甚至有冲突,无法部署在同一个 Ingress Controller 上

    Kubernetes 就又提出了一个 Ingress Class 的概念,让它插在 Ingress 和 Ingress Controller 中间,作为流量规则和控制器的协调人,解除了 Ingress 和 Ingress Controller 的强绑定关系。

    Kubernetes 用户可以转向管理 Ingress Class,用它来定义不同的业务逻辑分组,简化 Ingress 规则的复杂度。比如说,我们可以用 Class A 处理博客流量、Class B 处理短视频流量、Class C 处理购物流量。这些 Ingress 和 Ingress Controller 彼此独立,不会发生冲突。

    3. 使用 YAML 描述 Ingress / Ingress Class

    首先用命令 kubectl api-resources 查看它们的基本信息:

    NAME                              SHORTNAMES   APIVERSION                             NAMESPACED   KIND
    ingressclasses                                 networking.k8s.io/v1                   false        IngressClass
    ingresses                         ing          networking.k8s.io/v1                   true         Ingress
    

    *Ingress Controller 是一个处理流量的应用程序,稍后可以使用 Deployment 和 DaemonSet 来部署

    3.1 Ingress

    Ingress 可以使用 kubectl create 来创建样板文件,它需要用两个附加参数:

    • --class - 指定 Ingress 从属的 Ingress Class 对象

    • --rule - 指定路由规则,基本形式是 URI=Service,也就是说是访问 HTTP 路径就转发到对应的 Service 对象,再由 Service 对象转发给后端的 Pod

    $ export out="--dry-run=client -o yaml"
    $ kubectl create ing ngx-ing --rule="ngx.test/=ngx-svc:80" --class=ngx-ink $out
    
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: ngx-ing
    
    spec:
      ingressClassName: ngx-ink
    
      rules:
      - host: ngx.test
        http:
          paths:
          - backend:
              service:
                name: ngx-svc
                port:
                  number: 80
            path: /
            pathType: Exact
    

    这份 YAML 文档中有两个关键字段 ingressClassNamerulesrules 的格式稍显复杂:它将路由规则拆散为 hosthttp path,在 path 里又指定了路径的匹配方式,可以是精确匹配 Exact 或者是前缀匹配 Prefix,再用 backend 来指定转发的目标 Service 对象。

    3.2 Ingress Class

    Ingress Class 本身并没有什么实际的功能,只是起到联系 Ingress 和 Ingress Controller 的作用,所以它的定义非常简单,在 spec 里只有一个必需的字段 controller ,表示要使用哪个 Ingress Controller,具体的名字就要看实现文档了。

    比如,要使用 Nginx 开发的 Ingress Controller,那么就要用名字 nginx.org/ingress-controller

    apiVersion: networking.k8s.io/v1
    kind: IngressClass
    metadata:
      name: ngx-ink
    
    spec:
      controller: nginx.org/ingress-controller
    

    4. 使用 Ingress / Ingress Class

    使用 kubectl apply 创建 Ingress 和 Ingress Class 这两个对象:

    $ kubectl apply -f ngx-ing-class.yaml 
    ingressclass.networking.k8s.io/ngx-ink created
    $ kubectl apply -f ngx-ing.yaml 
    ingress.networking.k8s.io/ngx-ing created
    

    然后我们查看 Ingress 和 Ingress Class 的状态:

    $ kubectl get ingress -o wide 
    NAME      CLASS     HOSTS      ADDRESS   PORTS   AGE
    ngx-ing   ngx-ink   ngx.test             80      67s
    $ kubectl get ingressclass -o wide 
    NAME      CONTROLLER                     PARAMETERS   AGE
    ngx-ink   nginx.org/ingress-controller   <none>       84s
    

    可以使用 kubectl describe 查看详细的信息:

    $ kubectl describe ing ngx-ing 
    Name:             ngx-ing
    Labels:           <none>
    Namespace:        default
    Address:          
    Ingress Class:    ngx-ink
    Default backend:  <default>
    Rules:
      Host        Path  Backends
      ----        ----  --------
      ngx.test    
                  /   ngx-svc:80 (10.10.1.38:80,10.10.1.40:80,10.10.1.41:80)
    Annotations:  <none>
    Events:       <none>
    

    可以看到 Ingress 对象的路由规则 Host/Path 就是在 YAML 里设置的域名 ngx.test/

    5. 使用 Ingress Controller

    准备好了 Ingress 和 Ingress Class,接下来就需要部署真正处理路由规则的 Ingress Controller。

    Nginx Ingress Controller 以 Pod 的形式运行在 Kubernetes 里,同时支持 Deployment 和 DaemonSet 两种部署方式。我们现在根据 Nginx Ingress Controller Installation Docs 部署 Nginx Ingress Controller。

    在使用 kubectl 的主机上首先克隆仓库并进入部署文件夹:

    $ git clone https://github.com/nginxinc/kubernetes-ingress.git --branch v2.4.1
    $ cd kubernetes-ingress/deployments
    

    Nginx Ingress Controller 的安装略微麻烦一些,有很多个 YAML 需要执行,但如果只是做简单的试验,就只需要用到 4 个 YAML:

    $ kubectl apply -f common/ns-and-sa.yaml
    namespace/nginx-ingress created
    serviceaccount/nginx-ingress created
    
    $ kubectl apply -f rbac/rbac.yaml
    clusterrole.rbac.authorization.k8s.io/nginx-ingress created
    clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress created
    
    $ kubectl apply -f common/nginx-config.yaml
    configmap/nginx-config created
    
    $ kubectl apply -f common/default-server-secret.yaml
    secret/default-server-secret created
    

    前两条命令为 Ingress Controller 创建了一个独立的名字空间 nginx-ingress,还有相应的账号和权限,这是为了访问 apiserver 获取 Service、Endpoint 信息用的;后两条则是创建了一个 ConfigMap 和 Secret,用来配置 HTTP/HTTPS 服务。

    接下来我们还需要部署一些 Custom Resources,没有它们我们部署的 Ingress Controller 就无法运行:

    默认情况下,需要为虚拟服务器、虚拟服务器路由、传输服务器和策略创建自定义资源的定义。否则,Ingress Controller Pod 将不会变为 Ready 状态。如果要禁用该要求,请将 -enable-custom-resources 命令行参数配置为 Readyfalse 并跳过此部分。

    $ kubectl apply -f common/crds/k8s.nginx.org_policies.yaml 
    customresourcedefinition.apiextensions.k8s.io/policies.k8s.nginx.org created
    
    $ kubectl apply -f common/crds/k8s.nginx.org_transportservers.yaml 
    customresourcedefinition.apiextensions.k8s.io/transportservers.k8s.nginx.org created
         
    $ kubectl apply -f common/crds/k8s.nginx.org_virtualserverroutes.yaml 
    customresourcedefinition.apiextensions.k8s.io/virtualserverroutes.k8s.nginx.org created
    
    $ kubectl apply -f common/crds/k8s.nginx.org_virtualservers.yaml 
    customresourcedefinition.apiextensions.k8s.io/virtualservers.k8s.nginx.org created
    

    部署 Ingress Controller 不需要我们自己从头编写 Deployment,Nginx 已经为我们提供了示例 YAML (位置是:kubernetes-ingress/deployments/deployment/nginx-ingress.yaml),现在我们对其进行一些小小的改动:

    • metadata 里的 name 要改成自己的名字,比如 ngx-ing-dep

    • spec.selectortemplate.metadata.labels 也要修改成自己的名字,比如还是用 ngx-ing-dep

    • containers.image 可以改用 apline 版本,加快下载速度,比如 nginx/nginx-ingress:2.2-alpine

    • 最下面的 args 要加上 -ingress-class=ngx-ink,也就是前面创建的 Ingress Class 的名字,这是让 Ingress Controller 管理 Ingress 的关键

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: ngx-ing-dep
      namespace: nginx-ingress
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: ngx-ing-dep
    
      template:
        metadata:
          labels:
            app: ngx-ing-dep
    
        spec:
          serviceAccountName: nginx-ingress
          automountServiceAccountToken: true
          containers:
          - image: nginx/nginx-ingress:2.2-alpine
            imagePullPolicy: IfNotPresent
            name: nginx-ingress
            ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
            - name: readiness-port
              containerPort: 8081
            - name: prometheus
              containerPort: 9113
            readinessProbe:
              httpGet:
                path: /nginx-ready
                port: readiness-port
              periodSeconds: 1
            resources:
              requests:
                cpu: "100m"
                memory: "128Mi"
    
            securityContext:
              allowPrivilegeEscalation: true
              runAsUser: 101 #nginx
              runAsNonRoot: true
              capabilities:
                drop:
                - ALL
                add:
                - NET_BIND_SERVICE
            env:
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            args:
              - -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
              - -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
              - -ingress-class=ngx-ink
    

    有了 Ingress Controller,这些 API 对象的关联就更复杂了。然后我们创建对象:

    $ kubectl apply -f ngx-ing-dep.yaml 
    deployment.apps/ngx-ing-dep created
    

    注意 Ingress Controller 位于名字空间 nginx-ingress,所以查看状态需要用 -n 参数显式指定,否则我们只能看到 default 名字空间里的 Pod:

    $ kubectl get deploy -n nginx-ingress
    NAME          READY   UP-TO-DATE   AVAILABLE   AGE
    ngx-ing-dep   1/1     1            1           10m
    
    $ kubectl get pod -n nginx-ingress
    NAME                           READY   STATUS    RESTARTS   AGE
    ngx-ing-dep-7c48c74865-vzmnf   1/1     Running   0          11m
    

    现在 Ingress Controller 就算是运行起来了。还有最后一道工序,因为 Ingress Controller 本身也是一个 Pod,想要向外提供服务还是要依赖于 Service 对象。所以至少还要再为它定义一个 Service,使用 NodePort 或者 LoadBalancer 暴露端口,才能真正把集群的内外流量打通。

    这里还有个取巧的办法,使用 kubectl port-forward 直接把本地的端口映射到 Kubernetes 集群的某个 Pod 里,在测试验证的时候非常方便。

    $ kubectl port-forward -n nginx-ingress ngx-ing-dep-7c48c74865-vzmnf 8080:80 &
    

    可以修改 /etc/hosts 来手工添加域名解析,也可以使用 --resolve 参数,指定域名的解析规则,比如在这里把 ngx.test 强制解析到 127.0.0.1,也就是被 kubectl port-forward 转发的本地地址。

    和 Service 一样,Ingress 把请求转发到了集群内部的 Pod,但 Ingress 的路由规则不再是 IP 地址,而是 HTTP 协议里的域名、URI 等要素。

    再补充一点,目前的 Kubernetes 流量管理功能主要集中在 Ingress Controller 上,已经远不止于管理“入口流量”了,它还能管理“出口流量”,也就是 egress,甚至还可以管理集群内部服务之间的“东西向流量”。此外,Ingress Controller 通常还有很多的其他功能,比如 TLS 终止、网络应用防火墙、限流限速、流量拆分、身份认证、访问控制等等,完全可以认为它是一个全功能的反向代理或者网关。

  • 相关阅读:
    图像处理黑科技——弯曲矫正、去摩尔纹、切边增强、PS检测
    测试之路-我曾经跨过无数Bug,也怼过各种开发
    大国之“稻”书写自己的历史 国稻种芯-万祥军:稻作农耕文化
    7-42 子集和问题——组合子集
    【Linux网络】网络层协议:IP
    现有库存(on-hand inventory),库存水平(inventory level),库存位置(inventory position)
    Java并发编程--变量可见性、避免指令重排,还得是用它
    OPENSQL快速学习
    Spring Cloud Feign面试题
    OAuth 2.0 (第三方登录)前端流程实现
  • 原文地址:https://www.cnblogs.com/joexu01/p/16812302.html