들어가며
Kubernetes에서 Pod는 임시적인 특성을 가지고 있어 언제든지 생성되거나 삭제될 수 있습니다. 하지만 애플리케이션을 실제 운영 환경에서 사용하려면 안정적인 네트워크 엔드포인트가 필요합니다. 이때 Kubernetes Service가 핵심 역할을 담당합니다.
Service는 Pod 집합에 대한 네트워크 접근을 추상화하고 안정적인 엔드포인트를 제공합니다. 하지만 다양한 사용 사례에 따라 여러 종류의 Service 타입이 존재합니다. 이번 포스트에서는 각 Service 타입의 특징과 언제 사용해야 하는지 자세히 알아보겠습니다.
1. ClusterIP (기본 타입)
특징
- Kubernetes 클러스터 내부에서만 접근 가능한 가상 IP 제공
- 기본 Service 타입으로 별도 지정하지 않으면 자동으로 ClusterIP가 설정됨
- 클러스터 내부 통신용으로 사용
사용 사례
- 마이크로서비스 간 내부 통신
- 데이터베이스 서비스와 애플리케이션 간 연결
- 외부 노출이 필요 없는 백엔드 서비스
예시 YAML
apiVersion: v1
kind: Service
metadata:
name: my-backend-service
spec:
type: ClusterIP # 생략 가능 (기본값)
selector:
app: backend
ports:
- protocol: TCP
port: 80
targetPort: 8080
접근 방법
# 클러스터 내부에서만 접근 가능
curl http://my-backend-service.default.svc.cluster.local
2. NodePort
특징
- 모든 노드의 특정 포트를 통해 외부에서 접근 가능
- 기본 포트 범위: 30000-32767
- 각 노드의 IP:NodePort로 서비스 접근 가능
사용 사례
- 개발 환경에서 간단한 외부 접근
- 로드 밸런서 없이 외부 노출이 필요한 경우
- 특정 노드를 통한 직접 접근이 필요한 경우
예시 YAML
apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: webapp
ports:
- protocol: TCP
port: 80
targetPort: 8080
nodePort: 30080 # 생략 시 자동 할당
접근 방법
# 모든 노드의 IP를 통해 접근 가능
curl http://NODE_IP:30080
3. LoadBalancer
특징
- 클라우드 제공업체의 로드 밸런서를 자동으로 프로비저닝
- 외부 IP를 통해 서비스에 접근 가능
- 클라우드 환경에서만 완전히 지원 (AWS, GCP, Azure 등)
사용 사례
- 프로덕션 환경에서 외부 사용자 접근
- 고가용성이 필요한 웹 애플리케이션
- SSL 종료가 필요한 서비스
예시 YAML
apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 8080
loadBalancerSourceRanges:
- "10.0.0.0/8" # 접근 허용 IP 범위 제한
접근 방법
# 외부 IP를 통해 접근
curl http://EXTERNAL_IP
4. ExternalName
특징
- 외부 서비스를 클러스터 내부에서 참조할 수 있게 해주는 DNS 별칭
- 실제 Pod를 가리키지 않음
- CNAME 레코드를 통해 외부 서비스로 리다이렉션
사용 사례
- 외부 데이터베이스 서비스 참조
- 서드파티 API 엔드포인트 추상화
- 레거시 시스템과의 통합
예시 YAML
apiVersion: v1
kind: Service
metadata:
name: external-database
spec:
type: ExternalName
externalName: database.example.com
ports:
- protocol: TCP
port: 5432
접근 방법
# 클러스터 내부에서 외부 서비스 이름으로 접근
mysql -h external-database.default.svc.cluster.local -u user -p
5. Headless Service (헤드리스 서비스)
특징
- clusterIP: None으로 설정하여 가상 IP를 할당받지 않음
- DNS 쿼리 시 개별 Pod IP들을 직접 반환
- 로드 밸런싱을 하지 않고 클라이언트가 직접 Pod 선택
왜 헤드리스 서비스를 사용하는가?
문제 상황: MySQL 마스터-슬레이브 구조
일반 서비스를 사용하면:
[애플리케이션] → [Service] → [랜덤하게 Pod 선택]
↓
[Master Pod] 또는 [Slave Pod]
문제: 쓰기 작업이 Slave Pod로 갈 수 있음 → 데이터 불일치!
해결책: 헤드리스 서비스 사용
헤드리스 서비스를 사용하면:
[애플리케이션] → DNS 쿼리 → 모든 Pod IP 반환
↓
직접 선택
↓
[Master Pod] (쓰기용) / [Slave Pod] (읽기용)
구체적인 예시
1. 데이터베이스 클러스터
apiVersion: v1
kind: Service
metadata:
name: mysql-headless
spec:
clusterIP: None
selector:
app: mysql
ports:
- port: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql-headless
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
env:
- name: MYSQL_ROOT_PASSWORD
value: "password"
애플리케이션에서 사용하는 방법
# Python 예시
import socket
def get_mysql_pods():
# DNS 쿼리로 모든 Pod IP 반환
pod_ips = socket.getaddrinfo('mysql-headless.default.svc.cluster.local', 3306)
return [ip[4][0] for ip in pod_ips]
def connect_to_database():
pod_ips = get_mysql_pods()
# 첫 번째 Pod는 마스터 (쓰기용)
master_ip = pod_ips[0]
# 나머지 Pod들은 슬레이브 (읽기용)
slave_ips = pod_ips[1:]
# 쓰기 작업
write_connection = mysql.connect(host=master_ip, user='root', password='password')
# 읽기 작업 (로드 밸런싱)
read_connection = mysql.connect(host=random.choice(slave_ips), user='root', password='password')
2. Kafka 클러스터
apiVersion: v1
kind: Service
metadata:
name: kafka-headless
spec:
clusterIP: None
selector:
app: kafka
ports:
- port: 9092
// Java 예시 - Kafka Producer
Properties props = new Properties();
// 헤드리스 서비스로 모든 브로커 발견
props.put("bootstrap.servers", "kafka-0.kafka-headless:9092,kafka-1.kafka-headless:9092,kafka-2.kafka-headless:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
Producer<String, String> producer = new KafkaProducer<>(props);
일반 서비스와 비교
일반 서비스 (ClusterIP)
# DNS 쿼리 결과
$ nslookup mysql-service.default.svc.cluster.local
Address: 10.96.0.100 # 서비스 IP (가상 IP)
# 문제: 항상 랜덤하게 Pod 선택됨
헤드리스 서비스
# DNS 쿼리 결과
$ nslookup mysql-headless.default.svc.cluster.local
Address: 10.244.1.10 # mysql-0 Pod IP
Address: 10.244.1.11 # mysql-1 Pod IP
Address: 10.244.1.12 # mysql-2 Pod IP
# 장점: 애플리케이션이 직접 Pod 선택 가능
사용 사례
- 데이터베이스 클러스터: 마스터/슬레이브 구분이 필요한 경우
- 분산 시스템: Kafka, Elasticsearch, Redis Cluster 등
- 게임 서버: 각 방(Pod)에 직접 연결이 필요한 경우
- 커스텀 로드 밸런싱: 애플리케이션 레벨에서 로드 밸런싱 제어
실제 사용 시나리오
1. 3-Tier 웹 애플리케이션 구조
SSR 프론트엔드 (Next.js, Nuxt.js 등)
# Frontend (LoadBalancer) - 서버에서 실행
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: LoadBalancer
selector:
tier: frontend
ports:
- port: 80
targetPort: 3000
---
# Backend API (ClusterIP) - 프론트엔드에서 직접 접근 가능
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: ClusterIP
selector:
tier: backend
ports:
- port: 8080
targetPort: 8080
---
# Database (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: database-service
spec:
type: ClusterIP
selector:
tier: database
ports:
- port: 5432
targetPort: 5432
CSR 프론트엔드 (React SPA, Vue.js SPA 등)
# Frontend (LoadBalancer) - 정적 파일 서빙
apiVersion: v1
kind: Service
metadata:
name: frontend-service
spec:
type: LoadBalancer
selector:
tier: frontend
ports:
- port: 80
targetPort: 80
---
# Backend API (LoadBalancer) - 브라우저에서 직접 접근 필요
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
type: LoadBalancer
selector:
tier: backend
ports:
- port: 80
targetPort: 8080
---
# Database (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: database-service
spec:
type: ClusterIP
selector:
tier: database
ports:
- port: 5432
targetPort: 5432
2. 마이크로서비스 아키텍처
# API Gateway (LoadBalancer)
apiVersion: v1
kind: Service
metadata:
name: api-gateway
spec:
type: LoadBalancer
selector:
app: gateway
ports:
- port: 80
targetPort: 8080
---
# User Service (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: user-service
spec:
type: ClusterIP
selector:
app: user-service
ports:
- port: 8080
targetPort: 8080
---
# Order Service (ClusterIP)
apiVersion: v1
kind: Service
metadata:
name: order-service
spec:
type: ClusterIP
selector:
app: order-service
ports:
- port: 8080
targetPort: 8080
주의사항 및 팁
1. 보안 고려사항
- NodePort 사용 시 방화벽 설정 확인
- LoadBalancer 사용 시 sourceRanges로 접근 제한
- 민감한 서비스는 ClusterIP 사용 권장
2. 성능 최적화
- 불필요한 외부 노출 방지
- 적절한 Service 타입 선택으로 네트워크 홉 최소화
- sessionAffinity 설정 고려
3. 디버깅 팁
# Service 상태 확인
kubectl get svc
# Service 상세 정보 확인
kubectl describe svc my-service
# 엔드포인트 확인
kubectl get endpoints my-service
# DNS 해상도 테스트
nslookup my-service.default.svc.cluster.local
결론
Kubernetes Service의 다섯 가지 타입은 각각 고유한 용도와 특성을 가지고 있습니다:
- ClusterIP: 클러스터 내부 통신용
- NodePort: 간단한 외부 접근용
- LoadBalancer: 프로덕션 환경의 외부 접근용
- ExternalName: 외부 서비스 참조용
- Headless Service: StatefulSet과 함께 사용하여 개별 Pod 접근용
적절한 Service 타입을 선택하는 것은 애플리케이션의 보안, 성능, 관리 편의성에 직접적인 영향을 미칩니다. 각 서비스의 특성을 정확히 이해하고 사용 사례에 맞는 타입을 선택하여 안정적이고 효율적인 Kubernetes 애플리케이션을 구축하시기 바랍니다.
참고 자료
'DevOps > Kubernates' 카테고리의 다른 글
[k8s] Kubernetes Storage 완전 정복: PV, PVC, StorageClass 개념 정리 (0) | 2025.07.14 |
---|---|
[k8s] API Groups 란? (0) | 2025.03.26 |
[k8s] Local Kubernetes Distribution (1) | 2025.01.25 |
[CKA] Certified Kubernetes Administrator (신청 방법, 꿀팁) (0) | 2025.01.05 |
[Kubernetes] Failed to pull image “no matching manifest for linux/arm64/v8 in the manifest list entries (0) | 2024.03.31 |
댓글