
지난 포스팅에서 kubeadm으로 쿠버네티스 클러스터를 구축했습니다. 이제 실제로 컨테이너(파드)를 띄워봐야겠죠.
하지만 실습용으로 파드 몇 개 띄우는 것과 실제 회사에서 여러 사람이 함께 운영하는 것은 완전히 다른 이야기입니다.
실무에서는 이런 문제들이 생깁니다:
문제 1. 환경 충돌
개발팀이
my-app이라는 이름으로 파드를 띄웠는데, QA팀도 같은 이름으로 띄우려니까 "이미 존재합니다" 에러가 뜹니다. 개발 DB와 운영 DB가 같은 클러스터에 있으면? 상상만 해도 끔찍합니다.
문제 2. 설정 공유와 협업
팀원 A가 터미널에서 명령어로 파드를 띄웠습니다. 다음 날 팀원 B가 "어? 이 파드 누가 만들었지? 설정이 뭐였지?"라고 물어봅니다. 명령어로만 실행했기 때문에 기록이 없습니다.
문제 3. 배포 타이밍
배포 스크립트에서 파드를 삭제하고 즉시 새로운 파드를 생성했습니다. 그런데 "이미 존재합니다" 에러가 발생합니다. 삭제 완료 응답을 받았는데 왜 이런 일이?
이번 포스팅에서는 쿠버네티스가 이 세 가지 문제를 어떻게 해결하는지 알아보겠습니다. 🚀
같은 클러스터를 여러 팀이 공유할 때, 리소스 이름이 겹치면 충돌이 발생합니다.
# 개발팀이 실행
kubectl run my-app --image=nginx
# QA팀이 실행 (같은 클러스터)
kubectl run my-app --image=nginx
# ❌ Error: pod "my-app" already exists

Namespace는 하나의 클러스터 안에 논리적인 경계를 만들어 리소스를 격리합니다.
물리적으로 서버를 나누는 게 아니라, 같은 서버 안에 투명한 벽을 세워서 공간을 분리하는 개념입니다.
# 개발 네임스페이스 생성
kubectl create namespace dev
# QA 네임스페이스 생성
kubectl create namespace qa
# 이제 같은 이름이어도 괜찮음
kubectl run my-app --image=nginx -n dev
kubectl run my-app --image=nginx -n qa
# 확인
kubectl get pods -n dev # dev의 my-app
kubectl get pods -n qa # qa의 my-app (별개의 파드)

클러스터를 처음 만들면 쿠버네티스가 자기 자신을 관리하기 위해 미리 만들어둔 네임스페이스들이 있습니다:
kube-system: API Server, Scheduler 등 쿠버네티스 핵심 구성 요소가 실행되는 공간kube-public: 모든 사용자가 읽을 수 있는 공개 리소스 저장 공간kube-node-lease: 각 노드의 상태(살아있는지) 정보를 저장하는 공간default: 네임스페이스를 지정하지 않으면 기본적으로 사용되는 공간💡 네임스페이스를 지정하지 않으면 자동으로 'default' 네임스페이스에 등록됩니다.
# 시스템 네임스페이스 확인
kubectl get namespaces

💡 리눅스 Namespace와의 관계
리눅스의 Namespace(프로세스 격리)와 동일한 개념입니다. 충돌 방지와 격리 환경 제공이라는 목적이 같습니다.
Namespace로 공간을 분리했으니, 이제 각 공간에 명령을 내려볼 차례입니다.
kubectl은 마스터 노드의 API Server와 통신해서 명령을 전달합니다.
이때 필요한 정보:
이 정보들은 ~/.kube/config 파일에 YAML 형식으로 저장됩니다.
# config 파일 확인
cat ~/.kube/config

