Passa al contenuto principale

Velero (anteprima)

lionbackup sta diventando una destinazione di storage per Velero, lo strumento di backup per Kubernetes. A questo scopo esiste un plugin che registra lionbackup come provider di BackupStorageLocation, e un piccolo controller nel cluster che carica ogni backup completato come un unico file cifrato in un progetto lionbackup. Entrambi sono disponibili come beta 0.2.0-beta.1: da provare in un cluster di test, non ancora come unico backup.

Anteprima: ancora nessun dato dei volumi

Ciò che arriva a lionbackup è lo spool: manifest Kubernetes, metadati e log del backup. Il contenuto dei volumi (PVC) non viene ancora salvato; un PVC torna come definizione, ma vuoto. Per i dati dei volumi continuate ad affidarvi a un'altra via.

Cosa fa oggi l'anteprima, e cosa no​

Funziona oggiManca ancora
Velero funziona completamente contro uno spool nel cluster: backup e ripristino dei manifest, sync, GC, backup deleteDati dei volumi: il contenuto dei PVC non viene catturato (il node-agent di Velero conosce solo s3, azure, gcs e file system)
URL firmati: velero backup logs e describe --details funzionano, i backup terminano CompletedImport del ripristino: il bundle torna nello spool dall'esterno del cluster (client con token di lettura)
Upload: il controller raggruppa backups/<nome>/ in un solo .lbk, lo carica con un token di scrittura e annota il backup con il file_idJob di cattura e ripristino dei volumi; nuovi tentativi solo come "di nuovo tra 10 minuti"

Un backup Failed significa che non è stato scritto nulla; di solito il proprietario della directory di spool è sbagliato (vedi passo 1). Un PartiallyFailed si verifica solo se manca il controller o il segreto URL condiviso (passo 4).

Provarlo​

Servono un cluster di test, kubectl, la CLI di Velero (testata con Velero 1.18.3), un progetto lionbackup con un token di scrittura e il client lionbackup sulla propria postazione per la coppia di chiavi. L'immagine del plugin si scarica senza credenziali.

1. Creare la directory di spool​

Velero scrive in una directory del nodo, montata nel pod di Velero come hostPath (o come PVC). L'immagine ufficiale di Velero gira con l'ID utente 1002; fsGroup non si applica a un hostPath, quindi la directory deve appartenere a quell'ID. Altrimenti ogni backup fallisce con permission denied pur segnalando tutti gli oggetti come salvati; l'errore compare solo in status.failureReason.

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

2. Installare Velero con il plugin​

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 è corretto: il plugin in sé non ha bisogno di credenziali. Il token di scrittura va nel Secret del controller (passo 4), mai sulla BackupStorageLocation.

3. Montare lo spool nel pod di Velero​

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

Senza questo passo lo spool vive nel file system del pod e sparisce al riavvio successivo. La BackupStorageLocation dovrebbe poi riportare Available.

4. Controller e Secret​

Il controller gira nella stessa immagine come utente 1002, con lo spool montato in sola lettura e il Secret in /etc/lionbackup. Gli servono tre cose: il token di scrittura del progetto, un segreto URL casuale condiviso con il pod di Velero e la chiave pubblica per la cifratura. Generate la coppia di chiavi sulla vostra postazione; la chiave privata non entra mai nel cluster e senza di essa non c'è ripristino.

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

Salvate il manifest seguente come controller.yaml. Contiene ServiceAccount, Role, RoleBinding, Service e Deployment; la directory di lavoro /work deve poter contenere la directory di backup più grande dello spool.

---
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

Poi applicatelo, passate il segreto URL anche al deployment di Velero e impostate progetto e zona sulla BackupStorageLocation:

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

Senza token o chiave il controller registra nel log solo ciò che caricherebbe (observe-only). Altre chiavi config della BackupStorageLocation: lbCompressionMethod (ZSTD), lbCompressionLevel (5), lbChunksizeMb (64), lbUploadRateLimitMbit (0 = illimitato).

Gli URL firmati puntano al Service del controller nel cluster, quindi velero backup logs e describe --details funzionano dove quel Service è raggiungibile. Se la CLI di Velero gira fuori dal cluster, inoltrate la porta (kubectl -n velero port-forward svc/lionbackup-velero-controller 8080:8080) e impostate controllerURL sulla BackupStorageLocation a http://localhost:8080; la firma copre solo percorso e scadenza, non l'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"}'

È atteso Completed, e velero backup logs restituisce il log tramite l'URL firmato del controller. Poco dopo l'oggetto Backup porta l'annotazione lionbackup.cloud/file-id: l'identificativo del file caricato nel progetto, lo stesso mostrato da --list del client. Se l'upload fallisce, il motivo è nel log del controller, che riprova dopo dieci minuti.

6. Ripristino da lionbackup​

La via del ritorno inizia fuori dal cluster, con un token di lettura e la chiave privata; entrambi restano così lontani dal cluster. Il client scarica il bundle, copiate la directory del backup nello spool del cluster di destinazione e Velero la raccoglie alla sincronizzazione successiva della BackupStorageLocation:

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

Manifest, deployment, ConfigMap e definizioni dei PVC tornano. I dati nei volumi no; è il limite documentato dell'anteprima.

Cosa viene dopo​

La fase successiva salva il contenuto dei volumi: un job per PVC sul nodo del pod trasmette il volume come archivio nello spool, un job di ripristino corrispondente lo riempie di nuovo e un init container trattiene l'applicazione finché il volume non è di nuovo disponibile. A ciò si aggiunge un endpoint del controller che riporta un bundle tramite file_id direttamente nello spool. Fino ad allora vale la nota all'inizio di questa pagina.