Kubernetes Service Selector & Endpoints 트러블슈팅
문제 상황#
- API 요청 시 503 Service Unavailable 에러
- Istio 로그:
no_healthy_upstream - 파드는 Running 상태인데 서비스 연결 안 됨
원인 분석#
Service selector 라벨과 Pod 라벨 불일치
# 엔드포인트 확인 - <none>이면 문제!
kubectl -n staging-webs get endpoints api-gateway
NAME ENDPOINTS AGE
api-gateway <none> 34h # ← 파드 IP가 없음!
동작 원리#
┌─────────────────────────────────────────────────────────┐
│ Service (api-gateway) │
│ selector: │
│ app: staging-api-gateway │
│ app.kubernetes.io/part-of: staging-webs ← 필수! │
└─────────────────────────────────────────────────────────┘
│
▼ selector 라벨 가진 파드 검색
┌─────────────────────────────────────────────────────────┐
│ Endpoints (자동 생성) │
│ addresses: │
│ - 10.0.20.100 (파드1 IP) │
│ - 10.0.20.101 (파드2 IP) │
└─────────────────────────────────────────────────────────┘
│
▼ 트래픽 로드밸런싱
┌─────────────────────────────────────────────────────────┐
│ Pod (라벨이 selector와 매칭되어야 함) │
│ labels: │
│ app: staging-api-gateway ✓ │
│ app.kubernetes.io/part-of: staging-webs ✓ │
└─────────────────────────────────────────────────────────┘
핵심: Service selector의 모든 라벨이 Pod에 있어야 Endpoints에 등록됨
진단 방법#
1. 엔드포인트 확인#
kubectl -n <namespace> get endpoints <service-name>
# 정상: IP 있음
NAME ENDPOINTS AGE
api-gateway 10.0.20.100:8085 34h
# 문제: <none>
NAME ENDPOINTS AGE
api-gateway <none> 34h
2. Service selector 확인#
kubectl -n <namespace> get svc <service-name> -o yaml | grep -A 10 "selector:"
3. Pod 라벨 확인#
kubectl -n <namespace> get pod <pod-name> --show-labels
4. 라벨 비교#
# Service selector
selector:
app: staging-api-gateway
app.kubernetes.io/component: backend
app.kubernetes.io/instance: staging-api-gateway
app.kubernetes.io/name: java-service
app.kubernetes.io/part-of: staging-webs # ← 이거 있는지 확인!
# Pod labels
app=staging-api-gateway ✓
app.kubernetes.io/component=backend ✓
app.kubernetes.io/instance=staging-api-gateway ✓
app.kubernetes.io/name=java-service ✓
app.kubernetes.io/part-of=??? ✗ ← 없으면 매칭 실패!
해결#
방법 1: 앱 재배포 (ArgoCD sync)#
# ArgoCD CLI
argocd app sync <app-name>
# kubectl로 sync 트리거
kubectl -n argocd patch application <app-name> \
--type merge \
-p '{"operation":{"initiatedBy":{"username":"admin"},"sync":{}}}'
방법 2: 수동 라벨 추가 (임시 조치)#
kubectl -n <namespace> label pod <pod-name> app.kubernetes.io/part-of=staging-webs
방법 3: Deployment 라벨 수정#
kubectl -n <namespace> patch deployment <deployment-name> \
--type merge \
-p '{"spec":{"template":{"metadata":{"labels":{"app.kubernetes.io/part-of":"staging-webs"}}}}}'
예방 방법#
Helm 차트에서 selectorLabels 일관성 유지
- Service selector와 Deployment pod template labels가 동일해야 함
selectorLabels는 immutable
- 한번 정하면 변경하지 않는 것이 좋음
- 변경 시 Service와 Deployment 모두 업데이트 필요
ArgoCD 사용 시 주의
- 차트 수정 후 반드시 모든 관련 앱 sync
- OutOfSync 상태 방치하지 않기
관련 명령어 모음#
# 전체 진단 한번에
NS=staging-webs
SVC=api-gateway
echo "=== Endpoints ==="
kubectl -n $NS get endpoints $SVC
echo "=== Service Selector ==="
kubectl -n $NS get svc $SVC -o jsonpath='{.spec.selector}' | jq .
echo "=== Pod Labels ==="
kubectl -n $NS get pods -l app=$SVC --show-labels
echo "=== Istio 로그 (최근 에러) ==="
kubectl -n istio-system logs -l app=istio-ingressgateway --tail=10 | grep "no_healthy"
참고#
- Kubernetes Service: https://kubernetes.io/docs/concepts/services-networking/service/
- Label Selectors: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/