Gom3rye

Kubernetes- Session Affinity, Headless, ExternalName, None-Selector, Ingress 본문

현대 오토에버 클라우드 스쿨

Kubernetes- Session Affinity, Headless, ExternalName, None-Selector, Ingress

Gom3rye 2025. 7. 8. 17:43
728x90
반응형

Service API

그 외 서비스 기능

Session Affinity

ClusterIP 서비스에서 활성화하는 경우 파드에서 ClusterIP로 보내진 트래픽은 서비스에 연결된 어느 하나의 파드에 전송된 후 다음 트래픽도 계속 같은 파드에 보내지도록 설정할 수 있는 기능

  • 서비스에 대한 설정은 spec.sessionAffinity와 spec.sessionAffinityConfig를 사용한다.
  • 이 기능은 각 쿠버네티스 노드에 iptables로 구현되어 있으며 최대 세션 고정 시간(sessionAffinityConfig.clientIP.timeoutSeconds)를 설정할 수 있다.
  • spec.sessionAffinity 기본값은 None
서로 다른 노드에 있는 파드들끼리 통신하려면 ClusterIP(Load Balancing)가 있어야 한다.
Session: 서버에 저장된 데이터
- 메모리에 저장
- DB에 저장
    - RDBMS
    - In Memory DB (자동 백업이 됨)
  • 각각의 메모리에 정보 저장함 → 사용자가 요청을 보낼 때, 그 요청이 매번 다른 파드로 라우팅되면, 어떤 파드엔 로그인 세션 정보가 있고, 어떤 파드엔 없기 때문에, 어떤 페이지는 로그인 상태, 어떤 페이지는 로그인 안 된 상태로 보이게 되어 유저에게 혼란을 줄 수 있음 → session affinity 써서 해당 정보가 저장된 메모리를 가지고 있는 파드로 가게끔 설정 (상태 있는 앱(예: 로그인 세션)에서는 필수)
    • 참고) 파드는 휘발성이기 때문에, 재시작되면 메모리/세션 정보도 사라진다.
    • 이 때문에 실제 서비스 환경에서는 Redis, Memcached 같은 외부 세션 저장소를 써서 세션을 중앙에서 관리하는 게 일반적이다.
  • 또는 DB에 정보 저장하던지
  • Blue(예전꺼)/Green(최근꺼) Update: 새로 만든 걸 지우지 않는다. A/B 테스트를 위해서, 일정 시간 동안 안정한지 보려고 → 이때도 SessionAffinity 필요.
    • 사용자가 처음 연결된 파드(Green 또는 Blue)와 계속 연결되어야 하니까
    • 그렇지 않으면 트래픽이 Green ↔ Blue 간 왔다 갔다 하면서 세션 끊김이 발생할 수 있다.

📦 파드(Pod) vs 🖥️ 노드(Node) — 메모리 관점

항목                   설명

파드(Pod) 실제로 애플리케이션 컨테이너가 실행되는 단위. 각각의 파드는 자기만의 메모리, 세션, 상태를 가짐.
노드(Node) 파드들을 실행시키는 리눅스 서버 또는 VM. 여러 파드가 같은 노드에서 실행될 수 있지만, 메모리를 공유하지 않음.
# sample-session-affinity.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-session-affinity
spec:
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10 # 동일한 IP에서 10초 이내의 요청은 동일한 파드로 전송
  type: LoadBalancer
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080
    targetPort: 80
    nodePort: 30084
  selector:
    app: sample-app
    
