Pod IP는 계속 바뀐다 — 그래서 Service가 필요하다
Pod는 죽고 다시 뜰 때마다 IP가 바뀐다. 그래서 Pod IP를 직접 가리키면 안 된다. Service는 변하지 않는 가상 IP(와 이름)를 제공하고, 뒤에 있는 Pod들로 트래픽을 분산한다.
Service 타입
| 타입 | 용도 |
|---|---|
| ClusterIP | 기본값. 클러스터 내부에서만 접근 가능한 가상 IP. 내부 Pod 간 통신용. |
| Headless | ClusterIP 없이 각 Pod의 DNS를 직접 제공. StatefulSet과 함께 쓴다. |
| NodePort | 노드의 특정 포트를 열어 외부에서 접근. 내부적으로 ClusterIP를 포함한다. |
| LoadBalancer | 외부 로드밸런서를 통해 접근. NodePort를 포함한다. 클라우드에선 자동 제공, 온프레미스에선 MetalLB 등이 필요. |
정리하면 NodePort는 ClusterIP를 품고, LoadBalancer는 NodePort를 품는 포함 관계다. 즉 NodePort·LoadBalancer는 외부 노출과 내부 Pod 간 연결을 동시에 해준다.
Service와 Pod는 레이블로 연결된다
Service와 Pod는 별개의 리소스다. Service가 어떤 Pod로 트래픽을 보낼지는 selector로 결정하는데, 이 선택 기준이 바로 label이다. Pod가 죽고 새 IP로 떠도 같은 레이블만 달려 있으면 Service가 자동으로 다시 연결한다.
kubectl apply -f nginx-deploy.yaml
kubectl get pod --show-labels # Pod label 확인
kubectl apply -f nginx-ClusterIP-svc.yaml
kubectl get svc nginx-svc
kubectl get endpoints nginx-svc # 실제 연결된 Pod IP 확인
# busybox로 서비스명 호출 테스트
kubectl run busybox --image=busybox:1.28 --restart=Never -- sleep 1d
kubectl exec -it busybox -- sh
wget -O- nginx-svc
Service로 트래픽이 안 가면
kubectl get endpoints를 먼저 보자. endpoints가 비어 있으면 selector 레이블이 Pod 레이블과 안 맞는 것이다. CoreDNS와 FQDN 규칙
클러스터 내부 이름 해석은 CoreDNS가 담당한다. 서비스의 정식 주소(FQDN)는 다음 형식이다.
[svc_name].[namespace].svc.cluster.local
- 같은 네임스페이스면
svc_name만으로도 접근된다. - 다른 네임스페이스면 최소
svc_name.namespace까지 적어야 한다. - StatefulSet + Headless의 개별 Pod는
pod_name.svc_headless.namespace.svc.cluster.local로 접근한다.
예를 들어 dev 네임스페이스의 MySQL에 다른 네임스페이스에서 붙는다면 이렇게 된다.
jdbc:mysql://dev-mysql.dev.svc.cluster.local:3306/board
# dev-mysql : 서비스명
# dev : Pod가 위치한 네임스페이스
# svc.cluster.local : 클러스터 기본 도메인
왜 같은 네임스페이스면 서비스명만으로 되는가
비밀은 Pod 안의 /etc/resolv.conf에 있다. 여기 search 항목에 자기 네임스페이스의 도메인 접미사가 등록돼 있어, 짧은 이름을 질의하면 자동으로 접미사를 붙여 순차 탐색한다. default 네임스페이스 Pod는 default.svc.cluster.local을 붙여 찾기 때문에, 다른 네임스페이스 서비스는 짧은 이름으로 못 찾는 것이다.
CoreDNS는 이중화를 위해 보통 Deployment로 2개의 Pod가 실행된다.
DNS가 안 될 때 점검 순서
kubectl -n kube-system get pods -l k8s-app=kube-dns # 상태 확인
kubectl -n kube-system logs -l k8s-app=kube-dns # 로그 확인
kubectl -n kube-system rollout restart deployment coredns # 재시작
온프레미스 LoadBalancer — MetalLB
클라우드에선 LoadBalancer 타입 서비스를 만들면 알아서 외부 IP가 붙는다. 하지만 온프레미스/로컬엔 그걸 해줄 주체가 없어 EXTERNAL-IP가 영원히 <pending>에 머문다. MetalLB가 이 역할을 대신한다.
설치와 IP 풀 설정
0.13 버전부터 설정 방식이 바뀌었다. 예전엔 values.yaml의 configInline으로 IP 풀을 잡았지만, 0.15.2에서는 CRD 기반으로 IPAddressPool과 L2Advertisement 리소스로 설정한다.
helm pull metallb/metallb --version v0.15.2
tar xvfz metallb-0.15.2.tgz && mv metallb metallb-0.15.2
cd metallb-0.15.2 && cp values.yaml my-values.yaml
kubectl create ns metallb-system
helm install metallb ./ -f my-values.yaml -n metallb-system
kubectl apply -f ipaddresspool.yaml -n metallb-system
kubectl apply -f l2advertisement.yaml -n metallb-system
IPAddressPool 리소스 예시다. 이 IP 대역은 노드와 같은 브로드캐스트 도메인에 있어야 한다
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-ip-pool
namespace: metallb-system
spec:
addresses:
- 192.168.0.240-192.168.0.250
동작 원리
MetalLB의 Controller와 Speaker는 ClusterRole 권한으로 모든 네임스페이스의 서비스를 감시한다. LoadBalancer 타입 서비스가 생기면 Controller가 IP 풀에서 하나를 골라 EXTERNAL-IP로 주입하고, Speaker가 L2(ARP) 광고로 그 IP를 외부에 알린다. 외부 클라이언트가 그 IP로 접속하면 Speaker가 떠 있는 노드가 요청을 받는다.
서비스별로 IP 풀 나누기
풀을 여러 개 만들고 서비스 어노테이션으로 어떤 풀을 쓸지 지정할 수 있다. IPAddressPool의 metadata.name을 서비스의 metallb.universe.tf/address-pool 어노테이션과 맞추면 된다.
apiVersion: v1
kind: Service
metadata:
name: nginx-a
annotations:
metallb.universe.tf/address-pool: "a-pool"
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- port: 80
풀에 autoAssign: false를 주면, 어노테이션으로 명시한 서비스에만 IP를 주고 자동 할당은 막을 수 있다.
L7 라우팅 — Ingress와 Ingress Controller
Service가 L4(IP·포트) 수준이라면, Ingress는 호스트명과 URL 경로 기준으로 라우팅하는 L7 규칙이다. 하나의 진입점에서 /api는 A 서비스로, /web은 B 서비스로 나눠 보낼 수 있다.
중요한 건 Ingress 리소스 그 자체로는 아무것도 안 한다는 점이다. 규칙을 해석해 실제 트래픽을 처리하는 Ingress Controller(Pod)가 반드시 하나 이상 있어야 한다. Controller가 Ingress 리소스를 계속 감시하다가 새 규칙이 생기면 자기 설정을 거기 맞춰 갱신한다.
아래는 Traefik(k3s 기본 Controller)을 쓰는 Ingress 예시다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
spec:
ingressClassName: traefik
rules:
- host: mihee.test.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
대표적인 Ingress Controller로는 Traefik, Nginx Ingress Controller, HAProxy, Istio Gateway가 있다. 그중 Traefik은 동적 리버스 프록시로, Let's Encrypt 자동 HTTPS 발급·로드밸런싱·미들웨어 같은 기능을 제공한다.
- 쿠버네티스란 무엇인가?
- k3s로 클러스터 구성하기
- 쿠버네티스 네트워킹 — Service, DNS, MetalLB, Ingress
- 쿠버네티스 스토리지 — PV/PVC/StorageClass, OpenEBS
- Helm으로 애플리케이션 배포하기
- CI/CD + 모니터링 스택 구축
'기타 > Kubernetes' 카테고리의 다른 글
| CI/CD + 모니터링 스택 구축 (0) | 2026.05.31 |
|---|---|
| Helm으로 애플리케이션 배포하기 (0) | 2026.05.31 |
| k3s로 클러스터 구성하기 (0) | 2026.05.31 |
| 쿠버네티스란 무엇인가? (0) | 2026.05.31 |