NextJS deployment on sub path with ingress config - reactjs

I am trying to deploy nextjs app in the sub path using k8s ingress.
Here is my ingress config:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: simple-ingress
annotations:
kubernetes.io/ingress.class: 'nginx'
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
tls:
- hosts:
- 'example.com'
rules:
- host: 'example.com'
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: react-app
port:
number: 80
- path: /site
pathType: Prefix
backend:
service:
name: next-app
port:
number: 3002
And here is my nextjs config:
const nextConfig = {
staticPageGenerationTimeout: 60,
assetPrefix: isProd ? '/site' : '',
basePath: isProd ? '/site' : '',
images: {
path: isProd ? '/site/_next/image' : '/_next/image',
},
}
module.exports = withNx(nextConfig)
With the above configuration, 1st ingress works and i am able to reach my endpoints at example.com, in the same time example.com/site returns http 404. What am i doing wrong?

Related

Deploying multiple react apps on a single host on different paths

I have 2 react applications, one of which is served on example.com and it works fine.
Now I am working on a second application and I need it to be served on example.com/app.
I am using nginx as ingress.
Note:
Both apps run on seperate docker containers on AKS in same namespace.
Please find my ingress.yaml as below -
App1 served on example.com (works fine)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app1
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
kubernetes.io/ingress.allow-http: "true"
labels:
app: app1
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- backend:
service:
name: app1
port:
number: 80
path: /
pathType: Prefix
App2 which I need to serve on example.com/app (doesn't work). When I open example.com/app/ I see a blank page and no errors. When I open example.com/app the bundles fail to load.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app2
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
labels:
app: app2
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- backend:
service:
name: app2
port:
number: 80
path: /app(/|$)(.*)
pathType: Prefix
If it's just about the serving example.com/app not sure how your internal application showing or serving the data.
Can't you just do like
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-svc
spec:
rules:
- host: example.io
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc
port:
number: 80
- path: /app
pathType: Prefix
backend:
service:
name: svc2
port:
number: 80
ingressClassName: nginx
You can also separate out the two different ingress hosts with diff name
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
name: rewrite
namespace: default
spec:
ingressClassName: nginx
rules:
- host: example.io
http:
paths:
- path: /apps(/|$)(.*)
pathType: Prefix
backend:
service:
name: svc-2
port:
number: 80
while the other one is without the rule of rewrite forwarding all traffic to svc-1.
I had to add "homepage": "https://example.com/app" in my package.json to make it work.

Ingress doen't serve Django's static files

I have a simple app architecture:
A webapp build on Django Rest Framework providing an API
A Nginx reverse proxy used to serve webapp and its static files
A redis container used by webapp
A React app used as frontend
I tried to deploy this using Kubernetes in local first before deploying on the cloud (AWS or something similar).
Here my global deployment file (I intend to split in smaller part after).
apiVersion: v1
kind: Service
metadata:
name: redis
labels:
app: redis
spec:
ports:
- port: 6379
protocol: TCP
targetPort: 6379
selector:
app: redis
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis
ports:
- containerPort: 6379
---
apiVersion: v1
kind: Service
metadata:
name: webapp
labels:
app: webapp
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 8080
name: http
- port: 8081
protocol: TCP
targetPort: 8081
name: daphne
selector:
app: webapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: registry.gitlab.com/anima879/celestorbe/backend
imagePullPolicy: Always
volumeMounts:
- name: static-data
mountPath: /vol/web
env:
- name: SECRET_KEY
value: secretkey1234
- name: ALLOWED_HOSTS
value: "127.0.0.1,localhost,proxy"
ports:
- containerPort: 8080
- containerPort: 8081
imagePullSecrets:
- name: gitlab-registry-secret
volumes:
- name: static-data
hostPath:
path: static-data
---
apiVersion: v1
kind: Service
metadata:
name: front
labels:
app: front
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: front
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: front
spec:
selector:
matchLabels:
app: front
template:
metadata:
labels:
app: front
spec:
containers:
- name: front
image: registry.gitlab.com/anima879/celestorbe/front
imagePullPolicy: Always
volumeMounts:
- name: static-data
mountPath: /vol/web
ports:
- containerPort: 80
env:
- name: REACT_APP_API_URL
value: webapp
imagePullSecrets:
- name: gitlab-registry-secret
volumes:
- name: static-data
hostPath:
path: static-data
---
apiVersion: v1
kind: Service
metadata:
name: proxy
labels:
app: proxy
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: proxy
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: proxy
spec:
replicas: 1
selector:
matchLabels:
app: proxy
template:
metadata:
labels:
app: proxy
spec:
containers:
- name: proxy
image: registry.gitlab.com/anima879/celestorbe/proxy
imagePullPolicy: Always
volumeMounts:
- name: static-data
mountPath: /vol/web
ports:
- containerPort: 80
imagePullSecrets:
- name: gitlab-registry-secret
volumes:
- name: static-data
hostPath:
path: static-data
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: main-ingress
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/configuration-snippet: |-
rewrite ^/api/(.*)$ /$1 break;
rewrite_log on;
spec:
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: front
port:
number: 80
- path: /api(/|$)(.*)
pathType: Prefix
backend:
service:
name: proxy
port:
number: 80
- path: /ws
pathType: Prefix
backend:
service:
name: proxy
port:
number: 80
As you can see, I use an Ingress as the entrance for my application. Every url should go to the frontend by default (so the user have the UI to use the app). But when a request is made to the back, it starts with /api.
For instance:
localhost/login should display the login page from the React app
localhost/api/login should send a login request to the back (with the POST method)
I need to rewrite the url to the back to localhost/login because webapp doesn't understand URL starting with /api.
But if I go in the browser to an API endpoint, Django display a page that allows to use the endpoint.
My problem is here. When I tried to directly access the API from the browser, the CSS are not loaded:
The type of the CSS file are not correct, and I don't know how to solve this.
Also I suspect the Ingress tried to get the CSS from the frontend instead of Django.
I know it is a tricky issue, but if you have a better alternative or some workflow to solve the issue, i would appreciate.
Thank you.
P.S. When I don't use an Ingress and use a LoadBalancer service (On different port for Front and proxy), it works. Except the front can't do request to the back if I don't the IP of the proxy LoadBalancer. Because I want very low coupling, I don't think it is a good idea (But i may be wrong).

Problem when making frontend and backend communicate

i'm working with Minikube to make a full stack K8s application using React as a frontend and ASP NET Core as a backend.
Here there are my configuration
Deployments and Services
apiVersion: v1
kind: ServiceAccount
metadata:
name: web-frontend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend-deployment
labels:
app: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app:
frontend
spec:
serviceAccountName: web-frontend
containers:
- name: frontend
image: frontend
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: frontend-svc
spec:
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: backend
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend-deployment
labels:
app: backend
spec:
replicas: 1
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app:
backend
spec:
serviceAccountName: backend
containers:
- name: backend
image: backend
ports:
- containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
name: backend
spec:
selector:
app: backend
ports:
- protocol: TCP
port: 5000
targetPort: 5000
Dockerfiles for the frontend
FROM node:alpine as build-image
WORKDIR /app
COPY package.json ./
COPY package-lock.json ./
RUN npm i
COPY . .
CMD ["npm", "run", "start"]
This is instead my Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-backend-ingress
annotations:
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /?(.*)
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80
- path: /api/?(.*)
pathType: Prefix
backend:
service:
name: backend
port:
number: 5000
However, when I type minikube tunnel to expose the ingress IP locally I can reach the frontend, but when the frontend tries to get a fetch request to /api/something in the browser console I get GET http://localhost/api/patients/ 404 (Not Found) and an error SyntaxError: Unexpected token < in JSON at position 0.
Moreover, If I change the Ingress in this way
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: frontend-backend-ingress
annotations:
# nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend-svc
port:
number: 80
- path: /api/
pathType: Prefix
backend:
service:
name: backend
port:
number: 5000
Then I can issue curl localhost/api/something and I get the JSON result, but when the frontend tries to contact the backend I get
GET http://localhost/api/patients/ 500 (Internal Server Error)
SyntaxError: Unexpected end of JSON input
at main.358f50ad.js:2:473736
at s (main.358f50ad.js:2:298197)
at Generator._invoke (main.358f50ad.js:2:297985)
at Generator.next (main.358f50ad.js:2:298626)
at Al (main.358f50ad.js:2:439869)
at a (main.358f50ad.js:2:440073)
This looks strange because if I try the frontend and the backend outside kubernetes everything works fine and from the React application the result from the backend is correctly fetched (of course using the proxy inside the package.json)
To contact or make links between apps you could use their kubernetes native FQDN ( try to ping or telnet it if you want to test the connection but here is how it works:
Thr default FQDN of any service is:
<service-name>.<namespace>.svc.cluster.local.
In your above example, you should be able to contact you backend service from your frontend one with:
backend.YOURNAMESPACENAME.svc.cluster.local:5000
For services in same namespace, you don't need to use the FQDN to access services, just the service name would be enough:
backend:5000
I don't know where you exactly configure the links between the frontend and backend but however, you should "variabilize" this link and add the variable definition in kubernetes manifest.

webpack react ingress-nginx kubernetes shows Bad Gateway

All the deployments are running, all the pods are healthy. ingress-nginx is running. when I run kubectl get ing:
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-srv <none> myapp.com 192.168.49.2 80 13m
I set the hosts file for ingress.
this is the ingress-srv.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-srv
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/use-regex: "true"
spec:
defaultBackend:
service:
name: nginx-ingress-default-backend
port:
number: 80
rules:
- host: myapp.com
http:
paths:
- path: /posts
pathType: Exact
backend:
service:
name: query-srv
port:
number: 4002
- path: /?(.*)
pathType: Exact // I tried "Prefix"
backend:
service:
name: client-srv
port:
number: 8080
the issue ingress api is not routing correctly. and react app is set up with webpack-dev-server. it's port is set to 8080. this is client-depl.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-depl
spec:
replicas: 1
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
containers:
- name: client
image: kali/client
---
apiVersion: v1
kind: Service
metadata:
name: client-srv
spec:
selector:
app: client
ports:
- name: client
protocol: TCP
port: 8080
targetPort: 8080
webpack config for client:
module.exports = {
mode: "development",
entry: ["regenerator-runtime/runtime", "./index.js"],
output: {
path: path.resolve(__dirname, "public"),
filename: "main-bundle.js",
publicPath: "/",
},
module: {
rules: [{ test: /\.js$/, loader: "babel-loader", exclude: /node_modules/ }],
},
devtool: "eval-cheap-source-map",
devServer: {
historyApiFallback: true,
contentBase: path.resolve(__dirname, "public"),
overlay: true,
},
};
First a word a caution: The webpack dev server is not intended to be used to serve the react app in production. You might want to investigate an alternative like nginx.
You get an error of BadGateway from your ingress. That means that the ingress recognizes the configuration, but can not reach the real service. Usually this is caused by a problem of the port mapping, but since you stated that you run the dev server with port 8080 and configured that in the kubernetes services as well, it should be fine.
I noticed that your deployment does not declare the port, this should be fixed:
apiVersion: apps/v1
kind: Deployment
metadata:
name: client-depl
spec:
replicas: 1
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
containers:
- name: client
image: kali/client
ports:
- containerPort: 8080
But there is one more thing to consider when listening for connections beside the port: The network interface to use. The webpack dev server is preconfigured to listen only on the loopback (localhost) interface and to not be available externally. It is a dev server after all.
This can be changed using a different listen address, either in the webpack configuration or on the command line:
module.exports = {
//...
devServer: {
host: '0.0.0.0'
}
};
CLI: webpack serve --host 0.0.0.0
Because you are using pathType: Exact in your ingress definiton and exact expects that exact path like https://<url>//?(.*) . You need to change it to pathType: Prefix ıf you are willing to use prefix /?(.*) to match and route with urls such as http://<url>/<sample>
Also you can check path types in official documentation

JS files not laoding for Application when using Kubernetes nginx Ingress rules

I am trying to add path based ingress rules for react web application.
If I go to example.com/abc to abc application and example.com/xyz to Xyz application
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /abc(/|$)(.*)
backend:
serviceName: abc-service
servicePort: 80
- path: /xyz(/|$)(.*)
backend:
serviceName: xyz-service
servicePort: 80
But if I tried path / instead of /abc it works. Not for xyz
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- path: /(.*)
backend:
serviceName: abc-service
servicePort: 80
- path: /xyz(/|$)(.*)
backend:
serviceName: xyz-service
servicePort: 80
Try this:
kind: Ingress
metadata:
name: test-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- http:
paths:
- path: /abc/?(.*)
backend:
serviceName: abc-service
servicePort: 80
- path: /xyz/?(.*)
backend:
serviceName: xyz-service
servicePort: 80

Resources