쿠버네티스에서 설정은 사소해 보이지만 실제로는 핵심적인 요소입니다. 따옴표 하나, 잘못된 API 버전, 혹은 엉뚱한 YAML 들여쓰기 하나가 전체 배포를 망칠 수 있습니다.
이 블로그는 오랜 시간 검증되고 실전에서 효과를 본 설정 모범 사례들을 모았습니다. 이는 쿠버네티스 설정을 깔끔하고 일관성 있으며 관리하기 더 쉽게 만들어주는 작은 습관들입니다. 막 시작했든, 매일 애플리케이션을 배포하고 있든, 이 작은 습관들은 클러스터를 안정적으로 유지하고 미래의 당신이 스트레스 받지 않도록 지켜줄 것입니다.
이 블로그는 원래 Configuration Best Practices 페이지에서 영감을 받았으며, 쿠버네티스 커뮤니티의 많은 구성원들의 기여를 통해 발전해 왔습니다.
일반적인 설정 모범 사례
최신 안정 API 버전 사용하기
쿠버네티스는 빠르게 발전합니다. 오래된 API는 결국 사용 중단(deprecated)되고 작동을 멈춥니다. 따라서 리소스를 정의할 때는 항상 최신 안정 API 버전을 사용해야 합니다. 다음 명령으로 항상 확인할 수 있습니다:
kubectl api-resources
이 간단한 단계만으로도 향후 호환성 문제를 예방할 수 있습니다.
버저닝 시스템에 설정 저장하기
매니페스트 파일을 데스크톱에서 직접 적용하지 마십시오. 항상 Git과 같은 버전 관리 시스템에 저장하세요. 이는 당신의 안전망입니다. 문제가 발생했을 때, 이전 커밋으로 즉시 롤백하거나, 변경 사항을 비교하거나, 패닉 없이 클러스터 설정을 다시 만들 수 있습니다.
JSON 대신 YAML로 설정 작성하기
설정 파일을 JSON 대신 YAML로 작성하세요. 둘 다 기술적으로 작동하지만, YAML이 사람에게 더 편리합니다. 가독성이 좋고 불필요한 부분이 적으며 커뮤니티에서 널리 사용됩니다.
YAML은 불리언(boolean) 값에 대한 미묘한 함정이 있습니다:
오직 true 또는 false만 사용하세요.
yes, no, on, off는 사용하지 마세요.
이는 YAML 버전에 따라 작동할 수도, 안 할 수도 있습니다. 안전을 위해 불리언처럼 보이는 모든 값(예: "yes")은 따옴표로 묶으세요.
설정을 간단하고 최소한으로 유지하기
쿠버네티스가 이미 처리하는 기본값은 설정하지 마세요. 최소한의 매니페스트는 디버깅하기 더 쉽고, 검토하기 깔끔하며, 나중에 문제를 일으킬 가능성이 적습니다.
관련 오브젝트를 함께 그룹화하기
Deployment, Service, ConfigMap이 모두 하나의 애플리케이션에 속한다면, 이들을 하나의 매니페스트 파일에 함께 배치하세요. 변경 사항을 추적하고 하나의 단위로 적용하기가 더 쉽습니다. 이 구문의 예시는 Guestbook all-in-one.yaml 파일을 참조하십시오.
다음 명령으로 전체 디렉토리를 적용할 수도 있습니다:
kubectl apply -f configs/
명령어 하나로 해당 폴더의 모든 것이 배포됩니다.
유용한 어노테이션 추가하기
매니페스트 파일은 기계만을 위한 것이 아니라, 사람을 위한 것이기도 합니다. 어떤 것이 왜 존재하고 무엇을 하는지 설명하기 위해 어노테이션(annotations)을 사용하세요. 간단한 한 줄 설명은 나중에 디버깅할 때 몇 시간의 시간을 절약해 주고 더 나은 협업을 가능하게 합니다.
가장 유용한 어노테이션은 kubernetes.io/description입니다. 주석을 사용하는 것과 같지만, 배포 후에도 다른 모든 사람이 볼 수 있도록 API에 복사된다는 점이 다릅니다.
워크로드 관리: Pod, Deployment, Job
쿠버네티스에서 흔히 저지르는 초기 실수는 Pod를 직접 생성하는 것입니다. Pod는 작동하지만, 문제가 발생했을 때 스스로 재스케줄링되지 않습니다.
네이키드 Pod(Deployment 또는 StatefulSet과 같은 컨트롤러에 의해 관리되지 않는 Pod)는 테스트에는 괜찮지만, 실제 환경에서는 위험합니다.
왜냐고요? 해당 Pod를 호스팅하는 노드가 죽으면, Pod도 함께 죽고 쿠버네티스가 자동으로 복구하지 않기 때문입니다.
항상 실행되어야 하는 애플리케이션에는 Deployment 사용하기
Deployment는 원하는 수의 Pod가 항상 사용 가능하도록 ReplicaSet을 생성하고, Pod를 교체하는 전략(예: 롤링 업데이트)을 지정하므로, Pod를 직접 생성하는 것보다 거의 항상 선호됩니다. 새 버전을 롤아웃하고, 문제가 발생하면 즉시 롤백할 수 있습니다.
완료되어야 하는 작업에는 Job 사용하기
Job은 데이터베이스 마이그레이션이나 배치 처리 작업처럼 한 번 실행되고 종료되어야 하는 작업에 완벽합니다. Pod가 실패하면 재시도하고, 완료되면 성공을 보고합니다.
Service 설정 및 네트워킹
Service는 워크로드가 클러스터 내부(때로는 외부)에서 서로 통신하는 방식입니다. Service가 없으면 Pod는 존재하지만 아무도 도달할 수 없습니다. 이런 일이 발생하지 않도록 합시다.
Service를 사용하는 워크로드보다 먼저 Service 생성하기
쿠버네티스가 Pod를 시작할 때, 기존 Service에 대한 환경 변수를 자동으로 주입합니다. 따라서 Pod가 Service에 의존한다면, 해당 Service를 먼저 백엔드 워크로드(Deployment 또는 StatefulSet)보다 먼저, 그리고 해당 Service에 접근해야 하는 모든 워크로드보다 먼저 생성하세요.
예를 들어, foo라는 Service가 존재하면, 모든 컨테이너는 초기 환경에 다음 변수를 얻습니다:
FOO_SERVICE_HOST=<Service가 실행되는 호스트>
FOO_SERVICE_PORT=<Service가 실행되는 포트>
DNS 기반 서비스 디스커버리는 이러한 문제가 없지만, 그래도 따르는 좋은 습관입니다.
Service 디스커버리를 위해 DNS 사용하기
클러스터에 DNS 애드온이 있다면 (대부분은 있습니다), 모든 Service는 자동으로 DNS 항목을 얻습니다. 이는 IP 대신 이름으로 Service에 접근할 수 있다는 의미입니다:
curl http://my-service.default.svc.cluster.local
이는 쿠버네티스 네트워킹을 마법처럼 느끼게 하는 기능 중 하나입니다.
절대적으로 필요한 경우가 아니라면 hostPort와 hostNetwork 피하기
매니페스트에서 다음과 같은 옵션을 볼 수 있습니다:
hostPort: 8080
hostNetwork: true
하지만 중요한 점은:
이들은 Pod를 특정 노드에 묶어 스케줄링 및 확장을 어렵게 만듭니다. 각 <hostIP, hostPort, protocol> 조합은 고유해야 하기 때문입니다. hostIP와 protocol을 명시적으로 지정하지 않으면, 쿠버네티스는 hostIP로 0.0.0.0을, protocol로 TCP를 기본값으로 사용합니다.
디버깅 중이거나 네트워크 플러그인과 같은 것을 구축하는 경우가 아니라면, 이들을 피하세요.
단순히 테스트를 위해 로컬 접근이 필요하다면, kubectl port-forward를 시도해 보세요:
kubectl port-forward deployment/web 8080:80
자세한 내용은 클러스터에서 애플리케이션에 접근하기 위해 포트 포워딩 사용을 참조하십시오.
혹은 정말로 외부 접근이 필요하다면, type: NodePort Service를 사용하세요. 이것이 더 안전하고 쿠버네티스 네이티브 방식입니다.
내부 디스커버리를 위해 헤드리스 Service 사용하기
때로는 쿠버네티스가 트래픽을 로드 밸런싱하는 것을 원치 않을 수 있습니다. 각 Pod와 직접 통신하고 싶을 때가 있습니다. 이때 헤드리스 Service가 유용합니다.
clusterIP: None으로 설정하여 생성합니다.
단일 IP 대신, DNS는 모든 Pod의 IP 목록을 제공하며, 연결을 자체적으로 관리하는 애플리케이션에 완벽합니다.
레이블을 효과적으로 활용하기
레이블은 Pod와 같은 오브젝트에 붙는 키/값 쌍입니다. 레이블은 리소스를 조직화하고, 쿼리하며, 그룹화하는 데 도움을 줍니다. 그 자체로는 아무것도 하지 않지만, Service에서 Deployment에 이르는 모든 것이 원활하게 함께 작동하도록 만듭니다.
의미 있는 레이블 사용하기
좋은 레이블은 몇 달이 지난 후에도 무엇이 무엇인지 이해하는 데 도움을 줍니다. 애플리케이션 또는 Deployment의 의미론적 속성을 식별하는 레이블을 정의하고 사용하세요. 예를 들어;
labels:
app.kubernetes.io/name: myapp
app.kubernetes.io/component: web
tier: frontend
phase: test
app.kubernetes.io/name: 애플리케이션 이름tier: 계층 (프런트엔드/백엔드)phase: 단계 (테스트/프로덕션)
이러한 레이블을 사용하여 강력한 셀렉터(selector)를 만들 수 있습니다. 예를 들어:
kubectl get pods -l tier=frontend
이는 어떤 Deployment에서 왔는지와 상관없이 클러스터의 모든 프런트엔드 Pod를 나열합니다. 기본적으로 Pod 이름을 수동으로 나열하는 것이 아니라, 원하는 것을 설명하는 것입니다. 이러한 접근 방식의 예시는 guestbook 애플리케이션을 참조하십시오.
일반적인 쿠버네티스 레이블 사용하기
쿠버네티스는 실제로 공통 레이블 세트를 권장합니다. 이는 다양한 워크로드나 프로젝트에서 이름을 지정하는 표준화된 방법입니다. 이러한 컨벤션을 따르면 매니페스트가 더 깔끔해지며, Headlamp, 대시보드, 또는 서드파티 모니터링 시스템과 같은 도구들이 자동으로 무엇이 실행 중인지 이해할 수 있게 됩니다.
디버깅을 위한 레이블 조작
컨트롤러(ReplicaSet 또는 Deployment와 같은)는 레이블을 사용하여 Pod를 관리하므로, 레이블을 제거하여 Pod를 일시적으로 "분리"할 수 있습니다.
예시:
kubectl label pod mypod app-
app- 부분은 app 레이블 키를 제거합니다.
이렇게 되면 컨트롤러는 더 이상 해당 Pod를 관리하지 않습니다.
검사를 위해 격리하는 것과 같으며, 디버깅을 위한 "격리 모드"입니다. 레이블을 대화식으로 제거하거나 추가하려면 kubectl label을 사용하세요.
그런 다음 로그를 확인하고, 내부에 exec하여 작업이 끝나면 수동으로 삭제할 수 있습니다. 이것은 모든 쿠버네티스 엔지니어가 알아야 할 매우 저평가된 팁입니다.
유용한 kubectl 팁
이 작은 팁들은 여러 매니페스트 파일이나 클러스터로 작업할 때 삶을 훨씬 더 쉽게 만들어 줄 것입니다.
전체 디렉토리 적용하기
한 번에 하나의 파일씩 적용하는 대신, 전체 폴더를 적용하세요:
# 서버 측 적용(server-side apply)도 좋은 관행입니다.
kubectl apply -f configs/ --server-side
이 명령어는 해당 폴더에서 .yaml, .yml, .json 파일을 찾아 모두 함께 적용합니다.
더 빠르고 깔끔하며, 애플리케이션별로 항목을 그룹화하는 데 도움이 됩니다.
레이블 셀렉터를 사용하여 리소스 가져오기 또는 삭제하기
항상 리소스 이름을 하나씩 입력할 필요는 없습니다. 대신, 셀렉터를 사용하여 전체 그룹에 한 번에 작업하세요:
kubectl get pods -l app=myapp
kubectl delete pod -l phase=test
이는 특히 테스트 리소스를 동적으로 정리하고 싶을 때 CI/CD 파이프라인에서 유용합니다.
Deployment 및 Service 빠르게 생성하기
간단한 실험을 위해 항상 매니페스트를 작성할 필요는 없습니다. CLI에서 바로 Deployment를 생성할 수 있습니다:
kubectl create deployment webapp --image=nginx
그런 다음 Service로 노출하세요:
kubectl expose deployment webapp --port=80
이는 전체 매니페스트를 작성하기 전에 뭔가를 테스트하고 싶을 때 매우 유용합니다. 또한, 예시를 위해 클러스터에서 애플리케이션에 접근하기 위해 Service 사용을 참조하십시오.
결론
더 깔끔한 설정은 더 평온한 클러스터 관리자를 만듭니다. 몇 가지 간단한 습관을 지키세요: 설정을 간단하고 최소한으로 유지하고, 모든 것을 버전 관리하며, 일관된 레이블을 사용하고, 네이키드 Pod에 의존하지 않으면, 앞으로 몇 시간의 디버깅 시간을 절약할 수 있을 것입니다.
가장 좋은 점은? 깔끔한 설정은 항상 가독성이 좋습니다. 몇 달이 지난 후에도 당신이나 팀원 누구나 한눈에 무엇이 일어나고 있는지 정확히 알 수 있습니다.