15

Suppose I create multiple ingress objects in k8s which point to the same service, same path and are exactly the same, only they have different names eg-. ingress-1 and ingress-2. How are the requests handled in this case? Are the requests duplicated or either ingress handles the request?

PS;- I know this doesn't make much sense but I am testing something out.

Divyaanand Sinha
  • 366
  • 1
  • 3
  • 12

4 Answers4

22

Let's check how it works!

I have default nginx deployment

# kubectl -n test get pods -o wide | grep nginx
nginx-65f88748fd-6w6fj           1/1     Running   0          26h     10.8.253.25    k8s-vm02   <none>           <none>
nginx-65f88748fd-8fp7p           1/1     Running   0          26h     10.8.252.205   k8s-vm01   <none>           <none>
nginx-65f88748fd-c7j29           1/1     Running   0          26h     10.8.253.24    k8s-vm02   <none>           <none>
nginx-65f88748fd-frsbq           1/1     Running   0          26h     10.8.252.201   k8s-vm01   <none>           <none>
nginx-65f88748fd-p4zvm           1/1     Running   0          26h     10.8.252.204   k8s-vm01   <none>           <none>
nginx-65f88748fd-pd8gv           1/1     Running   0          25h     10.8.253.27    k8s-vm02   <none>           <none>
nginx-65f88748fd-rkcjl           1/1     Running   0          26h     10.8.252.206   k8s-vm01   <none>           <none>
nginx-65f88748fd-rn49k           1/1     Running   0          26h     10.8.253.26    k8s-vm02   <none>           <none>
nginx-65f88748fd-w9dz8           1/1     Running   0          26h     10.8.252.203   k8s-vm01   <none>           <none>
nginx-65f88748fd-xh42v           1/1     Running   0          25h     10.8.253.28    k8s-vm02   <none>           <none>

service

# kubectl -n test get svc
NAME    TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
nginx   ClusterIP   10.8.254.218   <none>        80/TCP    12d

and 2 similar Ingress resources in the same namespace

# kubectl -n test get ing
NAME             HOSTS            ADDRESS   PORTS   AGE
test-ingress-1   nginx.test.com             80      20m
test-ingress-2   nginx.test.com             80      20m

Their YAML's:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: test
  name: test-ingress-1
  annotations:
    kubernetes.io/ingress.class: "service"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: nginx.test.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: nginx
          servicePort: 80

and

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: test
  name: test-ingress-2
  annotations:
    kubernetes.io/ingress.class: "service"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: nginx.test.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: nginx
          servicePort: 80

one more thing about ingress.class: "service" - there are more then 1 ingress controllers in my environment and this particular ingress-controller

nginx-ingress-service-6gkhh                1/1     Running   0          4m20s   10.8.255.243   k8s-vm02   <none>           <none>

has been created specially for demonstrating this example, so pay no attention for it

Anyway, is Ingress resource nginx.test.com/foo working now?

# curl -H "Host: nginx.test.com" http://10.8.255.243:80/foo
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Yes, it is. What's under the hood? Nginx config inside ingress-controller has only one server_name nginx.test.com no matter how many similar Ingress resources we have

