대량 매니페스트를 kubectl apply -f site.yaml 로 적용할 때 일부 Deployment 에서만 패치 생성이 실패한다.
error: error when applying patch:
...
for: "site.yaml": error when patching "site.yaml":
doesn't match $setElementOrder list:
또는 서버 사이드 적용에서 이렇게 나온다.
Error from server: failed to create typed patch object
(<ns>/<name>; apps/v1, Kind=Deployment): .spec.template.spec...
kubectl apply 는 전략적 병합 패치를 만들 때 목록의 순서를 보존하려고 $setElementOrder 지시자를 함께 만든다. env 나 imagePullSecrets 처럼 이름을 키로 병합하는 목록에 같은 이름이 두 번 이상 들어 있으면, 순서 목록과 실제 목록이 대응되지 않아 패치 생성 자체가 실패한다.
쿠버네티스는 이 목록들을 배열이 아니라 맵처럼 다루므로 같은 키가 중복될 수 없다.
적용하려는 매니페스트에서 중복 키를 찾는다.
yq '.spec.template.spec.containers[].env[].name' <manifest>.yaml | sort | uniq -d
yq '.spec.template.spec.imagePullSecrets[].name' <manifest>.yaml | sort | uniq -d
여러 문서가 이어진 파일이면 대상 리소스만 잘라 낸 뒤 본다.
yq 'select(.kind=="Deployment" and .metadata.name=="<name>")' site.yaml > one.yaml
중복을 하나로 합친다.
imagePullSecrets:
- name: <pull-secret>
같은 name 을 가진 env 항목이 둘이면 뒤쪽 값만 남긴다. 값이 서로 다르면 어느 쪽이 맞는지 판단해야 하므로 생성기 쪽을 함께 고쳐야 재발하지 않는다.
이미 리소스가 꼬여 있으면 apply 대신 교체하거나 지우고 다시 만든다.
kubectl -n <namespace> replace -f fixed.yaml
중복은 손으로 쓴 YAML 보다 생성기가 만든 매니페스트에서 더 자주 생긴다. kustomize 오버레이가 같은 항목을 두 번 더하거나, 다운그레이드·롤백 과정에서 새 버전이 넣어 둔 항목이 남은 채 옛 버전 매니페스트가 다시 얹히는 경우다.
특히 버전 다운그레이드는 이 오류의 단골이다. 새 버전에서 추가된 env · volumeMount · initContainer 가 클러스터에 남아 있는데 옛 버전 매니페스트에는 그 항목이 없어, 병합 결과가 어긋난다. 다운그레이드는 매니페스트만 되돌리는 것으로 끝나지 않으므로 대상 리소스를 지우고 다시 만드는 쪽이 안전하다.
env 를 비롯한 컨테이너 필드 구조.