LabHub
배우기 러닝패스 코스

GitOps and Argo CD

My Value Keeps Reverting: Finding the Field's Owner

LabHub 에서 이어서 보기

한국어 원문으로 표시합니다.

목표

kwok 클러스터에서 서버측 적용으로 관리자 둘을 만들고 managedFields 를 직접 읽는다. 충돌과 강제, 필드 놓아 주기, scale 하위 자원의 충돌까지 실물로 일으켜 보고, 마지막에 Argo CD 의 무시 규칙과 이어 붙인다.

왜 중요한가

GitOps 를 켜도 클러스터의 객체를 쓰는 주체는 하나가 아니다. Argo CD 가 쓰고, 오토스케일러가 replicas 를 쓰고, 웹훅이 컨테이너를 끼워 넣고, 사람이 급할 때 kubectl 로 쓴다. 서버측 적용은 '누가 이 필드의 주인인가' 를 API 서버가 기록으로 들고 있는 장치다. 이 기록을 읽을 줄 알아야 세 가지 질문에 답할 수 있다 — 왜 내 적용이 거절되는가, 왜 강제로 밀어 넣으면 다음에 상대가 거절당하는가, 왜 값을 놓아 줬는데 옛 값으로 돌아가지 않는가. 그리고 이 기록은 Argo CD 쪽으로 이어진다. 무시 규칙의 managedFieldsManagers 는 필드 경로가 아니라 이 소유 기록으로 비교 대상을 고르기 때문이다.

단계

  1. kwok 클러스터에 네임스페이스 ga-ssa 를 만들고, /root/ga-ssa/deploy.yaml 에 Deployment web 을 쓰세요 — spec.replicas 2, 셀렉터와 라벨은 app: web, 컨테이너 하나(web, 이미지 nginx:1.25) 입니다. kubectl apply --server-side --field-manager=gitops 로 적용하세요.
  2. kubectl -n ga-ssa get deploy web -o json --show-managed-fields 로 소유 기록을 읽어, 관리자마다 한 줄씩 <관리자> | <연산> 형식으로 /root/ga-ssa/managers.txt 에 저장하세요. gitopsApply 연산으로 들어 있어야 합니다.
  3. /root/ga-ssa/hotfix.yaml 에 같은 Deployment 를 쓰되 spec.replicas 는 5, 이미지는 nginx:1.27 로 하세요. --field-manager=hotfix 로 서버측 적용을 시도하고, 실패 출력을 표준오류까지 합쳐 /root/ga-ssa/conflict.txt 에 저장하세요. --force-conflicts 는 아직 쓰지 않습니다.
  4. 같은 적용을 --force-conflicts 와 함께 다시 하고, 성공 출력을 /root/ga-ssa/force.txt 에 저장하세요. 이제 클러스터의 이미지는 nginx:1.27 이고, 그 필드의 주인은 hotfix 입니다.
  5. /root/ga-ssa/hotfix-slim.yaml 을 만드세요 — hotfix.yaml 과 같지만 spec.replicas 줄이 없습니다. --field-manager=hotfix 로 서버측 적용한 뒤, spec.replicas 를 소유한 관리자 목록을 /root/ga-ssa/release.txt 에 저장하세요(아무도 없으면 빈 파일). 클러스터의 replicas 가 몇이 되는지 확인하세요.
  6. /root/ga-ssa/web-csa.yaml 에 Deployment web-csa 를 쓰고(같은 네임스페이스, 라벨과 컨테이너 이름은 web-csa, 이미지 nginx:1.25) --server-side 없이 평범한 kubectl apply 로 올리세요. 두 Deployment(webweb-csa)의 metadata.annotations 키 목록을 /root/ga-ssa/csa.txt 에 저장하세요.
  7. /root/ga-ssa/scale-deploy.yaml 에 Deployment web-scale(레플리카 2, 라벨과 컨테이너 이름 web-scale, 이미지 nginx:1.25)을 쓰고 --field-manager=gitops 로 서버측 적용하세요. 그다음 kubectl -n ga-ssa scale deploy web-scale --replicas=4 로 늘리고, 같은 파일을 같은 관리자 이름으로 다시 적용해 보세요. 실패 출력을 /root/ga-ssa/scale-conflict.txt 에 저장합니다.
  8. /root/ga-ssa/live.yaml 에 클러스터의 web소유 기록까지 포함해 YAML 로 저장하세요(-o yaml --show-managed-fields). /root/ga-ssa/argocd-cm.yaml 에 키 resource.customizations.ignoreDifferences.apps_Deployment 를 두고 managedFieldsManagershotfix, jsonPointers/spec/replicas 를 적으세요. argocd admin settings resource-overrides ignore-differences /root/ga-ssa/live.yaml --argocd-cm-path /root/ga-ssa/argocd-cm.yaml 의 출력을 /root/ga-ssa/ignore.txt 에 저장하세요.

참고

이름을 밝히고 적용한다

kwok 클러스터에 네임스페이스 ga-ssa 를 만들고, /root/ga-ssa/deploy.yaml 에 Deployment web 을 쓰세요 — spec.replicas 2, 셀렉터와 라벨은 app: web, 컨테이너 하나(web, 이미지 nginx:1.25) 입니다. kubectl apply --server-side --field-manager=gitops 로 적용하세요.

--field-manager 는 '이 변경을 누가 했는가' 를 API 서버에 알려 주는 이름입니다. 서버측 적용은 이 이름별로 어떤 필드를 소유하는지 기록해 둡니다 — 클라이언트측 적용에는 없는 개념입니다.

소유 기록을 읽는다

