Service

inuit·2025년 4월 10일

All about 쿠버네티스

목록 보기
6/21
post-thumbnail

최근 업데이트일 2024-11-03

참고자료: Container부터 다시 살펴보는 Kubernetes Pod 동작 원리

쿠버네티스는 Pod과 직접 통신하는 방법 대신, 별도의 고정된 IP를 가진 서비스를 만들고 그 서비스를 통해 Pod에 접근하는 방식을 사용한다. 이러한 서비스에 대해서 알아보자.

쿠버네티스에서 Pod는 유동적인 IP를 가지므로, Pod의 재시작/재배포 시 IP가 바뀔 수 있다.

1. Service란?

  • 동일한 서비스를 제공하는 Pod 그룹에 대해 Virtual IP를 만들어서 단일 진입점 제공한다.
  • Kubernetes Cluster 내 어떤 Pod가 종료됐다가 다시 생성되면서 Pod IP가 바뀌었다면, 이를 다른 앱 내 Pod에게 알려준다.
  • Pod 집합의 변경과 상관없이 항상 고정된 IP 가진다.
  • 로드 밸런싱을 지원한다. (여러 Pod 간 트래픽 분산)
  • spec.selector를 통해 연결할 Pod 선택하며, Pod는 환경 변수나 DNS를 사용하여 서비스를 식별한다.

2. Service 주요 설정 필드

필드설명
spec.clusterIP가상 IP(Virtual IP), 단일 진입점 IP
spec.selector연결할 대상 Pod 지정
spec.ports.portService가 노출하는 포트
spec.ports.targetPort연결되는 Pod의 포트
spec.ports.nodePortNodePort 타입일 때 노드에 노출되는 포트
spec.externalNameExternalName 타입일 때 외부 DNS 이름 지정

3. Service 타입 및 종류

ClusterIP

클러스터 내부에서만 접근 가능한 IP를 제공한다.

  • 기본 타입 (spec.type 생략 시 적용)
  • Pod 집합에 대한 단일 진입점 IP를 생성한다.
  • Pod가 몇 개든지 간에 하나의 IP로 접근 가능하다.
  • IP는 10.96.0.0/12 대역에서 자동 할당된다.
  • 내부 DNS 등록: <서비스이름>.<네임스페이스>.svc.cluster.local
  • 클러스터 '내부'에서 로드밸런싱 기능을 포함한다.
  • 내부 DNS 이름으로 Pod 간 통신이 가능하다.

NodePort

클러스터 외부에서 접근 가능한 포트를 모든 노드에 열어준다.

  • ClusterIP를 내부적으로 생성한 후, 노드의 포트도 노출시킨다.
  • 외부에서 <Node IP>:<NodePort> 로 접근 가능하다.
  • 30000~32767의 포트 범위 내에서 지정할 수 있다. 포트 확인: netstat -napt | grep <NodePort>
  • 명시적으로 설정하지 않으면 자동으로 포트가 할당된다. (service-node-port-range)
  • 노드 IP + nodePort로 유입된 트래픽은 iptables의 KUBE-NODEPORTS Chain으로 전달된다.
  • 이 체인은 다시 NodePort로 어떤 서비스에 대한 TCP Packet인지 판단하여 해당 서비스 Chain으로 전달한다.
  • 클러스터 외부에서 접근 가능하다.
  • 여러 노드가 있어도 어느 노드로든 접근 가능하다.
  • 다만, 사용자가 살아있는 어떤 Node로 접근해야 하는지를 직접 알아야 한다.

자동으로 살아 있는 노드에 접근하기 위해 모든 노드를 바라보는 Load Balancer가 필요하다.


LoadBalancer

