쿠버네티스의 네트워크를 담당하는 Service리소스에 대해 알아봅시다.
Service도 마찬가지로 .YAML 형식으로 정의됩니다.
Pod IP와 또 다른 독자적인 IP를 부여받아 Endpoint를 제공하여 라벨링 시스템을 통해 Pod로 트레픽을 전달합니다.
마치 로드벨런서처럼 동작합니다.
Service란?
쿠버네티스는 Pod 자체에도 IP가 있어 curl로 호출을 할 수 있습니다.
그럼 Service리소스를 따로 만들어 네트워크 통신을 담당하게 만든 이유가 무엇일까요?
ALB없는 ASG를 상상하면 쉽지 않을까나
쿠버네티스에선 Pod 리소스를 불안정한 자원으로 여깁니다.
쉽게 생성했다 쉽게 삭제할 수 있는 리소스이기 때문이죠 .
이로인해 Pod의 Endpoint도 불안정하게 됩니다.
이를 해결하기 위해 Pod의 생명주기와는 상관없이 안정적인 Endpoint를 제공하는
Service라는 리소스가 등장하게 된 것이지요.
Service 리소스는 Pod의 앞단에 위치하여 트래픽을 Pod로 전달하는 리버스 프록시와 같은 역할을 수행합니다.
Pod의 IP가 변하더라도 Service를 통해 동일한 IP로 접근이 가능하게 됩니다.
한개의 파드가 죽어도 다른 Pod로 트래픽을 전달해주어서 안정성 및 가용성을 높힐 수 있습니다.
Service Discovery
service 리소스는 안정적인 IP제공 뿐만 아니라,
서비스 탐색 기능을 수행하는 도메인 이름 기반 Endpoint를 제공합니다.
다시말해 Service 리소스의 이름을 기반으로 DNS 참조가 가능합니다.
이를 통해 손쉽게 다른 서비스를 탐색 및 참조가 가능하지요.
예로 myservice라는 Service리소스를 생성하면 myservice라는 도메인 주소로 해당 Service에 요청할 수 있습니다.
# myservice.yaml
apiVersion: v1
kind: Service
metadata:
labels:
hello: world
name: myservice
spec:
ports:
- port: 8080
protocol: TCP
targetPort: 80
selector:
run: mynginx
Pod와 유사한 구조를 가지고 있는걸 볼 수 있습니다.
spec부분에서 Service 고유의 명세가 정의됩니다.
- apiVersion, kind는 Pod와 동일한 의미를 가집니다.
- metadata: 서비스의 메타 정보를 나타냅니다
- labels: Service에도 라벨 부여가 가능합니다.
- name: 이름을 지정하여 도메인 주소로 활동됩니다.
- spec: Service의 스펙을 정의합니다.
- ports: 포트를 정의합니다.
- port: Service로 들어오는 포트 지정
- protocol: 사용 프로토콜 을 지정합니다 (TCP, UDP, HTTP 등)
- targetPort: 트래픽을 전달할 컨테이너의 포트를 지정합니다.
- selector: 트래픽을 전달할 컨테이너의 라벨을 선택합니다.
위 YAML기분 run=mynginx 라벨을 가진 Pod에 Service 트래픽을 전달한다는 뜻입니다.
- ports: 포트를 정의합니다.

왜 label selector로 Pod를 선택할까
쿠버네티스는 각 리소스의 관계를 느슨한 연결관계로 표현하고자 합니다.
이는 특정 리소스를 직접 참조하는 것이 아닌 간접 참조한다는 것을 의미하지요.
Service에서 Pod의 이름이나 IP를 직접 참조하면 바뀔 때마다 재지정 해줘야 해 Service를 만든 이유가 없어집니다.
하지만 라벨링을 통해 느슨한 관계를 유지하면 그 label을 가진 어떤 Pod에도 트래픽 전달이 가능하게 됩니다.
Pod입장에도 Service에서 바라보는 특정 라벨만 달아주면 바로 등록, 트래픽 전딜이 가능합니다.
이렇게 유연한 구조로 애플리케이션을 구축할 수 있기 때문에 라벨링 시스템을 이용해 리소스간 관계를 표현합니다.
위에서 만든 Service를 생성해봅시다.

