How to adapt to a kubernetes path change in Python Flask routes - python-3.x

I have a Python Flask app I've deployed on IBM Kubernetes Service. My deployment YAML specifies path: /, which is handled in my code with #app.route('/'). That works fine. I then attempted to move the app by changing path: / to path: /foo in my deployment YAML. I was expecting the request coming into my app to still come in as /, but it's coming in as /foo. Ultimately what I'm trying to do is to be flexible in the deployment of the app without having the change source code. I don't see a way in either Kubernetes or Flask to create this level of indirection. Am I missing something?
Original YAML:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
tls:
- hosts:
- my....us-east.containers.appdomain.cloud
secretName: my...
rules:
- host: my....us-east.containers.appdomain.cloud
http:
paths:
- path: /
backend:
serviceName: my-service
servicePort: 5000

Need to check the content of your ingress yaml definition.
Here's an example yaml definition with rewrites
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
rules:
- host: rewrite.bar.com
http:
paths:
- backend:
serviceName: http-svc
servicePort: 80
path: /something(/|$)(.*)
For example, the ingress definition above will result in the following rewrites:
rewrite.bar.com/something rewrites to rewrite.bar.com/
rewrite.bar.com/something/ rewrites to rewrite.bar.com/
rewrite.bar.com/something/new rewrites to rewrite.bar.com/new
You can check Nginx Ingress controller Rewrite annotations here. You can also customize Ingress routing with annotations on IBM Cloud following the documentation here

Related

Why k8s ingress-nginx forward the reques to somewhere else?

In k8s, I deploy an app running with clusterIP(port:5270). Then I want to set up an ingress-nginx to forward the request to this app. here is the configure:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: k8s-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /core
pathType: Prefix
backend:
service:
name: core-service
port:
number: 5270
My service is a NestJS application, and this is my request to this service :
192.168.10.131/core/api/myorder/get-order-by-id?id=1
"myorder" is one of the module I have, and I add a global prefix by using app.setGlobalPrefix('api'); in the main.ts
When enter the request path, I will get :
{"statusCode":404,"message":"ENOENT: no such file or directory, stat '/app/client/index.html'"}
It seems that the ingress can send the request to the pods, but it will look for "/app/client/index.html" which is not expected. It should access the myorder module. there is a client folder under the /app.
If I use the Nodeport to access the service instead of the ingress, everything is fine.
Here is the change I have since I got your guy's solution:
I deployed a simple nest app, a very simple one:
It is only has a getHalleo function.
Here is the main.js:
Here is the yaml for deploying at k8s:
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8-ingress-test1
labels:
app: k8-ingress-test
spec:
replicas: 1
selector:
matchLabels:
app: k8-ingress-test
template:
metadata:
labels:
app: k8-ingress-test
spec:
containers:
- name: k8-ingress-test
image: 192.168.10.145:8080/k8s-ingress-test
imagePullPolicy: Always
ports:
- containerPort: 3146
---
apiVersion: v1
kind: Service
metadata:
name: k8-ingress-test-service
spec:
selector:
app: k8-ingress-test
ports:
- protocol: TCP
port: 3146
targetPort: 3146
Here is my new ingress-nginx:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: k8s-ingress
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /hello
pathType: Prefix
backend:
service:
name: k8-ingress-test-service
port:
number: 3146
I deleted "nginx.ingress.kubernetes.io/rewrite-target: /".
Here is my access URL:
192.168.10.131/hello
Based on my understanding, it should catch "/hello" and send "/hello" to the test app. since there is no 'hello' module in the app, I get the following :
{"statusCode":404,"message":"Cannot GET /hello","error":"Not Found"}
so, this is the reason why we have :
nginx.ingress.kubernetes.io/rewrite-target: /
this will edit the request from "/hello" to "/". In another word, it would send "/" to the test app instead of "/hello". so I can hit the getHello function.
For the 192.168.10.131/core/api/myorder/get-order-by-id?id=1, I can not do nginx.ingress.kubernetes.io/rewrite-target: / because it would rewrite the request to / which is wrong.
So, is there any way that I can catch the request including core, and delete the core part then send the /api/myorder/get-order-by-id?id=1 to the port 5270(and it also can catch "hello" then delete it then send "/" to the port 3146)?
Thanks
I think you might have some mis-understanding between the nginx ingress's rewrite-target annotation and your NestJS application's redirection.
With the following annotation:
nginx.ingress.kubernetes.io/rewrite-target: /
your request path /core/api/myorder/get-order-by-id?id=1 will rewrite to /, not /api/myorder/get-order-by-id?id=1.
Check the docs for more info: ingress-nginx annotations/#rewrite and examples.
Not familiar with NestJS, I guess your application doesnot return correctly to root path /.
What you probably need is Redirection in a NestJS application. NestJS Controllers Redirection
I solved the problem:
I add this to my yaml :
nginx.ingress.kubernetes.io/rewrite-target: /$2
and here is my path
- path: /hello(/|$)(.*)
so, we can have the following rewrite
/hello writes to /
/hello/ rewrites to /
/hello/new rewrites to /new
similar to the core

