EKS 파드는 IP 가 자꾸 바뀌는데, 고정 IP 로 API 를 부르려면?
Summary
EKS 에서 API 서버를 파드(Pod)로 띄워두면 곧바로 마주치는 문제가 있어요. 파드가 죽었다 살아날 때마다 IP 가 바뀐다는 거예요. 그래서 “이 IP 로 API 부르면 돼” 하고 어딘가에 적어두면, 파드가 한 번 재시작되는 순간 그 주소는 죽어버립니다.
결론부터 말하면, 파드 IP 는 절대 직접 바라보지 않는 게 정답 이에요. 파드 앞에 변하지 않는 “대표 주소” 역할을 하는 계층을 하나 두면 됩니다. 클러스터 안에서는 Service, AWS 바깥에서 진짜 고정 IP 가 필요하면 NLB + Elastic IP 조합이에요. 이 글에서 차근차근 풀어볼게요.
💡 이 글에서 다루는 것
- 왜 파드 IP 는 계속 바뀌는지
- 변하지 않는 대표 주소를 만들어주는
Service의 개념- “누가 호출하느냐”(클러스터 안 / AWS 바깥)에 따라 갈리는 선택
- 외부에서 고정 IP 가 필요할 때 쓰는
NLB + Elastic IP설정NLBvsALB, 그리고 IP 대신 도메인을 쓰는 선택지
이 글은 Docker 와 Kubernetes 의 관계 와 K8s YAML 읽는 법 의 후속편 성격이에요. 앞 글들이 “K8s 가 뭐고 YAML 을 어떻게 읽나” 였다면, 이 글은 그 위에서 만나는 현실적인 네트워크 문제 하나를 정면으로 풉니다.
1. 왜 파드 IP 는 자꾸 바뀔까?
먼저 약어부터 풀고 갈게요.
| 약어 | 풀이 | 한 줄 설명 |
|---|---|---|
| EKS | Elastic Kubernetes Service | AWS 가 대신 관리해주는 쿠버네티스 서비스 |
| K8s | Kubernetes | 컨테이너 여러 개를 자동으로 굴려주는 시스템 |
| Pod(파드) | Pod | K8s 에서 컨테이너를 담는 가장 작은 단위 |
여기서 K8s 의 가장 중요한 성질이 하나 있어요.
⚠️ 파드는 “언제든 죽고 다시 태어나는 것” 을 전제로 설계되어 있어요.
파드가 죽었다 살아나면 그건 같은 파드가 부활한 게 아니라 완전히 새로운 파드가 생기는 거예요. 그리고 새 파드는 매번 새 IP 를 받습니다. 즉 파드 IP 는 휴대폰의 임시 번호 같은 거예요. 절대 이걸 외워서 호출하면 안 됩니다. 파드가 죽는 순간 그 번호는 사라지니까요.
그래서 “파드 IP 를 직접 바라본다”는 발상 자체를 버리는 것 이 첫 단추예요.
2. 해결의 핵심: Service — 변하지 않는 대표 주소
K8s 는 이 문제를 풀려고 Service(서비스) 라는 오브젝트를 만들어 뒀어요.
비유하면 이래요.
- 파드 = 콜센터에 출근한 상담원 개개인 (오늘 A 씨, 내일 B 씨, 자리도 매번 바뀜)
- Service = 회사 대표 전화번호 (
1588-XXXX, 절대 안 바뀜)
고객(= API 를 호출하는 쪽)은 대표번호로만 전화하고, Service 가 알아서 지금 살아있는 파드 중 하나로 연결해 줍니다. 파드가 죽고 새로 떠도, Service 는 새 파드를 자동으로 찾아 연결하기 때문에 부르는 쪽은 아무것도 바꿀 필요가 없어요.
이 “지금 살아있는 파드 목록을 자동으로 추적하는” 기능이 Service 의 핵심이에요. 내부적으로는 label(라벨, 파드에 붙이는 꼬리표) 과 selector(셀렉터, 그 꼬리표를 고르는 조건) 의 매칭으로 파드를 찾습니다.
Service 를 하나 만들면 두 가지 고정 주소가 생겨요.
- 고정 내부 IP (ClusterIP) — 클러스터가 살아있는 동안 안 바뀜
- 고정 DNS 이름 — 예:
my-api.default.svc.cluster.local
실무에서는 IP 보다 DNS 이름 을 쓰는 걸 더 권장해요. 사람이 읽기 쉽고, 클러스터를 새로 만들어도 이름 규칙은 그대로 유지되니까요. (DNS = Domain Name System, 이름을 IP 로 바꿔주는 전화번호부 같은 시스템이에요.)
3. “누가 호출하느냐” 에 따라 답이 갈립니다
여기가 제일 중요한 분기점이에요. “고정 IP 로 바라본다”는 요구가 클러스터 안쪽이냐 바깥쪽이냐 에 따라 방법이 완전히 달라져요.
경우 A — 같은 EKS 클러스터 안의 다른 서비스가 호출
이건 제일 쉬워요. ClusterIP Service 하나면 끝납니다.
apiVersion: v1
kind: Service
metadata:
name: my-api # 이 이름이 곧 고정 주소가 됨
spec:
selector:
app: my-api # 이 꼬리표(label)를 가진 파드들로 연결
ports:
- port: 80 # 호출하는 쪽이 두드릴 포트
targetPort: 8080 # 파드(API 서버)가 실제로 듣는 포트
이렇게 하면 클러스터 안의 다른 파드들은 파드 IP 대신 http://my-api 또는 http://my-api.<네임스페이스>.svc.cluster.local 로 부르면 돼요. 파드가 백 번 죽었다 살아나도 이 주소는 안 변합니다.
💡 ClusterIP 는 Service 의 기본 타입이에요. 클러스터 내부에서만 통하는 고정 가상 IP 를 부여합니다.
type:을 따로 안 적으면 이게 기본값이에요. 네임스페이스(namespace) 는 클러스터 안을 나누는 폴더 같은 논리적 구획이고요.
경우 B — AWS 안의 다른 시스템(클러스터 바깥)이 “고정 IP” 로 호출
예를 들면 이런 상황이에요.
- EKS 바깥의 EC2 서버나 온프레미스(사내 자체 서버)에서 이 API 를 호출해야 한다
- 다른 VPC, 또는 외부 파트너사가 “우리는 특정 IP 만 방화벽에 등록해 둘 거니까, 변하지 않는 IP 하나를 달라” 고 한다
아마 이 글을 찾아온 분의 상황이 여기일 거예요. 이때는 NLB + Elastic IP 조합을 씁니다.
약어 풀이부터.
| 약어 | 풀이 | 한 줄 설명 |
|---|---|---|
| NLB | Network Load Balancer | AWS 로드밸런서 중 하나. 4계층(TCP)에서 동작하고 고정 IP 를 붙일 수 있음 |
| EIP | Elastic IP (탄력적 IP) | AWS 가 주는 변하지 않는 공인 IP. 내가 놓을 때까지 그대로 |
| VPC | Virtual Private Cloud | AWS 안에 만든 나만의 가상 네트워크 망 |
(로드밸런서 = 들어오는 트래픽을 여러 서버에 나눠주는 분배기예요. “4계층” 은 IP/포트 수준에서만 다룬다는 뜻이고, 뒤에 나오는 “7계층” 은 그보다 위인 HTTP 내용까지 본다는 뜻이에요.)
구조를 한 그림으로 요약하면 이렇습니다.
[외부 시스템]
│ (항상 같은 고정 IP 로 호출. 예: 3.34.x.x)
▼
[NLB + Elastic IP] ← 고정 IP 는 여기에 붙어있음
│ (NLB 가 살아있는 파드로 자동 분배)
▼
[Service]
│
▼
[Pod] [Pod] [Pod] ← 얘들 IP 는 계속 바뀌어도 상관없음
핵심은 고정 IP 가 파드가 아니라 NLB 에 붙어 있다 는 거예요. 파드가 죽었다 살아나면 NLB 가 알아서 “지금 살아있는 새 파드” 로 트래픽을 돌립니다. 외부에서 보는 IP 는 영원히 그대로고요.
4. 실제 설정 — NLB + Elastic IP 붙이기
EKS 에서는 AWS Load Balancer Controller 라는 부가 기능(애드온)을 클러스터에 설치한 뒤, Service 에 어노테이션(annotation, 추가 지시를 적는 메모 칸) 을 달아서 “NLB 를 만들고 이 Elastic IP 를 써라” 라고 시킵니다.
✅ 이 컨트롤러가 깔려 있어야 아래 어노테이션이 동작해요. 설치 안 되어 있으면 먼저
eksctl이나 Helm 으로 설치해야 합니다. 보통 클러스터 구축 단계에서 한 번 깔아두면 끝이에요.
1단계 — Elastic IP 미리 받아두기
먼저 고정 IP(EIP)를 하나 할당받아 둡니다.
aws ec2 allocate-address --domain vpc
정상이면 이런 응답이 떨어져요. 여기서 AllocationId 를 메모해 둡니다.
{
"PublicIp": "3.34.x.x",
"AllocationId": "eipalloc-0abc1234...",
"Domain": "vpc"
}
💡 NLB 가 여러 가용영역(AZ, Availability Zone — 물리적으로 떨어진 데이터센터 묶음)에 걸쳐 만들어지면, 보통 AZ 개수만큼 EIP 가 필요해요. 서브넷이 2개 AZ 에 있으면 EIP 도 2개 할당받아서 쉼표로 이어 붙입니다.
2단계 — Service 에 NLB + EIP 지정
apiVersion: v1
kind: Service
metadata:
name: my-api-public
annotations:
# NLB 를 쓰겠다
service.beta.kubernetes.io/aws-load-balancer-type: "external"
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
# 인터넷에서 접근 가능하게 (사내 전용이면 "internal")
service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
# 위에서 받은 고정 IP(EIP) 연결 ★ 이게 핵심
service.beta.kubernetes.io/aws-load-balancer-eip-allocations: "eipalloc-0abc1234..."
spec:
type: LoadBalancer
selector:
app: my-api
ports:
- port: 80
targetPort: 8080
이렇게 kubectl apply -f 로 던지면 NLB 가 그 EIP 를 달고 생성돼요. 그 뒤로 외부 시스템은 그 고정 IP 하나만 바라보면 됩니다. 파드 교체와 완전히 무관해져요.
적용 후 잘 붙었는지는 이렇게 확인해요.
kubectl get svc my-api-public
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
my-api-public LoadBalancer 10.100.x.x my-api-...elb.amazonaws.com 80:31234/TCP
✅
EXTERNAL-IP자리에 NLB 의 주소가 뜨면 성공이에요. 처음엔<pending>으로 보이다가 1~2분 뒤에 채워지니 조금 기다려 주세요.
🚨 사내(같은 VPC / 온프레)에서만 쓸 거라면
aws-load-balancer-scheme을internal로 두세요. 그러면 인터넷에 노출되지 않는 사설 고정 IP 형태가 됩니다. 외부 공개가 필요 없다면 보안상 이쪽이 훨씬 안전해요.
5. 그럼 ALB / Ingress 는 언제 쓰나?
API 서버라고 하셨으니 짚고 갈게요. 로드밸런서에는 NLB 말고 ALB 도 있어요.
| 비교 | NLB (Network LB) | ALB (Application LB) |
|---|---|---|
| 동작 계층 | 4계층 (TCP/IP·포트) | 7계층 (HTTP 내용까지) |
| 고정 IP | 붙일 수 있음 (EIP) | 못 붙임 (고정 DNS 이름만) |
| 경로별 라우팅 | 안 됨 | 됨 (/users → A, /orders → B) |
| API 서버에 언제 | IP 고정이 필수일 때 | 도메인·경로 라우팅이 필요할 때 |
정리하면 이래요.
- ALB 는 7계층(HTTP) 로드밸런서라 경로별 라우팅 같은 똑똑한 기능이 되지만, 고정 IP 를 직접 붙일 수 없어요. 고정 DNS 이름만 줍니다.
- 그래서 “IP 고정” 이 필수면 ALB 가 아니라 NLB 를 써야 해요.
- 만약 호출하는 쪽이 IP 가 아니라 도메인 이름 으로 불러도 된다면, ALB + Route 53(AWS 의 DNS 서비스)로
api.example.com같은 고정 도메인을 주는 게 더 깔끔하고 유연합니다.
💡 의외로 많은 경우, 상대방이 진짜 원하는 건 “고정 IP” 가 아니라 “안 바뀌는 주소” 예요. 도메인으로 합의가 되면 운영이 훨씬 편해지니, 요구사항을 한 번 되물어보는 걸 추천드려요.
6. 상황별 선택표
마지막으로 한 장으로 정리할게요.
| 호출하는 쪽 | 필요한 것 | 답 |
|---|---|---|
| 같은 클러스터 안 | 고정 내부 주소 | ClusterIP Service (DNS 이름 사용) |
| AWS / 외부에서 고정 IP 필수 | 절대 안 변하는 IP | NLB + Elastic IP |
| 외부에서 도메인이면 충분 | 안 변하는 주소(이름) | ALB(Ingress) + Route 53 |
그리고 이 글에서 가장 중요한 원칙 한 줄.
🚨 파드 IP 는 절대 직접 바라보지 않는다. 항상 그 앞에 Service(필요하면 NLB + EIP)를 두고, 변하지 않는 주소를 바라본다.
질문이 “AWS 안에서 고정 IP 로 EKS 의 API 파드를 호출” 이었다면, 정확히 경우 B — NLB + Elastic IP 가 맞는 답이에요.
일단 오늘은 여기까지…..
다음 글에서는 NLB 헬스체크(파드가 살아있는지 LB 가 확인하는 방법)와 보안그룹 설정까지 한 단계 더 들어가서 정리해볼게요.