Lab 4: Two Services Behind One Ingress
Pairs with Phase 4. You'll install an ingress controller, then route a real hostname to two different services: / to an nginx frontend, /api to the whoami backend from Lab 2.
This lab needs the port mappings from Lab 1's kind config. If you skipped that, recreate the cluster with the config first.
Step 1: Install ingress-nginx
The Ingress resource is just rules; without a controller pod doing the routing, nothing happens. Install the controller (this manifest is tailored for kind):
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120sExpected:
pod/ingress-nginx-controller-xxxxx condition metStep 2: Make sure the backend is up
Lab 2's whoami should still be running:
kubectl get deploy whoami svc whoamiIf it's gone, re-apply lab-2/deployment.yaml and lab-2/service.yaml.
Step 3: A frontend
Save as lab-4/frontend.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: frontend
spec:
selector:
app: frontend
ports:
- port: 80kubectl apply -f lab-4/frontend.yamlStep 4: The Ingress
Try writing lab-4/ingress.yaml yourself first: host app.localtest.me, path /api to service whoami port 80, path / to service frontend port 80.
Solution: ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
spec:
ingressClassName: nginx
rules:
- host: app.localtest.me
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: whoami
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80Two things people miss: ingressClassName: nginx (without it, the controller ignores your Ingress), and path order doesn't matter - Prefix matching picks the longest match, so /api wins over / for API calls.
kubectl apply -f lab-4/ingress.yaml
kubectl get ingressExpected (ADDRESS may take a minute to fill in):
NAME CLASS HOSTS ADDRESS PORTS AGE
app nginx app.localtest.me localhost 80 30sStep 5: Test from your browser
*.localtest.me is a public DNS name that always resolves to 127.0.0.1 - no /etc/hosts editing. Thanks to the kind port mappings, port 80 on your Mac lands on the ingress controller.
curl http://app.localtest.me/ # nginx welcome page HTML
curl http://app.localtest.me/api # whoami output: Hostname, IP, headersOpen both in the browser too. One hostname, one entry point, two services - this replaces every -p flag in your compose file.
Note the whoami output shows X-Forwarded-For and X-Forwarded-Host headers now: your request went Mac -> kind port mapping -> ingress controller -> Service -> pod.
Verify it worked
- [ ] You can explain why the controller install was a separate step from the Ingress resource
- [ ]
/and/apihit different services on the same hostname - [ ] You know what
ingressClassNamedoes and what happens without it - [ ] You found the forwarded headers in the whoami response and can trace the request path
Stretch goal: TLS
Install cert-manager and issue a self-signed cert for app.localtest.me. Real Let's Encrypt needs a public domain, but the cert-manager mechanics (Issuer, Certificate, annotated Ingress) are identical - and that's the part worth learning.
Next: Lab 5: Operations