kubectl apply vs set: 리소스 업데이트에 적합한 명령어 선택하기
kubectl apply, set, edit을 사용할 때 라이브 쿠버네티스 오브젝트와 Git 간의 드리프트를 방지하는 방법을 이해합니다.
kubectl apply vs set: 리소스 업데이트에 적합한 명령어 선택하기
kubectl apply와 kubectl set의 차이는 단순한 문법 차이가 아닙니다. 이는 선언된 소스 오브 트루스(Source of Truth)로 쿠버네티스를 관리하는 것과 라이브 오브젝트를 직접 변경하는 것의 차이입니다. 둘 다 유용하지만, 잘못된 위치에서 사용하면 문제가 발생할 수 있습니다.
변경 사항이 시스템의 원하는 상태가 되어야 할 때는 kubectl apply를 사용하세요. 일반적으로 일시적이거나 긴급한, 집중된 라이브 변경이 필요할 때는 kubectl set을 사용하세요. 라이브 오브젝트를 대화식으로 검사하고 패치해야 할 때는 kubectl edit을 사용하되, 이것이 Git에서 드리프트를 만드는 가장 쉬운 방법임을 이해하세요.
쿠버네티스 오브젝트는 API 서버에 저장된 원하는 상태(desired state)를 가지고 있습니다. 디플로이먼트(Deployment)는 몇 개의 레플리카가 존재해야 하는지, 어떤 이미지를 실행해야 하는지, 어떤 레이블이 파드를 식별하는지, 어떤 리소스가 요청되는지 등을 명시합니다. 컨트롤러는 실제 상태가 그 원하는 상태와 일치하도록 작동합니다. 업데이트 명령어는 원하는 상태를 변경하며, 컨트롤러가 나머지를 처리합니다.
kubectl apply를 사용하면 원하는 상태를 YAML 또는 JSON 파일에 유지합니다. 파일은 검토, 커밋, 프로모션, 롤백의 대상이 됩니다. 일반적인 명령어는 간단합니다:
kubectl apply -f deployment.yaml
오브젝트가 존재하지 않으면 쿠버네티스가 생성합니다. 존재하면 쿠버네티스가 매니페스트와 일치하도록 업데이트합니다. 동일한 파일을 다시 적용해도 새로운 동작 변경이 발생하지 않아야 합니다. 이러한 멱등성(idempotence)은 apply가 CI/CD 및 GitOps 워크플로우에서 잘 작동하는 이유 중 하나입니다.
클라이언트 측 kubectl apply는 역사적으로 마지막 적용 주석(last-applied annotation)을 사용하여 변경 사항을 계산했습니다. --server-side 플래그로 활성화되는 서버 측 적용(server-side apply)은 API 서버의 관리 필드(managed fields)를 통해 필드 소유권을 추적합니다. 세부 사항은 다르지만 운영 개념은 동일합니다: 선언된 구성이 원하는 상태를 소유합니다.
다음은 작은 디플로이먼트 매니페스트입니다:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
파일에서 이미지를 변경하고 kubectl apply -f deployment.yaml을 실행하면 디플로이먼트가 업데이트됩니다. 파일이 Git에 있다면 변경 사항을 검토할 수 있습니다. 롤아웃이 실패하면 커밋을 되돌리거나 디플로이먼트 프로세스가 리비전을 기록하는 방식에 따라 쿠버네티스 롤아웃 기록을 사용할 수 있습니다.
kubectl set은 다르게 작동합니다. 라이브 오브젝트의 특정 필드를 변경합니다. 일반적인 예는 컨테이너 이미지를 변경하는 것입니다:
kubectl set image deployment/web nginx=nginx:1.26
이 명령어는 빠르고 가독성이 좋습니다. 인시던트(incident) 중에는 속도가 중요합니다. 현재 이미지가 손상되어 디플로이먼트를 알려진 정상 이미지로 이동해야 하는 경우 kubectl set image가 가장 빠른 방법일 수 있습니다. 위험은 그 다음에 발생합니다. deployment.yaml이 여전히 nginx:1.25를 명시하고 있다면, 다음 kubectl apply -f deployment.yaml 실행 시 워크로드가 다시 1.25로 돌아갈 수 있습니다.
이러한 불일치는 구성 드리프트(configuration drift)입니다. 라이브 클러스터는 한 가지를 말하고, 소스 파일은 다른 것을 말합니다. 드리프트가 항상 치명적인 것은 아니지만, 사람들이 저장소를 신뢰하지 못하게 되므로 디버깅이 더 어려워집니다. 누군가 "프로덕션에서 무엇이 실행되고 있나요?"라고 물으면 정직한 대답은 "클러스터를 확인해보겠습니다"가 됩니다.
kubectl set에는 여러 유용한 하위 명령어가 있습니다:
kubectl set image deployment/web nginx=nginx:1.26
kubectl set env deployment/web FEATURE_FLAG=true
kubectl set resources deployment/web -c=nginx --requests=cpu=200m,memory=256Mi --limits=cpu=500m,memory=512Mi
이 명령어들은 개발 클러스터, 데모, 단기 테스트 및 후속 커밋이 있는 긴급 프로덕션 변경에 실용적입니다. 유지 관리되는 매니페스트를 대체하지는 않습니다.
kubectl edit은 라이브 오브젝트를 가져와 편집기에서 열고 저장할 때 변경된 오브젝트를 API 서버로 다시 보냅니다:
kubectl edit deployment/web
컨트롤러가 추가한 필드를 포함한 전체 라이브 YAML을 볼 수 있기 때문에 편리합니다. 또한 라이브 오브젝트에는 수동으로 관리해서는 안 되는 많은 필드(예: status, 생성된 메타데이터, 리소스 버전, 관리 필드)가 포함되어 있기 때문에 위험합니다. 쿠버네티스는 일부 잘못된 편집을 무시하거나 거부하지만, 모든 잘못된 편집이 구문적으로 유효하지 않은 것은 아닙니다.
kubectl edit의 일반적인 안전한 사용은 빠른 비프로덕션 실험입니다: 레플리카 증가, 어노테이션 변경, 프로브 값 테스트 등입니다. 일반적인 안전하지 않은 사용은 매주 프로덕션 디플로이먼트를 수동으로 편집하고 매니페스트를 업데이트하지 않는 것입니다. 이는 아무도 자신 있게 재구축할 수 없는 클러스터를 만듭니다.
실용적인 규칙은 다음과 같습니다: 변경 사항이 다음 배포까지 유지되기를 원한다면 매니페스트에 넣고 apply를 사용하세요. 라이브 오브젝트를 일시적으로만 건드리면 된다면 set 또는 edit을 사용한 다음 변경 사항을 되돌리거나 Git에 백포트(backport)하세요.
명령형 명령어가 허용될 뿐만 아니라 도움이 되는 몇 가지 경우가 있습니다. 디버깅 중에 로그 상세도를 높이는 환경 변수를 일시적으로 추가할 수 있습니다:
kubectl set env deployment/api LOG_LEVEL=debug
로그를 수집한 후 제거합니다:
kubectl set env deployment/api LOG_LEVEL-
디버그 설정을 영구적으로 유지해야 한다면 매니페스트에 커밋하세요. 기억에 의존하지 마세요.
또 다른 경우는 긴급 롤백입니다. 배포 파이프라인이 막히고 고객에게 영향을 미치는 경우 이미지를 직접 설정하는 것이 합리적일 수 있습니다:
kubectl set image deployment/api api=registry.example.com/api:2026-05-23-good
kubectl rollout status deployment/api
후속 조치는 즉시 이루어져야 합니다: 선언된 매니페스트가 긴급 상태와 일치하도록 풀 리퀘스트를 열거나 커밋하거나, 파이프라인이 정상화되면 일반 롤백 프로세스를 실행하세요. 라이브 수정은 시간을 벌어줍니다; 새로운 문서화되지 않은 배포 방법이 되어서는 안 됩니다.
kubectl apply에도 함정이 있습니다. 동일한 필드를 관리하는 여러 도구를 혼합하면 충돌이나 예상치 못한 덮어쓰기가 발생할 수 있습니다. 예를 들어, GitOps 컨트롤러, Helm, 그리고 사람이 동일한 디플로이먼트에 대해 kubectl apply를 실행하면 모두가 오브젝트의 일부를 소유하고 있다고 믿을 수 있습니다. 명확한 소유권을 선택하세요. Helm이 리소스를 관리한다면 Helm 값을 업데이트하고 Helm 릴리스 프로세스를 실행하세요. Argo CD 또는 Flux가 관리한다면 Git을 변경하고 컨트롤러가 조정하도록 하세요.
시크릿과 구성(Config)의 경우 특히 주의하세요. kubectl set env는 디플로이먼트에 빠른 변경을 적용할 수 있지만, 셸 기록이나 감사 로그에 값을 노출할 수 있습니다. 민감한 값의 경우 일반 시크릿 관리 프로세스를 통해 시크릿을 업데이트하세요. 팀에서 명시적으로 해당 워크플로우를 수락하지 않는 한 프로덕션 자격 증명을 임시 명령어에 붙여넣지 마세요.
라이브 오브젝트를 변경하기 전에 검사하세요:
kubectl get deployment web -o yaml
kubectl diff -f deployment.yaml
kubectl diff는 과소평가된 명령어입니다. 변경을 적용하기 전에 apply가 무엇을 변경할지 보여줍니다. 프로덕션에서 이 미리보기는 레이블 셀렉터를 실수로 제거하거나, 리소스 제한을 삭제하거나, 잘못된 환경의 매니페스트를 적용하는 등의 실수를 잡을 수 있습니다.
서버 측 적용의 경우 명령어는 다음과 같습니다:
kubectl apply --server-side -f deployment.yaml
서버 측 적용은 여러 주체가 다른 필드를 관리할 때 유용할 수 있지만, 소유권 규율의 필요성을 없애지는 않습니다. 두 관리자가 동일한 필드를 소유하려고 하면 쿠버네티스가 충돌을 보고할 수 있습니다. 이는 기능입니다; 워크플로우가 모호하다는 것을 알려주는 것입니다.
실제 클러스터에서 사용하는 간단한 결정 가이드는 다음과 같습니다. 새 애플리케이션 릴리스? 매니페스트를 변경하고 파이프라인을 통해 apply를 사용하세요. 스테이징에서 부하 테스트를 위해 레플리카를 늘리시나요? 되돌릴 수 있다면 kubectl scale 또는 kubectl set을 사용해도 괜찮습니다. 프로덕션에서 손상된 이미지를 핫픽스? kubectl set image는 허용될 수 있지만, 즉시 소스 오브 트루스를 변경하세요. CPU 요청을 영구적으로 조정? 매니페스트를 업데이트하세요. 리소스에 어떤 필드가 있는지 탐색? edit을 사용하기 전에 kubectl get -o yaml을 사용하세요.
팀이 이를 올바르게 처리하면 쿠버네티스에 대한 추론이 더 쉬워집니다. 저장소가 이야기를 전달합니다. 클러스터는 대부분의 시간 동안 저장소와 일치합니다. 일시적인 라이브 변경은 임시로 표시되고 정리됩니다. 인시던트는 여전히 스트레스가 많지만, 구성이 두 번째 미스터리가 되지는 않습니다.
명령어 자체가 중요한 것이 아닙니다. 중요한 것은 내일 신뢰할 수 있는 파일에서 클러스터 상태를 재구축할 수 있는지 여부입니다. kubectl apply는 그 습관을 지원합니다. kubectl set과 kubectl edit은 직접적인 조치가 유용한 순간을 위한 날카로운 도구입니다. 그 경계를 명확히 유지하면 피할 수 있는 많은 쿠버네티스 혼란을 피할 수 있습니다.
사람들이 같은 범주에 넣는 또 다른 명령어가 있습니다: kubectl patch입니다. 이것도 명령형이지만, edit보다 정확한 스크립트 변경에 더 적합합니다. 예를 들어, 디플로이먼트 어노테이션을 패치하여 재시작을 트리거하거나 자동화에서 작은 필드를 업데이트할 수 있습니다. 동일한 드리프트 규칙이 적용됩니다. 패치된 필드가 장기적인 원하는 상태를 나타내는 경우 소스 매니페스트도 업데이트하세요.
kubectl patch deployment web -p '{"spec":{"template":{"metadata":{"annotations":{"restartedAt":"2026-05-24T10:00:00Z"}}}}}'
재시작의 경우 목적에 맞게 만들어진 명령어를 선호하세요:
kubectl rollout restart deployment/web
이 명령어는 여전히 라이브 오브젝트를 변경하지만, 의도는 명확합니다: 현재 파드 템플릿에서 새 롤아웃을 시작하는 것입니다. Git에서 구성을 변경하는 것을 대체하지는 않습니다.
GitOps 환경에서는 경계가 더 엄격합니다. Argo CD 또는 Flux가 오브젝트를 소유하는 경우, 수동 kubectl set image는 컨트롤러가 드리프트를 감지하고 Git으로 다시 조정하기 때문에 자동으로 되돌려질 수 있습니다. 이는 인시던트 중에 놀라울 수 있습니다. 수동 프로덕션 변경을 하기 전에 GitOps 컨트롤러가 당신과 싸울지 알아두세요. 때로는 긴급 조치로 해당 애플리케이션의 조정을 일시 중지하고, 수정을 적용한 다음 일치하는 Git 변경 사항을 커밋하고 조정을 재개하는 것이 좋습니다.
또한 생성된 필드를 맹목적으로 커밋하지 않고 라이브 차이점을 캡처하는 방법을 알아야 합니다. kubectl get deployment web -o yaml에는 상태, 리소스 버전, 관리 필드 및 깨끗한 매니페스트에 포함되어서는 안 되는 기타 데이터가 포함됩니다. 핫픽스를 백포트해야 하는 경우 소스 매니페스트를 수동으로 편집하거나 Kustomize 또는 Helm 값과 같은 도구를 사용한 다음 kubectl diff를 실행하여 의도한 변경 사항을 확인하세요. 신중하게 정리하지 않는 한 소스 파일을 원시 라이브 YAML로 대체하지 마세요.
팀의 경우 가장 건강한 정책은 일반적으로 짧고 명시적입니다. 프로덕션 변경은 Git을 통해 이루어집니다. 긴급 라이브 변경은 필요할 때 허용되지만, 후속 소스 변경 또는 롤백이 필요합니다. 개발 클러스터는 더 느슨하지만, 개발 이상으로 승격되는 모든 것은 선언되어야 합니다. 이 정책은 금지된 명령어의 긴 목록보다 따르기 쉽습니다.
여기에는 인간적인 측면도 있습니다. 중단 중에 클러스터에 액세스할 수 있는 사람이 가능한 가장 빠른 수정을 할 수 있습니다. 팀이 이를 긴급 예외로 처리할 때는 괜찮습니다. 이러한 예외가 정상 운영이 될 때 위험해집니다. 프로덕션이 정기적으로 수동으로 수정된다면 배포 프로세스가 너무 느리거나, 너무 취약하거나, 신뢰할 수 없는 것입니다. 모든 사람에게 더 많은 라이브 편집 기술을 가르치기보다는 그 프로세스를 수정하세요.
RBAC는 워크플로우를 강화할 수 있습니다. 많은 팀이 개발에서는 광범위한 kubectl 액세스를 허용하지만 프로덕션 쓰기는 CI/CD 시스템, GitOps 컨트롤러 또는 소수의 온콜 그룹으로 제한합니다. 이것은 그 자체로 관료주의가 아닙니다. 원하는 상태를 변경할 수 있는 경로 수를 줄입니다. 무언가 변경되면 감사 로그와 Git 기록을 더 쉽게 추적할 수 있습니다.
학습을 위해 폐기 가능한 네임스페이스에서 세 가지 명령어를 모두 연습하는 것이 좋습니다. apply로 디플로이먼트를 생성하고, set으로 이미지를 변경하고, kubectl diff로 드리프트를 검사한 다음 매니페스트를 업데이트하고 다시 적용하세요. 안전한 환경에서 드리프트가 한 번 발생하는 것을 보면 프로덕션 규칙을 훨씬 쉽게 기억할 수 있습니다.