Ingress

发布时间 2023-07-06 11:10:48作者: XIN-0808

Ingress概念

Ingress为Kubernetes集群中的服务提供了入口,可以提供负载均衡、SSL终止和基于名称的虚拟主机,在生产环境中常用的Ingress有Treafik、Nginx、HAProxy、Istio等,几种常用的ingress功能对比和选型可以参考这里

Ingress原理

Ingress 工作原理

  • ingress-controller通过和 kubernetes APIServer 交互,动态的去感知集群中ingress规则变化,
  • 然后读取它,按照自定义的规则,规则就是写明了哪个域名对应哪个service,生成一段nginx配置,
  • 再写到nginx-ingress-controller的pod里,这个ingress-controller的pod里运行着一个Nginx服务,控制器会把生成的 nginx配置写入 /etc/nginx.conf文件中,
  • 然后reload一下使配置生效。以此达到域名区分配置和动态更新的作用。

在使用普通的Service时,集群中每个节点的kube-proxy在监听到Service和Endpoints的变化时,会动态的修改相关的iptables的转发规则。 客户端在访问时通过iptables设置的规则进行路由转发达到访问服务的目的。

而Ingress则跳过了kube-proxy这一层,通过Ingress Controller中的代理配置进行路由转发达到访问目标服务的目的。
image

部署使用Ingress

官网下载地址:https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.25.0/deploy/static/mandatory.yaml
国内gitee下载地址:https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml
修改配置

[root@master ingress]# grep -n5 nodeSelector mandatory.yaml
213-    spec:
214-      hostNetwork: true #添加为host模式
215-      # wait up to five minutes for the drain of connections
216-      terminationGracePeriodSeconds: 300
217-      serviceAccountName: nginx-ingress-serviceaccount
218:      nodeSelector:
219-        ingress: "true" #替换此处,来决定将ingress部署在哪些机器
220-      containers:
221-        - name: nginx-ingress-controller
222-          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
223-          args:

创建ingress

#给node1节点添加label
[root@master ingress]# kubectl label nodes node1 ingress=true
node/node1 labeled
[root@master ingress]# kubectl get nodes --show-labels 
NAME     STATUS   ROLES                  AGE   VERSION   LABELS
master   Ready    control-plane,master   41d   v1.20.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=master,kubernetes.io/os=linux,node-role.kubernetes.io/control-plane=,node-role.kubernetes.io/master=
node1    Ready    <none>                 41d   v1.20.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,ingress=true,kubernetes.io/arch=amd64,kubernetes.io/hostname=node1,kubernetes.io/os=linux
node2    Ready    <none>                 41d   v1.20.0   beta.kubernetes.io/arch=amd64,beta.kubernetes.io/os=linux,kubernetes.io/arch=amd64,kubernetes.io/hostname=node2,kubernetes.io/os=linux
[root@master ingress]# 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
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRole is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRole
clusterrole.rbac.authorization.k8s.io/nginx-ingress-clusterrole created
Warning: rbac.authorization.k8s.io/v1beta1 Role is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 Role
role.rbac.authorization.k8s.io/nginx-ingress-role created
Warning: rbac.authorization.k8s.io/v1beta1 RoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 RoleBinding
rolebinding.rbac.authorization.k8s.io/nginx-ingress-role-nisa-binding created
Warning: rbac.authorization.k8s.io/v1beta1 ClusterRoleBinding is deprecated in v1.17+, unavailable in v1.22+; use rbac.authorization.k8s.io/v1 ClusterRoleBinding
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 -n ingress-nginx get pod -o wide
NAME                                        READY   STATUS    RESTARTS   AGE     IP          NODE    NOMINATED NODE   READINESS GATES
nginx-ingress-controller-66bff489bb-zvqr8   1/1     Running   0          3m23s   10.0.0.81   node1   <none>           <none>
[root@master ingress]# kubectl -n ingress-nginx get deploy
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
nginx-ingress-controller   1/1     1            1           4m7s
[root@master ingress]# kubectl -n ingress-nginx get cm
NAME                              DATA   AGE
ingress-controller-leader-nginx   0      3m11s
kube-root-ca.crt                  1      4m15s
nginx-configuration               0      4m15s
tcp-services                      0      4m15s
udp-services                      0      4m15s

在node1查看

[root@node1 ~]# netstat -lntp|grep nginx
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      22644/nginx: master 
tcp        0      0 0.0.0.0:8181            0.0.0.0:*               LISTEN      22644/nginx: master 
tcp        0      0 0.0.0.0:443             0.0.0.0:*               LISTEN      22644/nginx: master 
tcp        0      0 127.0.0.1:10245         0.0.0.0:*               LISTEN      22604/nginx-ingress 
tcp        0      0 127.0.0.1:10246         0.0.0.0:*               LISTEN      22644/nginx: master 
tcp        0      0 127.0.0.1:10247         0.0.0.0:*               LISTEN      22644/nginx: master 
tcp6       0      0 :::10254                :::*                    LISTEN      22604/nginx-ingress 
tcp6       0      0 :::80                   :::*                    LISTEN      22644/nginx: master 
tcp6       0      0 :::8181                 :::*                    LISTEN      22644/nginx: master 
tcp6       0      0 :::443                  :::*                    LISTEN      22644/nginx: master 