kubernetes라는 Service도 보이는데 이는 쿠버네티스 API서버로 트래픽을 전송하는 Endpoint입니다.
쿠버네티스 설치 시 기본적으로 생성되지요.
# curl 요청할 client Pod 생성
kubectl run client --image nginx
# pod/client created
# Pod IP로 접근
kubectl exec client -- curl -s 10.42.1.33
# Service IP로 접근 (CLUSTER-IP)
kubectl exec client -- curl -s 10.43.44.10:8080
# Service 이름 (DNS 주소)로 접근
kubectl exec client -- curl -s myservice:8080
전부 동일하게 mynginx 서버의 결과가 반환됩니다.
Service Endpoint로 요청할 경우 포트를 써줍니다.
myservice의 IP 주소를 확인하기 위해 DNS lookup을 수행합니다.
# DNS lookup을 수행하기 위해 nslookup 명령을 설치합니다.
kubectl exec client -- sh -c "apt update && apt install -y dnsutils"
# myservice의 DNS를 조회합니다.
kubectl exec client -- nslookup myservice

myservice의 IP주소가 Service 주소가 동일합니다.
Service의 이름이 도메인 주소 역할을 수행한다는 것을 볼 수 있지요.
하나 살펴 볼 점은 DNS의 이름이 myservice가 아닌 myservice.default.svc.cluster.local로 나오는 것ㅇ르 볼 수 있습니다.
이는 Service 리소스의 전체 도메인 주소를 나타냅니다.
Service 도메인 주소 법칙
Service 리소스의 전체 도메인 이름 법칙은 다음과 같습니다.
<서비스 이름>.<네임스페이스>.svc.cluster.local
svc.cluster.local은 쿠버네티스 cluster domain으로 Service 도메인의 postfix(접미사)라 이해하면 됩니다.
Service의 전체 도메인 주소는 생략이 가능합니다.
생략시 규칙은 다음과 같습니다.
- <서비스 이름>.<네임스페이스> == <서비스 이름>.<네임스페이스>.svc.cluster.local
- <서비스 이름> == <서비스 이름>.<네임스페이스>.svc.cluster.local
클러스터 DNS 서버
Service에서 이름을 도메인으로 사용 가능한 이유는 쿠버네티스가 제공하는 DNS 서버가 있기 때문입니다.
리눅스 시스템에서 DNS 서버 설정을 담당하는 /etc/resolv.conf 파일을 확인해보겠습니다.

nameserver에 10.43.0.10라는 IP를 확인 할 수 있습니다.
쿠버네티스의 모든 Pod들은 바로 이 IP로 DNS를 조회합니다.
kubectl get svc -n kube-system
위 명령어로 namespace로 Service리소스를 한번 살펴보면.
kube-dns라는 Service가 10.43.0.10의 주인인 것을 확인할 수 있습니다.
위 서비스의 라벨을 확인해보면 k8s-app=kube-dns를 가집니다.
이 라벨과 맵핑되는 Pod를 찾아보면

coredns-xxx라는 Pod가 조회됩니다.
이게 클러스터 DNS 서버입니다.
모든Pod들은 클러스터 내부, 외부 DNS 질의를 coredns를 통해 수행합니다.
이래서 쿠버네티스 클러스터 안에서 자체적인 도메인 네임 시스템을 가질 수 있습니다.

Service 종류
Service 리소스는 총 4가지 타입이 있습니다.
하나씩 살펴봅시다.
ClusterIP
ClusterIP는 Service 리소스의 가장 기본 타입입니다.
ClusterIP타입의 서비스 끝점은 쿠버네티스 클러스터 내부에서만 접근이 가능합니다.
외부에서는 접근할 수 없지요.

그럼 ClusterIP 타입이 존재하는 이유가 뭘까요?
크게 2가지로 볼 수 있습니다.
1. 많은 경우 네트워크 보안 및 관리를 위해 한두 개의 Endpoint외에는 직접 트래픽을 전달받는 경우가 드뭅니다. 대신 외부로 열린 서비스 Endpoint로부터 트래픽을 전달받아 서비스를 제공합니다.
2. ClusterIP 타입은 더 확장된 쿠버네티스 네트워킹을 위한 기본 빌딩블럭으로 사용됩니다. 이를 기반으로 더 복잡한 네트워킹을 수행할 수 있습니다.
직접 ClusterIP 타입의 Service를 생성해 봅시다.
Pod template 파일 생성시 --expose, -port 80 옵션을 추가합니다.
kubectl run cluster-ip --image nginx --expose --port 80 \
--dry-run=client -o yaml > cluster-ip.yaml
vim cluster-ip.yaml

