Gom3rye
Kubernetes- Session Affinity, Headless, ExternalName, None-Selector, Ingress 본문
Kubernetes- Session Affinity, Headless, ExternalName, None-Selector, Ingress
Gom3rye 2025. 7. 8. 17:43Service 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란?
- NodePort 서비스에는 사용하지 않는다.
노드 간 통신 제외와 발신 측 IP 주소 유지
- NodePort 서비스와 LoadBalancer 서비스에서 쿠버네티스 노드에 도착한 요청은 노드를 통해 파드에도 로드밸런싱하게 되어 있으므로 불필요한 2단계 로드밸런싱이 이루어진다.
- LoadBalancer 서비스에서 로드밸런서가 로드밸런싱을 해서 노드에 도착한 요청은 노드를 통해 파드에도 로드밸런싱이 되는 구조이다.
- NodePort 서비스도 마찬가지인데 어떤 호스트의 NodePort에 도착한 요청은 노드를 통해서 파드에도 로드밸런싱 된다.
스케일업이나 다운에 영향을 안 받기 위해서는 ip가 클러스터 바깥에 있어야 한다.
→ NodePort, ExternalIP는 ip가 클러스터 바깥에 있음
Kubernetes에서의 외부 요청 처리 구조 및 IP 노출 전략
🔁 1. NodePort / LoadBalancer 서비스의 2단계 로드밸런싱 문제
- NodePort와 LoadBalancer 서비스는 외부 요청이 노드(Node)로 먼저 들어오고, 그 다음에 파드(Pod)로 전달됨.
- 이 과정에서 두 번의 로드밸런싱이 발생:
- (LoadBalancer의 경우) 클라우드 로드밸런서 → 노드
- (공통) 노드 → 해당 파드 중 하나
- 이런 방식은 예측 불가능한 분산을 일으킬 수 있으며, 클라이언트 IP가 파드까지 정확히 전달되지 않을 수 있음.
🌍 2. IP 주소와 클러스터 외부 노출
- 파드의 IP는 동적이며 스케일업/다운 시 변경됨 → 직접 접근 불가
- 외부에서 안정적으로 접근하려면 클러스터 바깥에 고정된 IP 또는 DNS 진입점이 필요
접근 방식 클러스터 외부 IP 있음? 문제점
| NodePort | ✅ (노드 IP 사용) | 노드 IP가 외부에 노출됨 → 보안/관리상 꺼림직 |
| ExternalIP | ✅ (노드에 외부 IP 직접 할당) | 마찬가지로 노드 IP 노출 필요 |
| LoadBalancer | ✅ (클라우드가 고정 IP 제공) | IP는 외부에 있지만, 내부 노드 로드밸런싱은 여전히 존재 |
| Ingress | ✅ (도메인 기반 + LoadBalancer 조합 가능) | 가장 유연하고 관리가 쉬운 방식 |
📌 실무에서는 노드 IP를 외부에 직접 노출하지 않는 방향이 권장됨
✅ 결론
- NodePort나 ExternalIP는 클러스터 외부에서 접근 가능한 IP를 제공하지만,
- → 노드 IP가 외부에 노출되어야 하고, 내부에서 또 로드밸런싱이 되므로 2단계 라우팅 문제가 존재함.
- LoadBalancer와 Ingress 방식은 파드나 노드의 변화(스케일업/다운)에 영향을 받지 않는 **고정 진입점(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: 원하는 목적지 멤버를 설정할 수 있는 다양한 앤드포인트
'현대 오토에버 클라우드 스쿨' 카테고리의 다른 글
| Kubernetes- Volume API (3) | 2025.07.10 |
|---|---|
| Kubernetes- Config & Storage API (4) | 2025.07.09 |
| Kubernetes Service의 type (1) | 2025.07.07 |
| Kubernetes- Job, CronJob, Service (2) | 2025.07.04 |
| Kubernetes- DaemonSet, StatefulSet, Job (3) | 2025.07.03 |