실무에서는 개발 클러스터, 스테이징 클러스터, 운영 클러스터 등 여러 개를 동시에 관리합니다.
개발(Dev) 클러스터: 개발자들이 자유롭게 코드를 배포하고 테스트하는 환경입니다.
스테이징(Staging) 클러스터: 운영 환경과 동일한 조건에서 최종 검증을 하는 환경입니다.
운영(Prod) 클러스터: 실제 고객이 사용하는 환경으로, 보안과 안정성이 최우선입니다.
매번 서버 주소와 인증 정보를 바꿀 수는 없겠죠?
Context는 "클러스터 + 인증정보 + 기본 네임스페이스"를 하나의 세트로 묶은 것입니다.
# 현재 사용 중인 컨텍스트 확인
kubectl config current-context
# 사용 가능한 컨텍스트 목록
kubectl config get-contexts
# 컨텍스트 전환 (개발 → 운영)
kubectl config use-context production
이제 명령어 한 줄로 제어 대상을 바꿀 수 있습니다.

💡 실습 환경 참고
현재 제 환경에는 클러스터가 하나만 있어서 컨텍스트도 하나만 보입니다.
실무에서는 보통dev-cluster,staging-cluster,prod-cluster등 여러 개가 나열됩니다.
이제 본격적으로 리소스를 생성해 봅시다. 쿠버네티스에는 두 가지 방식이 있습니다.
터미널에 명령어를 직접 입력해서 즉시 생성합니다.
# 파드 생성
kubectl run nginx --image=nginx
# Deployment 생성
kubectl create deployment my-deploy --image=nginx
장점: 빠르고 직관적입니다.
문제점:
"어떻게 만들어라"가 아니라 "이런 상태가 되게 해라"를 파일에 정의합니다.
# nginx-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.21
# 파일로 생성
kubectl apply -f nginx-pod.yaml
장점:
create로 배포 스크립트를 짜려면 이렇게 해야 합니다:
# create 방식 (복잡함)
if kubectl get pod nginx 2>/dev/null; then
kubectl delete pod nginx
kubectl create -f nginx-pod.yaml
else
kubectl create -f nginx-pod.yaml
fi
'해당 파드가 있다면 지우고 만들어라' 라는 조건문을 적어줘야 합니다.
apply는 이렇게 간단합니다:
# apply 방식 (간단함)
kubectl apply -f nginx-pod.yaml
이미 있으면 변경된 부분만 업데이트하고, 없으면 새로 만듭니다. 멱등성이 보장됩니다.
💡 apply의 똑똑한 점
apply는 무조건 지우고 새로 만드는 게 아닙니다!
- 리소스가 없으면 → 생성
- 리소스가 있고 변경사항 없으면 → 아무것도 안 함
- 리소스가 있고 변경사항 있으면 → 변경된 부분만 업데이트
이것이 바로 멱등성(Idempotency)입니다.
apply를 사용하면 쿠버네티스가 내부적으로 마지막으로 적용된 설정을 기록해둡니다.
# 리소스에 기록된 마지막 설정 확인
kubectl get pod nginx -o yaml | grep last-applied-configuration
다음에 다시 apply할 때, 이 기록과 새 YAML을 비교해서:
정확하게 계산합니다. create는 이런 기록이 없어서 업데이트 시 충돌이 발생할 수 있습니다.
코드로 인프라를 관리하는 현대적인 방식입니다.
코드(YAML) → Git → CI/CD → 클러스터 자동 배포
이 흐름이 가능하려면 선언형 방식이 필수입니다.
주로 사용되는 CI/CD 툴에서 yaml 파일 형식을 많이 사용합니다.
💡 실무 팁
테스트나 임시 작업은
create를 써도 괜찮습니다. 하지만 운영 환경이나 팀 프로젝트에서는 무조건 YAML 파일 + apply를 사용해야 합니다.
이제 앞서 말한 세 번째 문제로 돌아가봅시다.
# 파드 삭제
kubectl delete pod nginx
# pod "nginx" deleted (즉시 응답)
# 바로 다시 생성
kubectl apply -f nginx-pod.yaml
# ❌ Error: pod "nginx" already exists
분명 "deleted" 응답을 받았는데 왜 아직 존재한다고 할까요?
명령어를 실행하면 이런 일이 벌어집니다:
1. kubectl이 API Server에 요청
2. API Server가 문법만 확인하고 즉시 "OK" 응답 ⚡
3. 백그라운드에서 실제 작업 진행
API Server → etcd → Scheduler → Kubelet → 컨테이너 삭제
터미널에 "deleted" 응답이 떴을 때는 접수만 완료된 상태입니다.
실제 삭제는 백그라운드에서 진행 중입니다.
실제 작업이 완료될 때까지 기다리게 만듭니다.
# 삭제 완료까지 대기
kubectl delete pod nginx --wait=true
# 이제 바로 생성해도 괜찮음
kubectl apply -f nginx-pod.yaml
# ✅ Success
자동화 스크립트에서는 이런 상황이 자주 발생합니다:
# 잘못된 스크립트
kubectl delete -f old-version.yaml
kubectl apply -f new-version.yaml # ❌ 실패할 수 있음
# 올바른 스크립트
kubectl delete -f old-version.yaml --wait=true
kubectl apply -f new-version.yaml # ✅ 안전함
💡 강제 즉시 삭제
긴급 상황에서 즉시 삭제해야 한다면:
kubectl delete pod nginx --grace-period=0 --force
운영 중인 파드의 설정을 터미널에서 직접 바꿀 수도 있습니다.
# 이미지 버전 변경
kubectl set image pod nginx nginx=nginx:1.22
하지만 이건 매우 위험합니다!
1.22로 변경1.21apply1.21로 롤백됨! 🔥# 현재 실제 상태 확인
kubectl get pod nginx -o yaml
# 로컬 파일과 비교
kubectl diff -f nginx-pod.yaml
# ⚠️ 차이가 있다면 위험 신호!
# 1. YAML 파일 수정
vim nginx-pod.yaml
# image: nginx:1.22 로 변경
# 2. Git에 커밋
git add nginx-pod.yaml
git commit -m "Update nginx to 1.22"
# 3. apply로 반영
kubectl apply -f nginx-pod.yaml
이렇게 하면:
💡 (고급) Server-Side Apply
여러 사람이나 자동화 도구가 동시에 같은 리소스를 수정할 때 충돌을 방지하는 기능입니다.
초보자는 일단 넘어가도 괜찮고, 나중에 필요할 때 찾아보아도 괜찮습니다.사용법:
kubectl apply --server-side -f app.yaml
마지막으로 재미있는 사실 하나.
우리는 YAML로 파일을 작성하지만, kubectl apply를 실행하면 내부적으로 JSON으로 변환되어 API Server에 전달됩니다.
# 우리가 작성하는 YAML (사람 친화적)
apiVersion: v1
kind: Pod
metadata:
name: nginx # 주석 가능
spec:
containers:
- name: nginx
image: nginx
// API Server가 받는 JSON (기계 친화적)
{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "nginx"
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx"
}
]
}
}
YAML = 사람을 위한 포맷 (읽기 쉽고, 주석 가능)
JSON = 기계를 위한 포맷 (파싱 빠름, 프로그래밍 언어와 호환)
오늘은 실무에서 겪는 세 가지 문제와 쿠버네티스의 해결 방법을 알아봤습니다:
핵심은 선언적 방식(apply) 입니다:
이것이 바로 Infrastructure as Code (코드로 인프라 관리)의 시작입니다.
다음 포스팅에서는:
rollout restart)--prune)을 다뤄보겠습니다. 🚀
이 글에서 배운 핵심 명령어:
# Namespace
kubectl create namespace dev
kubectl get pods -n dev
# Context
kubectl config get-contexts
kubectl config use-context dev-cluster
# 선언적 관리
kubectl apply -f app.yaml
kubectl delete -f app.yaml --wait=true
# 상태 확인
kubectl diff -f app.yaml
kubectl get pod nginx -o yaml