Deployment

Damit Kubernetes nicht nur Selbstzweck ist, müssen Applications und Services installiert werden.

Eine detaillierte Beschreibung von Application Deployments ist unter https://kubernetes.io/docs/concepts/workloads/controllers/deployment/ zu finden. Mit einem Application Deployment werden Pods erzeugt, die in der Kubernetes Cloud "irgendwo" laufen. Diese Pods sollten niemals direkt über ihre IP-Adresse angesprochen werden, weil sich aufgrund der Natur von Kubernetes diese IP-Adressen jederzeit ohne Vorwarnung nicht vorhersagbar ändern können.

Namespace

Zunächst sollte ein Namespace für die Anwendung erstellt werden. Für diesen können dann individuelle Policies erstellt werden. Dazu wird die Datei hello-namespace.json erstellt:

 

{
"apiVersion": "v1",
"kind": "Namespace",
"metadata": {
"name": "hello-zone",
"labels": {
"name": "hello-zone"
}
}
}

Mit kubectl create -f hello-namespace.json wird der Namespace erstellt. Alle weiteren Objekte für unsere Hello World Anwendung werden in diesem Namespace erstellt. Die Policy wird später erstellt, wenn der Funktionstest erfolgreich war. Unter Namespaces ist die Policy für die Hello World Anwendung zu finden.

 

Hello World Application

Damit wir unseren Load-Balancer auch ausprobieren können, brauchen wir eine Anwendung. Mit curl --location -o hello-application.orig.yaml https://k8s.io/examples/service/access/hello-application.yaml wird erst einmal ein Template für eine Demoanwendung geholt. Das File wird nun wie folgt angepasst:

--- hello-application.orig.yaml    2020-12-11 20:12:34.906419624 +0000
+++ hello-application.yaml 2020-12-11 20:38:56.432717809 +0000
@@ -2,6 +2,7 @@
kind: Deployment
metadata:
name: hello-world
+ namespace: hello-zone
spec:
selector:
matchLabels:
@@ -11,6 +12,7 @@
metadata:
labels:
run: load-balancer-example
+ role: hellolbl
spec:
containers:
- name: hello-world

Damit wird die Hello World Anwendung in unserem neu erstellen Namespace erstellt. Im nächsten Schritt erfolgt das Deployment:


kubectl apply -f hello-application.yaml
kubectl get pods -n hello-zone -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hello-world-59966754c9-bgxx2 1/1 Running 0 100s 10.42.0.1 kube-w-0002 <none> <none>
hello-world-59966754c9-lcqmz 1/1 Running 0 100s 10.36.0.1 kube-w-0001 <none> <none>
curl 10.42.0.1:8080; echo
Hello Kubernetes!
curl 10.36.0.1:8080; echo
Hello Kubernetes!

Wie man sehen kann, laufen hier nun 2 Replicas auf verschiedenen Workern. Die Curl Kommandos zeigen auch, dass unsere Hello World Services auch laufen. Ein Zugriff von außen geht damit aber noch nicht. Dazu brauchen wir noch einen Kubernetes Load-Balancer.

Das sind jedoch erst einmal nur 2 Pods, die im Kubernetes Cluster unter irgendeiner IP-Adresse sichtbar sind. Wirklich anfangen kann man damit noch nichts, dazu muss noch ein Service eingerichtet werden.

Hello World Service

Damit die Pods auch sinnvoll nutzbar werden, muss ein Service erstellt werden. Mit

kubectl expose -n hello-zone deployment/hello-world

Werden die hello-world Pods zu einem Service zusammengefasst. Mit kubectl get svc -n default -o wide ist das Ergbnis zu sehen:

kubectl get svc -n hello-zone -o wide hello-world
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
hello-world ClusterIP 10.111.222.112 <none> 8080/TCP 114s run=load-balancer-example

Nun ist hello-world unter der IP-Adresse 10.111.222.112:8080 als Service erreichbar, allerdings auch nur innerhalb des Kubernetes Clusters. Man kann im Expose Befehl auch eine Cluster-IP Adresse fest vorgeben, aber davon wird abgeraten. Der Service sollte nur über einen Ingress Service nach außen sichtbar gemacht werden. Der Service ist die Kubernetes Komponente, die vom Ingress Service angesprochen werden soll.

Hello World Ingress

Jetzt bringen wir unseren Hello World Service nach außen. Bevor das funktioniert, muss allerdings ein Ingress Service installiert sein, dazu eignet sich z.B. Nginx. Unter ingress-nginx wird beschrieben, wie das geht.

Dazu muss die Datei hello-lb.yaml erstellt werden:

apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: nginx
name: hello
namespace: hello-zone
spec:
rules:
- host: test.example.com
http:
paths:
- backend:
serviceName: hello-world
servicePort: 8080
path: /
# This section is only required if TLS is to be enabled for the Ingress
tls:
- hosts:
- test.example.com
secretName: test.example.com

Mit kubectl apply -f hello-lb.yaml wird der Ingress-Nginx mit dem hello-world Service verbunden. ToDo: Das v1beta1 ist deprecated.

Zusammenspiel

In diesem Diagramm wird dargestellt, wie die Komponenten unserer Hello-World Anwendungen zusammenspielen:

 

 

 

Test

Mit Curl kann man sich das Ergebnis nun ansehen:

curl --verbose --insecure https://test.example.com; echo
* Trying 172.17.1.0:443...
* Connected to test.example.com (172.17.1.0) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use h2
* Server certificate:
* subject: CN=test.example.com; O=nginxsvc
* start date: Dec 11 08:52:39 2020 GMT
* expire date: Dec 11 08:52:39 2021 GMT
* issuer: CN=test.example.com; O=nginxsvc
* SSL certificate verify result: self signed certificate (18), continuing anyway.
* Using HTTP2, server supports multi-use
* Connection state changed (HTTP/2 confirmed)
* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
* Using Stream ID: 1 (easy handle 0x55da0f046d70)
> GET / HTTP/2
> Host: test.example.com
> user-agent: curl/7.71.1
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
* Connection state changed (MAX_CONCURRENT_STREAMS == 128)!
< HTTP/2 200
< date: Fri, 11 Dec 2020 10:05:11 GMT
< strict-transport-security: max-age=15724800; includeSubDomains
<
* Connection #0 to host test.example.com left intact
Hello Kubernetes!

Der Zugriff erfolgte über die OPNsense Firewall, die die Route für den Zugang zum Ingress-Nginx via BGP bekommen hat.

Namespaces

Nach dem ersten, erfolgreichen Test ist jetzt der Zeitpunkt, mit Network-Policies den Zugriff auf die hello-world Pods zu beschränken. Außer dem Ingress-Nginx soll da nichts auf die hello-world Pods zugreifen können. Wie das geht, ist auf der Seite Namespaces beschrieben.

Pods neu starten

Nach Änderungen an dem Deployment müssen ggf. die zugehörigen Pods neu gestartet werden. Kubernetes macht es da einem sehr einfach:

kubectl rollout restart deployment hello-world -n hello-zone

Wenn mindestens 2 Repliken laufen, dann werden diese nacheinander gestartet. Dadurch sollte gibt es keine Downtime für den betroffenen Service geben. Bei älteren Kubernetes Versionen funktioniert das evtl. nicht, in diesem Fall kann man es mit

kubectl patch deployment hello-world \
-n hello-zone \
-p "{\"spec\":{\"template\":{\"metadata\":{\"annotations\":{\"date\":\"`date +'%s'`\"}}}}}"

versuchen.