# 리소스 생성
kubectl apply -f sample-session-affinity.yaml
# 계속 한 파드로 가는지 확인 (From= 부분 보면 된다.)
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- curl -s <http://sample-session-affinity.default.svc.cluster.local:8080>
# 속성 수정
kubectl patch service sample-session-affinity -p '{"spec": {"sessionAffinity":"None"}}'

  • NodePort 서비스에도 session affinity를 활성화할 수 있지만 어느 쿠버네티스 노드에 전송하는지에 따라 같은 클라이언트 IP라고 같은 파드에 전송된다고 단정할 수 없으므로 주의해야 한다. (요청이 어떤 노드에 가느냐에 따라 라우팅되는 파드가 달라질 수 있어서 세션 유지가 깨질 수 있음)
    • NodePort란?
      • 각 노드(Node)의 특정 포트를 외부에 열어두고, 거기로 들어오는 트래픽을 내부 서비스로 전달하는 방식
      • 요청은 랜덤한 노드 중 하나로 들어가고, 그 노드가 백엔드 파드 중 하나로 요청을 전달함
    • sessionAffinity는 쿠버네티스 서비스 내부에서만 보장되는 기능, NodePort는 노드 레벨에서 처리되기 때문에, 각 노드가 어떤 파드로 라우팅할지는 노드의 kube-proxy나 iptables 설정에 따라 달라진다. → 다시 말해, "같은 IP"를 보고 같은 파드로 보내는 기능이, NodePort에선 일관되게 작동하지 않을 수 있다는 뜻
  • NodePort 서비스에는 사용하지 않는다.

노드 간 통신 제외와 발신 측 IP 주소 유지

  • NodePort 서비스와 LoadBalancer 서비스에서 쿠버네티스 노드에 도착한 요청은 노드를 통해 파드에도 로드밸런싱하게 되어 있으므로 불필요한 2단계 로드밸런싱이 이루어진다.
  • LoadBalancer 서비스에서 로드밸런서가 로드밸런싱을 해서 노드에 도착한 요청은 노드를 통해 파드에도 로드밸런싱이 되는 구조이다.
  • NodePort 서비스도 마찬가지인데 어떤 호스트의 NodePort에 도착한 요청은 노드를 통해서 파드에도 로드밸런싱 된다.

스케일업이나 다운에 영향을 안 받기 위해서는 ip가 클러스터 바깥에 있어야 한다.

→ NodePort, ExternalIP는 ip가 클러스터 바깥에 있음


Kubernetes에서의 외부 요청 처리 구조 및 IP 노출 전략

🔁 1. NodePort / LoadBalancer 서비스의 2단계 로드밸런싱 문제

  • NodePortLoadBalancer 서비스는 외부 요청이 노드(Node)로 먼저 들어오고, 그 다음에 파드(Pod)로 전달됨.
  • 이 과정에서 두 번의 로드밸런싱이 발생:
    1. (LoadBalancer의 경우) 클라우드 로드밸런서 → 노드
    2. (공통) 노드 → 해당 파드 중 하나
  • 이런 방식은 예측 불가능한 분산을 일으킬 수 있으며, 클라이언트 IP가 파드까지 정확히 전달되지 않을 수 있음.

🌍 2. IP 주소와 클러스터 외부 노출

  • 파드의 IP는 동적이며 스케일업/다운 시 변경됨 → 직접 접근 불가
  • 외부에서 안정적으로 접근하려면 클러스터 바깥에 고정된 IP 또는 DNS 진입점이 필요

접근 방식               클러스터 외부 IP 있음?                                       문제점

NodePort ✅ (노드 IP 사용) 노드 IP가 외부에 노출됨 → 보안/관리상 꺼림직
ExternalIP ✅ (노드에 외부 IP 직접 할당) 마찬가지로 노드 IP 노출 필요
LoadBalancer ✅ (클라우드가 고정 IP 제공) IP는 외부에 있지만, 내부 노드 로드밸런싱은 여전히 존재
Ingress ✅ (도메인 기반 + LoadBalancer 조합 가능) 가장 유연하고 관리가 쉬운 방식

📌 실무에서는 노드 IP를 외부에 직접 노출하지 않는 방향이 권장됨


