S3 как том в Kubernetes
S3 CSI подключает бакет TatNet к поду как каталог через GeeseFS. Это подходит для датасетов, медиа и артефактов. Для файлов PostgreSQL и других приложений, которым нужны дисковые гарантии, используйте блочные тома.
Драйвер устанавливается платформой в managed Kubernetes. Пользователь создаёт бакет, ключ доступа, Secret и PVC. Пример ниже использует отдельный префикс в существующем бакете для каждого PVC: сам бакет драйвер не создаёт.
Подготовка
Нужны kubectl, Python 3, авторизованный CLI TatNet
и kubeconfig кластера, скачанный из панели. Проверьте подключение:
export KUBECONFIG="$HOME/.kube/tatnet.yaml"
chmod 600 "$KUBECONFIG"
kubectl get nodes
kubectl get csidriver ru.yandex.s3.csi
kubectl -n tatnet-s3-csi get pods
Узлы и поды драйвера должны быть готовы. Если CSIDriver отсутствует,
обратитесь в поддержку: создавать PVC до включения регионального аддона бессмысленно.
Создайте отдельный приватный бакет и ограниченный им ключ. Имя бакета должно быть свободным; замените пример своим значением:
export S3_BUCKET=my-kubernetes-data
umask 077
S3_KEY_DIR=$(mktemp -d)
tatnet s3 bucket create "$S3_BUCKET"
tatnet s3 key create kubernetes-s3 --bucket "$S3_BUCKET" -o json > "$S3_KEY_DIR/key.json"
python3 - "$S3_KEY_DIR" <<'PYTHON'
import json, pathlib, sys
p = pathlib.Path(sys.argv[1])
key = json.loads((p / "key.json").read_text())
(p / "accessKeyID").write_text(key["access_key_id"])
(p / "secretAccessKey").write_text(key["secret_access_key"])
(p / "key-id").write_text(key["id"])
PYTHON
kubectl create namespace s3-csi-demo
kubectl label namespace s3-csi-demo pod-security.kubernetes.io/enforce=restricted
kubectl -n s3-csi-demo create secret generic s3-credentials \
--from-file=accessKeyID="$S3_KEY_DIR/accessKeyID" \
--from-file=secretAccessKey="$S3_KEY_DIR/secretAccessKey" \
--from-literal=endpoint=https://kzn-1.s3.tatcloud.ru \
--from-literal=region=kzn-1
Секрет выдаётся один раз. Сохраните его в своём менеджере секретов, а UUID
из файла key-id — для последующего отзыва. Затем удалите локальные файлы
с парой ключей:
rm "$S3_KEY_DIR/key.json" "$S3_KEY_DIR/accessKeyID" "$S3_KEY_DIR/secretAccessKey"
Не публикуйте Secret и kubeconfig в Git. В Kubernetes Secret доступен тем, кто может читать его или запускать поды в этом namespace: выдавайте такие права только доверенным пользователям. Ключ ограничен бакетом, а не префиксом; разные PVC в одном бакете не являются границей безопасности между арендаторами.
StorageClass и PVC
Сохраните как s3-volume.yaml, заменив REPLACE_BUCKET именем своего бакета.
Role разрешает CSI-контроллеру прочитать только один указанный Secret.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: s3-provisioner
namespace: s3-csi-demo
rules:
- apiGroups: [""]
resources: [secrets]
resourceNames: [s3-credentials]
verbs: [get]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: s3-provisioner
namespace: s3-csi-demo
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: s3-provisioner
subjects:
- kind: ServiceAccount
name: controller
namespace: tatnet-s3-csi
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: tatnet-s3-demo
annotations:
storageclass.kubernetes.io/is-default-class: "false"
provisioner: ru.yandex.s3.csi
reclaimPolicy: Retain
volumeBindingMode: Immediate
allowVolumeExpansion: false
parameters:
bucket: REPLACE_BUCKET
mounter: geesefs
options: "--no-systemd --list-type 2 --no-specials --fsync-on-close --memory-limit 128 --read-ahead-large 20480 --dir-mode 0770 --file-mode 0660 --uid 1000 --gid 1000"
csi.storage.k8s.io/provisioner-secret-name: s3-credentials
csi.storage.k8s.io/provisioner-secret-namespace: s3-csi-demo
csi.storage.k8s.io/node-stage-secret-name: s3-credentials
csi.storage.k8s.io/node-stage-secret-namespace: s3-csi-demo
csi.storage.k8s.io/node-publish-secret-name: s3-credentials
csi.storage.k8s.io/node-publish-secret-namespace: s3-csi-demo
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: data
namespace: s3-csi-demo
spec:
storageClassName: tatnet-s3-demo
accessModes: [ReadWriteMany]
resources:
requests:
storage: 1Gi
kubectl apply -f s3-volume.yaml
kubectl -n s3-csi-demo get pvc data --watch
Дождитесь Bound, затем завершите наблюдение через Ctrl+C.
1Gi нужен для учёта Kubernetes: это не квота и не резервирование места
в S3. Объём бакета не ограничивается этим значением; хранение и трафик
оплачиваются по фактическому использованию. Ограничение числа бакетов аккаунта
продолжает действовать. Класс не становится классом по умолчанию.
Подключение к приложению и проверка
Сохраните как s3-app.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: s3-check
namespace: s3-csi-demo
spec:
replicas: 1
selector:
matchLabels: {app: s3-check}
template:
metadata:
labels: {app: s3-check}
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 1000
seccompProfile: {type: RuntimeDefault}
containers:
- name: check
image: busybox:1.37.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
command: [sh, -ec, 'sleep infinity']
volumeMounts:
- {name: data, mountPath: /data}
volumes:
- name: data
persistentVolumeClaim:
claimName: data
kubectl apply -f s3-app.yaml
kubectl -n s3-csi-demo rollout status deployment/s3-check --timeout=180s
kubectl -n s3-csi-demo exec deployment/s3-check -- sh -ec \
'dd if=/dev/urandom of=/data/check.bin bs=1M count=32; sync; sha256sum /data/check.bin'
S3_PV=$(kubectl -n s3-csi-demo get pvc data -o jsonpath='{.spec.volumeName}')
kubectl get pv "$S3_PV" -o jsonpath='{.spec.csi.volumeHandle}{"\n"}'
Сохраните SHA-256. В volumeHandle указан бакет и префикс PVC; найдите
check.bin под этим префиксом в файловом менеджере S3, скачайте его и сравните
SHA-256. Это проверяет появление объекта в S3 независимо от кеша монтирования.
Пересоздайте под и сравните контрольную сумму ещё раз:
kubectl -n s3-csi-demo rollout restart deployment/s3-check
kubectl -n s3-csi-demo rollout status deployment/s3-check --timeout=180s
kubectl -n s3-csi-demo exec deployment/s3-check -- sha256sum /data/check.bin
ReadWriteMany позволяет подключать PVC с нескольких узлов. Для проверки
увеличьте число реплик до двух и убедитесь через kubectl get pods -o wide,
что они оказались на разных узлах; при необходимости задайте pod anti-affinity.
Пишите в разные файлы и проверяйте суммы с обоих подов. RWX не даёт
распределённых блокировок и согласованного кеша при одновременной записи
в один файл.
Подключение существующего префикса
Если данные уже лежат в бакете, вместо динамического PVC создайте статические
PV и PVC. Замените REPLACE_BUCKET/existing-prefix реальным путём. Secret
из подготовки должен давать доступ к этому бакету.
apiVersion: v1
kind: PersistentVolume
metadata:
name: tatnet-s3-existing
spec:
capacity: {storage: 1Gi}
accessModes: [ReadWriteMany]
persistentVolumeReclaimPolicy: Retain
storageClassName: ""
claimRef: {namespace: s3-csi-demo, name: existing}
csi:
driver: ru.yandex.s3.csi
volumeHandle: REPLACE_BUCKET/existing-prefix
nodeStageSecretRef: {name: s3-credentials, namespace: s3-csi-demo}
nodePublishSecretRef: {name: s3-credentials, namespace: s3-csi-demo}
volumeAttributes:
mounter: geesefs
options: "--no-systemd --list-type 2 --no-specials --fsync-on-close --memory-limit 128 --read-ahead-large 20480 --dir-mode 0770 --file-mode 0660 --uid 1000 --gid 1000"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: existing
namespace: s3-csi-demo
spec:
volumeName: tatnet-s3-existing
storageClassName: ""
accessModes: [ReadWriteMany]
resources:
requests: {storage: 1Gi}
В приложении замените claimName: data на claimName: existing. Значение
volumeHandle должно быть уникальным в кластере для этого подключения.
Для статического подключения Role и RoleBinding контроллера не нужны.
Ошибки и ограничения
PVC Pending: проверьтеkubectl -n s3-csi-demo describe pvc data, имя бакета, RoleBinding и доступ контроллера к нужному Secret.ContainerCreatingили ошибка монтирования: посмотрите события пода и логи node-плагина на том же узле в namespacetatnet-s3-csi.AccessDenied: проверьте ограничение ключа бакетом и корректность Secret. Не заменяйте ключ глобальным только ради устранения ошибки.--no-systemdобязателен на Talos.--list-type 2включает S3 List V2,--no-specialsотключает нестандартные специальные файлы.--fsync-on-closeожидает загрузки при закрытии файла. Приложение должно проверять ошибки записи и закрытия; для явной фиксации используйтеfsync.- GeeseFS работает внутри node-плагина: сбой узла или перезапуск плагина может
оборвать mount. При плановой замене плагина остановите потребителей на узле,
замените плагин и пересоздайте потребителей для повторного монтирования.
Автоматический rollout плагина отключён политикой
OnDelete. - Путь к S3 для managed-узлов проходит через региональный gateway endpoint. Используйте обычный HTTPS endpoint и проверку TLS; отдельный пользовательский private endpoint для этого примера создавать не требуется.
Удаление теста
Сначала остановите потребителей и сохраните имя PV:
S3_PV=$(kubectl -n s3-csi-demo get pvc data -o jsonpath='{.spec.volumeName}')
kubectl -n s3-csi-demo delete deployment s3-check
kubectl -n s3-csi-demo delete pvc data
kubectl get pv "$S3_PV"
PV перейдёт в Released. Политика Retain сохраняет объекты: убедитесь,
что они ещё доступны через S3, прежде чем удалять их вручную. Повторное
создание PVC с тем же именем не подключит старый префикс автоматически.
После проверки удалите тестовые объекты через файловый менеджер S3 или
S3-клиент. В выделенном для теста бакете можно удалить всё; в общем бакете
удаляйте только проверенный префикс. Если создавали статический PVC,
отдельно удалите его и tatnet-s3-existing; его объекты также сохранятся.
kubectl delete pv "$S3_PV"
kubectl delete storageclass tatnet-s3-demo
kubectl delete namespace s3-csi-demo
tatnet s3 key delete "$(cat "$S3_KEY_DIR/key-id")" --yes
tatnet s3 bucket delete "$S3_BUCKET" --yes
rm "$S3_KEY_DIR/key-id"
rmdir "$S3_KEY_DIR"
Удаление PV, Secret или кластера само по себе не удаляет сохранённые S3-объекты и не прекращает начисления за их хранение. Отзывайте ключ после остановки всех использующих его приложений.