Блог

Як перемістити або змінити налаштування PersistentVolume в k8s?

September 30, 2022

Одразу попереджаю, що інформація з цього посту може не працювати для деяких класів сторейджів.

В k8s робота зі сторейджем складається з трьох складових:

  • опис самого сторейджа за допомогою PersistentVolume (PV). Цей об’єкт описує тип сторейджа і вказує k8s як взаємодіяти зі сторейджем. Задача цього об’єкта абстрагувати k8s від деталей імплементації того чи іншого сторейджа в тому чи іншому клауді і т.д.
  • опис PersistentVolumeClaim (PVC). Цей об’єкт вже описує вимоги до сторейджа на рівні вашого аплікейшена.
  • об’явити прив’язку до стореджа на рівні конфігурації pod (поле .spec.volume) і далі вказати в якому контейнері і за яким шляхом замаунтити цей вольюм (.spec.containers.volumeMounts).

В цілому взаємодія зі стором мало чим відрізняється від взаємодії з іншими об’єктами k8s, але є один нюанс. Справа в тому, що деякі поля PersistentVolume імутабельні. Через це ви можете отримати помилки “field is immutable”:

{
  "error": {
    "kind": "Status",
    "apiVersion": "v1",
    "metadata": {},
    "status": "Failure",
    "message": "PersistentVolume \"pvc-b6be57df-83ec-4a69-98cf-1153f1b72448\" is invalid: nodeAffinity: Invalid value: core.VolumeNodeAffinity{Required:(*core.NodeSelector)(0xf695f60)}: field is immutable",
    "reason": "Invalid",
    "details": {
      "name": "pvc-b6be57df-83ec-4a69-98cf-1153f1b72448",
      "kind": "PersistentVolume",
      "causes": [
        {
          "reason": "FieldValueInvalid",
          "message": "Invalid value: core.VolumeNodeAffinity{Required:(*core.NodeSelector)(0xf695f60)}: field is immutable",
          "field": "nodeAffinity"
        }
      ]
    },
    "code": 422
  },
  "messages": [
    "PersistentVolume \"pvc-b6be57df-83ec-4a69-98cf-1153f1b72448\" is invalid: nodeAffinity: Invalid value: core.VolumeNodeAffinity{Required:(*core.NodeSelector)(0xf695f60)}: field is immutable"
  ],
  "isUsedForNotification": false
}

Отже у вас можуть виникати проблеми у випадку коли ви захочете змінити namespace або nodeAffinity PV (наприклад, коли ви змінили назву ноди k8s).

Перш ніж почати робити будь-які дії, краще зробити бекап даних, а також заборонити k8s видаляти PV. Це можно зробити додавши поле .spec.persistentVolumeReclaimPolicy: 'Retain' в специфікацію VP або за допомогою команди:

kubectl patch pv <PV_name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'

Наступним кроком треба вигрузити специфікації для усіх потрібних PVC:

kubectl get pvc <PVC_name> -o yaml > PVC_name.yaml

Після цього можна відв’язати PVC від PV. Для цього треба відредагувати кожний PV:

kubectl edit pv <PV_name>

і там видаляємо два поля: .spec.claimRef.uid та .spec.claimRef.resourceVersion. Після цього всі PV перейдуть у статус Released.

Тепер потрібно видалити всі PVC:

kubectl delete pvc <PVC_name> --force

Можливо вам ще потрібно буде видалити і пов’язані з ними поди. Це пов’язано з захистом через finalizers: [kubernetes.io/pvc-protection] в конфігруації PVC. Альтернативно можна відредагувати PVC і прибрати звідти фіналайзер.

Якщо вам потрібно відредагувати не тільки PersistentVolumeClaim, а ще й PersistentVolume (наприклад, nodeAffinity), то тоді вигрузити конфіг PV і після цього видалити PV з кластера:

kubectl get pv <PV_name> -o yaml > PV_name.yaml
kubectl delete pv <PV_name> --force

Наступним кроком вносимо потрібні правки в yaml, які ми вигрузили на попередніх етапах. Якщо ваша мета змінити назву ноди, зверніть увагу також на анотацію volume.kubernetes.io/selected-node в PV. Далі приміняємо ці файли на кластері:

kubectl apply -f PV_name.yaml PVC_name.yaml

# або з неймспейсом

kubectl apply -f PV_name.yaml PVC_name.yaml -n new_namespace

Після цього k8s має автоматично з’єднати нові PersistentVolume з відповідним PersistentVolumeClaim і відповідно PV отримає статус Bound. Статус PV можна перевірити наступною командою:

kubectl get pv <PV_name>