5

理解Kubernetes中的Nginx Ingress

 3 years ago
source link: https://www.51cto.com/article/703951.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
理解Kubernetes中的Nginx Ingress-51CTO.COM
理解Kubernetes中的Nginx Ingress
作者:徐福工程师 2022-03-15 08:36:34
文章对Nginx Ingress做了介绍,Kubernetes中可以选择的Ingress有很多,读者可以根据需要选择。

8715453838c884ce9518171476aed52335defd.png

Ingress有什么作用?管理集群外部对集群内服务的访问,典型如HTTP请求。它可以提供负载均衡、SSL终结和基于域名的虚拟主机访问。我们发现这些功能都比较容易实现,将集群内的服务暴露到集群外部,可以使用“NodePort”类型的Service,负载均衡可以使用HAProxy来实现,SSL终结功能部署七层反向代理就可以,基于域名的虚拟主机访问也同样比较容易实现,那为什么Kubernetes要引入Ingress API对象呢?

Ingress的潜力

Ingress的功能如开篇所述,可以使用其他技术实现,但是实际在操作过程中发现并没那么简单。在没有Ingress参与的情况下,将集群内服务暴露到集群外使用“NodePort”类型的Service,那需要给每个微服务都创建此类Service,当服务较多时,排障将非常复杂,协调主机端口使用也会让人抓狂。在集群外部署Nginx或Apache,SSL终结和基于域名的虚拟主机访问可以实现,但是服务发现和配置管理又是个挑战,集群外的Nginx和Apache感知不到集群中服务的增加和减少,需要人为配置,这对集群管理员来说,简直是个噩梦。幸好,Ingress来了。

安装服务到Kubernetes一般都比较容易,使用“kubectl apply”后面跟上yaml文件即可。当然也可以使用Kubernetes的包管理工具-Helm。“nginx ingress”根据环境,可选有三种安装方法:

  • 使用helm。
  • kubectl apply + yamlfiles。
  • 在minikube或MicroK8s中,插件方式安装。

笔者使用这篇文章介绍的方法安装Kubernetes集群,这里选用第二种方式安装“nginx ingress ”,执行下面命令(因版本更新较快,实际部署请参考官网):

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.1/deploy/static/provider/baremetal/deploy.yaml

安装会从“k8s.gcr.io”镜像仓拉取镜像,如果拉取失败可选择阿里云或其他。

查看增加的集群资源。

[root@master ~]# kubectl get all -n ingress-nginx
NAME                                            READY   STATUS      RESTARTS      AGE
pod/ingress-nginx-admission-create-7k9kt        0/1     Completed   0             14d
pod/ingress-nginx-admission-patch-5bcmq         0/1     Completed   1             14d
pod/ingress-nginx-controller-687578654b-f92bq   1/1     Running     3 (42d ago)   14d
NAME                                         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx-controller             NodePort    10.1.70.249   <none>        80:30305/TCP,443:31330/TCP   14d
service/ingress-nginx-controller-admission   ClusterIP   10.1.124.31   <none>        443/TCP                      14d
NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/ingress-nginx-controller   1/1     1            1           14d
NAME                                                  DESIRED   CURRENT   READY   AGE
replicaset.apps/ingress-nginx-controller-687578654b   1         1         1       14d
NAME                                       COMPLETIONS   DURATION   AGE
job.batch/ingress-nginx-admission-create   1/1           5s         14d
job.batch/ingress-nginx-admission-patch    1/1           7s         14d

因为集群是自建的,“ingress-nginx-controller”服务类型为“NodePort”,后面访问服务需要使用这样的方式:NodeIP+30305/31330+Path。

上步操作成功执行后,便可以创建Ingress类型的API对象了,笔者集群中提前部署一Web服务,Service信息如下:

[root@master ~]# kubectl get svc
NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.1.133.186   <none>        80/TCP    14h

创建Ingress API对象。

