1
votes

My goal is the following: I have two Flask apps in two separate Docker containers and I want to access them via different paths on the same IP address, like this: 127.0.0.1/app1, 127.0.0.1/app2 (but with a real IP address).

I want to do this with Kubernetes.

I have a Kubernetes cluster running (Azure Kubernetes Service), with a Deployment and Service for each of the two Docker containers. The pod for each app is running fine. I also installed an ingress controller (Nginx) in my cluster and now I am trying to make it work with a single Ingress resource.

If I do it as follows, it works perfectly for 1 single app (either one of them works on IP-address/):

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-name
  namespace: my-namespace
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /
        backend:
          serviceName: service1 (or service2)
          servicePort: 5000

But when I try the following it doesn't work:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-name
  namespace: my-namespace
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /app1
        backend:
          serviceName: service1 
          servicePort: 5000
      - path: /app2
        backend:
          serviceName: service2 
          servicePort: 5000

I am able to see the html page that gets rendered by the Flask app, for both applications on their respective paths, but none of the functionalities work.

Other than the fact that the paths don't always seem to work (I sometimes get redirected to IP-address/ when I try to connect to IP-address/app1 or IP-address/app1/), the problem is the following (I think):

Each of the Flask applications have a "/predict" route, that only accepts POST requests, where the respective calls for the apps are made (Each application is an AI application that makes a prediction for a given input).

The calls for both apps are made to IP-address/predict, instead of IP-address/app1/predict or IP-address/app2/predict. Also the static files cannot be accessed because of this path problem.

I don't know if this is the right way to do it? I tried playing around with the 'rewrite-target' as well, but haven't figured out a solution.

I hope someone can explain me what I'm doing wrong.

2

2 Answers

0
votes

Check your rewrite annotation in the ingress resource. Any request that you make will be rewritten to /. This means that if you call IP:80/app1 your container will receive IP:80/

nginx.ingress.kubernetes.io/rewrite-target: /

If you don't want that to happen, then remove that rewrite annotation. Your ingress would end up like this:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-name
  namespace: my-namespace
spec:
  rules:
  - http:
      paths:
      - path: /app1
        backend:
          serviceName: service1 
          servicePort: 5000
      - path: /app2
        backend:
          serviceName: service2 
          servicePort: 5000

Your containers will receive the full path based on /app1 and /app2.

UPDATE Check this example. I have two backends that listen on / and respond an html with a number that you configure via an environment variable.

---
apiVersion: v1
kind: Service
metadata:
  name: backend1
  namespace: default
  labels:
    mojix.service: backend1
spec:
  ports:
  - name: "8000"
    port: 8000
    targetPort: 8000
  selector:
    mojix.service: backend1
status:
  loadBalancer: {}

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  labels:
    mojix.service: backend1
  name: backend1
spec:
  replicas: 1
  selector:
    matchLabels:
      mojix.service: backend1
  template:
    metadata:
      creationTimestamp: null
      labels:
        mojix.service: backend1
    spec:
      containers:
      - name: backend1
        image: lozuwa/number_backend:latest
        env:
          - name: BACKEND_NUMBER
            value: "1"
      hostname: backend1
      restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
  name: backend2
  namespace: default
  labels:
    mojix.service: backend2
spec:
  ports:
  - name: "8000"
    port: 8000
    targetPort: 8000
  selector:
    mojix.service: backend2
status:
  loadBalancer: {}

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: default
  labels:
    mojix.service: backend2
  name: backend2
spec:
  replicas: 1
  selector:
    matchLabels:
      mojix.service: backend2
  template:
    metadata:
      creationTimestamp: null
      labels:
        mojix.service: backend2
    spec:
      containers:
      - name: backend2
        image: lozuwa/number_backend:latest
        env:
          - name: BACKEND_NUMBER
            value: "2"
      hostname: backend2
      restartPolicy: Always

I have the following ingress.

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: vizix-ingress
  namespace: default
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/ssl-redirect: "false"
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /app1
        backend:
          serviceName: backend1
          servicePort: 8000
      - path: /app2
        backend:
          serviceName: backend2
          servicePort: 8000

If you hit IP/app1 you get Response from 1 and if you hit IP/app2 you get Response from 2. My containers listen on / that is why I need the rewrite. What path does your application expect?

0
votes

You may consider to supply capture groups entirely within Ingress manifest and specify particular regular expression that will define the source Nginx rewrite rule.

I would expect to get it working as desired, after some adjustments in the genuine Ingress manifest:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-name
  namespace: my-namespace
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
  rules:
  - http:
      paths:
      - path: /app1(/|$)(.*)
        backend:
          serviceName: service1 
          servicePort: 5000
      - path: /app2(/|$)(.*)
        backend:
          serviceName: service2 
          servicePort: 5000

Probably, you would also need to add Trailing slash in the target URL, instructing Nginx engine to properly serve some static content.