✅ 결론

  • NodePortExternalIP는 클러스터 외부에서 접근 가능한 IP를 제공하지만,
  • 노드 IP가 외부에 노출되어야 하고, 내부에서 또 로드밸런싱이 되므로 2단계 라우팅 문제가 존재함.
  • LoadBalancerIngress 방식은 파드나 노드의 변화(스케일업/다운)에 영향을 받지 않는 **고정 진입점(IP 또는 도메인)**을 제공함.
  • 따라서,
  • 🔒 노드 IP를 숨기고,🌐 외부 접근을 안정화하려면 → LoadBalancer나 Ingress 사용이 가장 이상적
  • ⚖️ 트래픽 분산을 단순화하고,
  • 파드가 가동 중인 노드와 IP 주소 확인
kubectl get pods -o custom-columns="Name:{metadata.name}, Node:{spec.nodeName}, NodeIP:{status.hostIP}"
##
Name                                  Node          NodeIP
sample-deployment-5d9fcfcbb6-j4pvd   workernode1   192.168.56.101
sample-deployment-5d9fcfcbb6-t7cxf   workernode1   192.168.56.101
sample-deployment-5d9fcfcbb6-xf9pv   workernode1   192.168.56.101
  • 데몬셋의 경우 하나의 노드에 하나의 파드가 배치되기 때문에 굳이 다른 노드의 파드에 전송하지 않고 같은 노드에만 통신하고자 하는 경우 있을 수 있다.
    • 모든 노드에 배치되는 파드가 있다면 굳이 다른 노드를 안 가도 됨 → Daemonset
    • Daemonset과 통신하는 애들은 다른 노드에 가서 통신하지 않아도 됨. 그냥 자기 노드에 있는 Daemonset을 찾아서 통신하면 됨
  • 다른 노드의 파드에 전송하지 않고 같은 노드에만 통신하고자 하는 경우에는 spec.externalTrafficPolicy를 설정한다. (spec.externalTrafficPolicy : 클러스터 외부에서 들어온 요청을 어느 노드의 파드로 보낼지를 제어하는 설정)
    • 설정값
      • Cluster(기본값): 노드에 트래픽이 도착한 후 다른 노드에 있는 파드를 포함하여 다시 로드밸런싱함으로써 파드 부하를 균등하게 분산하는 설정이다.
      • Local: 노드에 트래픽이 도착한 후 노드가 로드밸런싱을 하지 않는다.

구분                                                localhost                                            externalTrafficPolicy: Local

의미 자기 자신 프로세스/컨테이너 간 통신 외부 요청이 들어왔을 때, 같은 노드의 파드로만 라우팅
대상 자기 파드 or 같은 노드의 다른 프로세스 외부 요청 대상인 서비스의 파드 (같은 노드에만 한정)
사용 범위 로컬 내부 통신용 서비스 레벨 트래픽 분산 제어용
외부에서 들어온 요청 처리 가능
# sample-nodeport-local.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-nodeport-local
spec:
  type: NodePort
  externalTrafficPolicy: Local
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 8080
    targetPort: 80
    nodePort: 30085
  selector:
    app: sample-app
    
# 서비스 생성
kubectl apply -f sample-nodeport-local.yaml
# 확인
curl -s <http://실제노드의IP:30085> 했을 때 데이터를 확인해봐야 한다.
# LoadBalancer에서도 사용 가능하다. (NodePort가 클라우드 환경이 아니거나 실습 환경에서 좀 더 쉽게 접근 가능한 방법일뿐)
  • NodePort 서비스의 경우는 동일한 노드에 파드가 없으면 요청에 응답을 할 수 없지만, LoadBalancer 서비스의 경우 별도로 헬스 체크를 통해 해당 노드에 서비스가 제대로 동작하는지 확인하고 노드에 파드가 없거나, 서비스가 정상적이지 않은 경우, 로드밸런서는 그 노드를 트래픽 라우팅 대상에서 자동으로 제외한다.
    • LoadBalancer는 헬스 체크를 이용해 요청을 보내도 되는 노드를 '스마트하게' 골라서 보내는 반면, NodePort는 모든 노드가 무조건 열려 있어서 파드가 없는 노드로 요청이 가면 실패할 수 있다.
    • 헬스 체크용 NodePort의 포트 번호는 spec.healthCheckNodePort 에서 설정 가능하다.

