I'm trying to learn about multipath in Kubernetes Ingress. First of all, I'm using minikube for this tutorial, I created a simple Web API using node js.
In this nodeJS, I created a simple Web API, with routing and controller
const express = require ('express');
const routes = require('./routes/tea'); // import the routes
const app = express();
app.use(express.json());
app.use('/', routes); //to use the routes
const listener = app.listen(process.env.PORT || 3000, () => {
console.log('Your app is listening on port ' + listener.address().port)
})
const express = require('express');
const router = express.Router();
const teaController = require('../controllers/tea');
router.get('/tea', teaController.getAllTea);
router.post('/tea', teaController.newTea);
router.delete('/tea', teaController.deleteAllTea);
router.get('/tea/:name', teaController.getOneTea);
router.post('/tea/:name', teaController.newComment);
router.delete('/tea/:name', teaController.deleteOneTea);
module.exports = router;
const os = require('os');
//GET '/tea'
const getAllTea = (req, res, next) => {
res.json({message: "GET all tea, " + os.hostname() });
};
//POST '/tea'
const newTea = (req, res, next) => {
res.json({message: "POST new tea, " + os.hostname()});
};
//DELETE '/tea'
const deleteAllTea = (req, res, next) => {
res.json({message: "DELETE all tea, " + os.hostname()});
};
//GET '/tea/:name'
const getOneTea = (req, res, next) => {
res.json({message: "GET 1 tea, os: " + os.hostname() + ", name: " + req.params.name});
};
//POST '/tea/:name'
const newComment = (req, res, next) => {
res.json({message: "POST 1 tea comment, os: " + os.hostname() + ", name: " + req.params.name});
};
//DELETE '/tea/:name'
const deleteOneTea = (req, res, next) => {
res.json({message: "DELETE 1 tea, os: " + os.hostname() + ", name: " + req.params.name});
};
//export controller functions
module.exports = {
getAllTea,
newTea,
deleteAllTea,
getOneTea,
newComment,
deleteOneTea
};
After that I created a docker image using this Dockerfile
FROM node:18.9.1-slim
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "node", "server.js" ]
And then, I created replicaset and service for this docker image
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: foo
spec:
selector:
matchLabels:
app: foo
replicas: 3
template:
metadata:
labels:
app: foo
spec:
containers:
- name: foo
image: emriti/tea-app:1.0.0
ports:
- name: http
containerPort: 3000
protocol: TCP
apiVersion: v1
kind: Service
metadata:
name: foo-nodeport
spec:
type: NodePort
ports:
- port: 3000
targetPort: 3000
nodePort: 31234
selector:
app: foo
Ingress for both Foo and Bar backend
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: foobar
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: foobar.com
http:
paths:
- path: /foo
pathType: Prefix
backend:
service:
name: foo-nodeport
port:
number: 3000
- path: /bar
pathType: Prefix
backend:
service:
name: bar-nodeport
port:
number: 3000
I also did these:
127.0.0.1 foobar.com
to /etc/hostsminikube tunnel
After that I run curl foobar.com/foo/tea
and I get this error:
curl : Cannot GET /
At line:1 char:1
+ curl foobar.com/foo
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-WebRequest], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand
I'm wondering if maybe someone has experienced a similar problem that I did and maybe already had the answer for that. Secondly how to debug the ingress if I meet similar issues?
The codes and manifest could be accessed on this repo
Thank you!
Your problem is here:
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
You haven't defined any capture groups in your rules, so this directive is rewriting all requests to /
. If a client asks for /foo/tea/darjeeling
, you request /
. If a client requests /foo/this/does/not/exist
, you request /
.
To make this work, you need to add appropriate capture groups to your rules:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: foobar
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
rules:
- host: foobar.com
http:
paths:
- path: /foo/(.*)
pathType: Prefix
backend:
service:
name: foo-nodeport
port:
number: 3000
- path: /bar/(.*)
pathType: Prefix
backend:
service:
name: bar-nodeport
port:
number: 3000
I suspect you would have found this much easier to debug if your application were to log incoming requests. You might want to look into that. I figured this by adding a sidecar proxy to your pods that would log requests, which made the problem relatively obvious (I could see that every request was for /
regardless of what URL the client was using).
There's more documentation about path rewriting when using the Nginx ingress service here.
Unrelated to your question:
You probably want to update your manifests to create Deployment resources instead of ReplicaSets.
For the way you've configured things here you don't need NodePort services.