Saltar al contenido principal

Velero (vista previa)

lionbackup se está convirtiendo en un destino de almacenamiento para Velero, la herramienta de backup para Kubernetes. Para ello existe un plugin que registra lionbackup como proveedor de BackupStorageLocation, y un pequeño controlador en el clúster que sube cada backup completado como un único archivo cifrado a un proyecto de lionbackup. Ambos están disponibles como beta 0.2.0-beta.1: para probar en un clúster de pruebas, todavía no como su único backup.

Vista previa: aún sin datos de volúmenes

Lo que llega a lionbackup es el spool: manifiestos de Kubernetes, metadatos y logs del backup. El contenido de los volúmenes (PVC) todavía no se respalda; un PVC vuelve como definición, pero vacío. Siga confiando en otra vía para los datos de volúmenes.

Qué hace hoy la vista previa, y qué no​

Funciona hoyFalta todavía
Velero funciona por completo contra un spool en el clúster: backup y restauración de manifiestos, sync, GC, backup deleteDatos de volúmenes: el contenido de los PVC no se captura (el node-agent de Velero solo conoce s3, azure, gcs y sistema de archivos)
URLs firmadas: velero backup logs y describe --details funcionan, los backups terminan CompletedImportación de restauración: el paquete vuelve al spool desde fuera del clúster (cliente con token de lectura)
Subida: el controlador empaqueta backups/<nombre>/ en un solo .lbk, lo sube con un token de escritura y anota el backup con el file_idJobs de captura y restauración de volúmenes; reintentos solo como "otra vez en 10 minutos"

Un backup Failed significa que no se escribió nada; normalmente el propietario del directorio del spool es incorrecto (véase el paso 1). Un PartiallyFailed solo ocurre cuando falta el controlador o el secreto de URL compartido (paso 4).

Probarlo​

Necesita un clúster de pruebas, kubectl, la CLI de Velero (probado con Velero 1.18.3), un proyecto de lionbackup con un token de escritura y el cliente de lionbackup en su estación de trabajo para el par de claves. La imagen del plugin se puede descargar sin credenciales.

1. Crear el directorio del spool​

Velero escribe en un directorio del nodo que se monta en el pod de Velero como hostPath (o como PVC). La imagen oficial de Velero se ejecuta con el ID de usuario 1002; fsGroup no se aplica a un hostPath, así que el directorio debe pertenecer a ese ID. De lo contrario, cada backup falla con permission denied aunque informe de todos los objetos como respaldados; el error solo aparece en status.failureReason.

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

2. Instalar Velero con el 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 es correcto: el plugin en sí no necesita credenciales. El token de escritura va en el Secret del controlador (paso 4), nunca en la BackupStorageLocation.

3. Montar el spool en el pod de 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

Sin este paso, el spool vive en el sistema de archivos del pod y desaparece tras el siguiente reinicio. La BackupStorageLocation debería informar Available después.

4. Controlador y Secret​

El controlador se ejecuta con la misma imagen como usuario 1002, con el spool montado en solo lectura y el Secret en /etc/lionbackup. Necesita tres cosas: el token de escritura del proyecto, un secreto de URL aleatorio que comparte con el pod de Velero y la clave pública para el cifrado. Genere el par de claves en su estación de trabajo; la clave privada nunca entra en el clúster y sin ella no hay restauración.

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

Guarde el siguiente manifiesto como controller.yaml. Contiene ServiceAccount, Role, RoleBinding, Service y Deployment; el directorio de trabajo /work debe poder alojar el directorio de backup más grande del 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

Después aplíquelo, entregue el secreto de URL también al deployment de Velero y registre proyecto y zona en la 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

Sin token o clave, el controlador solo registra en el log lo que subiría (observe-only). Otras claves config de la BackupStorageLocation: lbCompressionMethod (ZSTD), lbCompressionLevel (5), lbChunksizeMb (64), lbUploadRateLimitMbit (0 = ilimitado).

Las URLs firmadas apuntan al Service del controlador dentro del clúster, así que velero backup logs y describe --details funcionan allí donde ese Service es alcanzable. Si la CLI de Velero se ejecuta fuera del clúster, reenvíe el puerto (kubectl -n velero port-forward svc/lionbackup-velero-controller 8080:8080) y ponga controllerURL en la BackupStorageLocation a http://localhost:8080; la firma cubre solo la ruta y la caducidad, no el 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"}'

Se espera Completed, y velero backup logs devuelve el log a través de la URL firmada del controlador. Poco después, el objeto Backup lleva la anotación lionbackup.cloud/file-id: el identificador del archivo subido en el proyecto, el mismo que muestra el --list del cliente. Si la subida falla, el motivo está en el log del controlador, que lo reintenta pasados diez minutos.

6. Restauración desde lionbackup​

El camino de vuelta empieza fuera del clúster, con un token de lectura y la clave privada; así ambos se mantienen lejos del clúster. El cliente descarga el paquete, usted copia el directorio del backup al spool del clúster de destino y Velero lo recoge en la siguiente sincronización de la 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

Los manifiestos, deployments, ConfigMaps y definiciones de PVC vuelven. Los datos de los volúmenes no; ese es el límite documentado de la vista previa.

Qué viene después​

La siguiente etapa respalda el contenido de los volúmenes: un job por PVC en el nodo del pod transmite el volumen como archivo al spool, un job de restauración correspondiente lo rellena de vuelta y un init container retiene la aplicación hasta que el volumen vuelve a estar. A ello se suma un endpoint del controlador que recupera un paquete por file_id directamente al spool. Hasta entonces se aplica la nota al principio de esta página.