Kubernetes-Cluster
Auf unserem Kubernetes-Cluster laufen die meisten unserer Anwendungen. Das Produkt beschreibt den Cluster an sich mit allen Komponenten, die für den Betrieb notwendig sind. Nicht im Scope sind deployte Anwendungen, die hier nicht explizit aufgelistet sind.
Infos
- Verantwortlich: Tom
Nutzerdokumentation
Nutzung von kubectl
Mit kubectl können CRUD-Operationen auf Kubernetes-API-Ressourcen durchgeführt werden.
Wo und wie?
Lokal auf Deinem Gerät !
Installation von kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/
Installation von kubelogin: Aktuellstes Build von https://github.com/int128/kubelogin/releases nach /usr/local/bin/kubectl-oidc_login packen
Folgende Config in ~/.kube/config packen:
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUMvakNDQWVhZ0F3SUJBZ0lCQURBTkJna3Foa2lHOXcwQkFRc0ZBREFWTVJNd0VRWURWUVFERXdwcmRXSmwKY201bGRHVnpNQjRYRFRJeE1Ea3lNVEUxTXpVMU5sb1hEVE14TURreE9URTFNelUxTmxvd0ZURVRNQkVHQTFVRQpBeE1LYTNWaVpYSnVaWFJsY3pDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTXVICnkxWVkzaU5jdlRLbG16NTJRZWU1bFBjOXBmSHVkMUExdnIraGNCelUzdlZtRWFjd2txMEdVYTYvUXc0N3B2NDUKKy9HVHBKWmNNOU5aVG96Q1B2ZDJhVVVySGFSZS9JaURwK0NxNHhmUFVjZi9Za0I4WkxWdzZPMWNwcW1OZ2xWQQp5VDlkSzVURkYxZDBJM0N5Vms2Vzlic29BeTJFSWg2ai9iMDlMSmNxTjBmSVZvb2JEdndGYzBNZEdibGNrRzVmCnZpOTJicnNvRnB1TTVTQVBMUkc1ZitqdjZjdjlQb3JqU3ZQVVVQWjVGUHV4Z2dSRnZVeDZLSE5OeVFueVhoR3YKK2FUT2hTM0oxcmRyQzVBY21MbGdNOGlsZmgwVnlCZTk3bjg4bG1FNnA2VnZZUXI2eFJEVmc4aitPOHNmMWk4LwpwOFRLV0c4dlJwS0c4c3NkanA4Q0F3RUFBYU5aTUZjd0RnWURWUjBQQVFIL0JBUURBZ0trTUE4R0ExVWRFd0VCCi93UUZNQU1CQWY4d0hRWURWUjBPQkJZRUZIMG5CbFF2MFVSblBwTHhZdTRScmhyS2l0M3dNQlVHQTFVZEVRUU8KTUF5Q0NtdDFZbVZ5Ym1WMFpYTXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBQUp2SmFPTUJKMVJBYTFHQW1ibQp1dk5wcWd5Z2NxNGJlTnlaTzZabk1RcG15UkVzUEwxK09Nd2NpVFE4VHZtWlVibUUzZ2lUb24reWYzSGV2NXdNCk1TQnV3VkVmZ3RNcTFvTzd3Mk9MdXMrTkdOY1dmQVRVS0dWNS9SQlY0bDJmS1B5TDZYN0w5QlNxMzlVaUhsL0UKaDNHTWRJQWRkSWNuZERaS1FBSks0V05iNzc2RzhmYlhZQTBxM1dEZkNoQ1ZaWXNZeGNHNFc5L3ZSbHlxWEU2awplYncrL1htcFNjOXlBYUVSMVl0bytjRGdnMXJtWVM0R2J0cGVpQkZ4bXJKMUQ1UHJZL09QaFRtU2NJbGViVVBGCmhzRTlDWFZPQlZPZ0FiWTFpV09RL01NLzVzNEIzeHdKWTRVdlAwakU1ZGZ5a2J1SmRXdjlpNWlmUERiL282aloKR1lBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
server: https://master.kube.teckids.org
name: Teckids_PROD
contexts:
- context:
cluster: Teckids_PROD
user: Teckids_PROD
name: Teckids_PROD
current-context: Teckids_PROD
kind: Config
preferences: {}
users:
- name: Teckids_PROD
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
args:
- oidc-login
- get-token
- --oidc-issuer-url=https://ticdesk.teckids.org
- --oidc-client-id=RcO3weOpSnlenZcBaBbXKFoNpElQzxjU0O20T273
- --oidc-extra-scope=openid,profile,http://aleksis.org/_/oidc/groups
command: kubectl
env: null
interactiveMode: IfAvailable
provideClusterInfo: false
Nutzung
Achtung: Du musst Verbindungen zu master.kube.teckids.org über den Jump-Host shuttlen!
z.B.sshuttle -r jump.teckids.org master.kube.teckids.org
Struktur
Die meisten Kubectl-Befehle arbeiten mit den gleichen Ressourcen:
$ kubectl get
You must specify the type of resource to get. Valid resource types include:
* all
* certificatesigningrequests (aka 'csr')
* clusterrolebindings
* clusterroles
* componentstatuses (aka 'cs')
* configmaps (aka 'cm')
* controllerrevisions
* cronjobs
* customresourcedefinition (aka 'crd')
* daemonsets (aka 'ds')
* deployments (aka 'deploy')
* endpoints (aka 'ep')
* events (aka 'ev')
* horizontalpodautoscalers (aka 'hpa')
* ingresses (aka 'ing')
* jobs
* limitranges (aka 'limits')
* namespaces (aka 'ns')
* networkpolicies (aka 'netpol')
* nodes (aka 'no')
* persistentvolumeclaims (aka 'pvc')
* persistentvolumes (aka 'pv')
* poddisruptionbudgets (aka 'pdb')
* podpreset
* pods (aka 'po')
* podsecuritypolicies (aka 'psp')
* podtemplates
* replicasets (aka 'rs')
* replicationcontrollers (aka 'rc')
* resourcequotas (aka 'quota')
* rolebindings
* roles
* secrets
* serviceaccounts (aka 'sa')
* services (aka 'svc')
* statefulsets (aka 'sts')
* storageclasses (aka 'sc')error: Required resource not specified.
Use "kubectl explain <resource>" for a detailed description of that resource (e.g. kubectl explain pods).
See 'kubectl get -h' for help and examples.
Diese Ressourcen lassen sich dann:
- auflisten mit get
- detailliert anzeigen mit describe
- löschen mit delete
Mit -n wird der Namespace angegeben:
kubectl get -n ticdesk pod
Nodes
- Nodes auflisten
kubectl get nodes --show-labels
Port-Forwarding
kubectl port-forward -n prometheus alertmanager-prometheus-0 :9093
Logs
- Neben Pod muss Komponente tomcat|Apache im Pod angegeben werden.
kubectl logs -n kube-system mein-pod -c mein-container
- für bestimmte apps
kubectl logs -n ticdesk -l app=aleksis | grep MON
- stern: Logs mehrerer Pods gemeinsam tailen
stern -n ticdesk -l app=aleksis --tail=1
stern ist ebenfalls auf dem Jumphost vorinstalliert
CronJobs
- CronJobs können manuell gestartet werden:
CRONJOB=mein-cronjob
kubectl -n default create job ${CRONJOB}-manually --from=cronjob/${CRONJOB}
Pods
-
Alle Pods auflisten, mit IP und Node:
kubectl get pods --all-namespaces -o wide -
Alle Pods auflisten, die auf einem bestimmten Node laufen:
kubectl --all-namespaces get pods --field-selector spec.nodeName=worker-1 -
Bei kubectl get kann -w angehängt werden. kubectl beendet sich dann nicht, sondern zeigt Änderungen an, wenn sie passieren:
kubectl get pods --all-namespaces -o wide -w -
Pod interaktiv starten und automatisch löschen
kubectl run curl --rm --stdin --attach --tty --restart=Never --image=tutum/curl -- /bin/bash -
Pod anhang einer IP finden
kubectl get --all-namespaces --output json pods | jq '.items[] | select(.status.podIP=="10.244.10.237")' | jq .metadata
Known Issues
Unauthorized
Das kann verschiedene Gründe haben:
- Die Uhr des Clients geht falsch, dadurch werden Access-Tokens nicht rechtzeitig erneuert
- In der kubeconfig ist der falsche OIDC-Issuer eingetragen
SealedSecrets
Damit wir Secrets einfach in git-Repositories speichern können, nutzen wir "SealedSecrets".
Die Verschlüsselung ist asymmetrisch: Auf dem Client kann ein Secret verschlüsselt und dann auch in einem Git-Repository gespeichert werden. Das verschlüsselte SealedSecret wird in den Cluster deployt, und dort vom Sealed-Secrets-Controllers entschlüsselt.
Installation
https://github.com/bitnami-labs/sealed-secrets?tab=readme-ov-file#kubeseal
Plaintext verschlüsseln
NAMESPACE=<Namespace>
CONTEXT=Teckids_PROD
(read -p 'Password: ' -r -s password && echo -n "${password}" | kubeseal --controller-namespace=flux-system --controller-name=sealed-secrets --context=$CONTEXT --raw --scope=namespace-wide --namespace=$NAMESPACE);echo
Dann das Passwort eingeben, und mit Enter bestätigen. Man erhält dann das verschlüsselte Secret.
Datei verschlüsseln
Es ist auch möglich, eine ganze Datei zu verschlüsseln:
NAMESPACE=<Namespace>
CONTEXT=Teckids_PROD
kubeseal --controller-namespace=flux-system --controller-name=sealed-secrets --context=$CONTEXT --raw --scope=namespace-wide --namespace=$NAMESPACE < $DATEI
Cluster und Namespace müssen hier mit dem Ziel übereinstimmen, sonst kann das Secret dort nicht entschlüsselt werden. Wenn es mehrere Ziel-Cluster/-Namespaces gibt, muss man das Secret mehrmals (einmal pro Cluster/Namespace) verschlüsseln.
Resource anlegen
Die Ausgabe von kubeseal fügt man dann in ein SealedSecrets-Objekt ein. Das wird dann innerhalb des Clusters verschlüsselt:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
annotations:
sealedsecrets.bitnami.com/namespace-wide: "true"
name: NAME
namespace: NAMESPACE
spec:
encryptedData:
A: [… verschlüsselter Wert]
Secret aus bestehender Resource editieren
NAMESPACE=<Namespace>
CONTEXT=Teckids_PROD
SECRET=<Secret-Name>
KEY=<Key aus encryptedData>
TEMPSEC=$(mktemp)
kubectl -n $NAMESPACE get secret -o json $SECRET | jq -r '.data."'"$KEY"'"' | base64 -d >"$TEMPSEC"
edit "$(TEMPSEC)"
kubeseal --controller-namespace=flux-system --controller-name=sealed-secrets --context=$CONTEXT --raw --scope=namespace-wide --namespace=$NAMESPACE < "$TEMPSEC"
shred -u "$TEMPSEC"
Services
Alle Services des Typs LoadBalancer bekommen automatisch eine private IP aus dem Netz 10.98.2.0/24. Eine öffentlich routbare IP bekommt man mit den passenden Annotations am Service:
"projectcalico.org/ipv4pools": '["loadbalancer-ip-pool-public-v4"]'
"projectcalico.org/ipv6pools": '["loadbalancer-ip-pool-public-v6"]'
Bei öffentlichen Services darauf achten, spec.ipFamilyPolicy auf RequireDualStack zu setzen, damit IPv6 funktioniert.
Gateway / HTTP(S)-Services
Config/Deployment: https://code.teckids.org/teckids/sysadmin-flux/src/branch/main/infrastructure/kgateway/gateways
Verschiedene Gateways
Wir haben aktuell 3 Gateways für verschiedene Anwendungsfälle:
- teckids-gateway: Für unsere Services. Dort sind bereits Wildcard-Zertifikate für unsere Domains hinterlegt
- aleksaas-gateway: AlekSaaS-Instanzen
- schulsupport-gateway: Schulsupport. Z.B. Greenlight
OIDC-Gateway
Beim teckids-gateway haben wir die Möglichkeit, HTTPRoutes entsprechend abzusichern, sodass man nur nach erfolgreichem TIC-Desk-Login zugreifen darf. Wir können auch eine bestimmte Gruppenmitgliedschaft voraussetzen.
Beispiel einer solchen TrafficPolicy: https://code.teckids.org/teckids/sysadmin-flux/src/branch/main/apps/rspamd/rspamd/gateway/ticdesk-oauth2-trafficpolicy.yaml
Das Webinterface von rspamd dürfen nur Mitglieder der Gruppe ag-sysadmin aufrufen.
Betriebsdokumentation
Controlplane
- 3 Debian-VMs (master-.kube.teckids.org)
- API-Verbindung über HAProxy auf den Firewalls (siehe Netzwerk-Stack)
- etcd auch in Kubernetes
Worker-Nodes
- Aktuell 12 Debian-VMs mit 6 Cores und 16G RAM (worker-1.kube.teckids.org - worker-12.kube.teckids.org)
Netzwerk
- Calico als CNI
- BGP-Full-Mesh zwischen allen Nodes und Peering mit den Firewalls: https://docs.tigera.io/calico/latest/networking/configuring/bgp
- Config: https://code.teckids.org/teckids/sysadmin-flux/src/branch/main/infrastructure/calico