Velero - это программное обеспечение с открытым исходным кодом, предназначенное для создания резервных копий, восстановления после сбоев и миграции ресурсов Kubernetes-кластера, включая постоянные хранилища (PV). Решение входит в экосистему продуктов VMware — VMware Tanzu, ориентированных на использование в production-среде и упрощающих автоматизацию администрирования Kubernetes-кластеров. Velero распространяется по лицензии Apache License 2.0.
Созданные резервные копии могут применяться для миграции инфраструктуры, восстановления состояния кластера на определённый момент времени, а также для клонирования тестовых или рабочих окружений.
Процесс установки и эксплуатации Velero относительно простой, однако при его использовании необходимо учитывать несколько особенностей. В частности, Velero сохраняет резервные копии манифестов в S3-совместимом хранилище. Для этого можно использовать как облачный S3-bucket, так и развернуть собственное хранилище, например MinIO.
Требования:
- Готовый кластер k8s
- Minio хранилище или Ceph
- Docker или nerdctl
- Установлен Helm
- Репозиторий образов и Helm чартов: Harbor
Задача:
- Развернуть средство для бэкапирования на базе “Velero”
- Настроить расписание автоматического создания бэкапов
Как я вижу архитектуру резервного копирования для кластера Kubernetes в текущем гайде:

Шаг 1. Онлайн установка Velero
Здесь всё просто и нетривиально. Настраиваете свой S3 например MinIO.
1
2
| helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm repo update
|
Создаём файл с переменными:
1
| nano velero-values.yaml
|
Передаёте в файле переменных имя бакета, адрес сервера с MinIO, токен
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| credentials:
useSecret: true
secretContents:
cloud: |
[default]
aws_access_key_id=Keyid_ot_Minio
aws_secret_access_key=Access_ot_Minio
configuration:
backupStorageLocation:
- name: default
provider: aws
bucket: velero
default: true
accessMode: ReadWrite
config:
region: minio
s3ForcePathStyle: "true"
s3Url: https://s3.minio.domain.local
# если MinIO с самоподписанным TLS и ты не хочешь CA можно так:
# insecureSkipTLSVerify: "true"
# Если есть ЦА СЮДА добавить строку если нет удалить и активировать верхний параметр insecureSkipTLSVerify
caCert: LS0tLS1CRUdJTiBDRVJ.......................................
volumeSnapshotLocation: []
# Чтобы бэкапить содержимое PVC включаем node-agent daemonset
deployNodeAgent: true
nodeAgent:
disableHostPath: false
podVolumePath: /var/lib/kubelet/pods
pluginVolumePath: /var/lib/kubelet/plugins
|
И запускаете установку
1
| helm install velero vmware-tanzu/velero -n velero --create-namespace -f velero-values.yaml
|
Шаг 2. Оффлайн установка Velero
В данном сценарии мы самостоятельно загрузим каждый образ в наш приватный репозиторий на основе Harbor.
1
2
3
| helm repo add vmware-tanzu https://vmware-tanzu.github.io/helm-charts
helm repo update
helm pull vmware-tanzu/velero --untar
|
Переходим в директорию:
Выгружаем список образов:
1
| helm template velero ../velero -f values.yaml > rendered.yaml
|
Выгружаем образы в файл:
1
2
| grep image: rendered.yaml | awk '{print $2}' | sort -u > velero-images.txt
grep -oE 'image:\s*"?[^"]+' rendered.yaml | sed -E 's/^image:\s*"?//; s/"$//' | sort -u > velero-images.txt
|
Также нужно забрать плагин для работы с нашим Minio S3
Актуальную версию можно узнать так:
1
| cat values.yaml | grep velero/velero-plugin-for-aws
|
Вывод:
1
| # image: velero/velero-plugin-for-aws:v1.13.1
|
Открываем файл:
Добавить строку. У вас тег будет свой:
1
| docker.io/velero/velero-plugin-for-aws:v1.13.1
|
Выкачиваем:
1
2
3
| for img in $(cat velero-images.txt); do
docker pull $img
done
|
Сохраняем:
1
| docker save -o velero-images.tar $(cat velero-images.txt)
|
Упаковываем HELM чарт:
1
2
| cd ~
tar -cvf velero.tgz velero
|
Переносим в закрытый контур.
Шаг 2.1. Загрузка пакетов в приватный репозиторий
Распаковываем:
1
| tar -xvf /home/admin/velero.tgz
|
Достать архив с образами:
1
2
| mv velero/cm-images.txt ./
mv velero/velero.tgz ./
|
Удаляем мусор:
1
| rm velero/rendered.yaml
|
Упаковываем чарт:
1
2
| helm package ./velero -d .
helm push velero-*.tgz oci://harbor.domain.local/helm
|
Загрузить образы:
1
| sudo docker load -i velero-images.tar
|
Далее я создал проект с именем platform в Harbor.

