먼저 문제 상황을 직접 체험해봅시다.
# problem-demo.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp
spec:
replicas: 3
selector:
matchLabels:
app: webapp
template:
metadata:
labels:
app: webapp
spec:
containers:
- name: webapp
image: nginx
ports:
- containerPort: 80
# 1. 애플리케이션 배포
kubectl apply -f problem-demo.yaml
# 2. Pod IP 확인
kubectl get pods -o wide
# 결과 예시:
# webapp-xxx-xxx 10.244.1.10
# webapp-xxx-xxx 10.244.2.15
# webapp-xxx-xxx 10.244.1.20
# 3. Pod에 직접 접근 시도 (클러스터 내부에서)
kubectl exec -it webapp-xxx-xxx -- curl http://10.244.1.10
# 4. Pod 재시작 후 IP 변화 확인
kubectl delete pod webapp-xxx-xxx
kubectl get pods -o wide
# IP가 바뀜!
Service는 Pod들의 집합에 대한 안정적인 네트워크 엔드포인트를 제공합니다.
┌─────────────────┐ ┌──────────────┐
│ 클라이언트 │───▶│ Service │
└─────────────────┘ │ (고정 IP/DNS) │
└──────┬───────┘
│ 로드밸런싱
┌─────────┼─────────┐
▼ ▼ ▼
┌─────┐ ┌─────┐ ┌─────┐
│Pod 1│ │Pod 2│ │Pod 3│
└─────┘ └─────┘ └─────┘
| 타입 | 접근 범위 | 용도 | IP 할당 |
|---|---|---|---|
| ClusterIP | 클러스터 내부 | 마이크로서비스 통신 | 내부 IP |
| NodePort | 외부 (노드 포트) | 개발/테스트 | 내부 IP + 노드 포트 |
| LoadBalancer | 외부 (로드밸런서) | 운영 환경 | 외부 IP |
| ExternalName | 외부 서비스 연결 | 외부 서비스 추상화 | DNS 이름 |
가장 기본적인 Service 타입으로, 클러스터 내부에서만 접근 가능합니다.
# webapp-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp-service
spec:
type: ClusterIP # 기본값이므로 생략 가능
selector:
app: webapp
ports:
- port: 80 # Service가 받을 포트
targetPort: 80 # Pod의 포트
protocol: TCP
# 1. Service 생성
kubectl apply -f webapp-clusterip.yaml
# 2. Service 정보 확인
kubectl get services
kubectl describe service webapp-service
# 3. Service IP 확인
SERVICE_IP=$(kubectl get service webapp-service -o jsonpath='{.spec.clusterIP}')
echo "Service IP: $SERVICE_IP"
# 4. 클러스터 내부에서 Service로 접근
kubectl run test-pod --image=busybox --rm -it --restart=Never -- sh
# Pod 내부에서:
wget -qO- http://webapp-service
wget -qO- http://webapp-service.default.svc.cluster.local
exit
# 1. Endpoints 확인 (Service가 연결하는 Pod들)
kubectl get endpoints webapp-service
# 2. Service와 Pod 연결 상태 확인
kubectl describe endpoints webapp-service
# 3. DNS 해상도 테스트
kubectl run dns-test --image=busybox --rm -it --restart=Never -- nslookup webapp-service
# 1. 새로운 Pod 추가 (다른 label)
kubectl run extra-pod --image=nginx --labels="app=webapp,version=v2"
# 2. Endpoints 변화 확인
kubectl get endpoints webapp-service
# 3. Label 수정 실험
kubectl label pod extra-pod app=other --overwrite
kubectl get endpoints webapp-service
NodePort는 클러스터의 모든 노드에 특정 포트를 열어 외부 접근을 허용합니다.
# webapp-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp-nodeport
spec:
type: NodePort
selector:
app: webapp
ports:
- port: 80 # Service 포트
targetPort: 80 # Pod 포트
nodePort: 30080 # 노드 포트 (30000-32767 범위)
protocol: TCP
# 1. NodePort Service 생성
kubectl apply -f webapp-nodeport.yaml
# 2. Service 정보 확인
kubectl get services
kubectl describe service webapp-nodeport
# 3. 노드 IP 확인
kubectl get nodes -o wide
# 4. 외부에서 접근 테스트
# http://[NODE-IP]:30080 으로 브라우저에서 접근
# 또는 curl 명령어로:
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}')
curl http://$NODE_IP:30080
외부 클라이언트
│
▼ :30080
┌─────────────┐
│ Node │
└──────┬──────┘
│ :80
▼
┌─────────────┐
│ Service │
└──────┬──────┘
│ :80
▼
┌─────────────┐
│ Pod │
└─────────────┘
# webapp-with-hostname.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-hostname
spec:
replicas: 3
selector:
matchLabels:
app: webapp-hostname
template:
metadata:
labels:
app: webapp-hostname
spec:
containers:
- name: webapp
image: nginxdemos/hello
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: webapp-hostname-service
spec:
type: NodePort
selector:
app: webapp-hostname
ports:
- port: 80
targetPort: 80
nodePort: 30081
# 1. 호스트명을 보여주는 앱 배포
kubectl apply -f webapp-with-hostname.yaml
# 2. 여러 번 접근하여 로드밸런싱 확인
for i in {1..10}; do
curl -s http://$NODE_IP:30081 | grep "Server name"
sleep 1
done
클라우드 환경에서 외부 로드밸런서를 자동으로 프로비저닝합니다.
# webapp-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp-loadbalancer
spec:
type: LoadBalancer
selector:
app: webapp-hostname
ports:
- port: 80
targetPort: 80
protocol: TCP
# 1. LoadBalancer Service 생성
kubectl apply -f webapp-loadbalancer.yaml
# 2. 외부 IP 할당 대기
kubectl get services webapp-loadbalancer -w
# EXTERNAL-IP가 <pending>에서 실제 IP로 변경될 때까지 대기
# 3. 외부 IP로 접근
EXTERNAL_IP=$(kubectl get service webapp-loadbalancer -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://$EXTERNAL_IP
📌 주의사항:
클러스터 내부에서 외부 서비스를 마치 내부 서비스처럼 사용할 수 있게 합니다.
# external-database.yaml
apiVersion: v1
kind: Service
metadata:
name: external-database
spec:
type: ExternalName
externalName: database.example.com
ports:
- port: 5432
# 1. ExternalName Service 생성
kubectl apply -f external-database.yaml
# 2. DNS 해상도 확인
kubectl run dns-test --image=busybox --rm -it --restart=Never -- nslookup external-database
# 3. 애플리케이션에서 사용
# 애플리케이션 코드에서는 'external-database:5432'로 접근
실제 마이크로서비스 환경을 구성하여 Service Discovery를 체험해봅시다.
# microservice-demo.yaml
# Frontend 서비스
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
replicas: 2
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
spec:
containers:
- name: frontend
image: nginxdemos/hello
env:
- name: BACKEND_URL
value: "http://backend-service:8080"
ports:
- containerPort: 80
---
# Backend 서비스
apiVersion: apps/v1
kind: Deployment
metadata:
name: backend
spec:
replicas: 3
selector:
matchLabels:
app: backend
template:
metadata:
labels:
app: backend
spec:
containers:
- name: backend
image: httpd
ports:
- containerPort: 80
---
# Frontend Service
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: NodePort
selector:
app: frontend
ports:
- port: 80
nodePort: 30090
---
# Backend Service
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend
ports:
- port: 8080
targetPort: 80
# 1. 마이크로서비스 배포
kubectl apply -f microservice-demo.yaml
# 2. 서비스 확인
kubectl get services
# 3. Frontend에서 Backend 호출 테스트
kubectl exec -it deployment/frontend -- sh
# 컨테이너 내부에서:
wget -qO- http://backend-service:8080
curl http://backend-service:8080/server-status
exit
# 4. 서비스 DNS 확인
kubectl exec -it deployment/frontend -- nslookup backend-service
# Service DNS 이름 패턴들
service-name # 같은 네임스페이스
service-name.namespace # 다른 네임스페이스
service-name.namespace.svc # 풀 도메인
service-name.namespace.svc.cluster.local # 완전한 FQDN
여러 서비스를 하나의 진입점으로 관리합니다.
# simple-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: webapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: webapp.local
http:
paths:
- path: /frontend
pathType: Prefix
backend:
service:
name: frontend-service
port:
number: 80
- path: /backend
pathType: Prefix
backend:
service:
name: backend-service
port:
number: 8080
# 1. Ingress Controller 설치 (minikube 예시)
minikube addons enable ingress
# 2. Ingress 생성
kubectl apply -f simple-ingress.yaml
# 3. Ingress 상태 확인
kubectl get ingress
kubectl describe ingress webapp-ingress
# 4. hosts 파일 수정 후 테스트
# /etc/hosts에 추가: [MINIKUBE-IP] webapp.local
# 브라우저에서 http://webapp.local/frontend 접근
특정 클라이언트를 항상 같은 Pod로 라우팅합니다.
# session-affinity.yaml
apiVersion: v1
kind: Service
metadata:
name: webapp-sticky
spec:
selector:
app: webapp-hostname
ports:
- port: 80
targetPort: 80
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 300
# healthcheck-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: webapp-health
spec:
replicas: 3
selector:
matchLabels:
app: webapp-health
template:
metadata:
labels:
app: webapp-health
spec:
containers:
- name: webapp
image: nginx
ports:
- containerPort: 80
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 20
# 1. 헬스체크가 있는 앱 배포
kubectl apply -f healthcheck-deployment.yaml
# 2. Pod 상태 확인
kubectl get pods -w
# 3. Endpoints 확인 (Ready가 아닌 Pod는 제외됨)
kubectl get endpoints webapp-service
# 4. 헬스체크 실패 시뮬레이션
kubectl exec -it deployment/webapp-health -- rm /usr/share/nginx/html/index.html
kubectl get pods -w
# 1. Service가 Pod를 찾지 못하는 경우
kubectl get endpoints [service-name]
kubectl describe service [service-name]
# 2. Label selector 확인
kubectl get pods --show-labels
kubectl get service [service-name] -o yaml | grep selector -A 3
# 3. 포트 설정 확인
kubectl describe service [service-name] | grep Port
kubectl describe pod [pod-name] | grep Port
# 4. DNS 문제 해결
kubectl exec -it [pod-name] -- nslookup [service-name]
kubectl get pods -n kube-system | grep dns
| 확인 항목 | 명령어 | 해결 방법 |
|---|---|---|
| Pod 실행 상태 | kubectl get pods | Pod 재시작 또는 로그 확인 |
| Label 매칭 | kubectl get pods --show-labels | Label 수정 |
| Endpoints 연결 | kubectl get endpoints | Selector 확인 |
| 포트 설정 | kubectl describe service | Port 매핑 수정 |
| DNS 해상도 | nslookup service-name | CoreDNS 확인 |
# 모든 실습 리소스 삭제
kubectl delete deployment webapp webapp-hostname backend frontend webapp-health
kubectl delete service webapp-service webapp-nodeport webapp-loadbalancer webapp-sticky frontend-service backend-service external-database
kubectl delete ingress webapp-ingress
# 최종 확인
kubectl get all
| 상황 | 권장 Service 타입 | 이유 |
|---|---|---|
| 마이크로서비스 통신 | ClusterIP | 내부 통신만 필요 |
| 개발/테스트 | NodePort | 간단한 외부 접근 |
| 운영 환경 | LoadBalancer | 안정적인 외부 접근 |
| 외부 API 연동 | ExternalName | 외부 서비스 추상화 |
Service는 Kubernetes에서 네트워킹의 핵심이며, 마이크로서비스 아키텍처를 구현하는 데 필수적인 요소입니다!