외부 LoadBalancer 인스턴스를 provisioning하여 서비스를 외부에 노출한다.

  • ClusterIP + NodePort가 자동으로 생성된다.
  • 클라우드 플랫폼(AWS, GCP, Azure, OpenStack 등)에서 쿠버네티스가 API를 호출해서 사용한다.
  • 퍼블릭 IP를 가진 로드밸런서가 생성되고, 그 IP를 통해 외부에서 접근 가능하다.
  • 실제 트래픽은 Load Balancer → Node → NodePort → Pod로 전달된다.
  • 외부 클라이언트 접근에 가장 이상적인 서비스 타입이다.
  • 노드 수나 상태에 영향을 받지 않고, 자동으로 살아있는 노드 중 하나로 트래픽을 전달한다.
  • 클라우드 인프라에서만 사용 가능하지만 온프레미스 환경에서도 MetalLB, Kube-VIP, OpenELB 등을 사용하여 구현할 수 있다.

ExternalName

클러스터 내부에서 외부 DNS 이름을 참조할 수 있게 해주는 서비스

  • 클러스터 내부에서 마치 자체 서비스처럼 이름으로 접근하면, 실제로는 그 요청이 외부의 DNS 도메인으로 연결되게 만든다.
  • spec.externalName에 FQDN(Fully Qualified Domain Name)을 지정한다. (e.g. example.com)
  • 클러스터 내부의 Pod들이 외부 시스템을 사용할 수 있게 해준다.
  • Service의 이름으로 DNS 요청 시, 지정한 외부 도메인으로 redirect된다.
  • 실제로 clusterIP가 할당되지 않으며 DNS level에서 처리된다.
    • CNAME 응답을 받아서 이를 외부 DNS 주소로 직접 통신한다.
  • 클러스터 내부 DNS 이름으로 외부 서비스를 참조할 수 있다.
  • 요청이 네트워크 경로가 내부가 아닌 외부 DNS로 연결된다.

4. Headless Service

spec.clusterIP: None 으로 설정하여 로드밸런싱 없이 Pod에 개별 접근할 수 있다.

  • 서비스 IP, 로드 밸런싱이 필요하지 않을 때 사용하며, 쿠버네티스 서비스 구현에 의존하지 않고 다른 Service Discovery Mechanism을 사용할 수 있도록 보장한다.
  • CoreDNS는 Headless Service의 이름에 대해 A 레코드를 여러 개 등록하고 그 A 레코드는 각 Pod의 IP를 가리킨다.
  • <headless-service-name>.default.svc.cluster.local 같은 이름을 DNS 조회하면, 서비스 뒤에 연결된 각 Pod들의 IP 목록이 응답된다.
  • 즉, Service와 연결된 Pod의 endpoint로 DNS 레코드(<pod-name>.<namespace>.pod.cluster.local)가 생성돼서 coreDNS에 등록되어 DNS resolving Service 지원한다.
  • StatefulSet 등 Pod 이름이 고정된 경우에 유용하다. <pod-name>.<namespace>.pod.cluster.local
cat /etc/resolv.conf  # CoreDNS IP 확인
curl pod-ip.namespace.pod.cluster.local  # 직접 Pod에 접속

※ Round-Robin DNS는 지원하지 않는다.

Round-Robin DNS: 서비스마다 DNS를 부여하고 백엔드 Pod들의 IP를 Record로 나열하여 Round-robin 방식으로 로드 밸런싱하는 방법

  • 쿠버네티스는 DNS Round-Robin 로드밸런싱을 아래 이유로 공식적으로 지원하지 않는다.
    • DNS의 TTL 설정을 고려하지 않는다.
    • 클라이언트 측 DNS 캐싱이 불균형 발생할 수 있다.
    • 트래픽이 특정 Pod으로 몰릴 수 있다.

대신, kube-proxy나 IPVS 등을 통해 실제 트래픽 분산 처리를 수행한다.


5. 백엔드 연결을 위한 kube-proxy: 기초

