저번 글에서 어떤 오브젝트가 있는지 간단히 살펴보았다. 이번 글에서는 그 중에서도 Service(서비스) 에 대해 중점적으로 다뤄보고, 각 서비스의 유형에 따라 간단한 실습도 함께 진행해볼 예정이다.
서비스(Service) 는 클러스터 내부 또는 외부의 클라이언트와 파드 간의 연결을 안정적으로 제공하는 추상적 리소스이다.
Kubernetes에서 파드는 일시적인 존재로, 동적으로 생성되거나 종료된다. 이 때문에 파드의 IP는 고정되어 있지 않으며, 클라이언트가 특정 파드에 직접 요청을 보내기란 어렵다. 이를 해결하기 위해 서비스가 사용된다.
selector
를 통해 특정 라벨을 가진 파드들을 자동으로 찾아 엔드포인트 목록으로 관리한다.http://my-service
와 같이 서비스 이름으로 요청을 보내면, CoreDNS가 해당 요청을 서비스의 ClusterIP로 변환해준다. (-> 서비스 디스커버리 Service Discovery)쿠버네티스 서비스는 타입에 따라 트래픽 유입 방식이 달라진다. 대표적으로 ClusterIP, NodePort, LoadBalancer, ExternalName의 네 가지 유형이 존재한다.
ClusterIP는 쿠버네티스 서비스의 기본 설정값으로 클러스터 내에서만 파드에 접근될 수 있도록 하는 유형이다. 클러스터 내부에서만 접근 가능한 IP를 할당하며, 외부에서는 접근할 수 없다.
먼저 ClusterIP 유형의 서비스와 해당 서비스에 매핑될 디플로이먼트를 yaml로 정의 후 생성한다.
# service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app.kubernetes.io/name: web-deploy # 서비스가 연결할 pod 정의
type: ClusterIP
ports:
- protocol: TCP
port: 80 # 클라이언트가 서비스에 요청할 때 사용할 포트
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-service-01
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: web-deploy
template:
metadata:
labels:
app.kubernetes.io/name: web-deploy
spec:
containers:
- name: nginx
image: nginx:latest
kubectl apply -f service-clusterip.yaml
kubectl apply -f deployment.yaml
kubectl get all
이후 클러스터 내부의 다른 파드에서 해당 서비스의 ClusterIP로 curl
명령어를 통해 요청을 보내면 접속이 잘 되는 것을 확인할 수 있다.
각 노드의 특정 포트를 통해 외부 접근을 제공하는 유형이다. NAT를 사용하는 클러스터 내에서 각 노드들의 지정된 포트(30000~32767)를 외부에 노출시켜 준다.
먼저 NodePort 유형의 서비스와 해당 서비스에 매핑될 디플로이먼트를 yaml로 정의 후 생성한다.
# service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: web-service-nodeport
spec:
selector:
app.kubernetes.io/name: web-deploy
type: NodePort
ports:
- protocol: TCP
nodePort: 31001
port: 80
targetPort: 80
# nodePort : 노드의 네트워크 인터페이스에서 열려 있는 포트, 외부 트래픽을 받아 ClusterIP로 전달하는 진입점 역할
# port : 서비스가 클러스터 내부에서 사용하는, ClusterIP에서 동작하는 포트 (client → service)
# targetPort : pod의 어플리케이션 쪽에서 서비스를 향해 열려 있는 포트 (service → pod)
kubectl apply -f service-nodeport.yaml
kubectl apply -f deployment.yaml
가상 머신에서 쿠버네티스를 실행 중이라고 하면 포트포워딩 설정이 필요하다.
31051 호스트 포트로 접속하면 이를 10.0.2.7 노드의 NodePort 31001로 포트포워딩하고, 31052 호스트 포트로 접속하면 이를 10.0.2.8 노드의 NodePort 31001로 포트포워딩한다.
127.0.0.1:31051
로 요청.10.0.2.7(워커노드):31001
로 포트포워딩.kube-proxy
가 요청 수신. kube-proxy
가 31001
포트로 들어온 요청을 가로채서 매핑된 서비스의 ClusterIP:Port(80)로 전달.80
포트로 실제 어플리케이션이 응답한다.클라우드 환경(AWS, Azure, GCP)에서 사용되는 서비스 유형으로, 클러스터 외부에서 접근할 수 있도록 퍼블릭 IP와 로드밸런서를 자동으로 생성해준다.
서비스 정의 시 type: LoadBalancer
로 설정하면, Kubernetes는 클라우드 벤더의 API를 호출해 로드밸런서를 생성하고, 퍼블릭 IP를 할당한다.
외부 사용자는 이 퍼블릭 IP를 통해 서비스에 직접 접근할 수 있으며, 요청은 로드밸런서를 거쳐 클러스터 내부적으로는 NodePort + ClusterIP 구조를 그대로 따른다.
하지만, 로컬 환경에서는 클라우드처럼 로드밸런서를 자동 생성할 벤더 API가 없다. 따라서, LoadBalancer 타입 서비스를 생성해도 아무 IP도 할당되지 않는다. 이때 클러스터 내부에서 가짜 클라우드 벤더 역할을 할 수 있는 것이 바로 MetalLB이다.
MetalLB는 베어메탈이나 로컬 환경에서도 LoadBalancer 서비스를 사용할 수 있게 해주는 컨트롤러이다.
클러스터 내부에서 클라우드 벤더 역할을 흉내 내어, 사용자가 정의한 IP 풀에서 가상의 외부 IP를 할당한다. 로드밸런서 리소스를 생성하는 것이 아니라, 트래픽을 특정 노드로 전달할 수 있는 방식(ARP or BGP)을 통해 로드밸런서처럼 행동하는 방식이다.
ARP (L2 모드 : Address Resolution Protocol)
=> 같은 네트워크 안에서만 작동하므로, 로컬 테스트에 적합.
BGP (L3 모드 : Border Gateway Protocol)
=> BGP 라우터가 필요하므로, 보통 기업 데이터 센터에서 사용
동작 흐름
pending
으로 표시externalIP
로 설정# metallb-config.yaml
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: metallb-config
namespace: metallb-system
spec:
addresses:
- 10.0.2.20-10.0.2.40 # 10.0.2.20-10.0.2.40 범위 중에서 할당
autoAssign: true
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: metallb-config
namespace: metallb-system
spec:
ipAddressPools:
- metallb-config
IPAddressPool
을 설정하고, 퍼블릭 IP가 자동 할당되도록 한다.
# service-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: web-service-loadbalancer
spec:
selector:
app.kubernetes.io/name: web-deploy
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
위와 같이 정의하면 MetalLB가 감지해서 IP 풀에서 IP를 하나 골라 자동으로 외부 IP를 서비스에 붙여준다.
클라우드 환경용 LoadBalancer 서비스를 정의하는 YAML은 다음과 같다.
externalIPs
나 nodePort
를 수동으로 지정할 필요 없이, 클라우드 벤더가 퍼블릭 IP 및 로드밸런서를 자동으로 생성해준다.
# service-loadbalancer.yaml
apiVersion: v1
kind: Service
metadata:
name: web-service-loadbalancer
spec:
selector:
app.kubernetes.io/name: web-deploy
type: LoadBalancer
ports:
- protocol: TCP
port: 80
targetPort: 80
내부의 서비스가 외부의 도메인을 가리키도록 하는 유형이다.
# service-externalname.yaml
apiVersion: v1
kind: Service
metadata:
name: web-service-externalname
spec:
type: ExternalName
externalName: www.google.com
kubectl exec -it [pod-name] -- /bin/bash
curl "web-service-externalname"
CoreDNS가 요청을 www.google.com
으로 리다이렉트한다.
이번 글에서는 쿠버네티스에서 서비스가 어떻게 외부와 통신하며, 클러스터 내부에서는 어떤 방식으로 트래픽이 전달되는지 서비스 유형별로 정리해보았다. 다음 글에서는 Ingress를 중심으로 외부 트래픽 제어 방식을 살펴 보며 쿠버네티스 네트워크에 대해 더 깊이 이해해보도록 하겠다.