1> ---
2> apiVersion: networking.k8s.io/v1
3> kind: Ingress
4> metadata:
5>   name: self-nginx
6>   namespace: test
7>   annotations:
8>     nginx.ingress.kubernetes.io/rewrite-target: /
9> spec:
10>   ingressClassName: nginx
11>   rules:
12>   - host: mynginx.example.com
13>     http:
14>       paths:
15>       - path: /testpath
16>         pathType: Prefix
17>         backend:
18>           service:
19>             name: nginx
20>             port:
21>               number: 80

如果没有将前面安装的nginx ingress配置为默认的Ingress,需要加入第10行。否则即使ingress资源提交到API Server,“nginx ingress controller”也没有反应。获取“ingressClassName”的值。

[root@master ~]# kubectl get ingressclass
NAME    CONTROLLER             PARAMETERS   AGE
nginx   k8s.io/ingress-nginx   <none>       14d

查看创建的Ingress。

[root@master ~]# kubectl get ingress
NAME         CLASS   HOSTS                 ADDRESS          PORTS   AGE
self-nginx   nginx   mynginx.example.com   192.168.52.132   80      125m

通过Ingress访问Web服务(如果域名没有解析,修改/etc/hosts文件)。

[root@master nginx]# curl mynginx.example.com:30305/testpath
hello kubernetes!

Nginx Ingress的部署和使用不难,最重要是熟悉它的工作原理,这样在遇到问题时才能迅速定位。“ingress-nginx-controller” Pod里面仅运行一个容器,但是这个容器里面却有多个守护进程,重要的有两个:controller和nginx。进入Pod执行ps命令查看:

[root@master ~]# kubectl exec -it ingress-nginx-controller-687578654b-f92bq -n ingress-nginx  -- /bin/bash
bash-5.1$ ps 
PID   USER     TIME  COMMAND
    1 www-data  0:00 /usr/bin/dumb-init -- /nginx-ingress-controller --election-id=ingress-controller-leader --controller-class=k8s.io/ingress-nginx --config
    7 www-data 11:28 /nginx-ingress-controller --election-id=ingress-controller-leader --controller-class=k8s.io/ingress-nginx --configmap=ingress-nginx/ingr
   25 www-data  0:00 nginx: master process /usr/local/nginx/sbin/nginx -c /etc/nginx/nginx.conf
  247 www-data  0:01 nginx: worker process
  248 www-data  0:00 nginx: cache manager process

Controller是管理者,实现服务发现和自动配置功能,见下图(下载自官网)。

766a4f3974342435bcc416158c3122d6101ded.png

nginx ingress工作原理

这幅图看起来很复杂,其实用一句话就可以概括:“Ingress Controller”(即图中的IC)相当于系统管理员,需求者提交Ingress资源到API Server,IC从API Server获取Ingress资源,因为IC既了解Ingress资源又了解Nginx,它完成Ingress的“翻译”,随即更新Nginx的配置文件,并执行reload操作,核心逻辑就是这样。

上面我们给“nginx” Service创建了Ingress资源,访问路径配置为“/testpath”,现在进入“ingress-nginx-controller”Pod看下Nginx的配置文件:/etc/nginx/nginx.conf。

关于虚拟主机“mynginx.example.com”的配置有200多行,删掉无关的。

## start server mynginx.example.com 
server {
    server_name mynginx.example.com ;   
    listen 80  ;
    listen 443  ssl http2 ;   
    set $proxy_upstream_name "-";   
    ssl_certificate_by_lua_block {
      certificate.call()
    }   
    location ~* "^/testpath" {      
      set $namespace      "test";
      set $ingress_name   "self-nginx";
      set $service_name   "nginx";
      set $service_port   "80";
      set $location_path  "/testpath";
      set $global_rate_limit_exceeding n;     
      ......  
      set $balancer_ewma_score -1;
      set $proxy_upstream_name "test-nginx-80";
      set $proxy_host          $proxy_upstream_name;
      set $pass_access_scheme  $scheme;     
      ......    
      rewrite "(?i)/testpath" / break;
      proxy_pass http://upstream_balancer;    
      proxy_redirect                          off;    
    } 
    location ~* "^/" {      
      set $namespace      "test";
      set $ingress_name   "self-nginx";
      set $service_name   "";
      set $service_port   "";
      set $location_path  "/";
      set $global_rate_limit_exceeding n;   
      ......    
      proxy_pass http://upstream_balancer;    
      proxy_redirect                          off;      
    } 
  }
  ## end server mynginx.example.com