cluster-ip.yaml 파일을 열면 기본 ClusterIP타입 Service 리소스와 대응 Pod가 생성된 것을 볼 수 있습니다.
로컬 호스트에서 직접 서비스 Endpoint로 요청을 보낸 것이 아니라 클라이언트 Pod를 만들어 서비스 끝점으로 접근한 것입니다.
NodePort
NodePort 타입은 도커 컨테이너 포트 매핑과 비슷하게 로컬 호스트의 특정 포트를 Service의 특정 포트와 연결시켜 외부 트래픽을 Service까지 전달합니다.

YAML명세를 살펴봅시다.
# node-port.yaml
apiVersion: v1
kind: Service
metadata:
name: node-port
spec:
type: NodePort # type 추가
ports:
- port: 8080
protocol: TCP
targetPort: 80
nodePort: 30080 # 호스트(노드)의 포트 지정
selector:
run: node-port
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: node-port
name: node-port
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80
- type: 타입을 NodePort로 지정합니다.
- nodePort: 호스트 서버에서 사용할 포트 번호를 정의합니다. 쿠버네티스에서 제공하는 NodePort range는 30000~32767입니다.

실행 후 확인해보면 NodePort 타입이 잘 적용된걸 볼 수 있습니다.
Ehgks 8080(Service port)포트와 30080(Node Port)포트가 매핑되어 있는 것을 볼 수 있습니다.
마스터 노드, 워커 노드 모두 동일한 NodePort로 서비스에 접근할 수 있습니다.


worker1: 192.168.0.96
master: 192.168.0.83
ahen 30080 포트로 접근이 가능합니다.
이러한 것이 가능하게 만들어주는 컴포넌트가 바로 kube-proxy입니다.
kube-proxy는 리눅스 커널의 netfilter를 이용해 리눅스 커널 레벨에서 특정 트래픽을 중간에 가로채 다른 곳으로 라우팅 해주는 역할을 수행합니다.
이를 통해 모든 노드에서 동일한 NodePort로 원하는 서비스에 접근할 수 있게 제공합니다.
LoadBalancer
쿠버네티스는 노드 앞단에 로드 밸런서를 두고 해당 로드밸런서가 각 노드로 트래픽을 분산할 수 있게 로드밸런서 타입을 제공합니다.

이를 이용하면 퍼블릭 클라우드 플랫폼에서 제공하는 로드밸런서를 Service 리소스에 연결할 수 있습니다.
로드밸런서를 사용하는 이유는 다음과 같습니다.
1. 보안적 측면으로 호스트 서버의 노드포트 대역을 직접 외부에 공개할 필요 없이 서버를 내부 네트워크에 두고 로드밸런서만 외부 네트워크에 위치하여 well-known 포트로 서비스 Endpoint를 제공해 네트워크 보안성을 높힐 수 있습니다.
2. 사용자가 각 서버IP를 직접 알 필요 없이 로드밸런서의 IP 또는 도메인 주소만 가지고 요청을 보낼 수 있어 편리합니다.
ClusterIP 타입 서비스가 Pod 레벨에서의 안정적인 서비스 Endpoint를 제공하는 것이라면,
로드벨런서 타입 서비스는 노드 레벨에서의 안정적인 서비스 Endpoint를 제공하는 것입니다.
보통 로드밸런서는 AWS 같은 곳에서 제공하는 것을 사용합니다.
하지만 k3s에선 지원받을 수 없어 소프트웨어로 구현된 가상의 로드벨런서를 사용합니다.
# load-bal.yaml
apiVersion: v1
kind: Service
metadata:
name: load-bal
spec:
type: LoadBalancer # 타입 LoadBalancer
ports:
- port: 8080
protocol: TCP
targetPort: 80
nodePort: 30088 # 30088로 변경
selector:
run: load-bal
---
apiVersion: v1
kind: Pod
metadata:
labels:
run: load-bal
name: load-bal
spec:
containers:
- image: nginx
name: nginx
ports:
- containerPort: 80

