Zum Hauptinhalt springen

Velero (Vorschau)

lionbackup wird ein Speicherziel für Velero, das Backup-Werkzeug für Kubernetes. Dazu gibt es ein Plugin, das Velero als BackupStorageLocation-Anbieter registriert, und einen kleinen Controller im Cluster, der jedes abgeschlossene Backup als eine verschlüsselte Datei in ein lionbackup-Projekt hochlädt. Beides steht als Beta 0.2.0-beta.1 bereit: zum Ausprobieren im Testcluster, noch nicht als einzige Sicherung.

Vorschau: noch keine Volumendaten

Was bei lionbackup ankommt, ist der Spool: Kubernetes-Manifeste, Metadaten und Logs des Backups. Die Inhalte von Volumes (PVC) werden noch nicht gesichert; ein PVC kommt als Definition zurück, aber leer. Verlassen Sie sich für Volumendaten weiterhin auf einen anderen Weg.

Was die Vorschau heute tut, und was nicht​

Funktioniert heuteFehlt noch
Velero läuft vollständig gegen einen Spool im Cluster: Backup und Wiederherstellung von Manifesten, Sync, GC, backup deleteVolumendaten: PVC-Inhalte werden nicht erfasst (Veleros node-agent kennt nur s3, azure, gcs und Dateisystem)
Signierte URLs: velero backup logs und describe --details funktionieren, Backups enden CompletedRestore-Import: das Bundle kommt außerhalb des Clusters zurück in den Spool (Client mit Read-Token)
Upload: der Controller bündelt backups/<Name>/ zu einer .lbk, lädt sie mit einem Write-Token hoch und vermerkt die file_id am BackupCapture- und Restore-Jobs für Volumes; Wiederholung nur „in 10 Minuten noch einmal“

Ein Failed beim Backup heißt, dass nichts geschrieben wurde; meist stimmt dann der Eigentümer des Spool-Verzeichnisses nicht (siehe Schritt 1). Ein PartiallyFailed tritt nur noch auf, wenn der Controller oder das gemeinsame URL-Geheimnis fehlt (Schritt 4).

Ausprobieren​

Sie brauchen einen Testcluster, kubectl, die Velero-CLI (getestet mit Velero 1.18.3), ein lionbackup-Projekt mit einem Write-Token und den lionbackup-Client auf Ihrem Arbeitsplatz für das Schlüsselpaar. Das Plugin-Image ist ohne Anmeldung abrufbar.

1. Spool-Verzeichnis anlegen​

Velero schreibt in ein Verzeichnis auf dem Knoten, das als hostPath (oder als PVC) in den Velero-Pod eingehängt wird. Das offizielle Velero-Image läuft unter der Benutzerkennung 1002; auf einem hostPath wirkt fsGroup nicht, das Verzeichnis muss dieser Kennung also gehören. Andernfalls scheitert jedes Backup mit permission denied, meldet aber trotzdem alle Objekte als gesichert; der Fehler steht nur in status.failureReason.

mkdir -p /var/lib/lionbackup/spool
chown -R 1002:1002 /var/lib/lionbackup/spool
chmod 775 /var/lib/lionbackup/spool

2. Velero mit dem Plugin installieren​

velero install \
--provider lionbackup.cloud/lionbackup \
--plugins git.prod.lionbackup.cloud/lionbackup/velero-plugin-lionbackup:0.2.0-beta.1 \
--bucket spool \
--no-secret \
--use-volume-snapshots=false \
--backup-location-config spoolPath=/var/lib/lionbackup/spool \
--wait

--no-secret ist richtig: Das Plugin selbst braucht keine Zugangsdaten. Das Write-Token gehört in das Secret des Controllers (Schritt 4), nie an die BackupStorageLocation.

3. Spool in den Velero-Pod einhängen​

kubectl -n velero patch deployment velero --type=json -p '[
{"op":"add","path":"/spec/template/spec/volumes/-","value":{"name":"lionbackup-spool","hostPath":{"path":"/var/lib/lionbackup/spool","type":"DirectoryOrCreate"}}},
{"op":"add","path":"/spec/template/spec/containers/0/volumeMounts/-","value":{"name":"lionbackup-spool","mountPath":"/var/lib/lionbackup/spool"}}
]'
kubectl -n velero rollout status deploy/velero --timeout=180s
kubectl -n velero get backupstoragelocation default

Ohne diesen Schritt lebt der Spool im Dateisystem des Pods und ist beim nächsten Neustart weg. Die BackupStorageLocation sollte danach Available melden.

4. Controller und Secret​

Der Controller läuft im selben Image, als Benutzer 1002, mit dem Spool schreibgeschützt und dem Secret unter /etc/lionbackup. Er braucht drei Dinge: das Write-Token des Projekts, ein zufälliges URL-Geheimnis, das er mit dem Velero-Pod teilt, und den öffentlichen Schlüssel für die Verschlüsselung. Das Schlüsselpaar erzeugen Sie auf Ihrem Arbeitsplatz; der private Schlüssel kommt nie in den Cluster, ohne ihn gibt es keinen Restore.

lionbackup --generate-key --key-name ./velero
kubectl -n velero create secret generic lionbackup-velero \
--from-literal=token=<WRITE-TOKEN> \
--from-literal=url-secret="$(head -c 32 /dev/urandom | base64)" \
--from-file=key.pub=./velero.pub

Speichern Sie das folgende Manifest als controller.yaml. Es enthält ServiceAccount, Role, RoleBinding, Service und Deployment; der Arbeitsbereich /work muss das größte Backup-Verzeichnis des Spools aufnehmen können.

---
apiVersion: v1
kind: ServiceAccount
metadata:
name: lionbackup-velero-controller
namespace: velero
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: lionbackup-velero-controller
namespace: velero
rules:
- apiGroups: ["velero.io"]
resources: ["backups"]
verbs: ["get", "list", "watch", "patch"]
- apiGroups: ["velero.io"]
resources: ["backupstoragelocations"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: lionbackup-velero-controller
namespace: velero
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: lionbackup-velero-controller
subjects:
- kind: ServiceAccount
name: lionbackup-velero-controller
namespace: velero
---
apiVersion: v1
kind: Service
metadata:
name: lionbackup-velero-controller
namespace: velero
spec:
selector:
app.kubernetes.io/name: lionbackup-velero-controller
ports:
- name: http
port: 8080
targetPort: http
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: lionbackup-velero-controller
namespace: velero
labels:
app.kubernetes.io/name: lionbackup-velero-controller
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app.kubernetes.io/name: lionbackup-velero-controller
template:
metadata:
labels:
app.kubernetes.io/name: lionbackup-velero-controller
spec:
serviceAccountName: lionbackup-velero-controller
securityContext:
runAsUser: 1002
runAsGroup: 1002
runAsNonRoot: true
containers:
- name: controller
image: git.prod.lionbackup.cloud/lionbackup/velero-plugin-lionbackup:0.2.0-beta.1
command: ["/plugins/velero-plugin-lionbackup"]
args: ["controller"]
ports:
- name: http
containerPort: 8080
env:
- name: VELERO_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: LIONBACKUP_SPOOL_PATH
value: /var/lib/lionbackup/spool
- name: LIONBACKUP_KEYFILE
value: /etc/lionbackup/key.pub
- name: LIONBACKUP_WORKDIR
value: /work
- name: LIONBACKUP_TOKEN
valueFrom:
secretKeyRef:
name: lionbackup-velero
key: token
- name: LIONBACKUP_VELERO_URL_SECRET
valueFrom:
secretKeyRef:
name: lionbackup-velero
key: url-secret
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 3
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
cpu: "2"
memory: 1Gi
volumeMounts:
- name: lionbackup-spool
mountPath: /var/lib/lionbackup/spool
readOnly: true
- name: lionbackup-secret
mountPath: /etc/lionbackup
readOnly: true
- name: work
mountPath: /work
volumes:
- name: lionbackup-spool
hostPath:
path: /var/lib/lionbackup/spool
type: Directory
- name: lionbackup-secret
secret:
secretName: lionbackup-velero
items:
- key: key.pub
path: key.pub
- name: work
emptyDir:
sizeLimit: 10Gi

Danach anwenden, das URL-Geheimnis auch dem Velero-Deployment geben und Projekt und Zone an der BackupStorageLocation hinterlegen:

kubectl apply -f controller.yaml
kubectl -n velero set env deployment/velero \
LIONBACKUP_VELERO_URL_SECRET="$(kubectl -n velero get secret lionbackup-velero -o jsonpath='{.data.url-secret}' | base64 -d)"
kubectl -n velero patch backupstoragelocation default --type=merge -p \
'{"spec":{"config":{"lbProject":"<PROJECT-UUID>","lbZone":"de01-1","lbEnvironment":"prod"}}}'
kubectl -n velero rollout status deploy/velero --timeout=180s
kubectl -n velero rollout status deploy/lionbackup-velero-controller --timeout=180s

Ohne Token oder Schlüssel meldet der Controller im Log nur, was er hochladen würde (observe-only). Weitere config-Schlüssel der BackupStorageLocation: lbCompressionMethod (ZSTD), lbCompressionLevel (5), lbChunksizeMb (64), lbUploadRateLimitMbit (0 = unbegrenzt).

Die signierten URLs zeigen auf den Controller-Service im Cluster. velero backup logs und describe --details funktionieren deshalb dort, wo dieser Service erreichbar ist. Läuft die Velero-CLI außerhalb des Clusters, leiten Sie den Port weiter (kubectl -n velero port-forward svc/lionbackup-velero-controller 8080:8080) und setzen controllerURL in der BackupStorageLocation auf http://localhost:8080; die Signatur deckt nur Pfad und Ablaufzeit ab, nicht den Host.

5. Backup​

velero backup create demo-1 --include-namespaces demo-app --wait
velero backup logs demo-1 | tail -n 3
kubectl -n velero get backup demo-1 \
-o jsonpath='{.status.phase} {.metadata.annotations.lionbackup\.cloud/file-id}{"\n"}'

Erwartet ist Completed, und velero backup logs liefert das Protokoll über die signierte URL des Controllers. Kurz darauf trägt das Backup-Objekt die Annotation lionbackup.cloud/file-id: die Kennung der hochgeladenen Datei im Projekt, dieselbe, die --list des Clients zeigt. Scheitert der Upload, steht der Grund im Log des Controllers, und er versucht es nach zehn Minuten erneut.

6. Wiederherstellung aus lionbackup​

Der Weg zurück beginnt außerhalb des Clusters, mit einem Read-Token und dem privaten Schlüssel; beides bleibt so dem Cluster fern. Der Client holt das Bundle, Sie kopieren das Backup-Verzeichnis in den Spool des Zielclusters, und Velero nimmt es beim nächsten Sync der BackupStorageLocation auf:

lionbackup --config read.yaml --list
lionbackup --config read.yaml --restore <FILE-ID> --identity ./velero.key --target ./restored
# ./restored/…/backups/demo-1/ -> <spoolPath>/spool/backups/demo-1/ des Zielclusters
velero restore create demo-restore --from-backup demo-1 --wait

Manifeste, Deployments, ConfigMaps und PVC-Definitionen kommen zurück. Die Daten in den Volumes kommen nicht zurück; das ist die dokumentierte Grenze der Vorschau.

Wie es weitergeht​

Die nächste Stufe sichert die Inhalte von Volumes: ein Job je PVC auf dem Knoten des Pods streamt das Volume als Archiv in den Spool, ein passender Restore-Job füllt es zurück, und ein Init-Container hält die Anwendung an, bis das Volume wieder da ist. Dazu kommt ein Endpunkt des Controllers, der ein Bundle per file_id direkt in den Spool zurückholt. Bis dahin gilt der Hinweis am Anfang dieser Seite.