Headless Service

대상이 되는 개별 파드의 ip주소가 직접 반환되는 서비스

  • 일반 서비스(Service)는 클러스터 내부에 가상 IP(ClusterIP)를 만들어서 여러 파드에 대한 트래픽을 한 곳으로 모아서 분산(로드밸런싱)해준다.
  • 반면, Headless Service는 가상 IP를 만들지 않고, 서비스 이름으로 개별 파드들의 실제 IP 주소 목록을 DNS로 직접 알려준다.
  • → 그래서 클라이언트가 DNS 질의를 하면 파드 IP들이 쭉 나오는 구조
  • IP 앤드포인트
    • ClusterIP: 쿠버네티스 클러스터 내부에서만 접근 가능한 가상 IP
    • ExternalIP: 특정 쿠버네티스 노드의 외부 IP
    • NodePortIP: 모든 쿠버네티스 노드의 IP 주소 (0.0.0.0)
    • LoadBalancer: 클러스터 외부에서 제공되는 로드밸런서의 가상 IP
  • 특징:
    • 가상 IP가 없다! → 대신 파드들의 실제 IP 주소를 DNS를 통해 알려준다
    • DNS가 라운드 로빈 방식으로 파드 IP 리스트를 반환 → 클라이언트가 직접 원하는 파드로 선택 가능 (로드밸런싱을 위한 IP 주소는 제공되지 않고 DNS 라운드 로빈을 사용한 앤드포인트를 제공)
    • StatefulSet에서 특히 많이 사용 → 파드별로 고정된 이름과 IP로 직접 접속 가능 (StatefulSet이 Headless Service를 사용하는 경우에만 파드명으로 IP 주소를 디스커버리 할 수 있다.)
  • Headless Service를 사용할 때
    • 파드 개별 IP를 직접 알아야 할 때
    • 특히 StatefulSet처럼 각각의 파드가 고유한 정체성(이름과 IP)을 가지고 있어야 하는 경우
    • 예를 들어 데이터베이스 클러스터에서 각각의 노드에 직접 접근해야 할 때 유용하다.
  • 헤드리스 서비스 생성 시 만족해야 하는 조건
    • 서비스의 spec.type이 ClusterIP여야 한다.
    • 서비스의 spec.clusterIP가 None 이어야 한다.
# headless 서비스에 사용할 statefulset
# sample-statefulset-headless.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: sample-statefulset-headless
spec:
  serviceName: sample-headless
  replicas: 3
  selector:
    matchLabels:
      app: sample-app1
  template:
    metadata:
      labels:
        app: sample-app1
    spec:
      containers:
        - name: nginx-container
          image: amsy810/echo-nginx:v2.0

# sample-headless.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-headless
spec:
  type: ClusterIP
  clusterIP: None
  ports:
  - name: "http-port"
    protocol: "TCP"
    port: 80
    targetPort: 80
  selector:
    app: sample-app1 # statefulset이랑 연결해야 하니까 이름 바꿔주고 따로 하나 만들기
    
# 리소스 생성
kubectl apply -f sample-statefulset-headless.yaml
kubectl apply -f sample-nodeport-local.yaml
# 서비스 이름(sample-headless)으로 확인
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-headless.default.svc.cluster.local
## statefulset ip로 잘 가는 것을 확인할 수 있다.
;; ANSWER SECTION:
sample-headless.default.svc.cluster.local. 30 IN A 10.244.2.132
sample-headless.default.svc.cluster.local. 30 IN A 10.244.2.133
sample-headless.default.svc.cluster.local. 30 IN A 10.244.1.62
# 원래 여기다 pod의 이름을 쓸 수가 없었는데 파드 이름을 이용해서 통신도 가능하다.
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-statefulset-headless.default.svc.cluster.local
  • 스테이트풀셋 외의 파드명으로 해석
    • spec.hostname과 spec.subdomain을 추가해야 한다.
    • 이때 spec.hostname은 pod명이 아니어도 된다.