Изменяйте имя репозитория и проект в переменных на свои:
1
2
3
4
5
6
7
8
9
| export HARBOR="harbor.domain.local"
export PROJECT="platform"
docker login ${HARBOR}
while read -r img; do
new="${HARBOR}/${PROJECT}/${img}"
docker tag "${img}" "$new"
docker push "$new"
done < velero-images.txt
|
Создаём ns и секрет, если Harbor с авторизацией. Если нет, то пропускаем:
1
2
3
4
5
6
| kubectl create ns velero
kubectl -n velero create secret docker-registry harbor-pull \
--docker-server=harbor.k8s.domain.local \
--docker-username='<USER>' \
--docker-password='<PASS>' \
--docker-email='devnull@example.com'
|
Шаг 2.2. Настройка Minio хранилища
В Minio создаём пользователя с именем velero-s3 и токен, если они ещё не созданы и bucker с именем velero-backup
Полученный токен и ключ будем использовать далее в файле.
Если хотите использовать Minio внутри кубернетес через https, то преобразуйте сертификат от ЦА, которым подписан MinIO в Base64:
1
| cat ca.crt | base64 -w0
|
Получится строка вида:
1
| LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
|
И добавьте в строку в файле:
1
| nano velero-values.yaml
|
Файл с переменными:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
| image:
repository: harbor.domain.local/platform/docker.io/velero/velero
imagePullSecrets:
- harbor-pull
kubectl:
image:
repository: harbor.domain.local/platform/docker.io/bitnamilegacy/kubectl
tag: "1.33"
initContainers:
- name: velero-plugin-for-aws
image: harbor.domain.local/platform/docker.io/velero/velero-plugin-for-aws:v1.13.1
volumeMounts:
- mountPath: /target
name: plugins
credentials:
useSecret: true
# создаст secret внутри velero namespace с ключом "cloud" :contentReference[oaicite:6]{index=6}
# Полученный ранее токен от MinIO вставляем здесь в aws...
secretContents:
cloud: |
[default]
aws_access_key_id=Keyidddddddddd_ot_Minio
aws_secret_access_key=Accesskeyyyy_ot_Minio
configuration:
backupStorageLocation:
- name: default
provider: aws
bucket: velero
default: true
accessMode: ReadWrite
config:
region: minio
s3ForcePathStyle: "true"
s3Url: https://s3.minio.domain.local
# если MinIO с самоподписанным TLS и ты не хочешь CA можно так:
# insecureSkipTLSVerify: "true"
# Если есть ЦА СЮДА добавить строку если нет удалить и активировать верхний параметр insecureSkipTLSVerify
caCert: LS0tLS1CRUdJTiBDRVJ.......................................
volumeSnapshotLocation: []
# Чтобы бэкапить содержимое PVC включаем node-agent daemonset
deployNodeAgent: true
nodeAgent:
disableHostPath: false
podVolumePath: /var/lib/kubelet/pods
pluginVolumePath: /var/lib/kubelet/plugins
|
Шаг 2.3. Запускаем Offline установку Velero
1
| helm install velero oci://harbor.domain.local/helm/velero -n velero --create-namespace -f velero-values.yaml
|
После того, как поды поднялись проверяем синхронизацию с Minio:
1
| kubectl -n velero get backupstoragelocation
|
Ожидаем:
1
2
| NAME PHASE LAST VALIDATED AGE DEFAULT
default Available 19s 26m true
|
Если недоступен, то смотрим ошибки здесь:
1
| kubectl -n velero describe backupstoragelocation default
|
И можно посмотреть логи:
1
| kubectl logs deployment/velero -n velero.
|
Шаг 3. Тестовый бэкап
Создаём файл:
Внутри создаём тестовый namespace и configmap:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| apiVersion: v1
kind: Namespace
metadata:
name: velero-test
---
apiVersion: v1
kind: ConfigMap
metadata:
name: velero-test-config
namespace: velero-test
data:
test: "ok"
owner: "velero"
note: "restore-me"
|
Применяем и смотрим что создался Namespace:
1
2
| kubectl apply -f test-ns.yaml
kubectl -n velero-test get configmap
|
Создаём файл бэкапа:
Указываем имя бэкапа и namespace, который будем бэкапить:
1
2
3
4
5
6
7
8
9
| apiVersion: velero.io/v1
kind: Backup
metadata:
name: velero-test-backup
namespace: velero
spec:
includedNamespaces:
- velero-test
ttl: 24h0m0s
|
Применяем:
1
| kubectl apply -f backup-test.yaml
|
Проверяем, чтобы статус создания бэкапа был Completed

1
| kubectl -n velero get backups.velero.io velero-test-backup -o jsonpath='{.status.phase}{"\n"}'
|
Удаляем Namespace
1
| kubectl delete ns velero-test
|
Дожидаемся удаления и проверяем
1
| kubectl get ns velero-test
|
Создаём файл запроса восстановления:
И восстанавливаем из бэкапа:
1
2
3
4
5
6
7
| apiVersion: velero.io/v1
kind: Restore
metadata:
name: velero-test-restore
namespace: velero
spec:
backupName: velero-test-backup
|
Применяем:
1
| kubectl apply -f test-restore.yaml
|
Проверяем:
1
2
| kubectl get ns velero-test
kubectl -n velero-test get configmap
|
Тест можно считать успешным, если:
- backup velero-test-backup перешёл в Completed
- namespace velero-test удалился
- после restore namespace velero-test появился снова
- ConfigMap velero-test-config вернулся
Шаг 4. Пример задания (cronjob) на бэкап
Для начала должны быть включены node-agent без них не будут бэкапиться PVC с нод. Это мы сделали при первоначальной установке Velero.
1
| kubectl -n velero get pods
|

Создаём задание на бэкапирование:
1
| nano prom-graf-backup.yaml
|
Каждый день в 2 часа ночи создавать бэкап namespace Monitoring в котором у меня живут Promethues, Grafana, Alertmanager со всеми PVC данными и хранить 7 дней.
Прописываю это в файле
1
2
3
4
5
6
7
8
9
10
11
12
| apiVersion: velero.io/v1
kind: Schedule
metadata:
name: prom-graf-daily
namespace: velero
spec:
schedule: "0 2 * * *"
template:
includedNamespaces:
- monitoring
defaultVolumesToFsBackup: true
ttl: 168h
|
Применяем:
1
| kubectl apply -f prom-graf-backup.yaml
|
Проверка:
1
| kubectl -n velero get schedules
|
Ожидаем:

Готово. Задание на бэкапы создано. Установка и настройка Velero завершены
