Nginx Ingress Controller原理简介及使用示例

目录

一、概念介绍

二、安装Nginx Ingress Controller

三、使用实例

四、原理解析

如何实现高可用?


一、概念介绍

介绍Nginx Ingress Controller之前,需要先理解三个概念。

  • Ingress:对集群中服务的外部访问进行管理的 API 对象,可以理解为kubernetes中一种定义从集群外部到集群内服务的HTTP/HTTPS 路由的资源类型;
  • Ingress Controller:负责通过负载均衡器(如nginx/haproxy)来实现 Ingress中定义的路由,通常以pod形式运行;
  • IngressClass:一种定义某个具体Ingress Controller的资源类型,kubernetes 1.18-1.21中支持,Ingress通过IngressClass来指定使用哪一种Ingrss Controller。

关于Ingress的详细介绍可参看官方文档:Ingress | Kubernetes

为什么需要使用Ingress暴露服务?Kubernetes还支持通过NodePort、LoadBalancer向集群外部暴露内部服务,与这两种方式相比,ingress可以通过单一IP为多个服务提供访问,可以通过主机名和路径将流量路由到特定服务,使得服务访问更加灵活,同时还可以用来提供https服务。

Nginx Ingress Controller即是Nginx公司使用nginx实现的一种Ingress Controller。(注意区别于kubernetes社区开发的ingress-nginx,两者均是Ingress Controller,功能略有差异,Nginx Ingress Controller额外定义了两种资源类型VirtualServer和VirtualServerRoute,实现了一些Ingress不支持的的功能。)

Nginx Ingress Controller:nginxinc/kubernetes-ingress: NGINX and NGINX Plus Ingress Controllers for Kubernetes (github.com)

Kubernetes Ingress Nginx:kubernetes/ingress-nginx: NGINX Ingress Controller for Kubernetes (github.com)

功能差异:Which Ingress Controller Do I Need? | NGINX Ingress Controller

二、安装Nginx Ingress Controller

1、添加helm仓库:

$ helm repo add nginx-stable https://helm.nginx.com/stable
$ helm repo update

2、拉取helm chart到本地,并编辑配置文件values.yaml:

$ helm pull nginx-stable/nginx-ingress
$ tar -zxvf nginx-ingress-0.10.0.tgz
# 根据实际生产环境及需要修改配置文件
$ vi nginx-ingress/values.yaml

3、使用本地修改好的chart安装nginx ingress:

$ kubectl create namespace nginx-ingress
$ helm install cmiot ./nginx-ingress -n nginx-ingress

至此即完成了Nginx Ingress Controller的安装。注意目前的最新版本,即helm chart 0.10.0、github v1.12.0版本仅支持k8s 1.21及以下版本,不支持最新的1.22版本,详情见:Kubernetes v1.22 · Issue #1832 · nginxinc/kubernetes-ingress (github.com)

下面介绍一些我们测试环境在安装第二步中修改的values.yaml的几个地方:

  • controller.kind: daemonset,使用daemonset方式安装controller。
  • controller.hostNetwork: true,controller的pod直接使用主机的网络接口,即直接使用主机网路接口暴露controller pod。
  • controller.nodeSelector: kubernetes.io/hostname: wx-tky-ops-136,将controller pod绑定在hostname为wx-tky-ops-136的节点上;配合hostNetwork: true,可直接通过主机wx-tky-ops-136的IP地址从集群外部访问到内部服务。
  • controller.service.type: ClusterIP,通过ClusterIP类型的service暴露controller,供集群内部使用。因为已经通过hostNetwork: true向集群外部暴露controller,所以此处不再需要使用NodePort/LoadBalancer。
  • controller.ingressClass: nginx,定义该controller对应的IngressClass资源的名称为nginx。

安装完成后,可以看到如下一些资源:

# kubectl get po -n nginx-ingress
NAME                                   READY   STATUS    RESTARTS   AGE
cmiot-nginx-ingress-8585f89c5b-jbjpz   1/1     Running   0          4h15m
# kubectl get IngressClass
NAME    CONTROLLER                     PARAMETERS   AGE
nginx   nginx.org/ingress-controller   <none>       19h

三、使用实例

配置两个deployment和两个service:

# kubectl apply -f nginx_ingress_example.yaml
# nginx_ingress_example.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-1
  labels:
    app: nginx-1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-1
  template:
    metadata:
      labels:
        app: nginx-1
    spec:
      containers:
      - name: nginx
        image: zhangrongjie/test-nginx-1:v1
        ports:
        - containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment-2
  labels:
    app: nginx-2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-2
  template:
    metadata:
      labels:
        app: nginx-2
    spec:
      containers:
      - name: nginx
        image: zhangrongjie/test-nginx-2:v1
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx1-svc
  labels:
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: nginx-1
---
apiVersion: v1
kind: Service
metadata:
  name: nginx2-svc
  labels:
spec:
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: nginx-2

访问测试:

# kubectl get svc
NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP   23h
nginx1-svc   ClusterIP   10.101.144.127   <none>        80/TCP    2m34s
nginx2-svc   ClusterIP   10.100.202.70    <none>        80/TCP    2m34s

# curl http://10.101.144.127/nginx1/
nginx1