# kubectl -n kube-system exec nginx-ingress-service-6gkhh -- cat /etc/nginx/nginx.conf
...
## start server nginx.test.com
    server {
        server_name nginx.test.com ;

        listen 80;

        listen [::]:80;

        set $proxy_upstream_name "-";

        location ~* ^/foo\/?(?<baseuri>.*) {
...
            proxy_pass http://test-nginx-80;
...
    ## end server nginx.test.com

upstreams:

    upstream test-nginx-80 {

        keepalive 32;

        server 10.8.252.203:80 max_fails=0 fail_timeout=0;
        server 10.8.253.26:80 max_fails=0 fail_timeout=0;
        server 10.8.252.201:80 max_fails=0 fail_timeout=0;
        server 10.8.253.25:80 max_fails=0 fail_timeout=0;
        server 10.8.252.204:80 max_fails=0 fail_timeout=0;
        server 10.8.253.24:80 max_fails=0 fail_timeout=0;
        server 10.8.252.205:80 max_fails=0 fail_timeout=0;
        server 10.8.252.206:80 max_fails=0 fail_timeout=0;
        server 10.8.253.27:80 max_fails=0 fail_timeout=0;
        server 10.8.253.28:80 max_fails=0 fail_timeout=0;

    }

Let's delete the test-ingress-1 Ingress resource

# kubectl -n test delete ing test-ingress-1
ingress.extensions "test-ingress-1" deleted

Ingress is still working:

# curl -H "Host: nginx.test.com" http://10.8.255.243:80/foo
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

So, you can create as many similar Ingress resources as you want (In case of using nginx-ingress-controller)

UPD: Let's create one more deployment in the same namespace:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: apache
  namespace: test
spec:
  replicas: 3
  selector:
    matchLabels:
      app: apache
  template:
    metadata:
      labels:
        app: apache
    spec:
      containers:
      - name: frontend
        image: httpd
        ports:
        - containerPort: 80

and one more service

apiVersion: v1
kind: Service
metadata:
  name: apache
  namespace: test
spec:
  ports:
  - name: 80-80
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: apache
  type: ClusterIP

Next let's change Ingress resource test-ingress-2:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  namespace: test
  name: test-ingress-2
  annotations:
    kubernetes.io/ingress.class: "service"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: nginx.test.com
    http:
      paths:
      - path: /foo
        backend:
          serviceName: apache # <-------
          servicePort: 80

Finally namespace test includes this list of resources:

# kubectl -n test get all
NAME                                 READY   STATUS    RESTARTS   AGE
pod/apache-cfdf8d79c-2m666           1/1     Running   0          13m
pod/apache-cfdf8d79c-d995s           1/1     Running   0          13m
pod/apache-cfdf8d79c-tq8d7           1/1     Running   0          13m
pod/nginx-65f88748fd-6w6fj           1/1     Running   0          45h
pod/nginx-65f88748fd-8fp7p           1/1     Running   0          45h
pod/nginx-65f88748fd-c7j29           1/1     Running   0          45h
pod/nginx-65f88748fd-frsbq           1/1     Running   0          46h
pod/nginx-65f88748fd-p4zvm           1/1     Running   0          45h
pod/nginx-65f88748fd-pd8gv           1/1     Running   0          45h
pod/nginx-65f88748fd-rkcjl           1/1     Running   0          45h
pod/nginx-65f88748fd-rn49k           1/1     Running   0          45h
pod/nginx-65f88748fd-w9dz8           1/1     Running   0          45h
pod/nginx-65f88748fd-xh42v           1/1     Running   0          45h

NAME             TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
service/apache   ClusterIP   10.8.254.47    <none>        80/TCP    12m
service/nginx    ClusterIP   10.8.254.218   <none>        80/TCP    12d

# kubectl -n test get ing
NAME             HOSTS            ADDRESS   PORTS   AGE
test-ingress-1   nginx.test.com             80      9m15s
test-ingress-2   nginx.test.com             80      54s

the ingreess-controller's nginx.conf shows two lists of upstreams

    upstream test-apache-80 {

        keepalive 32;

        server 10.8.253.31:80 max_fails=0 fail_timeout=0;
        server 10.8.252.208:80 max_fails=0 fail_timeout=0;
        server 10.8.253.32:80 max_fails=0 fail_timeout=0;

    }

    upstream test-nginx-80 {

        keepalive 32;

        server 10.8.252.204:80 max_fails=0 fail_timeout=0;
        server 10.8.252.205:80 max_fails=0 fail_timeout=0;
        server 10.8.253.27:80 max_fails=0 fail_timeout=0;
        server 10.8.253.25:80 max_fails=0 fail_timeout=0;
        server 10.8.253.24:80 max_fails=0 fail_timeout=0;
        server 10.8.252.206:80 max_fails=0 fail_timeout=0;
        server 10.8.253.26:80 max_fails=0 fail_timeout=0;
        server 10.8.252.203:80 max_fails=0 fail_timeout=0;
        server 10.8.253.28:80 max_fails=0 fail_timeout=0;
        server 10.8.252.201:80 max_fails=0 fail_timeout=0;

    }

but it proxies requests only to one of them

    ## start server nginx.test.com
    server {
        server_name nginx.test.com ;

        listen 80;

        listen [::]:80;

        set $proxy_upstream_name "-";

        location ~* ^/foo\/?(?<baseuri>.*) {

            set $namespace      "test";
            set $ingress_name   "test-ingress-1"; <------
            set $service_name   "nginx";
            set $service_port   "80";
            set $location_path  "/foo";

...

            rewrite /foo/(.*) /$1 break;
            rewrite /foo / break;
            proxy_pass http://test-nginx-80;  <-------

            proxy_redirect                          off;
# curl -H "Host: nginx.test.com" http://10.8.255.243:80/foo
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...

Each of ingress resources appears there in accordance with the creation time. Deleting Ingress resource

ingress.networking.k8s.io "test-ingress-1" deleted

makes ingress-controller proxy traffic to Apache upstreams

# curl -H "Host: nginx.test.com" http://10.8.255.243:80/foo
<html><body><h1>It works!</h1></body></html>

to make it clear we can recreate Ingress resources in reverse order:

# kubectl -n test delete ing test-ingress-1 test-ingress-2
ingress.extensions "test-ingress-1" deleted
ingress.extensions "test-ingress-2" deleted

# kubectl -n test create -f /tmp/ing2.yaml
ingress.networking.k8s.io/test-ingress-2 created # Apache
# kubectl -n test create -f /tmp/ing1.yaml
ingress.networking.k8s.io/test-ingress-1 created # Nginx

# curl -H "Host: nginx.test.com" http://10.8.255.243:80/foo
<html><body><h1>It works!</h1></body></html> # Apache

One more note: There are some cases of reloading the ingress-controller's config, I mean when it reloads config and how it actually reloads config? But that's a totally different question...

Konstantin Vustin
  • 6,521
  • 2
  • 16
  • 32
  • Thanks for taking the time to test and update here Konstantin. I am wondering how it would behave if two Ingress rules were added with the same host/path but with a different backend service. Would be overwrite with the latest one? Or would it fail? – Amudhan Mar 04 '20 at 04:50
  • 2
    @Amudhan I've updated the answer. Hope it is clear now!) – Konstantin Vustin Mar 04 '20 at 10:43
  • Thanks Konstantin. So, the creation/deletion order determines to which backend service the traffic will be routed. I wish there is at least a warning log message in the ingress controller. – Amudhan Mar 04 '20 at 11:07
  • why is it warning if traffic routes just like you've described in the Ingress resource? – Konstantin Vustin Mar 04 '20 at 11:29
  • 1
    What I meant was, when a second Ingress resource is added which defines the same rule (host/path), but a different backend service, it is actually a conflict created by the user. The order in which the ingress is created/deleted determines which service will receive the traffic. It is a good QA testcase, but not a desirable setup, right? So, that is why I mentioned that, it would be good if the Ingress controller emits a warning log saying something like 'conflicting Ingress resources'. Do I make sense? – Amudhan Mar 04 '20 at 14:12
4

If you mean Ingress objects with the same Controller: generally nothing interesting, most of the de-dup redundant routes.

If you mean two Controllers each pointed at a different (but identical) Ingress: both Controllers set themselves up with the requested routes. They are totally independent and don't know anything about each other.

coderanger
  • 52,400
  • 4
  • 52
  • 75
  • sorry for the ambiguity , i mean multiple ingress objects with same configs, can you elaborate what you mean by dedup redundant routes? does this mean request is copied and handled by both the objects, or handled by either of the objects. – Divyaanand Sinha Mar 03 '20 at 10:20
  • Actually, in case of more then one ingress controller, all ingress controllers would fight for the object created. They wouldn't work in harmony. That's why you write in an annotation which IC to use. – suren Mar 03 '20 at 10:34
  • @suren they don’t really fight over it (except for flapping the address field in the status), they just both implement the routes. There is no external state for them to argue about other than that one status field. Usually this isn’t desired but both proxies are totally independent. – coderanger Mar 03 '20 at 19:03
  • @coderanger right. But my point is that it can lead to inconsistent behavior. – suren Mar 03 '20 at 19:18
1

Ingress controllers (whether it is nginx or haproxy or any other controller) work more or less in the same way. If an Ingress object is created or modified, they listen for that and for every the host/path in the ingress, they create a rule saying that if the request reaches the Ingress controller with that particular host/path, it should forward the traffic to the service that is configured for that path.

So, in your example, you are saying that you are adding multiple Ingress objects having the same host/path configs pointing to the same service. I would think that it is upto the Ingress controller implementation; how they want to handle duplicate Ingress rules. It doesn't make sense to add duplicate rules for the same hostname (doesn't matter whether it points to same service or different service, but in case of different backend service, it is a bigger problem/ambiguity).

But I would assume that even if an Ingress controller allows these kind of configs and programs multiple rules for the same host/path, it wont do multiplexing. Probably it would match the first rule and forward to the service behind it.

How it determines which service to route to? It is based on, on which hostname the traffic reached the Ingress controller.

You can easily check the behavior. Exec into the controller and see its conf file. For nginx ingress controller, the conf file will be in /etc/nginx/nginx.conf which will define the rules and actions.

Amudhan
  • 696
  • 8
  • 18
0

We had a situation where such a configuration was needed in production. And this is how I saw it worked. When you create the second ingress with everything same except just the ingress name, it didn’t affect anything. Only after I deleted the first ingress, the second ingress that I created took over the control. So if the set up is indentical it doesn’t override or duplicate, looks like the first one that was created always has the prio.