# 파드명으로 해석하기 실습
# sample-subdomain.yaml 로 파드 1개와 서비스 만들기
apiVersion: v1
kind: Pod
metadata:
  name: sample-subdomain
  labels:
    app: sample-app2
spec:
  hostname: sample-hostname
  subdomain: sample-subdomain
  containers:
  - name: nginx-container
    image: amsy810/echo-nginx:v2.0

---
apiVersion: v1
kind: Service
metadata:
  name: sample-subdomain
spec:
  type: ClusterIP
  clusterIP: None
  ports: []
  selector:
    app: sample-app2
    
# 리소스 생성 (---로 이어서 두 파일을 썼으니 두 개 생길 것)
kubectl apply -f sample-subdomain.yaml
## pod/sample-subdomain created
## service/sample-subdomain created

# Hostname 이름.Subdomain/서비스명.네임스페이스명.svc.cluster.local로 해석 가능
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-hostname.sample-subdomain.default.svc.cluster.local
## ;; ANSWER SECTION:
## sample-hostname.sample-subdomain.default.svc.cluster.local. 30 IN A 10.244.2.139
# 서비스 이름뿐만 아니라 pod의 이름으로도 통신이 가능한 것을 확인할 수 있다.
  • 쉽게 비유하자면,
    • 일반 서비스: 우체국 창구 하나(가상 IP)에서 편지 받아서 여러 집(파드)로 배달
    • Headless Service: 우체국이 각 집 주소(파드 IP)를 직접 알려주고, 편지를 보내는 사람이 직접 집으로 보냄

ExternalName 서비스

쿠버네티스 안에서 서비스 이름을 사용하면, 실제로는 외부 도메인(CNAME)으로 연결되도록 만들어 주는 DNS 리다이렉션 서비스

  • 일반적인 서비스와 다르게 서비스 이름의 이름 해석을 외부 도메인으로 CNAME으로 반환한다.
  • 다른 이름을 사용하거나 클러스터 내부에서의 엔드포인트를 쉽게 변경하고자 할 때 사용한다.
  • 즉, my-service.default.svc.cluster.local 이라는 쿠버네티스 서비스 이름을 사용하면, 실제로는 external.example.com 같은 외부 주소로 DNS 이름이 치환되어 통신하게 된다.
  • 쓰는 상황
    • 클러스터 내부 앱이 외부 서비스(예: 외부 DB, 외부 API)를 호출해야 하는데, 그 외부 주소를 코드에 직접 쓰고 싶지 않을 때.
    • 외부 도메인을 쿠버네티스 서비스처럼 일관된 이름으로 다루고 싶을 때.
    • 혹은 향후 주소가 바뀌더라도 서비스 이름만 유지되면 코드는 바꿀 필요 없게 하려고.

ExternalName 서비스 생성

# sample-externalname.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-externalname
  namespace: default
spec:
  type: ExternalName
  externalName: external.example.com # 나중에 실제 내가 쓸 도메인을 적으면 된다.
    
# 리소스 생성
kubectl apply -f sample-externalname.yaml
# 확인 -> clusterip가 처음으로 안 만들어졌다.
kubectl get service
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP            PORT(S)      AGE
kubernetes             ClusterIP      10.96.0.1       <none>                 443/TCP      6d21h
sample-externalip      ClusterIP      10.102.42.51    192.168.202.169        8080/TCP     23h
sample-externalname    ExternalName   <none>          external.example.com   <none>       9s

