kubernetes 核心技术-ingress

ingress是什么

介绍service时有说了service暴露服务的三种方式ClusterIP、NodePort与LoadBalance,这几种方式都是在service的维度提供的,service的作用体现在两个方面,对集群内部,它不断跟踪pod的变化,更新endpoint中对应pod的对象,提供了ip不断变化的pod的服务发现机制,对集群外部,他类似负载均衡器,可以在集群内外部对pod进行访问。但是,单独用service暴露服务的方式,在实际生产环境中不太合适:

  • ClusterIP的方式只能在集群内部访问。
  • NodePort方式的话,端口范围只能是 30000-32767(可调),且每个端口只能是一个服务,在测试环境使用还行,当有几十上百的服务在集群中运行时,NodePort的端口管理是灾难。
  • LoadBalance方式受限于云平台,而且它没有过滤条件,没有路由。

所以,在生产环境中都不建议直接使用service的方式暴露服务。

所幸k8s还提供了一种集群维度暴露服务的方式,也就是ingress。ingress可以简单理解为service的service

Ingress 处于多个服务的前端,扮演着 “智能路由” 或者集群入口的角色;Ingress 可以给 Service 提供集群外部访问的 URL、负载均衡、SSL 终止、HTTP 路由等

一个请求进来,Ingress 会根据已配置的请求转发的规则,把请求路由到某个或多个service中。

举个例子,现在集群有api、文件存储、前端3个service,可以通过一个ingress对象来实现图中的请求转发:

 

 ingress规则是很灵活的,可以根据不同域名、不同path转发请求到不同的service,并且支持https/http。

ingress与ingress-controller

ingress有两个概念,ingress和ingress-controller:

  • ingress对象:

    指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以理解为配置模板。

  • ingress-controller:

    具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发。

简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请求,比如哪些域名哪些path要转发到哪些服务等等。

最粗暴的类比,ingress-controller即nginx,ingress对象即nginx.conf

ingress-controller

ingress-controller并不是k8s自带的组件,实际上ingress-controller只是一个统称,用户可以选择不同的ingress-controller实现,目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx两个,其他还有很多第三方维护的ingress-controller,具体可以参考官方文档。但是不管哪一种ingress-controller,实现的机制都大同小异,只是在具体配置上有差异。一般来说,ingress-controller的形态都是一个pod,里面跑着daemon程序和反向代理程序。daemon负责不断监控集群的变化,根据ingress对象生成配置并应用新配置到反向代理,比如nginx-ingress就是动态生成nginx配置,动态更新upstream,并在需要的时候reload程序应用新配置。为了方便,后面的例子都以k8s官方维护的nginx-ingress为例。

ingress

