• kubernetes(二十) Ingress


    什么是Ingress

    Ingress是kubernetes中用来对集群外部进来的请求进行负载、路由控制的一种机制。通过ingress,可以方便的将集群内的service以http或https方式对外提供服务,而且不用将各个服务再单独暴露。Ingress功能由Ingress resource和Ingress Controllers共同协作完成。

    Ingress resource

    Ingress resource是kubernetes中的一种资源,是类似于pod、deployment的一种API对象,可以通过kubectl命令创建、销毁、更新,其对应的Kind是Ingress,可以在其spec属性中定义服务路由的规则信息。通常Ingress resource中定义的规则需要支持以下的特性:

    • 基于内容路由

      • 基于目的主机路由:根据请求头中的请求地址(域名),将请求路由到不同的服务中。如请求地址是foo.example.com的分发到一组服务中,请求地址是bar.example.com分发到别的服务组中
      • 基于请求路径路由:根据请求URL中的路径信息,将请求路由到不同的服务中。如将请求路径/serviceA/**的请求转发到服务A中,将请求路径/serviceB/**的请求转发到服务B中
    • 对每一个主机都可以单独设置TLS/SSL连接信息

    Ingress Controllers

    Ingress resource只是定义了路由的规则信息,真正利用这些信息对请求进行控制是通过Ingress Controllers来实现的。不像kube-controller-manager中的其他controller组件,它们是被作为集群的一部分随着集群直接安装好,Ingress controllers 需要我们自己来安装启动,并且kubernetes支持很多种实现来满足不同需求,具体参考Ingress Controllers

    在kubernetes中,Ingress Controller以pod形式运行,监控API Server的/ingress接口后端的backend services,如果service发生变化,Ingress Controller自动更新转发规则。如Nginx Ingress Controller的工作过程如下:

    • 监听API Server获取所有的ingress 的定义
    • 基于Ingress的定义,进行规则合并,生成Nginx所需的配置文件/etc/nginx/nginx.conf
    • 执行nginx -s reload命令,重新加载nginx.conf

    为什么需要Ingress

    1. 端口竞争

      通常情况下,我们部署在kubernetes集群中的应用需要给外部访问,这时我们需要在kubernetes中定义NodePort、LoadBalancer等类型的Servcie来实现,具体参考Service。其中LoadBalancer需要在提供相应机制的云环境中才能使用,所以在自建的kubernetes集群中都是通过NodePort类型的Service来实现,就是将宿主机的port的Service的Port做个映射,通过访问宿主机的端口来对service进行访问。在kubernetes集群中只有一个应用,或者应用数量比较少,能够正确分配各个应用对应的宿主机端口时还可以应付。随着应用的追加,端口的映射就变的混乱起来,有的应用还会因为限制必须使用特定的端口号,而这些端口号可能前期已经分配给了别的应用,这时就出现了端口竞争

    2. 服务动态更新

      为了避免端口竞争,可以在系统中部署反向代理服务(nginx、HAproxy)等,这时可以把对外的集群服务都通过反向代理服务来暴露,这样就带来了另一个问题,当有新的服务追加进来,或者旧的服务需要删除,这时还要重新编辑反向代理的配置文件,然后对反向代理服务进行升级。上线/下线一个应用却需要编辑另一个应用的配置文件,服务的升级、回滚等等都需要考虑,不仅麻烦还容易出错

    Ingress如何解决上面问题

    当采用Ingress机制时,部署新应用,只需要创建针对新应用的Ingress resource,Ingress Controllers就会自动把Ingress resource中的规则合并起来,作为整体路由规则对外服务,并且服务都通过Ingress Controllers统一对外提供,也解决了端口竞争的问题。接下来以nginx-ingress为例来讲解具体的原理以及在集群中部署Ingress。

    nginx-ingress

    1. nginx-ingress实现的原理

      1. 用户通过kubectl命令向API server发送创建ingress source对象的请求,
      2. ingress-controller监听API server获取到自己对应的ingress source的变化(当集群中有多个ingress controller时,创建ingress source对象时可以指定ingress.class属性,参考Using multiple Ingress controllers),
      3. ingress-controller获取ingress source中的规则后,根据自己的模版生成相应的配置文件,之后reload配置文件,使新的配置生效
      4. 外部请求进入到ingress,访问到实际ingress-controller后,ingress-controller根据配置文件中的规则将请求分发到具体的service
      5. 其中nginx-ingress-controller是在nginx之上又额外添加了一些功能,如监听API server,自动合并规则,并重新加载等,当用容器形式部署时可以通过Deployment、RC等形式启动,和启动普通容器方法是一样的,想要让nginx-ingress-controller能被外部访问,也可以用NodePort形式的service来实现,或者通过Daemonset形式发布时直接指定暴露端口的形式。
    2. 部署nginx-ingress-controller
      目前部署nginx-ingress-controller有两个官方的安装方式:nginxinc/kubernetes-ingresskubernetes/ingress-nginx,具体对比参考区别

      部署前可参阅下这个异同,下面以kubernetes/ingress-nginx为例,进行nginx-ingress-controller的部署

      1. 下载启动文件

        $ mkdir -p /opt/k8s/yml/ingress-nginx
        $ cd /opt/k8s/yml/ingress-nginx
        $ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
        
        
        • 如果搭建的kubernetes是1.14之前的版本,需要把下载的mandatory.yaml文件中217行的kubernetes.io/os改成beta.kubernetes.io/os
      2. 下载镜像,上传到自己的私有镜像仓库

        mandatory.yaml中指定的原始镜像是quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0,在国内可能没有办法下载,改成从阿里镜像仓库下载,为了启动时不用重新从互联网下载,把这个镜像push到我们自己的镜像仓库中

        $ docker pull registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.30.0
        $ docker tag registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.30.0 192.168.0.107/k8s/nginx-ingress-controller:0.30.0
        $ docker push 192.168.0.107/k8s/nginx-ingress-controller:0.30.0
        
        
    3. 修改下载的mandatory.yaml,将镜像名称改成我们私有仓库中的镜像名称,之后启动服务

      ```
      $ cd /opt/k8s/yml/ingress-nginx
      $ kubectl create -f mandatory.yaml
      namespace/ingress-nginx created
      configmap/nginx-configuration created
      configmap/tcp-services created
      configmap/udp-services created
      serviceaccount/nginx-ingress-serviceaccount created
      clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
      role.rbac.authorization.k8s.io/nginx-ingress-role created
      rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
      clusterrolebinding.rbac.authorization.k8s.io/nginx-ingress-clusterrole-nisa-binding created
      deployment.apps/nginx-ingress-controller created
      limitrange/ingress-nginx created
      
      ```
      * 主要是发布了deployment类型的nginx-ingress-controller,默认replicate是1,权限通过nginx-ingress-serviceaccount来设置
      * 创建了一个serviceaccount:nginx-ingress-serviceaccount,并赋予相关的权限
      
      1. 因为我们是在裸机上部署的kubernetes,还需要部署一个service,将nginx-ingress-controller暴露出去,使集群外的服务能够访问,此处采用NodePort类型的service

        $ cd /opt/k8s/yml/ingress-nginx
        $ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/provider/baremetal/service-nodeport.yaml
        
        $ kubectl create -f service-nodeport.yaml
        service/ingress-nginx created
        
        
        • 这样我们通过创建一个service,把nginx-ingress-controller暴露出去,通过如下命令查看具体暴露的端口号

          $ kubectl get svc -n ingress-nginx
          NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
          ingress-nginx   NodePort   10.254.10.208   <none>        80:18797/TCP,443:29468/TCP   108s
          
          
          • 如果kubernetes给我们随机选择的端口号不能满足要求,可以通过修改service-nodeport.yaml,指定自己需要的端口号
      2. 验证安装情况

        $ kubectl get pods -n ingress-nginx
        NAME                                        READY   STATUS    RESTARTS   AGE
        nginx-ingress-controller-7fdc95bf86-rgmdn   1/1     Running   0          22m
        
        
        • 如果状态不是running,通过kubectl describe查看具体原因
      3. 查看安装的controller版本

        
        $ kubectl exec -it nginx-ingress-controller-7fdc95bf86-rgmdn -n ingress-nginx -- /nginx-ingress-controller --version
        
        NGINX Ingress controller
          Release:       0.30.0
          Build:         git-7e65b90c4
          Repository:    https://github.com/kubernetes/ingress-nginx
          nginx version: nginx/1.17.8
        
        
        
        
        • 其中nginx-ingress-controller-7fdc95bf86-rgmdn是上一步骤获取到的pod的名称

    验证

    1. 利用官方给我们提供的例子http-svc,启动一个服务,通过Ingress controller的端口访问我们的服务。因为官方例子http-svc.yaml中的镜像在gcr.io中,国内无法访问,需要修改成阿里仓库中的镜像registry.aliyuncs.com/google_containers/echoserver:1.4,这个镜像的作用是接收客户端的请求,返回服务端和客户端的header信息。

      启动文件:

      $ cd /opt/k8s/yml/ingress-nginx
      
      $ cat > http-svc.yaml<< EOF
      apiVersion: apps/v1
      kind: Deployment
      metadata:
      apiVersion: apps/v1
        name: http-svc
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: http-svc
        template:
          metadata:
            labels:
              app: http-svc
          spec:
            containers:
            - name: http-svc
              image: registry.aliyuncs.com/google_containers/echoserver:1.4
              ports:
              - containerPort: 8080
              env:
                - name: NODE_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: spec.nodeName
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
                - name: POD_IP
                  valueFrom:
                    fieldRef:
                      fieldPath: status.podIP
      
      ---
      
      apiVersion: v1
      kind: Service
      metadata:
        name: http-svc
        labels:
          app: http-svc
      spec:
        ports:
        - port: 80
          targetPort: 8080
          protocol: TCP
          name: http
        selector:
          app: http-svc
      EOF    
      	
      

      启动命令

      $ cd /opt/k8s/yml/ingress-nginx
      $ kubectl create -f http-svc.yaml
      
      
    2. 创建一个Ingress resources

      目前我们只创建了一个后端服务,所以创建一个简单的Ingress,把所有请求都路由到http-svc:80

      $ cd /opt/k8s/yml/ingress-nginx
      $ cat > single-ingress.yml <<EOF
      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: single-ingress
        annotations:
      	 kubernetes.io/ingress.class: "nginx"
      spec:
        backend:
          serviceName: http-svc
          servicePort: 80
      EOF
      
      

      创建 ingress

      $ cd /opt/k8s/yml/ingress-nginx
      $ kubectl create -f single-ingress.yml 
      
      

      查看ingress信息

      $ kubectl get ingress -o wide
      NAME             HOSTS   ADDRESS         PORTS   AGE
      single-ingress   *       10.254.10.208   80      11m 
      $ kubectl describe ingresses single-ingress
      Name:             single-ingress
      Namespace:        default
      Address:          10.254.10.208
      Default backend:  http-svc:80 (172.30.22.3:8080)
      Rules:
        Host  Path  Backends
        ----  ----  --------
        *     *     http-svc:80 (172.30.22.3:8080)
      Annotations:
        kubernetes.io/ingress.class:  nginx
      Events:
        Type    Reason  Age   From                      Message
        ----    ------  ----  ----                      -------
        Normal  CREATE  11m   nginx-ingress-controller  Ingress default/single-ingress
        Normal  UPDATE  11m   nginx-ingress-controller  Ingress default/single-ingress 
      
    3. 通过Ingress controller暴露出来的http端口或者https端口访问http-svc服务,查看Ingress controller服务暴露的地址(按照前面的部署流程,我们是通过一个NodePort类型的service来暴露的)

      $  kubectl get svc -n ingress-nginx
      NAME            TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE
      ingress-nginx   NodePort   10.254.10.208   <none>        80:18797/TCP,443:29468/TCP   12h
      
      

      通过18797端口访问服务

      # http端口访问
      $ curl http://192.168.0.107:18797
      # https端口访问
      $curl --insecure https://192.168.0.107:29468
      
      CLIENT VALUES:
      client_address=172.30.22.7
      command=GET
      real path=/
      query=nil
      request_version=1.1
      request_uri=http://192.168.0.107:8080/
      
      SERVER VALUES:
      server_version=nginx: 1.10.0 - lua: 10001
      
      HEADERS RECEIVED:
      accept=*/*
      host=192.168.0.107:18797
      user-agent=curl/7.58.0
      x-forwarded-for=172.30.22.1
      x-forwarded-host=192.168.0.107:18797
      x-forwarded-port=80
      x-forwarded-proto=http
      x-real-ip=172.30.22.1
      x-request-id=9c9b6f86b5a0d0a664c0f9f01a0bde47
      x-scheme=http
      BODY:
      -no body in request-
      
      
      • 其中192.168.0.107是集群中一个节点的IP地址
      • 返回信息中client_address是nginx-ingress-controller对应的pod的地址,说明请求先进入到nginx-ingress-controller中,之后路由到具体的服务中。对于如何正确拿到client IP地址,可以参照Source IP address,也可参照我的另一篇文章[kubernetes 网络](kubernetes 网络.md)
      • 头信息x-forwarded-host中存放了我们真实访问的服务器的地址

    这样我们就完成了通过nginx-ingress-controller对后端服务的访问(如果用spring cloud做过微服务开发,会对这个操作很熟悉,因为和zuul、getway等网关功能很类似)。

    追加服务

    假如我们又在集群中新启动了一个服务,为了能从外部访问这个服务,我们也要把这个新服务通过nginx-ingress-controller对外暴露,我们可以新追加一个ingress source对象,定义我们这个新服务相关的信息.

    1. 启动新服务,我们这个新服务是自己编写的一个spring boot 工程,里面只有一个restful接口

      @RequestMapping("/header/list")
      public String listHeader(HttpServletRequest request) {
      	
      	log.info("host is" + request.getHeader("host"));
      	
      	log.info("remoteAddr is " + request.getRemoteHost());
      	
      	log.info("remotePort is " + request.getRemotePort());
      	
      	return "OK";
      }
      
      
      • 接收外部请求后打印出client相关信息
    2. 启动这个新服务

      $ cd /opt/k8s/yml/ingress-nginx
      $ cat > clientip.yml <<EOF
      apiVersion: v1
      kind: Service
      metadata:
        name: clientip
      spec:
        selector:
          app: clientip
        ports:
        - name: http
          port: 8080
          targetPort: 8080
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: clientip-deployment
      spec:
        selector:
          matchLabels:
            app: clientip
        replicas: 1
        template:
          metadata:
            labels:
              app: clientip
          spec:
            nodeSelector:
              sample: slave
            containers:
            - name: clientip
              image: 192.168.0.107/k8s/client-ip-test:0.0.2
              ports:
              - containerPort: 8080
      
      EOF
      
      

      启动服务

      $ cd /opt/k8s/yml/ingress-nginx
      $ kubectl create -f clientip.yml
      
      
    3. 新服务对应的ingress source文件

      $ cd /opt/k8s/yml/ingress-nginx
      $ cat > clientip-ingress.yml<< EOF
      apiVersion: networking.k8s.io/v1beta1
      kind: Ingress
      metadata:
        name: clientip-ingress
        annotations:
           kubernetes.io/ingress.class: "nginx"
           nginx.ingress.kubernetes.io/rewrite-target: /$2
      spec:
        rules:
        - http:
            paths:
            - path: /clientip
              backend:
                serviceName: clientip
                servicePort: 8080
              path: /clientip(/|$)(.*)
      EOF
      
      
      • 表示请求uri以clientip开头的服务会路由到这个新启动的服务中
      • 追加了rewrite-target的注解,因为虽然请求以/clientip开头,但是我么真实的请求地址中并没有clientip,所以需要用rewrite-target功能把请求地址重写,详情参考rewrite

      创建新的ingress source

      $ cd /opt/k8s/yml/ingress-nginx
      $ kubectl create -f clientip-ingress.yml
      ingress.networking.k8s.io/clientip-ingress created
      
      
    4. 启动后通过nginx-ingress-controller访问

      # 访问新服务,注意我们在请求路径中的clientip
      $ curl http://192.168.0.107:18797/clientip/header/list
      OK
      
      # 访问原来部署好的服务
      $ curl http://192.168.0.107:18797
      CLIENT VALUES:
      client_address=172.30.22.7
      command=GET
      real path=/
      query=nil
      request_version=1.1
      request_uri=http://192.168.0.107:8080/
      
      SERVER VALUES:
      server_version=nginx: 1.10.0 - lua: 10001
      
      HEADERS RECEIVED:
      accept=*/*
      host=192.168.0.107:18797
      user-agent=curl/7.58.0
      x-forwarded-for=172.30.22.1
      x-forwarded-host=192.168.0.107:18797
      x-forwarded-port=80
      x-forwarded-proto=http
      x-real-ip=172.30.22.1
      x-request-id=883570a193258f151a6d8bd5f96761af
      x-scheme=http
      BODY:
      -no body in request-
      
      
      • 可以看到,新追加的服务也可以通过nginx-ingress-controller进行访问,并且原来部署好的服务不受影响仍旧可以访问
  • 相关阅读:
    span设置宽和高当没有内容的时候也可撑开
    span设置宽和高当没有内容的时候也可以撑开
    块级元素以及内联元素大总结
    内存泄露问题
    Sqlcompletefree
    运用SET ANSI_PADDING OFF创建某个字段为自增列的表,以及插入数据
    Sql Server中通配符
    sql server 2008查询窗口怎么显示行数
    sql server 2008语句中的go有什么用?
    SQL Server 2008 R2[ALTER]列属性修改
  • 原文地址:https://www.cnblogs.com/gaofeng-henu/p/12463697.html
Copyright © 2020-2023  润新知