# 서비스 이름으로 요청 -> CNAME이 반환됨
kubectl run --image=amsy810/tools:v2.0 --restart=Never --rm -i testpod --command -- dig sample-externalname.default.svc.cluster.local CNAME
## ;; ANSWER SECTION:
## sample-externalname.default.svc.cluster.local. 30 IN CNAME external.example.com.
  • 외부 서비스와 느슨한 결합 확보
    • 클러스터 내부에서는 파드와 통신에 서비스 이름 해석을 사용하여 느슨한 결합(Loose Coupling)을 유지하고 있다.
    • 클러스터 내부에서 SaaS나 IaaS등 외부에 있는 서비스를 사용할 때도 가능하면 느슨한 결합으로 구성하는 것을 권장한다.
    • 애플리케이션 등에 외부의 End Point를 등록해두면 목적지가 변경되었을 때 애플리케이션 측의 설정 변경이 필요하다.
    • ExternalName 서비스를 이용하면 목적지가 변경되어도 ExternalName 서비스를 변경하는 것만으로 가능하다.
    • 목적지 변경에 대한 대응을 쿠버네티스 내부에서 끝낼 수 있으므로 외부 서비스와 느슨한 결합을 유지할 수 있다.
    • 위치 투명성을 확보할 수 있다.
      • “리소스가 어디에 있든 간에, 사용하는 쪽에서는 위치를 신경 쓸 필요가 없다” 는 의미
      • Ex) 애플리케이션은 external-api.default.svc.cluster.local 만 알면 됨 → 그게 외부에 있든, 내부에 있든, GCP든 AWS든 위치를 신경 안 써도 됨
  • 외부 서비스와 내부 서비스 간의 전환
    • ExternalName을 사용하면 외부 서비스와의 느슨한 결합을 확보하고 외부 서비스와 쿠버네티스에 배포된 클러스터 내부 서비스와의 전환도 유연하게 할 수 있다.
    • 애플리케이션측은 FQDN을 지정해두고 이름 해석이 되면 ExternalName의 CNAME 레코드 또는 ClusterIP의 A레코드가 반환 되는 형태가 되어 애플리케이션 측은 변경 없이 내부 서비스와 외부 서비스를 전환할 수 있다.
    • 주의해야 할 점은 ClusterIP 서비스에서 ExternalName 서비스로 전환할 때 spec.clusterIP를 명시적으로 공란으로 만들어줘야 한다.

항목                           설명

🎯 목표 외부 시스템과 느슨한 결합 → 유지보수, 유연성 향상
🛠️ 도구 ExternalName, ConfigMap, 서비스 이름 해석(DNS)
💡 이점 코드 수정 없이 외부 주소 변경 가능, 위치 투명성 확보
✅ 권장 외부 SaaS/IaaS API와도 이렇게 연결할 것

None-Selector 서비스

spec.selector가 없거나 생략된 서비스

  • None-Selector 서비스에서는 서비스명으로 이름을 해석하면 자신이 설정한 멤버에 대해서 로드밸런싱을 수행한다.
  • 클라이언트 사이드 로드밸런싱용 앤드포인트를 제공한다. (지금까지는 서버 사이드였지만 이건 클라이언트가 직접 안에 들어와서 로드밸런싱을 한다.)
  • 대부분의 경우는 ClusterIP를 가지고 지원한다.

항목                               일반 서비스                                       None-Selector 서비스

파드 연결 방식 selector로 자동 연결 수동으로 Endpoints 리소스 연결
로드밸런싱 방식 서버 사이드 (쿠버네티스가) 클라이언트 사이드 (앱이 직접)
사용 목적 대부분의 앱 외부 시스템, 고급 네트워킹, 클라이언트 로드밸런싱
  • LoadBalancer나 NodePort를 가지고 하는 것이 안되는 것은 아니지만 쿠버네티스 외부 로드밸런서에서 수신한 트래픽이 쿠버네티스 클러스터로 전송된 후 다시 쿠버네티스 클러스터 외부로 전송하는 형태가 되기 때문에 유용하지 못하다.
  • externalName을 지정하지 않고 Selector가 존재하지 않는 서비스를 생성한 후 앤드포인트 리소스를 수동으로 만들면 유연한 서비스를 만드는 것이 가능하다.
  • ExternalName 서비스는 CNAME이 반환되는데 None-Selector 서비스는 ClusterIP의 A레코드가 반환된다.
