kubectl edit и immutable поля Pod
Что у живого Pod можно поменять, что нельзя и как обойти ограничение.
Что делает kubectl edit
Pod - наименьшая единица, которую запускает Kubernetes. Внутри Pod работает один или несколько контейнеров. spec - это раздел манифеста, который описывает желаемое состояние объекта.
kubectl edit <kind>/<name> берёт текущий spec из API, открывает его
во временном файле в редакторе и после выхода отправляет изменения
обратно. Если ничего не поменял, команда тихо завершается. Если
поменял, изменения уходят на API-сервер и проходят проверку.
Это три шага, собранные в одну команду:
kubectl get pod web -o yaml > /tmp/pod.yaml
vi /tmp/pod.yaml
kubectl apply -f /tmp/pod.yaml
Редактор по умолчанию - vi. Сменить его можно так:
export KUBE_EDITOR=nano
kubectl edit pod web
Карта: что меняется, что нет
Сначала общая картина, ниже разберём по пунктам.
Можно обычным edit | Только через отдельный путь | Нельзя, нужно пересоздать |
|---|---|---|
image контейнера | resources (subresource resize) | nodeSelector |
labels, annotations | serviceAccountName | |
activeDeadlineSeconds | initContainers, volumes | |
tolerations (добавление) | securityContext |
Что можно менять у запущенного Pod
Когда Pod уже создан, API-сервер разрешает менять лишь узкий набор полей. Главное исключение - образ контейнера.
spec.containers[*].image менять можно. kubelet скачает новый
образ и перезапустит контейнер. Сам Pod при этом остаётся, новый Pod
не создаётся.
kubectl set image pod/web app=nginxinc/nginx-unprivileged:1.27
kubectl set image правит образ напрямую, без открытия редактора.
Формат: pod/<имя> <контейнер>=<образ>. Имя контейнера здесь - app,
не имя образа. Посмотреть имена контейнеров можно так:
kubectl get pod web -o jsonpath='{.spec.containers[*].name}'
Кроме image на живом Pod можно менять ещё несколько полей:
metadata.labels, metadata.annotations,
spec.activeDeadlineSeconds и spec.tolerations (только добавление).
Что менять нельзя
Большинство полей Pod после создания неизменяемы (immutable). При попытке их поправить валидатор вернёт ошибку.
spec.containers[*].resources: обычнымeditнельзя, но есть штатный путь через subresourceresize(см. ниже).spec.serviceAccountName: нельзя.spec.nodeSelector: нельзя.spec.initContainers: нельзя.spec.volumes,spec.securityContext: нельзя.
Попытка вызовет ошибку, и kubectl предложит сохранить твою версию во временный файл:
error: pods "web" is invalid:
spec: Forbidden: pod updates may not change fields other than
`spec.containers[*].image`, ...
Это сделано специально. kubelet уже запустил контейнер с конкретными настройками. Если поменять их на лету, манифест говорил бы одно, а контейнер делал другое.
Ресурсы меняются на лету: subresource resize
С Kubernetes 1.35 CPU и память живого Pod меняются без пересоздания. Это
называется in-place resize, и работает через отдельный subresource
resize, а не через обычный update.
# обычный путь по-прежнему запрещён
kubectl patch pod web --type=json \
-p '[{"op":"replace","path":"/spec/containers/0/resources/limits/cpu","value":"200m"}]'
# spec: Forbidden: pod updates may not change fields other than ...
# а так лимит меняется у работающего Pod
kubectl patch pod web --subresource resize --type=json \
-p '[{"op":"replace","path":"/spec/containers/0/resources/limits/cpu","value":"200m"}]'
# pod/web patched
Результат смотрят через kubectl get pod web -o jsonpath='{.spec.containers[0].resources}'.
Kubernetes сообщает о ходе операции условиями PodResizePending и
PodResizeInProgress в статусе Pod: часть изменений применяется мгновенно,
часть требует, чтобы на ноде нашлось место.
Правило «Pod почти неизменяем» это не отменяет. Resize касается только
ресурсов. Сменить nodeSelector, volumes или serviceAccountName живому
Pod по-прежнему нельзя.
Как поменять то, что immutable
Ресурсы разобрали выше, они меняются через resize. Для остальных
неизменяемых полей путь один: пересоздать Pod. Для этого есть
kubectl replace --force:
replace --force не правит Pod, а подменяет его. Старый объект удаляется
целиком, новый создаётся с тем же именем и всем остальным новым.
kubectl get pod web -o yaml > /tmp/pod.yaml
# правишь нужное поле в файле
kubectl replace --force -f /tmp/pod.yaml
--force означает: удали старый Pod и создай новый из файла. Это
короткая запись для двух команд:
kubectl delete pod web
kubectl apply -f /tmp/pod.yaml
Имя у Pod сохранится, но это полностью новый объект. Новый UID (внутренний идентификатор объекта), новый IP, сброшенные счётчики. Pod не обновлён, он удалён и создан заново.
Почему в проде Pod без контроллера не запускают
Deployment - объект, который управляет набором одинаковых Pod и умеет обновлять их без простоя. Когда меняешь шаблон в Deployment, он не правит существующие Pod, а создаёт новые на замену. Поэтому у Deployment можно менять и image, и resources, и nodeSelector:
kubectl set image deploy/app app=nginx:1.27
В проде приложения держат под Deployment, чтобы обновления шли через плавную замену, а не через удаление и пересоздание вручную.
Домашка
Теорию закрепляет задание на своём кластере: 2 шага с проверкой. Кластер поднимается по треку Kubernetes локально, задания лежат в репозитории k8s-practice.
./run.sh start k8s-edit-pod # развернуть стартовое состояние
./run.sh verify k8s-edit-pod # проверить текущий шаг
Коротко
kubectl editэто три шага в одном: получить YAML, править в редакторе, применить diff.- У живого Pod меняются
image, labels, annotations,activeDeadlineSecondsи добавлениеtolerations. nodeSelector,volumes,initContainers,serviceAccountName,securityContextimmutable, ошибка выглядит какspec: Forbidden.- CPU и память с 1.35 меняются через
kubectl patch --subresource resize. - Для остальных immutable-полей нужен
kubectl replace --force, это delete + create с новым UID и IP. - У Deployment таких ограничений нет: он заменяет Pod новыми по шаблону.
Прошли материал до конца? Отметьте урок пройденным.
Прогресс сохранён в этом браузере
Почистишь историю или откроешь с телефона, и отметки пропадут. Оставь почту, они переедут в аккаунт. Пароль придумывать не нужно.
Готово. теперь в аккаунте: прогресс виден с любого устройства. Мой прогресс