서비스(Service): 유동적인 IP 주소에 고정된 간판 달기
수시로 생성되고 삭제되며 IP가 바뀌는 파드들을 안정적으로 호출할 수 있도록 고정된 진입점을 제공하는 서비스를 배웁니다.
디플로이먼트를 통해 수많은 파드들이 죽었다 살아나기를 반복하며 안정적으로 개수를 유지할 수 있게 되었습니다. 그런데 여기서 아주 치명적인 문제가 발생합니다. 쿠버네티스의 파드는 다시 생성될 때마다 매번 완전히 새로운 IP 주소를 부여받는다는 점입니다. 백엔드 서버(파드)의 주소가 1분마다 바뀐다면, 프론트엔드는 도대체 어느 주소로 API 요청을 보내야 할까요? 이 혼란을 깔끔하게 정리해 주는 쿠버네티스의 간판, 서비스(Service)에 대해 알아봅시다.
시시각각 변하는 파드 IP의 딜레마
프론트엔드 애플리케이션(A)이 데이터를 가져오기 위해 백엔드 API 서버(B)를 호출해야 하는 상황을 가정해 보겠습니다.
가장 단순한 방법은 A가 B 파드의 IP 주소를 직접 코드에 적어두는 것입니다. 하지만 파드는 매우 일시적인 존재입니다. 노드에 오류가 발생하거나 업데이트가 진행되면 B 파드는 죽고 새로운 B 파드가 생성됩니다. 이때 새로 태어난 B 파드의 IP는 이전 파드와 전혀 다른 임의의 주소를 가지게 됩니다.
A의 입장에선 갑자기 통신하던 목적지가 사라져 버리는 셈입니다. 개발자가 매번 바뀌는 파드의 IP를 추적해서 프론트엔드의 설정을 계속 수정하는 것은 불가능에 가깝습니다. 뭔가 "뒤에서 파드가 어떻게 죽고 살아나든 상관없이, 언제나 변하지 않는 고정된 주소"가 절실하게 필요해집니다.
든든한 간판, 서비스(Service)의 역할
이 문제를 해결하기 위해 쿠버네티스는 서비스(Service)라는 오브젝트를 제공합니다.
서비스는 여러 개의 파드 묶음을 대표하는 가상의 주소이자 로드밸런서 역할을 합니다. 레스토랑의 대표 전화번호(서비스)로 전화를 걸면, 현재 근무 중인 상담원(파드) 중 아무나 전화를 받아서 응대하는 것과 같은 이치입니다. 특정 상담원이 퇴사하고 신입이 들어와도 고객은 대표 전화번호 하나만 알면 언제든 레스토랑과 통화할 수 있습니다.
프론트엔드 파드는 이제 개별 백엔드 파드의 IP를 전혀 몰라도 됩니다. 오직 백엔드 '서비스'의 고정된 이름이나 고정 IP만 호출하면, 서비스가 알아서 현재 살아있는 백엔드 파드들 중 하나로 트래픽을 토스해 줍니다.
서비스 도입 전후의 차이
서비스 없음
→파드가 죽고 다시 태어날 때마다 IP가 변경되어 클라이언트 통신 단절 발생
서비스 도입
→클라이언트는 고정된 서비스 IP만 호출하며, 파드 IP 변경에 영향을 받지 않음
라벨 셀렉터: 서비스는 누구에게 트래픽을 보낼까?
그렇다면 서비스는 어떻게 자신이 트래픽을 보내줘야 할 파드들을 찾아낼까요? 바로 라벨(Label)과 셀렉터(Selector)라는 매칭 시스템을 사용합니다.
꼬리표를 통한 매칭 시스템
디플로이먼트가 파드를 생성할 때, 파드마다 일종의 명찰(Label)을 달아줍니다. 예를 들어 `app=backend`, `tier=api` 같은 식입니다. 그리고 서비스 설정(YAML)에는 셀렉터(Selector)를 정의해 둡니다. "내 서비스로 들어오는 트래픽은 라벨이 `app=backend`인 파드들에게만 보내라"라고 명시하는 것이죠.
서비스는 클러스터 내부를 감시하며 이 조건에 맞는 파드들의 현재 IP 목록(엔드포인트)을 실시간으로 추적하고 업데이트합니다. 덕분에 파드가 100개가 되든 0개가 되든 서비스는 당황하지 않고 트래픽을 정확히 분배할 수 있습니다.
서비스 매니페스트 (YAML)
apiVersion: v1
kind: Service
metadata:
name: backend-service
spec:
selector:
app: backend # 이 라벨을 가진 파드들을 타겟으로 삼음
ports:
- protocol: TCP
port: 80 # 서비스가 노출할 포트
targetPort: 8080 # 실제 파드가 응답을 대기하는 포트서비스의 종류: 클러스터 내부용 vs 외부 노출용
서비스는 목적에 따라 여러 가지 타입(Type)으로 나누어집니다. 외부 사용자에게 서비스를 공개할 것인지, 아니면 우리끼리만 통신할 것인지에 따라 적절한 타입을 선택해야 합니다.
- ClusterIP (기본값)
클러스터 내부에서만 접근할 수 있는 가상의 IP를 생성합니다. 프론트엔드가 백엔드를 호출할 때나 내부 DB와 통신할 때처럼, 외부에서 굳이 직접 접속할 필요가 없는 컴포넌트들을 연결할 때 주로 사용합니다.
- NodePort
각 노드(물리적 서버)의 특정 포트(예: 30000~32767)를 열어서, 외부 사용자가 노드의 IP와 해당 포트를 통해 서비스에 접근할 수 있게 해줍니다.
- LoadBalancer
AWS나 GCP 같은 클라우드 환경에서 제공하는 로드밸런서 장비와 연동하여, 외부 클라이언트가 안정적으로 접근할 수 있는 퍼블릭 IP를 할당받는 방식입니다. 상용 서비스를 외부 사용자에게 오픈할 때 가장 널리 쓰이는 표준적인 방법입니다.
어떤 서비스 타입을 선택해야 할까?
| 상황 | 선택 | 이유 |
|---|---|---|
| 클러스터 내부 통신 (예: 프론트엔드 -> 백엔드 API) | ClusterIP 선택 | 외부에 굳이 포트를 노출하여 보안 위험을 늘릴 필요가 없기 때문 |
| 클라우드 환경에서 외부 사용자가 웹사이트 접속 | LoadBalancer 선택 | 안정적인 퍼블릭 IP를 확보하고 트래픽을 분산할 수 있기 때문 |
개념 퀴즈
퀴즈 답을 맞춰야 학습 완료가 됩니다.다음 중 쿠버네티스 서비스(Service)에 대한 설명으로 가장 거리가 먼 것은 무엇인가요?
3줄 요약
- 1쿠버네티스 파드는 생성될 때마다 IP가 변경되므로, 직접 IP로 통신하는 것은 불가능합니다.
- 2서비스(Service)는 라벨 셀렉터를 이용해 타겟 파드들을 그룹화하고, 고정된 엔드포인트와 로드밸런싱을 제공합니다.
- 3내부 통신용(ClusterIP)과 외부 노출용(LoadBalancer) 등 목적에 따라 알맞은 서비스 타입을 선택해야 합니다.