React App in Kubernetes is not connecting properly

I'm new to Kubernetes and I'm trying to deploy a React app to my cluster. Here's the basic info:
Docker Desktop, single-node Kubernetes cluster
React development frontend, exposing port 3000
Node.js/Express backend, exposing port 8080
NGINX Ingress Controller, serving my React frontend on "localhost:3000" and routing my Fetch API requests (fetch("localhost:3000/api/...", OPTIONS)) to the backend (which works)
I am having an issue when opening the React app. The Ingress Controller correctly routes to the app but the 3 bundles (bundle.js, main.chink.js, the third one which I don't remember) aren't loaded. I get the following error:
GET http://localhost/static/js/main.chunk.js net::ERR_ABORTED 404 (Not Found) ingress (one example)
I understand why this error happens. The Ingress Controller correctly routes the traffic but only loads the index.html file. In this file, there are calls to 3 scripts (referring to the bundles) which aren't loaded. I understand the error, the files don't get sent to the browser so the index.html file can't load them in, but do not know how to fix it.
Does anyone have any suggestions? de and then pulled from Docker Hub. Does anybody know what a possible solution could be? For example, does deploying the build/ folder (built React app using "npm run build") fix this? Do I have to use nginx inside my Dockerfile to build the container?
Ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: titanic-ingress
#labels:
#name: titanic-ingress
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /
pathType: Exact
backend:
service:
name: titanicfrontendservice
port:
number: 3000
- path: /api
pathType: Exact
backend:
service:
name: titanicbackendservice
port:
number: 8080
Ingress controller deployment yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-ingress
namespace: nginx-ingress
spec:
replicas: 1
selector:
matchLabels:
app: nginx-ingress
template:
metadata:
labels:
app: nginx-ingress
#annotations:
#prometheus.io/scrape: "true"
#prometheus.io/port: "9113"
spec:
serviceAccountName: nginx-ingress
containers:
- image: nginx/nginx-ingress:1.10.0
imagePullPolicy: IfNotPresent
name: nginx-ingress
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
- name: readiness-port
containerPort: 8081
#- name: prometheus
#containerPort: 9113
readinessProbe:
httpGet:
path: /nginx-ready
port: readiness-port
periodSeconds: 1
securityContext:
allowPrivilegeEscalation: true
runAsUser: 101 #nginx
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
env:
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
args:
- -nginx-configmaps=$(POD_NAMESPACE)/nginx-config
- -default-server-tls-secret=$(POD_NAMESPACE)/default-server-secret
#- -v=3 # Enables extensive logging. Useful for troubleshooting.
- -report-ingress-status
- -external-service=nginx-ingress
#- -enable-prometheus-metrics
#- -global-configuration=$(POD_NAMESPACE)/nginx-configuration
Ingress controller service yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-ingress
namespace: nginx-ingress
spec:
externalTrafficPolicy: Local
type: LoadBalancer
ports:
- port: 3000
targetPort: 80
protocol: TCP
name: http
- port: 443
targetPort: 443
protocol: TCP
name: https
selector:
app: nginx-ingress
TL;DR
Switch your pathType in both /api and / path to Prefix.
I've included some explanation with fixed Ingress resource below.
For the reproduction purposes I used the titanic manifests that you provided in the another question:
Github.com: Strobosco: Titanicfullstack
The issue with your configuration is with: pathType.
Using your Ingress resource with pathType: Exact showed me blank page.
Modifying your Ingress resource with pathType: Prefix solved the issue.
Side note!
The message: "Would you have survived the sinking of the Titanic?" showed.
The exact Ingress configuration should be following:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: titanic-ingress
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /
pathType: Prefix # <-- IMPORTANT
backend:
service:
name: titanicfrontendservice
port:
number: 3000
- path: /api
pathType: Prefix # <-- IMPORTANT
backend:
service:
name: titanicbackendservice
port:
number: 8080
Why I think it happened?
Citing the official documentation:
Path types
Each path in an Ingress is required to have a corresponding path type. Paths that do not include an explicit pathType will fail validation. There are three supported path types:
ImplementationSpecific: With this path type, matching is up to the IngressClass. Implementations can treat this as a separate pathType or treat it identically to Prefix or Exact path types.
Exact: Matches the URL path exactly and with case sensitivity.
Prefix: Matches based on a URL path prefix split by /. Matching is case sensitive and done on a path element by element basis. A path element refers to the list of labels in the path split by the / separator. A request is a match for path p if every p is an element-wise prefix of p of the request path.
-- Kubernetes.io: Docs: Concepts: Services networking: Ingress: Path types (there are some examples on how the path matching is handled)
Ingress controller is forced to match only the / path leaving rest of the dependencies (apart from the index.html) on other paths like /super.jpg and /folder/awesome.jpg to error with 404 code.
Side note!
You can test yourself this behavior by spawning an nginx Pod and placing example files in it. After applying the Ingress resource with / and pathType: Exact you won't be able to request it through the Ingress controller but you could access them within the cluster.
I encourage you to check the additional resources:
Kubernetes.io: Docs: Concepts: Services networking: Ingress
Kubernetes.github.io: Ingress nginx: User guide: Ingress path matching
The issue with your ingress.yaml is that the route for your ui should be /* and placed below the backend routing. Also, check your routing for APIs
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: titanic-ingress
#labels:
#name: titanic-ingress
spec:
ingressClassName: nginx
rules:
- host: localhost
http:
paths:
- path: /api/*
pathType: Exact
backend:
service:
name: titanicbackendservice
port:
number: 8080
- path: /*
pathType: Exact
backend:
service:
name: titanicfrontendservice
port:
number: 3000

MERN app in microk8s kubernetes with ingress, page does not display

I have a Mongo, Express, React Node app that is currently deployed in a microk8s pod. I am trying to setup Ingress for the app. The express server is setup to serve the react with express.static like below
app.use(express.static('DemoApp/build'));
app.get('*', function(req, res, next) {
res.sendFile(path.resolve(__dirname, 'DemoApp', 'build', 'index.html'));
});
Everything works fine when I navigate to the ip:port of the kubernetes cluster and even everything works fine when I navigate to the FQDN for the ingress host but as soon as I add a path to the ingress then the app only shows a white screen. One note, my kubernetes node and workstations are all running inside an internal private network. I am not trying to expose anything outside of that. I have tried to follow the step provided in this post (ReactJS app displays whitescreen using Kubernetes Ingress) but it has not fixed the issue. I have the built in ingress controller enabled for microk8s.
Below is the YAML I am using for ingress that doesnt work
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: demo-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
namespace: default
spec:
rules:
- host: apps.sst.com
http:
paths:
- path: /demo(/|$)(.*)
backend:
serviceName: mern-demo
servicePort: 4000
Here is the YAML for ingress that does work
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: demo-ingress
annotations:
namespace: default
spec:
rules:
- host: apps.sst.com
http:
paths:
- path:
backend:
serviceName: mern-demo
servicePort: 4000
One other note is that my app is using react router. Based on further research I am wondering if this affects things at all.
Any help would be greatly appreciated. Thanks
rewrite-target annotation which was used in your YAML
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
works only for Nginx Ingress. As you are using your default ingress it will not work. You would need to install it manually as it is mentioned here.
Now depends on your env if it's local or cloud you would need to configure MetalLB. For more details you can check this thread.
There was also similar StackOverflow question regarding Nginx Ingress issue on MicroK8s, you can read about it in this SO thread.
However, do you need to use rewrite? You cannot use multiple paths and default backend? Something like this:
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
spec:
backend:
serviceName: hello-world
servicePort: 60000
rules:
- http:
paths:
- path: /world
backend:
serviceName: hello-world
servicePort: 60000
- path: /kube
backend:
serviceName: hello-kubernetes
servicePort: 80

ingress configuration doesnt connect to service api in azure

I have below configuration with one path for ui and another path for web api but api path doesnt work.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myingress
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/use-private-ip: "true"
appgw.ingress.kubernetes.io/ssl-redirect: "true"
appgw.ingress.kubernetes.io/backend-path-prefix: /
spec:
tls:
- secretName: mysecret
rules:
- host: dev-ingress.com
- http:
paths:
- path: /
backend:
serviceName: uidev
servicePort: 80
- path: /gateway
backend:
serviceName: ocelotapigatewaydev
servicePort: 80
uidev path works but ocelotapigatewaydev dot net web api is not working when i test the api with gateway/api.
Strangely if i have html at the root of the ocelotapigatewaydev web api project then it loads it..Not sure how to fix it.
Changing api calls path to below has fixed the issue
- path: /gateway/.*
courtesy:
https://kubernetes.github.io/ingress-nginx/user-guide/ingress-path-matching/

Invalid host header and default backend 404 with Kubernetes ingress controller

Accessing my nodejs/react site using the URL displays "Invalid Host header". Accessing it through the public IP displays "default backend - 404".
I am using Kubernetes nginx controller with Azure cloud and load balancer.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myrule
namespace: mynamespace
annotations:
kubernetes.io/ingress.class: nginx
certmanager.k8s.io/cluster-issuer: letsencrypt-staging
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
tls:
- hosts:
- mysite.uknorth.cloudapp.azure.com
secretName: tls-secret
rules:
- host: mysite.uknorth.cloudapp.azure.com
http:
paths:
- backend:
serviceName: service-ui
servicePort: 8080
path: /
- backend:
serviceName: service-api
servicePort: 8999
path: /api
Any guidance appreciated.
So let's assume the SSL part is ok (link) since you can reach the nginx ingress controller.
Your rewrite annotation is not necessary for what you need. Take a look at these rules:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myrule
namespace: mynamespace
annotations:
kubernetes.io/ingress.class: nginx
certmanager.k8s.io/cluster-issuer: letsencrypt-staging
spec:
tls:
- hosts:
- mysite.uknorth.cloudapp.azure.com
secretName: tls-secret
rules:
- host: mysite.uknorth.cloudapp.azure.com
http:
paths:
- backend:
serviceName: service-ui
servicePort: 8080
path: /
- backend:
serviceName: service-api
servicePort: 8999
path: /api
Whatever you send to /api/.* will be redirected to service-api. And whatever you send to / will be send to service-ui.
Thanks for your feedback. It turns out the problem was not with the ingress rule above. The service-ui was running the incorrect command parameters thus not acknowledging the request. I missed the fact that the service-api was responding correctly.
In short, check the endpoints and running services are configured correctly - more a lesson for me than anyone else. I received a response by curling the service locally but that didn't mean it could handle https requests over ingress as the service was configured incorrectly.
Also, another lesson for me, ask the developers if the correct image is being used for the build. And ask them again if they say yes.

Resources