kubectl -n ga-ssa get deploy web -o json --show-managed-fields 로 소유 기록을 읽어, 관리자마다 한 줄씩 <관리자> | <연산> 형식으로 /root/ga-ssa/managers.txt 에 저장하세요. gitopsApply 연산으로 들어 있어야 합니다.

이 옵션을 빼면 .metadata.managedFields 가 null 로 나옵니다 — kubectl 이 기본으로 감추기 때문입니다. jq 로 배열을 돌면서 manager 와 operation 을 뽑으세요.

두 번째 관리자가 같은 필드를 쓰려 하면

/root/ga-ssa/hotfix.yaml 에 같은 Deployment 를 쓰되 spec.replicas 는 5, 이미지는 nginx:1.27 로 하세요. --field-manager=hotfix 로 서버측 적용을 시도하고, 실패 출력을 표준오류까지 합쳐 /root/ga-ssa/conflict.txt 에 저장하세요. --force-conflicts 는 아직 쓰지 않습니다.

서버는 '이 필드는 다른 사람이 들고 있다' 며 거절하고, 어떤 필드인지 목록으로 알려 줍니다. 이 거절이 없으면 두 자동화가 서로의 값을 조용히 덮어쓰게 됩니다. 정답지에서 실패로 멈추지 않게 조심하세요.

소유권을 빼앗는다

같은 적용을 --force-conflicts 와 함께 다시 하고, 성공 출력을 /root/ga-ssa/force.txt 에 저장하세요. 이제 클러스터의 이미지는 nginx:1.27 이고, 그 필드의 주인은 hotfix 입니다.

강제는 충돌을 없애는 것이 아니라 소유권을 옮기는 것입니다. 옛 주인은 그 필드에서 빠지므로, 다음번에 옛 주인이 자기 값을 다시 적용하면 이번에는 그쪽이 충돌을 만납니다 — 자동화 둘이 강제를 번갈아 쓰면 끝나지 않는 싸움이 됩니다.

필드를 놓아 주면 값은 주인에게 돌아가지 않는다

/root/ga-ssa/hotfix-slim.yaml 을 만드세요 — hotfix.yaml 과 같지만 spec.replicas 줄이 없습니다. --field-manager=hotfix 로 서버측 적용한 뒤, spec.replicas 를 소유한 관리자 목록을 /root/ga-ssa/release.txt 에 저장하세요(아무도 없으면 빈 파일). 클러스터의 replicas 가 몇이 되는지 확인하세요.

매니페스트에서 필드를 빼면 그 관리자는 소유 목록에서 그 필드를 놓습니다. 그런데 값은 옛 주인의 값으로 돌아가지 않습니다 — 아무도 소유하지 않는 필드는 API 기본값이 됩니다. 소유자 목록은 jq 로 .fieldsV1."f:spec"."f:replicas" 가 null 이 아닌 항목을 고르면 됩니다.

클라이언트측 적용은 무엇을 남기나

/root/ga-ssa/web-csa.yaml 에 Deployment web-csa 를 쓰고(같은 네임스페이스, 라벨과 컨테이너 이름은 web-csa, 이미지 nginx:1.25) --server-side 없이 평범한 kubectl apply 로 올리세요. 두 Deployment(webweb-csa)의 metadata.annotations 키 목록을 /root/ga-ssa/csa.txt 에 저장하세요.

클라이언트측 적용은 '내가 지난번에 보낸 매니페스트' 를 주석 하나에 통째로 넣어 두고 그것과 비교해 무엇을 지울지 정합니다. 그래서 소유자 개념이 없고, 두 도구가 같은 객체를 다루면 서로의 필드를 지웁니다. 서버측으로 올린 쪽에는 그 주석이 없습니다.

kubectl scale 은 다른 문으로 들어온다

/root/ga-ssa/scale-deploy.yaml 에 Deployment web-scale(레플리카 2, 라벨과 컨테이너 이름 web-scale, 이미지 nginx:1.25)을 쓰고 --field-manager=gitops 로 서버측 적용하세요. 그다음 kubectl -n ga-ssa scale deploy web-scale --replicas=4 로 늘리고, 같은 파일을 같은 관리자 이름으로 다시 적용해 보세요. 실패 출력을 /root/ga-ssa/scale-conflict.txt 에 저장합니다.

kubectl scale 은 객체 전체가 아니라 scale 이라는 하위 자원에 씁니다. 그래서 소유 기록에 관리자 kubectl 과 하위 자원 scale 이 따로 남고, 충돌 메시지도 그 사실을 알려 줍니다. 오토스케일러가 만드는 충돌이 정확히 이 모양입니다.

소유자 이름으로 비교에서 빼기

/root/ga-ssa/live.yaml 에 클러스터의 web소유 기록까지 포함해 YAML 로 저장하세요(-o yaml --show-managed-fields). /root/ga-ssa/argocd-cm.yaml 에 키 resource.customizations.ignoreDifferences.apps_Deployment 를 두고 managedFieldsManagershotfix, jsonPointers/spec/replicas 를 적으세요. argocd admin settings resource-overrides ignore-differences /root/ga-ssa/live.yaml --argocd-cm-path /root/ga-ssa/argocd-cm.yaml 의 출력을 /root/ga-ssa/ignore.txt 에 저장하세요.

Argo CD 에서 '오토스케일러가 쓴 필드는 비교하지 않는다' 를 표현하는 방법이 이 두 가지입니다 — 경로로 빼거나 관리자 이름으로 빼거나. 다만 미리보기 명령은 경로 규칙만 렌더하므로, 관리자 이름 규칙이 실제로 무엇을 거를지는 소유 기록을 사람이 읽어 판단해야 합니다.