모든 노드에 존재하며 Service 요청을 적절한 Pod으로 전달하는 역할을 한다.

  • 트래픽을 서비스 뒤의 Pod으로 전달하는 방식을 선택할 수 있으며, 사용 환경에 따라 다양한 프록시 모드를 제공한다.
  • 서버와 클라이언트 사이에서 대리인의 역할을 하며 Node(Host)의 Interface(eth0)와 Pod의 Interface(veth0)를 중계한다.
  • 서비스와 파드 연결(다른 노드간에도 가능) + 서비스 디스커버리(서비스 검색 후 IP와 포트로 트래픽 전달) + 로드 밸런싱(클라이언트 트래픽을 파드로 분산)
  • Node Port와 같은 Service를 통해 외부에 port를 노출시켜야 할 때, 그 port를 Listen하는 역할도 담당한다.

proxy mode 종류

  • userspace
    • 초기 방식
    • 클라이언트 트래픽을 수신해서 해당 서비스의 Pod로 전달하며, IPVS와 iptables 사용하지 않는다.
    • 클라이언트 요청 → kube-proxy 수신 → Pod 전달
  • iptables
    • 기본 방식
    • 쿠버네티스에서 Node와 Pod는 쉽게 교체되는 구조이기 때문에, netfilter와 iptables를 이용한다.
    • Cluster IP가 생성되거나 Pod가 추가될 때, Endpoint 연결을 위해 자신이 동작하고 있는 Node의 iptables에 rule을 추가한다.
    • clusterIP로 접속하면 해당 룰을 통해 Pod로 직접 연결된다.
    • netfilter(리눅스 커널의 일부인 프레임워크)는 네트워크 트래픽을 패킷 필터링하거나, NAT 기능으로 패킷의 소스 또는 목적지 IP주소를 변경해서 실제 서비스의 파드 IP주소로 변환하며, 트래픽 포워딩을 한다.
  • ipvs
    • 리눅스 커널이 지원하는 L4 로드밸런싱 기술 이용하며, 별도의 ipvs 지원 모듈 설정 후 적용 가능하다.
    • IP 주소가 Dummy NIC에 바인딩되어, LoadBalancer나 External IP의 트래픽 분산 성능 향상시킨다.
    • 즉, 가상의 인터페이스에 IP를 할당하여 실제 NIC를 사용했을 때 충돌 위험성을 피하고 Virtual IP만 트래픽 분산용으로 사용하고, 실제 패킷 처리는 ipvs가 처리한다.

6. Session Affinity

Service에서 특정 클라이언트의 요청이 항상 동일한 Pod으로 가도록 설정한다.

sessionAffinity: ClientIP
  • iptables 기반 kube-proxy에서 recent 모듈을 사용해 구현한다.
    • recent는 리눅스 커널 netfilter 기능 중 하나로, 최근에 접속했던 IP 주소를 기억하고, 조건에 따라 처리하는 기능을 제공한다.
  • 특정 클라이언트의 Source IP 기준으로 이전에 접근한 Pod 기억한다.
  • iptables에 IP–Pod 매핑을 저장하고, 이후 트래픽도 동일한 Pod으로 전달한다.
  • 클라이언트가 원하지 않는데 해당 Pod로 갈 수 있기 때문에, 헤더나 쿠키, 세션 등을 잘 이용해야 한다.

7. Endpoint란?

Service와 연결된 Pod의 IP와 Port 정보 집합으로 IP/Port를 Endpoints 리소스로 관리한다.

  • Service가 selector로 Pod를 찾아서 자동 생성되며, kubectl get endpoints로 확인 가능하다.
  • spec.selector를 생략하는 경우 수동 정의해야 한다. 이때 Service의 metadata.name과 동일해야 하며, 지정한 subsets.addresses는 외부에 존재하는 IP가 될 수도 있다.
    • 외부 서버를 내부에서 접근하고 싶을 때나 Pod가 아닌 고정된 IP/Port로 구성된 백엔드에 붙고 싶을 때 사용한다.
profile
It’s always white night here.

0개의 댓글