생성된 Serice 리소스를 보면, TYPE이 로드밸런서로 설정된게 보입니다.
또한 EXTERNAL-IP 열에 IP 타입이 생성되어 있는것을 볼 수 있습니다.
k3s에서는 호스트 서버의 IP와 동일하게 IP가 지정되지만,
EKS, GKE, AKS같으 퍼블릭 클라우드 서비스에서 제공하는 쿠버네티스 클러스터를 이용할 경우 각 클라우드 플랫폼에서 제공하는 로드밸런서의 IP 및 DNS를 확인할 수 있습니다.
ExternalName
외부 DNS 주소에 클러스터 내부에서 사용할 새로운 별칭을 만듭니다.

클러스터 내 google-svc라는 별칭을 이용하여 google.com으로 연결할 수 있는 서비스 끝점을 생성합니다.
# external.yaml
apiVersion: v1
kind: Service
metadata:
name: google-svc # 별칭
spec:
type: ExternalName
externalName: google.com # 외부 DNS
위처럼 만들게 되면 Pod에서 google.com 뿐만 아니라 google-svg라는 이름으로 google.com 참조가 가능해집니다.
ExternalName은 쿠버네티스 클러스터에 편입되지 않는 외부 서비스에 쿠버네티스 네트워킹 기능을 연결하고 싶은 경우 사용합니다.
네트워크 모델
쿠버네티스 네트워크 모델의 특징은 다음과 같습니다.
- 각 Node간 NAT없이 통신이 가능해야 합니다.
- 각 Pod간 NAT없이 통신이 가능해야 합니다.
- Node와 Pod간 NAT없이 통신이 가능해야합니다.
- 각 Pod는 고유의 IP를 부여받습니다.
- 각 Pod IP 네트워크 제공자를 통해 할당받습니다.
- Pod IP는 클러스터 내부 어디서든 접근이 가능해야합니다.
한마디로 NAT를 통한 네트워킹 싫어 입니다.
그 이유는 쿠버네티스의 전신인 Borg라는 구글 내부 클러스터 시스템에서는 NAT 이용했는데 그럼 모든 컨테이너들이 동일한 IP를 갖게 되어 Port로 서로 구분해야했습니다.
이럼 서로 다른 애플리케이션이 동일 포트를 사용하면 충돌이 일어나고 해결해야했습니다.
이는 인프라 설정에 애플리케이션이 얽혀 결합도가 높아지게 됐죠.
이러한 문제점을 교훈삼아 쿠버네티스는 컨테이너의 네트워크 환경을 노드 레벨의 네트워크 환경과 분리하여 고립도를 높이려 했고, Pod라는 독립적인 네트워크 환경을 구성하는 리소스를 만들게 되었습니다.
이런 네트워크 모델을 다음과 같은 장점을 가집니다.
- 모든 리소스가 다른 모든 리소스를 고유의 IP로 접근할 수 있다.
- NAT통신으로 인한 부작용에 대해 신경쓸 필요가 없다.
- 새로운 프로토콜을 재정의할 필요 없이 기조의 TCP, UDP, IP 프로토콜을 그대로 이용할 수 있다.
- Pod끼리의 네트워킹이 어느 노드에서든지 동일하게 동작합니다.
마무리
쿠버네티스 네트워크 기술을 깊이 이해하려면 kube-proxy, iptables, netfilter등 네트워크 기술에 대해 알아야 합니다.
하지만 쿠베를 활용하는 입장에선 Service 리소스만 잘 이용해도 쿠버네티스 네트워킹 기술을 사용할 수 있습니다.
사용자 레벨에서 쿠버네티스 네트워크를 설정할 수 있게 추상화 한것이 바로 Service 리소스입니다.
다음엔 쿠버네티스에서 제공하는 기본 컨트롤러에 대해 알아봅시다.
'개발 > DevOps' 카테고리의 다른 글
| [Kubernetes] 쿠버네티스 컨트롤러 2 - StatefulSet, Job & CronJob (0) | 2025.10.04 |
|---|---|
| [Kubernetes] 쿠버네티스 컨트롤러 1 - ReplicaSet, Deployment (0) | 2025.10.02 |
| [Kubernetes] Pod 파해치기 3 - ConfigMap, Secret, Downward (0) | 2025.09.23 |
| [Kubernetes] Pod 파해치기 2 - livenessProbe, readlinessProbe, initContainers (0) | 2025.09.23 |