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
Related
I am facing the 502 Bad gateway issue in my Application Gateway.
I am using Azure Kubernetes Service to deploy my cluster which is connected to Ingress Application Gateway.
Configuration Files:
kube-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myApp
namespace: en02
labels:
app: myApp
spec:
selector:
matchLabels:
app: myApp
replicas: 1
template:
metadata:
labels:
app: myApp
spec:
containers:
- name: myApp
image: somecr.azurecr.io/myApp:1.0.0.30
resources:
limits:
memory: "64Mi"
cpu: "100m"
ports:
- containerPort: 5100
env:
- name: ASPNETCORE_HOSTINGSTARTUPASSEMBLIES
value: "Microsoft.AspNetCore.ApplicationInsights.HostingStartup"
- name: "ApplicationInsights__ConnectionString"
value: "myKey"
---
apiVersion: v1
kind: Service
metadata:
namespace: en02
name: myApp
spec:
selector:
app: myApp
ports:
- port: 30153
targetPort: 5100
protocol: TCP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
namespace: en02
name: etopia
annotations:
kubernetes.io/ingress.class: azure/application-gateway
appgw.ingress.kubernetes.io/health-probe-path: "/api/home"
spec:
rules:
- http:
paths:
- path: /myApp/
backend:
service:
name: myApp
port:
number: 30153
pathType: Exact
Result of
kubectl describe ingress -n en02
Name: ingress
Labels: <none>
Namespace: en02
Address: public-ip
Ingress Class: <none>
Default backend: <default>
Rules:
Host Path Backends
---- ---- --------
*
/myApp/ myApp:30153 (10.0.0.106:5100)
Annotations: appgw.ingress.kubernetes.io/health-probe-path: /api/home
kubernetes.io/ingress.class: azure/application-gateway
Events: <none>
I am getting expected results from 10.0.0.106:5100/api/home and Application Gateway health status is 200.
No matter what I do, I always get Bad Gateway error, I was able to access a sample app on port 80 (where the ingress path was /) but if I specify anything in ingress path (/cashify/) it always give me bad gateway.
I tried adding readinessProbe to container but it doesn't work (However I am already getting 200 under application gateway health status).
Please help.
Please check if below can be worked around.
Please try to update deployment yaml to use wild card path specification to access apis with different paths.
deployment.yml
apiVersion: extensions/v1beta1
kind: Ingress
....
annotations:
kubernetes.io/ingress.class: azure/application-gateway
spec:
rules:
- host: xxx
http:
paths:
- path: /api/* #wild card path
backend:
serviceName: apiservice
servicePort: 80
- backend:
.....
servicePort: 80
Note from MS docs: If you want Application Gateway to probe on a different protocol, host name, or path and to recognize a
different status code as Healthy, configure a custom probe and
associate it with the HTTP settings.
As you said you have defined readinessProbe , please check if path of those probes is correct.
Check same with 1. livenessProbe 2. readinessProbe
Also please note that readinessProbe and livenessProbe are supported when configured with httpGet.
References:
bad request - path based routing · kubernetes-ingress · GitHub
application-gateway-troubleshooting-502
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
I have one service and a single ingress resource with kubenetes nginx ingress controller. I want the /student path of my url to go to the root of the application and match any other url segments which follow the student.
For example: http://example.com/student/ver should match the /ver route of my application.
However, my ingress always hit the application with the /student url path prefixing the other url segments. If I call http://example.com/student/ver, my application is hit with the same url (student/ver).
My ingress:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/rewrite-target: /
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
name: ingress-resource
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: lesson-streaming
servicePort: 80
path: /student
I spent days with this and was not successful once.
Edit:
The ingress is changed to the following - not my requests say http 404
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/rewrite-target: /$2
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-redirect: "false"
name: ingress-resource
namespace: default
spec:
rules:
- host: example.com
http:
paths:
- backend:
serviceName: lesson-streaming
servicePort: 80
path: /student(/|$)(.*)
You can follow the link to use the rewrite-target annotation correctly and keep the right key nginx.ingress.kubernetes.io/rewrite-target.
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(/|$)(.*)
Im using k8 - v1.25.2 along with nginx-Ingress v2.4.1 https://docs.nginx.com/nginx-ingress-controller/installation/installation-with-manifests/
nginx.ingress.kubernetes.io/rewrite-target: /$2 - doesnt work
instead use :
nginx.org/rewrites: "serviceName=app1-svc rewrite=/;serviceName=app2-svc rewrite=/"
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-ingress
namespace: default
annotations:
nginx.org/rewrites: "serviceName=app1-svc rewrite=/;serviceName=app2-svc rewrite=/"
spec:
ingressClassName: nginx
rules:
- host: apps.relo.com
http:
paths:
- backend:
service:
name: app1-svc
port:
number: 2041
path: /app1
pathType: Prefix
- backend:
service:
name: app2-svc
port:
number: 2042
path: /app2
pathType: Prefix
I have tried many times to execute properly path based routing in nginx ingress controller but I have found the main reason behind the path based routing not working.
replace the (nginx.ingress.kubernetes.io/rewrite-target: /) to (nginx.org/rewrites: "serviceName=servicename rewrite=/")
must add ingressClassName in spec
in my case
I have kubernetes 1.23.0 & nginx/1.21.6
I hope this will resolve the above problem.
Best of luck
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.
I am new to K8s and this is my first time trying to get to grips with it. I am trying to set up a basic Nodejs Express API using this deployment.yml:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: api
spec:
replicas: 1
template:
metadata:
labels:
app: api
spec:
containers:
- image: registry.gitlab.com/<project>/<app>:<TAG>
imagePullPolicy: Always
name: api
env:
- name: PORT
value: "8080"
ports:
- containerPort: 8080
hostPort: 80
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
timeoutSeconds: 1
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
timeoutSeconds: 1
imagePullSecrets:
- name: registry.gitlab.com
Which is being deployed via gitlab-ci. This is working and I have set up a service to expose it:
apiVersion: v1
kind: Service
metadata:
name: api-svc
labels:
app: api-svc
spec:
ports:
- port: 80
targetPort: 80
protocol: TCP
name: http
selector:
app: api
type: LoadBalancer
But I have been looking into ingress to have a single point of entry for possibly multiple services. I have been reading through Kubernetes guides and I read through this Kubernetes Ingress Example and this is the ingress.yml I created:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: ingress
spec:
backend:
serviceName: api-svc
servicePort: 80
But this did not work, when I visited the external IP address that was generated from the ingress and I just 502 error pages.
Could anyone point me in the right direction, what am I doing wrong or what am I missing? I see that in the example link above that there is an nginx-rc.yml which I deployed exactly like in the example and that was created but still got nothing from the endpoint. The API was accessible from the Service external IP though..
Many Thanks
I have looked into it again and think I figured it out.
In order for Ingress to work on GCE you need to define your backend service das a NodePort not as ClusterIP or LoadBalancer.
Also you need to make sure the http health check to / works (you'll see the Google L7 Loadbalancer hitting your service quite a lot on that url) and then it's available.
Thought I would post my working deployment/service/ingress
So after much effort in getting this working, here is what I used to get it working:
Deployment
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: backend-api-v2
spec:
replicas: 2
template:
metadata:
labels:
app: backend-api-v2
spec:
containers:
- image: registry.gitlab.com/<project>/<app>:<TAG>
imagePullPolicy: Always
name: backend-api-v2
env:
- name: PORT
value: "8080"
ports:
- containerPort: 8080
livenessProbe:
httpGet:
# Path to probe; should be cheap, but representative of typical behavior
path: /healthz
port: 8080
initialDelaySeconds: 30
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
timeoutSeconds: 5
imagePullSecrets:
- name: registry.gitlab.com
Service
apiVersion: v1
kind: Service
metadata:
name: api-svc-v2
labels:
app: api-svc-v2
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 31810
protocol: TCP
name: http
selector:
app: backend-api-v2
Ingress
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: app-ingress
spec:
rules:
- host: api.foo.com
http:
paths:
- path: /v1/*
backend:
serviceName: api-svc
servicePort: 80
- path: /v2/*
backend:
serviceName: api-svc-v2
servicePort: 80
The important bits to notice as #Tigraine pointed out is the service is using type: NodePort and not LoadBalancer, I have also defined a nodePort but I believe it will create one if you leave it out.
It will use the default-http-backend for any routes that don't match the rules this is a default container that GKE runs in the kube-system namespace. So if I visited http://api.foo.com/bob I get the default response of default backend - 404.
Hope this helps
Looks like you're exposing your service to port 80 but your container is exposing 8080 so any request to the service is going to fail.
Also, have a look at the sample ingress resource (https://github.com/nginxinc/kubernetes-ingress/blob/master/examples/complete-example/cafe-ingress.yaml), you need to also define which hosts / paths route when the ingress controller is hit. (i.e. example.foo.com --> api-svc)