k8s 1주 3일차 - 서비스

진웅·2025년 5월 26일

K8S Basics

목록 보기
5/40

Kubernetes Service 완전 정복: 네트워킹의 핵심

🎯 학습 목표

  • Service가 필요한 이유와 해결하는 문제점 이해
  • 4가지 Service 타입의 특징과 사용 사례 파악
  • 실제 애플리케이션에서 Service 활용법 습득
  • Service Discovery와 로드밸런싱 메커니즘 이해

📋 목차

  1. Service가 필요한 이유
  2. Service 기본 개념
  3. ClusterIP Service
  4. NodePort Service
  5. LoadBalancer Service
  6. ExternalName Service
  7. Service Discovery 실습
  8. Ingress 소개

1. Service가 필요한 이유

🤔 Pod 직접 접근의 문제점

먼저 문제 상황을 직접 체험해봅시다.

# 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: 문제 상황 체험

# 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가 바뀜!

Pod 직접 접근의 문제점들

  1. 동적 IP: Pod 재시작 시 IP 변경
  2. 복제본 관리: 여러 Pod 중 어느 것에 접근할지?
  3. 로드밸런싱: 트래픽을 어떻게 분산할지?
  4. 외부 접근: 클러스터 외부에서 어떻게 접근할지?
  5. 서비스 디스커버리: 다른 서비스가 이 앱을 어떻게 찾을지?

2. Service 기본 개념

🌐 Service란?

Service는 Pod들의 집합에 대한 안정적인 네트워크 엔드포인트를 제공합니다.

┌─────────────────┐    ┌──────────────┐
│   클라이언트      │───▶│   Service    │
└─────────────────┘    │ (고정 IP/DNS) │
                       └──────┬───────┘
                              │ 로드밸런싱
                    ┌─────────┼─────────┐
                    ▼         ▼         ▼
                 ┌─────┐  ┌─────┐  ┌─────┐
                 │Pod 1│  │Pod 2│  │Pod 3│
                 └─────┘  └─────┘  └─────┘

📊 Service 타입 비교

타입접근 범위용도IP 할당
ClusterIP클러스터 내부마이크로서비스 통신내부 IP
NodePort외부 (노드 포트)개발/테스트내부 IP + 노드 포트
LoadBalancer외부 (로드밸런서)운영 환경외부 IP
ExternalName외부 서비스 연결외부 서비스 추상화DNS 이름

3. ClusterIP Service

🏠 ClusterIP: 클러스터 내부 통신

가장 기본적인 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

📝 실습 2: ClusterIP Service 생성

# 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

🔍 ClusterIP 동작 원리

# 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

💡 Label Selector 실습

# 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

4. NodePort Service

🚪 NodePort: 외부 접근의 첫 단계

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

📝 실습 3: NodePort Service 테스트

# 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      │
└─────────────┘

📝 실습 4: 로드밸런싱 확인

# 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

5. LoadBalancer Service

☁️ LoadBalancer: 클라우드 환경의 진짜 해답

클라우드 환경에서 외부 로드밸런서를 자동으로 프로비저닝합니다.

# 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

📝 실습 5: LoadBalancer Service

# 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

📌 주의사항:

  • 로컬 환경(minikube, kind)에서는 LoadBalancer가 pending 상태로 남을 수 있습니다
  • 실제 클라우드 환경(AWS, GCP, Azure)에서만 외부 IP가 할당됩니다

6. ExternalName Service

🔗 ExternalName: 외부 서비스 추상화

클러스터 내부에서 외부 서비스를 마치 내부 서비스처럼 사용할 수 있게 합니다.

# external-database.yaml
apiVersion: v1
kind: Service
metadata:
  name: external-database
spec:
  type: ExternalName
  externalName: database.example.com
  ports:
  - port: 5432

📝 실습 6: ExternalName Service

# 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'로 접근

7. Service Discovery 실습

🔍 마이크로서비스 간 통신

실제 마이크로서비스 환경을 구성하여 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

📝 실습 7: 마이크로서비스 통신

# 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

🌐 DNS 네이밍 규칙

# Service DNS 이름 패턴들
service-name                           # 같은 네임스페이스
service-name.namespace                 # 다른 네임스페이스
service-name.namespace.svc             # 풀 도메인
service-name.namespace.svc.cluster.local # 완전한 FQDN

8. Ingress 소개

🚀 Ingress: HTTP 라우팅의 고급 기법

여러 서비스를 하나의 진입점으로 관리합니다.

# 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

📝 실습 8: Ingress 기본 사용

# 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 접근

9. 고급 Service 기능

🎯 Session Affinity

특정 클라이언트를 항상 같은 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

🔧 헬스체크와 Readiness

# 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

📝 실습 9: 헬스체크 동작 확인

# 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

10. 트러블슈팅 가이드

🔧 일반적인 문제들

# 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

📊 Service 디버깅 체크리스트

확인 항목명령어해결 방법
Pod 실행 상태kubectl get podsPod 재시작 또는 로그 확인
Label 매칭kubectl get pods --show-labelsLabel 수정
Endpoints 연결kubectl get endpointsSelector 확인
포트 설정kubectl describe servicePort 매핑 수정
DNS 해상도nslookup service-nameCoreDNS 확인

🧹 실습 리소스 정리

# 모든 실습 리소스 삭제
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의 핵심 역할

  1. 안정적인 엔드포인트: Pod IP 변경에 관계없이 일관된 접근점 제공
  2. 로드밸런싱: 여러 Pod 간 트래픽 분산
  3. 서비스 디스커버리: DNS 기반 서비스 검색
  4. 외부 접근: 클러스터 외부에서 애플리케이션 접근

🎯 Service 타입 선택 가이드

상황권장 Service 타입이유
마이크로서비스 통신ClusterIP내부 통신만 필요
개발/테스트NodePort간단한 외부 접근
운영 환경LoadBalancer안정적인 외부 접근
외부 API 연동ExternalName외부 서비스 추상화

💡 Best Practices

  1. 명확한 네이밍: 서비스 용도가 명확한 이름 사용
  2. Label 관리: 일관된 Label 스키마 적용
  3. 헬스체크: Readiness/Liveness Probe 설정
  4. 포트 표준화: 일관된 포트 번호 사용
  5. 네임스페이스 활용: 환경별 서비스 분리

Service는 Kubernetes에서 네트워킹의 핵심이며, 마이크로서비스 아키텍처를 구현하는 데 필수적인 요소입니다!

profile
bytebliss

0개의 댓글