从配置文件中可以看到发往“/testpath”的请求完成一次跳转后最终发送给“upstream_balancer”,其在Nginx配置文件中的定义如下:

upstream upstream_balancer {
    ### Attention!!!
    #
    # We no longer create "upstream" section for every backend.
    # Backends are handled dynamically using Lua. If you would like to debug
    # and see what backends ingress-nginx has in its memory you can
    # install our kubectl plugin https://kubernetes.github.io/ingress-nginx/kubectl-plugin.
    # Once you have the plugin you can use "kubectl ingress-nginx backends" command to
    # inspect current backends.
    #
    ###   
    server 0.0.0.1; # placeholder 
    balancer_by_lua_block {
      balancer.balance()
    }   
    keepalive 320;    
    keepalive_timeout  60s;
    keepalive_requests 10000;   
  }

因Nginx配置文件严重依赖Lua,这里看到的信息不直观。为了看到后端服务,按照注释,为kubectl安装“ingress-nginx”插件。在前面Nginx配置文件有下面一行:

set $proxy_upstream_name "test-nginx-80";

指出域名“mynginx.example.com”的backend名为“test-nginx-80”。查看Ingress的backends(省略无关行)。

[root@master ~]# kubectl ingress-nginx backends -n ingress-nginx
[
  {
    "name": "test-nginx-80",
    "service": {
      "metadata": {
        "creationTimestamp": null
      },
      "spec": {
        "ports": [
          {
            "name": "http",
            "protocol": "TCP",
            "port": 80,
            "targetPort": 80
          }
        ],
        "selector": {
          "app": "nginx"
        },
        "clusterIP": "10.1.133.186",
        "clusterIPs": [
          "10.1.133.186"
        ],
        "type": "ClusterIP",
        "sessionAffinity": "None",
        "ipFamilies": [
          "IPv4"
        ],
        "ipFamilyPolicy": "SingleStack",
        "internalTrafficPolicy": "Cluster"
      },
      "status": {
        "loadBalancer": {}
      }
    },
    "port": 80,
    "sslPassthrough": false,
    "endpoints": [
      {
        "address": "10.244.1.26",
        "port": "80"
      }
    ],
    "sessionAffinityConfig": {
      "name": "",
      "mode": "",
      "cookieSessionAffinity": {
        "name": ""
      }
    },
    "upstreamHashByConfig": {
      "upstream-hash-by-subset-size": 3
    },
    "noServer": false,
    "trafficShapingPolicy": {
      "weight": 0,
      "weightTotal": 0,
      "header": "",
      "headerValue": "",
      "headerPattern": "",
      "cookie": ""
    }
  },  
......
  
]

从输出中可以看到后端其实就是名为“nginx”的Service对应的Endpoints,它的IP是“10.244.1.26”。

[root@master ~]# kb get endpoints
NAME    ENDPOINTS        AGE
nginx   10.244.1.26:80   19h

这里需要强调一点,Nginx Ingress并不将流量转发给nginx service,而是直接转发到后端的Pods,转发策略也完全由Ingress Controller来决定。这样不仅减少了一次DNAT,也能实现更丰富的负载均衡策略。Ingress资源中出现的Service对象只是为了选择后端的Endpoints。

文章对Nginx Ingress做了介绍,Kubernetes中可以选择的Ingress有很多,读者可以根据需要选择。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK