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.
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 heute | Fehlt noch |
|---|---|
Velero läuft vollständig gegen einen Spool im Cluster: Backup und Wiederherstellung von Manifesten, Sync, GC, backup delete | Volumendaten: 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 Completed | Restore-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 Backup | Capture- 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.