# sample-none-selector.yaml
apiVersion: v1
kind: Service
metadata:
  name: sample-none-selector
spec:
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 8080
    targetPort: 80
---
apiVersion: v1
kind: Endpoints
metadata:
  name: sample-none-selector
subsets:
- addresses:
  - ip: 192.168.1.1
  - ip: 192.168.1.2
  ports:
  - protocol: TCP
    port: 80
    
# 리소스 생성
kubectl apply -f sample-none-selector.yaml
# 서비스 확인
kubectl describe service sample-none-selector
##
Name:              sample-none-selector
Namespace:         default
Labels:            <none>
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP Family Policy:  SingleStack
IP Families:       IPv4
IP:                10.101.212.58
IPs:               10.101.212.58
Port:              <unset>  8080/TCP
TargetPort:        80/TCP
Endpoints:         192.168.1.1:80,192.168.1.2:80
Session Affinity:  None
Events:            <none>

# sample-none-selector에게 요청하면 ip: 192.168.1.1, ip: 192.168.1.2 쪽으로 요청이 갈 것(로드밸런싱)

사용                                                            예시설명

외부 DB 접속 외부 PostgreSQL, MongoDB replica set 등
레거시 시스템 연동 클러스터 외부에 있는 서버들을 서비스처럼 사용
StatefulSet 라운드로빈 클라이언트가 여러 파드로 직접 요청 보내게 할 때
gRPC 클라이언트 여러 엔드포인트를 직접 관리하며 라운드로빈 처리

Ingress

  • Ingress를 제외한 모든 서비스는 L4 로드밸런싱을 제공하는 리소스인데 Ingress는 L7 로드밸런싱을 제공하는 리소스이다.
  • Ingress는 서비스들을 묶는 서비스들의 상위 객체로 서비스 종류의 하나로서가 아닌 독립된 리소스로 구현한다.
  • Ingress를 사용할 때는 kind: Service가 아니라 kind: Ingress이다.
  • 쿠버네티스 Network Policy 리소스에 Ingress/Egress 라는 설정 항목이 있는데 Ingress와는 전혀 상관 없다.

리소스와 컨트롤러

  • 쿠버네티스는 분산 시스템이며 매니페스트로 정의한 리소스를 쿠버네티스에 등록하는 것으로 시작한다.
  • 하지만 등록만으로는 아무런 처리가 이뤄지지 않고 실제 처리를 하는 컨트롤러라는 시스템 구성 요소가 필요하다.
  • 디플로이먼트에서는 레플리카셋을 생성하거나 레플리카 수를 변경해 가면서 롤링 업데이트를 하는 디플로이먼트 컨트롤러라고 하는 시스템 구성 요소가 쿠버네티스 클러스터에서 동작한다.
  • 디플로이먼트 컨트롤러가 없는 경우 매니페스트로 디플로이먼트 리소스를 생성해도 레플리카셋은 생성되지 않는다.
  • 각 리소스는 등록된 후 여러 컨트롤러가 실제로 처리를 함으로써 시스템이 동작한다.

인그레스 리소스와 인그레스 컨트롤러

  • 인그레스가 가리키는 것은 다양한 개념의 집합 (인그레스 : 여러 서비스의 집합)
  • 인그레스 리소스란 매니페스트에 등록된 API 리소스를 의미하고 인그레스 컨트롤러는 인그레스 리소스가 쿠버네티스에 등록되었을 때 어떤 처리를 수행해주는 객체
  • 처리의 예
    • GCP의 CGLB를 조작해서 L7 로드밸런서 설정을 하거나 Nginx 설정을 변경해서 리로드를 하는 등의 처리
    • AWS에서는 ALB

