Ingress
Un LoadBalancer usa un indirizzo pubblico dal pool di indirizzi per ogni servizio configurato.
In un LoadBalancer implementato localmente gli indirizzi del pool possono essere insufficienti. Se il LoadBalancer è fornito dal Cloud Provider, ogni sua istanza ha un costo.
Ingress espone servizi multipli tramite un unico Load Balancer.
Ha due componenti:
- Ingress controller
- Ingress object
Molte installazioni di cluster Kubernetes, come Kind, non forniscono un Ingress Controller di serie, quindi va installato.
Per l'uso di un cluster Kubernetes di uno specifico Cloud Controller, leggere la documentazione appropriata.
Ingress su Kind
Kind supporta tre Ingress Controllers:
- Contour
- Ingress Kong
- Ingress Nginx
Useremo qui Ingress Nginx-
Modifica del Cluster Kind
Occorre modificare la costruzione del cluster.
Cancellare il cluster corrente con la nostra procedura shell:
cd
./teardown,sh
Creiamo una nuova procedura shell di setup del cluster chiamata ingress.sh
:
vim ingerss.sh
#!/bin/sh
set -o errexit
# crea il contenitore del registry se non esiste
reg_name='kind-registry'
reg_port='5000'
if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
docker run \
-d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" \
-v $HOME/.docker/registry:/var/lib/registry registry:2
fi
# crea un cluster con il registry locale abilitato in containerd
cat <<EOF | kind create cluster --image kindest/node:v1.24.0 --config=-
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
containerdConfigPatches:
- |-
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
endpoint = ["http://${reg_name}:5000"]
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
extraMounts:
- hostPath: /data
containerPath: /data
- role: worker
extraMounts:
- hostPath: /data
containerPath: /data
- role: worker
extraMounts:
- hostPath: /data
containerPath: /data
EOF
# connette il registry alla rete del cluster se non connesso
if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
docker network connect "kind" "${reg_name}"
fi
# Documenta il local registry
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: local-registry-hosting
namespace: kube-public
data:
localRegistryHosting.v1: |
host: "localhost:${reg_port}"
help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
EOF
La sezione aggiunta, dopo la linea - role: control-plane
è:
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 80
protocol: TCP
- containerPort: 443
hostPort: 443
protocol: TCP
Rendiamola eseguibile:
chmod +x ingress.sh
Copiamo la procedura di setup in una nuova:
cp setup.sh setup-ingress.sh
Modifichiamo la nuova procedura setup-ingress.sh
sostituendo le linee:
echo "~/std.sh"
~/std.sh
con
echo "~/ingress.sh"
~/ingress.sh
Lanciamo il nuovo cluster che supporta Ingress:
./setup-ingress.sh
Deployment di Ingress
Copiamo dalla rete il Deployment del controller di Ingress e poniamolo nella directory ~/scripts
:
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml
mv deploy.yaml ~/scripts/ingress-deploy.yml
Eseguiamo il deployment e attendiamo che sia pronto:
kubectl apply -f ~/scripts/ingress-deploy.yml
echo Waiting up to 120s for nginx-ingress pod ...
kubectl wait --namespace ingress-nginx \
--for=condition=ready pod \
--selector=app.kubernetes.io/component=controller \
--timeout=120s
Test di Ingress
Prepariamo un Manifest di test:
vim ~/scripts/nginx-test.yml
kind: Pod
apiVersion: v1
metadata:
name: foo-app
labels:
app: foo
spec:
containers:
- command:
- /agnhost
- netexec
- --http-port
- "8080"
image: registry.k8s.io/e2e-test-images/agnhost:2.39
name: foo-app
---
kind: Service
apiVersion: v1
metadata:
name: foo-service
spec:
selector:
app: foo
ports:
# Default port used by the image
- port: 8080
---
kind: Pod
apiVersion: v1
metadata:
name: bar-app
labels:
app: bar
spec:
containers:
- command:
- /agnhost
- netexec
- --http-port
- "8080"
image: registry.k8s.io/e2e-test-images/agnhost:2.39
name: bar-app
---
kind: Service
apiVersion: v1
metadata:
name: bar-service
spec:
selector:
app: bar
ports:
# Default port used by the image
- port: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
rules:
- http:
paths:
- pathType: ImplementationSpecific
path: /foo(/|$)(.*)
backend:
service:
name: foo-service
port:
number: 8080
- pathType: ImplementationSpecific
path: /bar(/|$)(.*)
backend:
service:
name: bar-service
port:
number: 8080
Lo sottomettiamo:
kubectl apply -f ~/scripts/nginx-test.yml
E attendiamo che i pod vengano creati.
Connessioni di prova:
curl localhost/foo/hostname
foo-app
curl localhost/bar/hostname
bar-app
Al termine cancelliamo dal manifest:
kubectl delete -f ~/scripts/nginx-test.yml
Altro Esempio
Il seguente esempio è preso dal libro The Kubernetes Book di Nigel Poulton.
Abbiamo un Manifest con due pod e due servizi:
mkdir -p ~/scripts/ig
vim ~/scripts/ig/app.yml
apiVersion: v1
kind: Service
metadata:
name: svc-shield
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
selector:
env: shield
---
apiVersion: v1
kind: Service
metadata:
name: svc-hydra
spec:
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
selector:
env: hydra
---
apiVersion: v1
kind: Pod
metadata:
name: shield
labels:
env: shield
spec:
containers:
- image: nigelpoulton/k8sbook:shield-ingress
name: shield-ctr
ports:
- containerPort: 8080
imagePullPolicy: Always
---
apiVersion: v1
kind: Pod
metadata:
name: hydra
labels:
env: hydra
spec:
containers:
- image: nigelpoulton/k8sbook:hydra-ingress
name: hydra-ctr
ports:
- containerPort: 8080
imagePullPolicy: Always
Sottomettiamo il manifest:
kubectl apply -f ~/scripts/ig/app.yml
Abbiamo due possibilitè per Ingress:
- Host Based Routing - il routing al servizio avviene sulla base dello host di destinazione
- Path Based Routing - il routing al servizio avviene sulla base del Path nella URL
Nell'esercizio la situazione è:
- Host-based: shield.mcu.com >> svc-shield
- Host-based: hydra.mcu.com >> svc-hydra
- Path-based: mcu.com/shield >> svc-shield
- Path-based: mcu.com/hydra >> svc-hydra
L'oggetto Ingress viene specificato dal manifest:
vim ~/scripts/ig/ig-all.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mcu-all
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: shield.mcu.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-shield
port:
number: 8080
- host: hydra.mcu.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: svc-hydra
port:
number: 8080
- host: mcu.com
http:
paths:
- path: /shield
pathType: Prefix
backend:
service:
name: svc-shield
port:
number: 8080
- path: /hydra
pathType: Prefix
backend:
service:
name: svc-hydra
port:
number: 8080
Note
L'annotazione nginx.ingress.kubernetes.io/rewrite-target: /
e' indispensabile per permettere a Ingress il reindirizzamento della richiesta.
E' un esempio di Annotation interpretata da un Controller specifico.
La specifica ingressClassName: nginx
non è indispensabile se sul nostro cluster abbiamo solo lo Nginx Ingress Controller.
Qualora vi fossero più Ingress controller dovremmo configurare una classe per ciascuno, per esempio col Manifest:
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: igc-nginx
spec:
controller: nginx.org/ingress-controller
Sottoponiamo il manifest:
kubectl apply -f ~/scripts/ig/ig-all.yml
Ispezione degli oggetti Ingress:
kubectl get ing
Descrizione di un oggetto Ingress:
kubectl describe ing mcu-all
Notare la sezione Regole:
Rules:
Host Path Backends
---- ---- --------
shield.mcu.com
/ svc-shield:8080 (10.244.1.10:8080)
hydra.mcu.com
/ svc-hydra:8080 (10.244.2.5:8080)
mcu.com
/shield svc-shield:8080 (10.244.1.10:8080)
/hydra svc-hydra:8080 (10.244.2.5:8080)
Notare anche il campo Address: localhost
Modificare il file /etc/hosts
aggiungendo le linee:
127.0.0.1 shield.mcu.com
127.0.0.1 hydra.mcu.com
127.0.0.1 mcu.com
Aprire un browser e testare i seguenti URL:
- shield.mcu.com
- hydra.mcu.com
- mcu.com/shield
- mcu.com/hydra
Al termine ripulire:
kubectl delete -f ~/scripts/ig/ig-all.yml
kubectl delete -f ~/scripts/ig/app.yml
Rimettere anche a posto /etc/hosts
.