Backup mein Kubernetes - aber sicher!
Mit einiger Zeit sammelt sich so einiges im Kubernetes Cluster an. Sachen, die man nicht sauber mit Helm installiert hat. Oder die man irgendwie mal zwischendurch zum Ausprobieren geaendert und dann doch so belassen hat. Uns graust es schon vorm Datenverlust. Wie kann man sowas alles backupen?
Wie wir vielleicht schon mitgekriegt haben, liegen die meisten Resourcen in Kubernetes in YAML-Files vor, Damit wurden zumindest die meisten Sachen erzeugt - sei es als HELM Chart oder einfaches Deployment-File - local oder uebers Netz, einerlei.
Schmutzig, klein, genial ist Kube-Backup, ein handliches Backup-Tool fuer Kubernetes.
Voraussetzungen sei wieder unser laufender Minikube, es funktioniert aber mit jedem anderen Kubernetes.
Zielbild ist es, unsere Cluster-Konfiguration auf Github zu sichern. Immer und immer wieder. Wie machen wir das? Mit einem Cronjob! Also automatisch.
Als erstes erstellen wir ein SSH-Schluesselpaar auf unserem, Minikuberechner:
Shell
ssh-keygen -f ./id_rsa | |
ssh-keyscan github.com > known_hosts |
Dann erstellen wir ein neues Github-Repository. Gerne auch private, denn das ist neuerdings erlaubt. Unter Deploykeys fuegen wir den frisch erstellten SSH-Public-Key unseres Minikuberechners id_rsa.pub hinzu.
Vom SSH-Private-Key und die known_hosts erstellen wir eine Secret im Kubernetes:
Shell
kubectl create secret generic kube-backup-ssh -n kube-system --from-file=id_rsa --from-file=known_hosts |
Wenn wir schon dabei sind, erstellen wir noch mit gpg --generate-key
den GPG-Key und auch daraus ein Secret:
Shell
gpg --export-secret-key -a root@minikube > gpg-private.key | |
kubectl create secret generic kube-backup-gpg -n kube-system --from-file=gpg-private.key |
Das Git-Repo mit unserer Kubernetes ist mit git-crypt entsprechend zu preparieren:
Shell
cd git/minikube | |
apt install git-crypt | |
git-crypt init | |
echo "*.secret.yaml filter=git-crypt diff=git-crypt" > .gitattributes | |
git-crypt add-gpg-user eumel@eumel.gnuu.de | |
git-crypt add-gpg-user root@minikube |
Den GPG-Public-Key vom Minikube-Rechner muss ich erstmal von seinem GPG-Schluesselbund exportieren:
Shell
gpg --armor --output minikube.asc --export root@minikube |
und in meinem persoenlichen GPG-Schluesselbund importieren:
Shell
gpg --import minikube.asc |
Um den Schluessel wie oben mit git-crypt add-gpg-user root@minikube
hinzufuegen zu koennen, muss ich diesem noch vertrauen:
Shell
gpg --edit-key root@minikube | |
>trust | |
5 | |
y |
Zurueck zum Kubernetes-Cluster. Dort gibts erstmal ein ClusterRoleBinding zu deployen:
rbac.yaml
kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: kube-backup-reader rules: - apiGroups: - '*' resources: - '*' verbs: ["get", "list"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: kube-backup namespace: default subjects: - kind: ServiceAccount name: kube-backup namespace: kube-system roleRef: kind: ClusterRole name: kube-backup-reader apiGroup: rbac.authorization.k8s.io
Shell
kubectl apply -f rbac.yaml |
Nun das Herzstueck, der CronJob:
cronjob-ssh.yaml
apiVersion: v1 kind: ServiceAccount metadata: name: kube-backup namespace: kube-system --- apiVersion: batch/v1beta1 kind: CronJob metadata: name: kube-state-backup namespace: kube-system labels: app: kube-backup spec: schedule: "*/5 * * * *" concurrencyPolicy: Replace successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 jobTemplate: spec: template: metadata: labels: app: kube-backup name: kube-backup spec: containers: # - image: quay.io/plange/kube-backup:1.12.0-1 - image: eumel8/kube-backup:latest imagePullPolicy: Always name: backup resources: {} securityContext: runAsUser: 1000 env: - name: GIT_REPO value: "git@github.com:eumel8/minikube.git" - name: GITCRYPT_ENABLE value: "true" - name: RESOURCETYPES value: "ingress deployment configmap secret pvc svc rc ds thirdpartyresource networkpolicy statefulset storageclass cronjob" volumeMounts: - mountPath: /backup/ name: cache - mountPath: /backup/.ssh name: sshkey - mountPath: /secrets name: gpgkey dnsPolicy: ClusterFirst terminationGracePeriodSeconds: 30 serviceAccountName: kube-backup volumes: - name: sshkey secret: defaultMode: 420 secretName: kube-backup-ssh - name: gpgkey secret: defaultMode: 420 secretName: kube-backup-gpg - name: cache emptyDir: {} restartPolicy: OnFailure
Ein kurzer Blick auf das File, bevor wir es deployen.
Ich habe das Repo geforked und ein eigenes Image gebaut und auf Dockerhub gepusht. Das geht mit ein paar Klicks, wenn man einen Gihub- und einen Dockerhub-Account hat. Im Dockerfile kann man schauen, was im Image alles drin ist. Ich habe Alpine und git-crypt aktualisiert. Auch eine neuere Version von kubectl ist drin. Ob es das richtige File aus dem Internet ist, wird mit einer sha256 Pruefsumme ueberprueft. So kann uns niemand Schadsoftware unterschieben.
Das entrypoint.sh ist die Arbeitslogik. Dort habe ich mal ein paar Debug-Marker gesetzt, als es auf den ersten Versuch nicht funktionierte. Sollte aber wieder alles wie im Original sein und veranschaulichen, wie so ein Dockerfile bzw. Docker-Image fuer Kubernetes funktioniert.
Jetzt aber los:
Shell
kubectl apply -f cronjob-ssh.yaml |
Wenn alles klappt, sollte in unserem Github-Repository mit der Kubernetes-Config etwas passieren.
commit d685ad67d808842258622a6f07326b5c0c3f2a56 Author: kube-backup <kube-backup@example.com> Date: Fri Oct 4 18:23:32 2019 +0000 Automatic backup at Fri Oct 4 18:23:32 UTC 2019
Auf der Github-Webseite sollten wir nur Datensalat bei den Secrets sehen (bin). Im Klartext (also base64 Inhalt) sehen wir die Dateien auf unserem eigenen Rechner und auf dem Minikube-Rechner wegen git-crypt.
Im Cluster sehen wir sowas wie
Shell
# kubectl get jobs -n kube-system | |
NAME COMPLETIONS DURATION AGE | |
kube-state-backup-1570216080 1/1 29s 3m3s | |
kube-state-backup-1570216140 1/1 29s 2m3s | |
kube-state-backup-1570216200 1/1 29s 63s | |
kube-state-backup-1570216260 0/1 3s 3s |
Sicherlich keine perfekte Loesung, aber eine automatisierte. Ueber die Variable RESOURCETYPES kann ich noch steuern, welche Resourcetypen ich backupen moechte. Eine kleine Auswahl ist schon dabei.
Credits gehen an https://github.com/pieterlange | https://www.fullstaq.com | https://twitter.com/fullstaq