由于配置了 hostnetwork,nginx 已经在 node 主机本地监听 80/443/8181 端口。其中 8181 是 nginx-controller 默认配置的一个 default backend(Ingress 资源没有匹配的 rule 对象时,流量就会被导向这个 default backend)。
这样,只要访问 node 主机有公网 IP,就可以直接映射域名来对外网暴露服务了。如果要 nginx 高可用的话,可以在多个 node上部署,并在前面再搭建一套 LVS+keepalived 做负载均衡

演示

创建deployment和server

[root@master ingress]# cat service-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx
          imagePullPolicy: IfNotPresent
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-app-svc
spec:
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    targetPort:
  selector:
    app: nginx
[root@master ingress]# kubectl create -f service-nginx.yaml
deployment.apps/nginx-app created
service/nginx-app-svc created
[root@master ingress]# kubectl get deploy nginx-app  -o wide
NAME        READY   UP-TO-DATE   AVAILABLE   AGE   CONTAINERS   IMAGES   SELECTOR
nginx-app   2/2     2            2           69s   nginx        nginx    app=nginx
[root@master ingress]# kubectl get svc nginx-app-svc -o wide
NAME            TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE   SELECTOR
nginx-app-svc   ClusterIP   10.97.44.108   <none>        80/TCP    79s   app=nginx
[root@master ingress]# kubectl describe svc nginx-app-svc
Name:              nginx-app-svc
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          app=nginx
Type:              ClusterIP
IP Families:       <none>
IP:                10.97.44.108
IPs:               10.97.44.108
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.81:80,10.244.1.82:80
Session Affinity:  None
Events:            <none>

创建ingress的方法

方法一:(extensions/v1beta1 Ingress 在1.22版本即将弃用)

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: nginx-app-ingress
spec:
  rules:
  - host: www.test.com
    http:
      paths:
      - path: /
        backend:
          serviceName: nginx-app-svc
          servicePort: 80

方法二:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-app-ingress
spec:
  rules:
  - host: www.test.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-app-svc
            port:
              number: 80

创建ingress

[root@master ingress]# cat ingress-app.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: nginx-app-ingress
spec:
  rules:
  - host: www.test.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: nginx-app-svc
            port:
              number: 80
[root@master ingress]# kubectl create -f ingress-app.yaml
ingress.networking.k8s.io/nginx-app-ingress created
[root@master ingress]# kubectl get ingress nginx-app-ingress 
NAME                CLASS    HOSTS          ADDRESS   PORTS   AGE
nginx-app-ingress   <none>   www.test.com             80      73s
[root@master ingress]# kubectl describe ingress nginx-app-ingress 
Name:             nginx-app-ingress
Namespace:        default
Address:          
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
Rules:
  Host          Path  Backends
  ----          ----  --------
  www.test.com  
                /   nginx-app-svc:80 (10.244.1.81:80,10.244.1.82:80)
Annotations:    <none>
Events:
  Type    Reason  Age    From                      Message
  ----    ------  ----   ----                      -------
  Normal  CREATE  2m38s  nginx-ingress-controller  Ingress default/nginx-app-ingress

ingress支持的path类型:

  • ImplementationSpecific:对于这种path类型,匹配取决于IngressClass。可以将其视为一个单独的pathType或者将其认为和Prefix或者Exact路径类型一样。
  • Exact:精确匹配URL路径,并且区分大小写
  • Prefix: 根据URL中的,被/分割的前缀进行匹配。匹配区分大小写并且按照元素对路径进行匹配。path元素指的是路径中由/分隔符分隔的标签列表。

注意:如果路径的最后一个元素是请求路径中最后一个元素的子字符串,那么这个是不匹配的。【举例:/foo/bar匹配/foo/bar/baz,但是不匹配/foo/barbaz】参考

测试

添加hosts
image
测试访问
image

查看ingress-nginx动态生成upstream配置

[root@master ingress]# kubectl -n ingress-nginx get pod
NAME                                        READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-66bff489bb-zvqr8   1/1     Running   0          37m
[root@master ingress]# kubectl -n ingress-nginx exec -it  nginx-ingress-controller-66bff489bb-zvqr8 -- /bin/bash
bash-5.0$ vi /etc/nginx/nginx.conf
...
## start server www.test.com                                         
        server {                                                             
                server_name www.test.com ;                                   
                                                                             
                listen 80  ;                                                 
                listen [::]:80  ;                                            
                listen 443  ssl http2 ;                                      
                listen [::]:443  ssl http2 ;                                 
                                                                             
                set $proxy_upstream_name "-";                                
                                                                             
                ssl_certificate_by_lua_block {                               
                        certificate.call()                                   
                }                                                            
                                                                             
                location / {                                                 
                                                                             
                        set $namespace      "default";   
                        set $ingress_name   "nginx-app-ingress";
                        set $service_name   "nginx-app-svc";    
                        set $service_port   "80";               
                        set $location_path  "/";          
...

image