We are using AKS (Azure Kubernetes Service) for managed Kubernetes clusters and for the biggest part we are happy with the benefit the platform brings but we face some issues as well.
On AKS if you host a service of LoadBalancer type it automatically creates a new dynamic IP address (Azure resource) and assigns it to the service. This is not very optimal if you want to whitelist and simply does not make sense hence we switched to Nginx ingress controller (no particular reason to choose Nginx). We have a lot of apps - APIs, SPAs, 1 ingress controller for the whole cluster and separate cluster per environment - QA/Sta/Prod etc.. So we need to manage routing somehow and the ingress path parameter felt like the way to go. Example:
http://region.azurecloud.com/students/
http://region.azurecloud.com/courses/
where students and courses are the ingress paths and then you can add /api/student for example to access a particular API. The result would be http://region.azurecloud.com/students/api/student/1 which is not perfect but does the job for now.
This is how the ingress looks like:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: students-api-ingress
namespace: university
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- host: https://region.azurecloud.com
http:
paths:
- backend:
serviceName: students-api-service
servicePort: 8001
path: /students(/|$)(.*)
This however does not work very well with SPA applications such as React, Vue or Angular. We face the same problem regardless of technology. They are hosted behind Nginx in docker so this is how the Dockerfile looks like:
# build environment
FROM node:12.2.0-alpine as build
WORKDIR /app
COPY package*.json /app/
RUN npm install --silent
COPY . /app
RUN npm run build
# production environment
FROM nginx:1.16.0-alpine
COPY --from=build /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
And here is the nginx.conf file:
server {
listen 80;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html =404;
index index.html index.htm;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin: $http_origin');
add_header 'Access-Control-Allow-Origin: GET, POST, DELETE, PUT, PATCH, OPTIONS');
add_header 'Access-Control-Allow-Credentials: true');
add_header 'Vary: Origin');
}
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
add_header 'Access-Control-Allow-Credentials' 'true' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, PATCH, DELETE, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;
}
include /etc/nginx/extra-conf.d/*.conf;
}
The problem comes when assets such as .js files or images are accessed by the application. It creates the url in the format ingress.host/asset.name such as http://region.azurecloud.com/2342424ewfwer.js instead of including the ingress path as well which would look http://region.azurecloud.com/spa/2342424ewfwer.js and the result is a 404 not found error for all assets.
The applications work properly if the ingress path is just set to / without any rewrite annotations but this is a problem because you cannot have multiple applications using the base ingress host. One solution is to use a separate ingress controller for each SPA application but this brings us back to the initial issue with load balancers - separate load balancer and IP address for each SPA app which is what we want to avoid here.
I guess I am not the only person who is hosting SPA applications behind nginx ingress controller on Kubernetes but all similar topics I managed to find ended pretty much nowhere with no clear solution what should be done or the suggestions did not work for us. I wonder where does the problem come from - the nginx web server or the ingress controller and are ingress controllers generally the way to go for managing application routing on Kubernetes. I would appreciate any help or advice on this.
Thank you, R