# curl http://10.100.202.70/nginx2/
nginx2

创建ingress:

# kubectl apply -f test-ingress.yaml
# test-ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: test-ingress
spec:
# 1.18及以后版本需要通过ingressClassName指定使用的controller,或者在ingressClass中设置默认controller,否则ingress不生效
  ingressClassName: nginx
  rules:
  - host: testnginx.com
    http:
      paths:
      - path: /nginx1/
        backend:
          serviceName: nginx1-svc
          servicePort: 80
      - path: /nginx2/
        backend:
          serviceName: nginx2-svc
          servicePort: 80
# kubectl get ingress
NAME           CLASS   HOSTS           ADDRESS   PORTS   AGE
test-ingress   nginx   testnginx.com             80      52m

访问测试:

查看nginx ingress controller pod所在的节点IP:

# kubectl get po -n nginx-ingress -o wide
NAME                                   READY   STATUS    RESTARTS   AGE   IP             NODE          NOMINATED NODE   READINESS GATES
cmiot-nginx-ingress-8585f89c5b-jbjpz   1/1     Running   2          8h    192.168.0.12   *.*   <none>           <none>

因为已经通过hostNetwork暴露该pod,所以在192.168.0.12这个node上可以看到80/443/8080端口已经开放,所以直接在本地写入相应的域名解析记录即可访问(测试时用):

# echo '192.168.0.12 testnginx.com' >> /etc/hosts

此时访问不同的路径即可路由到相应的内部服务上:

# curl http://testnginx.com/nginx1/
nginx1

# curl http://testnginx.com/nginx2/
nginx2

四、原理解析

现在进入nginx ingress controller的pod:

# kubectl get po -n nginx-ingress                                   
NAME                                   READY   STATUS    RESTARTS   AGE
cmiot-nginx-ingress-8585f89c5b-jbjpz   1/1     Running   2          8h

# kubectl exec -it cmiot-nginx-ingress-8585f89c5b-jbjpz -n nginx-ingress -- /bin/bash
nginx@worker1:/$ cd /etc/nginx/conf.d/
nginx@worker1:/etc/nginx/conf.d$ ls
default-test-ingress.conf
nginx@worker1:/etc/nginx/conf.d$ cat default-test-ingress.conf 
# configuration for default/test-ingress

upstream default-test-ingress-testnginx.com-nginx1-svc-80 {
        zone default-test-ingress-testnginx.com-nginx1-svc-80 256k;
        random two least_conn;
        server 10.0.0.140:80 max_fails=1 fail_timeout=10s max_conns=0;

}
upstream default-test-ingress-testnginx.com-nginx2-svc-80 {
        zone default-test-ingress-testnginx.com-nginx2-svc-80 256k;
        random two least_conn;
        server 10.0.0.146:80 max_fails=1 fail_timeout=10s max_conns=0;
}

server {
        listen 80;
        server_tokens on;
        server_name testnginx.com;
        set $resource_type "ingress";
        set $resource_name "test-ingress";
        set $resource_namespace "default";

        location /nginx1/ {
                set $service "nginx1-svc"; 
                proxy_http_version 1.1;
                proxy_connect_timeout 60s;
                proxy_read_timeout 60s;
                proxy_send_timeout 60s;
                client_max_body_size 1m;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Port $server_port;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_buffering on;
                proxy_pass http://default-test-ingress-testnginx.com-nginx1-svc-80;
        }
        location /nginx2/ {
                set $service "nginx2-svc"; 
                proxy_http_version 1.1;
                proxy_connect_timeout 60s;
                proxy_read_timeout 60s;
                proxy_send_timeout 60s;
                client_max_body_size 1m;
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header X-Forwarded-Host $host;
                proxy_set_header X-Forwarded-Port $server_port;
                proxy_set_header X-Forwarded-Proto $scheme;
                proxy_buffering on;
                proxy_pass http://default-test-ingress-testnginx.com-nginx2-svc-80;
        }
}

可以看到, nginx ingress controller 程序已经将ingress定义的路由规则转化为相应的nginx配置文件,因此我们访问nginx ingress controller才能实现相应的路由功能。从上面的nginx配置文件也可看出, nginx ingress 控制器并非直接将请求转发给相应服务,而是通过http请求头部信息确定客户端尝试访问哪个服务,通过与该服务关联的Endpoint对象查看pod IP,并将客户端的请求转发给其中的一个pod。

关于 nginx ingress controller的详细工作原理,可以参看:How NGINX Ingress Controller Works | NGINX Ingress Controller。总结如下:

nginx ingress controller pod由单一容器组成,该容器中有三类进程:IC(Ingress Controller)、Nginx master、Nginx worker。IC进程通过Kubernetes API读取ingress等资源的最新内容,通过容器中的模板文件(在容器根目录下)创建对应的nginx配置文件,写入容器的/etc/nginx目录,然后IC运行nginx -s reload命令重载nginx配置使其生效。

如何实现高可用?

可以通过keepalived+VIP+nodeselector实现,controller pod所在的节点上运行ps -ef可以看到nginx-ingress进程(还可以看到nginx进程,但实际上这些进行并不直接运行的主机系统上),可依此判断pod是否正常运行。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

洒满阳光的午后

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值