ingress是一个API对象,和其他对象一样,通过yaml文件来配置。ingress通过http或https暴露集群内部service,给service提供外部URL、负载均衡、SSL/TLS能力以及基于host的方向代理。ingress要依靠ingress-controller来具体实现以上功能。上述例子的图,如果用ingress来表示,大概就是如下配置:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: abc-ingress
  annotations: 
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  tls:
  - hosts:
    - api.abc.com
    secretName: abc-tls
  rules:
  - host: api.abc.com
    http:
      paths:
      - backend:
          serviceName: apiserver
          servicePort: 80
  - host: www.abc.com
    http:
      paths:
      - path: /image/*
        backend:
          serviceName: fileserver
          servicePort: 80
  - host: www.abc.com
    http:
      paths:
      - backend:
          serviceName: feserver
          servicePort: 8080

与其他k8s对象一样,ingress配置也包含了apiVersion、kind、metadata、spec等关键字段。有几个关注的点,在spec字段中,tls用于定义https密钥、证书。rule用于指定请求路由规则。这里值得关注的是metadata.annotations字段。前面有说ingress-controller有很多不同的实现,而不同的ingress-controller就可以根据"kubernetes.io/ingress.class:"来判断要使用哪些ingress配置,同时,不同的ingress-controller也有对应的annotations配置,用于自定义一些参数。列如上面配置的'nginx.ingress.kubernetes.io/use-regex: "true"',最终是在生成nginx配置中,会采用location ~来表示正则匹配。

使用 Nginx Ingress Controller

本次实践的主要目的就是将入口统一,不再通过 service的 NodePort 或者 LoadBalancer 等方式将端口暴露出来,而是使用 Ingress 提供的反向代理负载均衡功能作为我们的唯一入口。

1.部署 Tomcat(需要被代理的服务)

部署 Tomcat 但仅允许在内网访问,我们要通过 Ingress 提供的反向代理功能路由到该 Tomcat 之上

apiVersion: apps/v1
kind: Deployment
metadata:
      name: tomcat-app
spec:
      replicas: 2
      selector:
        matchLabels:
          name: tomcat
      template:
        metadata:
          labels:
            name: tomcat
        spec:
          containers:
          - name: tomcat
            image: tomcat
            ports:
            - containerPort: 8080

---

apiVersion: v1
kind: Service
metadata:
      name: tomcat-http
spec:
      ports:
        - port: 8080
          targetPort: 8080
      # ClusterIP, NodePort, LoadBalancer
      type: ClusterIP
      selector:
        name: tomcat

Deployment创建了tomcat的pod,副本为2个,端口为8080;再创建一个type为clusterIP的service

[root@master ingress]# kubectl get pods -o wide
NAME                          READY   STATUS    RESTARTS   AGE   IP           NODE    NOMINATED NODE   READINESS GATES
tomcat-app-585d667458-62mgb   1/1     Running   0          27s   10.244.2.3   node2   <none>           <none>
tomcat-app-585d667458-w5pk5   1/1     Running   0          27s   10.244.1.6   node1   <none>           <none>
[root@master ingress]# kubectl get svc
NAME          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
kubernetes    ClusterIP   10.96.0.1       <none>        443/TCP    13d
tomcat-http   ClusterIP   10.101.91.247   <none>        8080/TCP   36s

2.安装 Nginx Ingress Controller

官方文档中,部署只要简单的执行一个yaml

 wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml

mandatory.yaml这一个yaml中包含了很多资源的创建,包括namespace、ConfigMap、role,ServiceAccount等等所有部署ingress-controller需要的资源。

主要是deployment部分:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      hostNetwork: true
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      nodeSelector:
        kubernetes.io/os: linux
      containers:
        - name: nginx-ingress-controller
          image: lizhenliang/nginx-ingress-controller:0.30.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 101
            runAsUser: 101
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
              protocol: TCP
            - name: https
              containerPort: 443
              protocol: TCP
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP

。。。。。。

这里要注意增加 hostNetwork: true,意思是开启主机网络模式,暴露 Nginx 服务端口 80与443,并在10254端口做了健康检查。

apply执行这个yaml,会创建多个资源,包括ns、configmap、role、rolebinding等等,当然最重要是一个以pod为形态的nginx:

[root@master ingress]# kubectl apply -f ingress-controller.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

检查下:

[root@master ingress]# kubectl get pod -n ingress-nginx -o wide
NAME                                       READY   STATUS    RESTARTS   AGE    IP              NODE    NOMINATED NODE   READINESS GATES
nginx-ingress-controller-766fb9f77-k8qvk   1/1     Running   0          8m7s   172.31.93.202   node2   <none>           <none>

可以看到,nginx-controller的pod已经部署在在node2上了,172.31.93.202就是外部请求进来时实际访问的ip,到node2上看下本地端口:

[root@node2 ~]# netstat -pntl |grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      8944/nginx: master  
tcp        0      0 0.0.0.0:8181            0.0.0.0:*               LISTEN      8944/nginx: master  
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      8944/nginx: master  
tcp        0      0 127.0.0.1:10245         0.0.0.0:*               LISTEN      8920/nginx-ingress- 
tcp        0      0 127.0.0.1:10246         0.0.0.0:*               LISTEN      8944/nginx: master  
tcp        0      0 127.0.0.1:10247         0.0.0.0:*               LISTEN      8944/nginx: master  
tcp6       0      0 :::10254                :::*                    LISTEN      8920/nginx-ingress- 
tcp6       0      0 :::80                   :::*                    LISTEN      8944/nginx: master  
tcp6       0      0 :::8181                 :::*                    LISTEN      8944/nginx: master  
tcp6       0      0 :::443                  :::*                    LISTEN      8944/nginx: master  

由于配置了hostnetwork,nginx已经在node2主机本地监听80/443/8181端口。其中8181是nginx-controller默认配置的一个default backend。这样,只要访问node2主机有公网IP,就可以直接映射域名来对外网暴露服务了。

3.配置ingress资源

部署完ingress-controller,接下来就按照测试的需求来创建ingress资源:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: nginx-web
  annotations:
    # 指定 Ingress Controller 的类型
    kubernetes.io/ingress.class: "nginx"
    # 指定我们的 rules 的 path 可以使用正则表达式
    nginx.ingress.kubernetes.io/use-regex: "true"
    # 连接超时时间,默认为 5s
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "600"
    # 后端服务器回转数据超时时间,默认为 60s
    nginx.ingress.kubernetes.io/proxy-send-timeout: "600"
    # 后端服务器响应超时时间,默认为 60s
    nginx.ingress.kubernetes.io/proxy-read-timeout: "600"
    # 客户端上传文件,最大大小,默认为 20m
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    # URL 重写
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  # 路由规则
  rules:
  # 主机名,只能是域名,修改为你自己的
  - host: exa.xulan.com
    http:
      paths: /
      - path:
        backend:
          # 后台部署的 Service Name,与上面部署的 Tomcat 对应
          serviceName: tomcat-http
          # 后台部署的 Service Port,与上面部署的 Tomcat 对应
          servicePort: 8080

查看ingress:

[root@master ingress]# kubectl get ing
NAME        CLASS    HOSTS           ADDRESS   PORTS   AGE
nginx-web   <none>   exa.xulan.com             80      17h

4.测试访问

在本地hosts文件中添加域名映射:

172.31.93.202 exa.xulan.com

然后在浏览器访问 http://exa.xulan.com/

可见实际访问到的是tomcat,这个例子中请求顺序为:ingress(exa.xulan.com:80)>>  svc(ClusterIP:8080)>> pod(两个tomcat,8080)

这只是一个简单的用ingress代理svc的例子,在测试过程中使用kubectl edit svc修改svc的type,发现三种类型(ClusterIP、NodePort、LoadBalancer)都可以被ingress代理。

在实际应用中,一般使用ingress来实现通过不同域名的不同path来访问不同的后端服务,类似于这样:

 

参考:https://segmentfault.com/a/1190000019908991

https://www.cnblogs.com/zjq-blogs/p/14067613.html

posted @ 2021-07-14 16:17  酒红色  阅读(395)  评论(0编辑  收藏  举报