인그레스의 종류

  • 클러스터 외부 로드밸런서를 사용한 인그레스
    • AWS의 EKS 인그레스
    • GCP의 GKE 인그레스
  • 클러스터 내부 인그레스용 파드를 배포하는 인그레스
    • Nginx 인그레스

클러스터 외부 로드밸런서를 사용한 인그레스

  • 외부 로드밸런서를 사용한 인그레스의 경우 인그레스 리소스 생성만으로 로드밸런서의 가상 IP가 할당되어 사용 가능하다.
  • 인그레스의 트래픽은 외부 로드밸런서가 트래픽을 수신한 후 외부 로드밸런서에서 SSL 터미네이션이나 경로 기반 라우팅을 통해서 NodePort에 트래픽을 전송해서 대상 파드까지 도달한다.

클러스터 내부에 인그레스용 파드를 배포하는 인그레스

  • 클러스터 내부에 인그레스용 파드를 배포하는 인그레스 패턴은 인그레스 리소스에서 정의한 L7 수준의 로드밸런싱 처리를 하기 위해서 인그레스용 파드를 클러스터 내부에 생성해야 한다.

항목                    ClusterIP                             NodePort                                           Ingress (L7)

접근 범위 클러스터 내부 전용 클러스터 외부에서 접근 가능 외부에서 도메인/경로 기반 접근 가능
IP 타입 가상 IP (내부 전용) 노드의 실제 IP 사용 외부 로드밸런서 + 내부 서비스 연결
로드밸런싱 L4 (IP/Port) L4 (IP/Port) L7 (HTTP Path, Host 기반)
사용 목적 파드 간 통신 간단한 외부 노출 복잡한 라우팅, TLS, 인증 등 고급 처리
외부 접근 ✅ (Ingress Controller 필요)

우리가 서비스를 만들면 대부분은 Ingress(L7 LoadBalancer)로 연결한다.

인그레스 배포

  • 외부 인그레스는 public cloud 서비스에 접속해서 배포 (딱히 yaml 파일 만들지 않아도 된다.)
  • Nginx 인그레스 컨트롤러 배포
    • Nginx 인그레스에는 인그레스 컨트롤러 자체가 L7 수준의 처리를 하는 파드이기도 하므로 이름은 컨트롤러지만 실제 처리도 수행한다.
  • 주의!!) 정의된 규칙에 일치하지 않을 경우 기본 목적지용 디플로이먼트도 생성해야 한다.
  • 따라서 대부분의 경우는 정의되지 않은 요청이 들어올 때 반환되는 404 Not Found 페이지를 준비하면 된다.
  • 디플로이먼트는 L7 처리를 하는 Nginx 인그레스 컨트롤러 파드나 기본 백엔드용 파드의 레플리카 수가 고정이면 트래픽이 증가할 때 제대로 처리하지 못할 가능성이 있으므로 파드에 오토스케일링하는 HPA의 사용도 고려해야 한다.
  • 인그레스 컨트롤러를 한 번에 배포하기 위한 매니페스트는 깃허브에 공개되어 있다.

서비스 종류 총 정리

  • ClusterIP: 쿠버네티스 클러스터 내부에서만 통신 가능한 IP
  • NodePort: 모든 노드의 IP 주소(0.0.0.0)
  • LoadBalancer: 클러스터 외부에서 제공되는 로드밸런서의 가상 IP
  • IngressController: L7 로드밸런서
  • ExternalIP: 특정 쿠버네티스 노드의 IP 주소 이용
  • Headless: 파드의 IP 주소를 이용한 DNS 라운드 로빈
  • ExternalName: CNAME을 사용한 느슨한 결합
  • None-Selector: 원하는 목적지 멤버를 설정할 수 있는 다양한 앤드포인트
728x90
반응형