티스토리 뷰
서비스 API 카테고리
서비스 API 카테고리로 분류된 리소스는 클러스터상의 컨테이너에 대한 엔드포인트를 제공하거나 레이블과 일치하는 컨테이너의 디스커버리에 사용되는 리소스이다. 내부적으로 사용되는 리소스를 제외하고 사용자가 직접 사용하는 것은 L4 로드 밸런싱을 제공하는 서비스 리소스와 L7 로드 밸런싱을 제공하는 인그레스 리소스, 두 종류가 있다. 또한, 서비스 리소스에는 제공 목적에 따라 클러스터 내부나 클러스터 외부에서 접속할 수 있는 가상 IP 등의 엔드포인트를 제공하고 있다.
쿠버네티스 클러스터 네트워크와 서비스
EKS 기본 아키텍처 게시글에서 확인할 수 있는데 EKS 클러스터를 생성하면 노드별로 Amazon VPC CNI가 설치되어 내부 네트워크가 자동으로 구성되기 때문에 파드는 서비스를 사용하지 않고도 파드 간 통신이 가능하지만 서비스를 사용하면 다음 두 가지 큰 장점을 얻을 수 있다.
- 파드에 대한 트래픽 로드 밸런싱
- 서비스 디스커버리와 클러스터 내부 DNS
파드에 대한 트래픽 로드 밸런싱
디플로이먼트를 사용하여 여러 파드를 기동할 수 있는데, 파드는 기동될 때마다 각기 다른 IP 주소를 할당받기 때문에 로드 밸런싱하는 구조를 자체적으로 구현하려면 각 파드의 IP 주소를 매번 조회하거나 전송 대상의 목적지를 설정해야 한다. 또한, 서비스는 로드 밸런싱의 접속 창구가 되는 엔드포인트도 제공한다. 엔드포인트는 외부 로드 밸런서가 할당하는 가상 IP 주소(Virtual IP 주소)나 클러스터 내부에서만 사용할 수 있는 가상 IP 주소(ClusterIP) 등 여러 가지 종류를 제공한다.
서비스 디스커버리와 클러스터 내부 DNS
서비스 디스커버리는 특정 조건의 대상이 되는 멤버를 보여주거나 이름에 엔드포인트를 판별하는 기능으로 서비스(Service)가 해당 기능을 제공하고 있다. 즉, 서비스에 속해 있는 파드를 보여주거나 서비스명에서 엔드포인트 정보를 반환하는 것을 말한다.
DNS를 사용한 서비스 디스커버리는 클러스터 내부 DNS 서버에 자동으로 등록되는 서비스 엔드포인트 정보를 사용할 수 있다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: sample-deployment
spec:
replicas: 3
selector:
matchLabels:
app: sample-app
template:
metadata:
labels:
app: sample-app
spec:
containers:
- name: nginx-container
image: amsy810/echo-nginx:v2.0
---
apiVersion: v1
kind: Service
metadata:
name: sample-clusterip
spec:
type: ClusterIP
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
selector:
app: sample-app

다른 파드에서 서비스로 할당되는 엔드포인트에 접속하려면 당연히 목적지가 필요하지만, 할당된 IP 주소를 사용하는 방법 외에도 자동 등록된 DNS 레코드를 사용할 수 있다. 위 사진에서 확인할 수 있듯이 자동 할당된 IP 주소에 연결된 DNS명을 사용하여 같은 HTTP 요청을 보내도 같은 결과를 얻을 수 있는 것을 확인할 수 있다.
ClusterIP

ClusterIP 서비스는 쿠버네티스 클러스터 내부에서만 통신 가능한, Internal Network에 생성되는 가상 IP가 할당된다. 따라서 해당 유형의 서비스에 접근하는 클라이언트도 클러스터 내부에 위치해야 하며, 외부에서 접속이 필요한 경우에는 별도의 NodePort나 LoadBalancer, 혹은 Ingress 설정이 필요하다.
ClusterIP로 트래픽이 전달되는 방식은 각 노드에 상주하는 kube-proxy가 핵심 역할을 한다. 클라이언트가 서비스의 ClusterIP로 요청을 보내면, kube-proxy는 노드의 iptables에 설정된 분산 규칙을 통해 요청을 서비스에 연결된 파드 중 하나로 무작위로 전달한다. 이 과정을 통해 로드 밸런싱이 자동으로 수행되며, 파드가 어느 노드에 위치해 있는지와 관계없이 요청은 적절한 파드로 분산된다.
apiVersion: v1
kind: Service
metadata:
name: sample-clusterip
spec:
type: ClusterIP
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
selector:
app: sample-app
type: ClusterIP를 지정하면 ClusterIP 서비스를 생성할 수 있고, 설정 항목은 다음과 같다. 그리고 기본적으로 쿠버네티스 서비스에 등록된 클러스터 내부 DNS 레코드를 사용하여 호스트(sample-clusterip)를 지정하는 것이 바람직하다.
- spec.ports[].port: ClusterIP에서 수신할 포트 번호를 지정
- spec.ports[].targetPort: 목적지 컨테이너 포트 번호를 지정
NodePort

NodePort는 모든 쿠버네티스 노드의 IP 주소:포트에서 수신한 트래픽을 컨테이너에 전송하는 형태로 외부와 통신할 수 있는 서비스다. 즉, Listen할 때 0.0.0.0:포트를 사용하여 모든 IP 주소로 바인드하는 형태다.
NodePort 생성
apiVersion: v1
kind: Service
metadata:
name: sample-nodeport
spec:
type: NodePort
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
nodePort: 30000
selector:
app: sample-app

매니페스트를 사용하여 NodePort 서비스를 생성할 수 있고, 설정 항목은 다음과 같다. 그리고 생성한 NodePort 서비스를 확인해보면 컨테이너 내부에서의 통신에서 ClusterIP를 사용하기 위해 ClusterIP도 자동으로 할당된 것을 확인할 수 있다.
- spec.ports[].port: ClusterIP에서 수신할 포트 번호
- spec.ports[].targetPort: 목적지 컨테이너 포트 번호
- spec.ports[].nodePort: 모든 쿠버네티스 노드 IP 주소에서 수신할 포트 번호
NodePort에서 사용할 수 있는 포트 범위는 많은 쿠버네티스 환경에서 30000 ~ 32767(쿠버네티스 기본값)이며, 범위 외의 포트를 지정하려고 하면 에러가 발생할 수 있다. 또한, 자동으로 할당되는 NodePort도 같은 범위 안에서 사용 가능하다.

NodePort에서 사용하고 있는 쿠버네티스 노드에서 포트 상태를 확인해보면, 모든 노드의 30000/TCP 포트로 Listen하고 있는 상태가 되어 모든 쿠버네티스 노드의 IP 주소로 쿠버네티스 클러스터 외부에서도 통신이 가능하다. 따라서 노드에 curl -s http:{IP 주소}:30000으로 요청하면 통신이 가능할 것을 확인할 수 있다.
NodePort 주의점
복수의 포트로 Listen 상태를 가질 수 없기 때문에 여러 NodePort 서비스에서 같은 포트를 사용하는 것은 불가능하며, 클라이언트가 노드 IP와 포트를 직접 알아야 하고, 클러스터 내부 네트워크가 외부에 노출될 수 있다는 단점이 있다. 또한, NodePort를 사용하면 하나의 쿠버네티스 노드에 할당된 IP 주소로 통신해야 하기 때문에 그 노드가 단일 장애점(Single Point of Failure, SPoF)가 되어 버린다. 이러한 보안적으로 취약한 부분을 LoadBalancer 서비스를 사용하여 보완할 수 있다.
LoadBalancer 서비스

LoadBalancer 서비스는 쿠버네티스 클러스터 외부의 로드 밸런서에 외부 통신이 가능한 가상 IP를 할당하여 외부와 트래픽을 송수신할 때 사용하는 서비스이다. LoadBalancer 타입을 사용하면 쿠버네티스 노드와 별도로 외부 로드 밸런서를 사용하기 때문에 노드 장애가 발생해도 가용성을 확보한 덕분에 크게 문제가 않는다. 클러스터 내부 구조를 외부에 노출하지 않도록 구성할 수 있다.
클라이언트가 LoadBalancer에 접근하면 LoadBalancer를 통해 노드의 IP와 포트를 통해 NodePort로 인입되고, ClusterIP를 통해 iptables 분산 룰로 파드에 전달된다. 즉, 클라이언트는 로드밸런서의 주소만 알면 되기 때문에 클러스터 내부 구조를 외부에 노출하지 않도록 구성할 수 있다. 이때, LoadBalancer 타입은 OSI 4계층 수준에서 분산 처리가 이루어진다. AWS 환경에서 보면 NLB에 대응된다.
LoadBalancer 서비스 생성
apiVersion: v1
kind: Service
metadata:
name: sample-clusterip
spec:
type: ClusterIP
ports:
- name: "http-port"
protocol: "TCP"
port: 8080
targetPort: 80
nodePort: 30000
selector:
app: sample-app
LoadBalancer 서비스를 생성하면 컨테이너 내부에서의 통신은 ClusterIP를 사용하기 위해 ClusterIP도 자동 할당되고, NodePort도 자동적으로 할당되기 때문에 spec.ports[].nodePort[] 지정도 가능하다. 할당된 가상 IP는 쿠버네티스 노드에 분산되기 때문에 쿠버네티스 노드를 스케일링할 때 변경하지 않아도 된다. 그리고 클러스터 외부에서 Load Balancing(ELB)에 할당된 가상 IP를 통해 통신이 가능하다. 설정 항목은 다음과 같다.
- spec.ports[].port: LoadBalancer에 할당되는 가상 IP와 ClusterIP에서 수신할 포트 번호 지정
- spec.ports[].targetPort: 목적지 컨테이너 포트 번호 지정
- spec.ports[].nodePort: 모든 쿠버네티스 워커 노드 IP 주소에서 수신할 포트 번호 지정
- NodePort도 자동 할당되므로 지정하지 않는 경우 랜덤 포트 지정


watch -d kubectl get pod,svc,ep 명령어를 사용하여 LoadBalancer 서비스와 엔드포인트가 생성된 것을 확인할 수 있다. sample-lb 엔드포인트의 IP 주소 3개는 실제 트래픽이 전달될 파드 IP 주소이다.


위 LoadBalancer 서비스를 생성하면 AWS에서 CNB가 생성된 것을 확인할 수 있다. 리스터는 외부로부터 TCP:8080을 받아서 각 내부 서브넷에 등록된 인스턴스의 TCP:30000 포트로 포워딩한다. 그러면 파드 내 컨테이너가 리스닝 중인 실제 80번 포트로 라우팅하여 트래픽이 전달되는 과정을 거치게 된다.
하지만, AWS에서는 이제 CLB보다는 ALB와 NLB로 목적에 맞게 구분해서 사용하는 것을 권장하고, 이를 위해서는 AWS Load Balancer Controller를 설치하여 Kubernetes 리소스(Service, Ingress 등)와 연동해 자동으로 ALB 또는 LNB를 프로비저닝하고 관리할 수 있도록 구성해야 한다.
AWS Load Balancer Controller
Amazon Load Balancer Controller는 Kubernetes 클러스터에서에서 AWS ELB를 관리하도록 도와주는 Add-on 도구(클러스터를 운영하는데 필수적인 기본 컴포넌트들(coredns, kube-proxy, vpc-cni 등)을 Amazon EKS가 관리형 서비스 형태로 제공하는 기능)로 크게 두 가지 기능을 한다.
- Kuberenetes Ingress 리소스에서 Application Load Balancer를 프로비저닝할 수 있도록 만들어준다.
- Kubernetes Service 리소스에서 Network Load Balancer를 프로비저닝할 수 있도록 만들어준다.

클러스터에 AWS Load Balancer Controller를 설치하면 컨트롤 플레인과 상호 작용하면서 파드 정보를 확인하고, 이벤트를 모니터링하여 클러스터 내부에 어떤 파드가 있는지를 파악한다. 이 정보를 가지고 AWS ELB의 대상 그룹(Target Group) 내 파드를 등록하거나 제거하는 작업을 수행한다. 그러면 ELB는 대상 그룹에 파드를 직접 매핑해서 중간 단계(NodePort, ClusterIP)를 거치지 않고 바로 파드로 통신할 수 있어 conntrack이나 iptables에 의한 처리 과정이 생략되고, 효율적인 통신이 가능하다. 이러한 동작이 가능한 이유는 노드와 파드가 같은 VPC IP 대역을 사용하고 있기 때문이다.
IRSA 생성
# IAM Policy json 파일 다운로드
curl -o iam_policy.json https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/refs/heads/main/docs/install/iam_policy.json
# AWSLoadbBalancerControllerIAMPolicy 생성
aws iam create-policy \
--policy-name AWSLoadBalancerControllerIAMPolicy \
--policy-document file://iam_policy.json
# IRSA 생성 - 위에서 만든 IAM 정책을 연결
eksctl create iamserviceaccount \
--cluster=$CLUSTER_NAME \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--attach-policy-arn=arn:aws:iam::$ACCOUNT_ID:policy/AWSLoadBalancerControllerIAMPolicy \
--override-existing-serviceaccounts \
--approve
# IRSA 정보 확인
eksctl get iamserviceaccount \
--cluster $CLUSTER_NAME
# Kubernetes 서비스 어카운트 확인
kubectl get serviceaccounts \
-n kube-system aws-load-balancer-controller \
-o yaml | yh
AWS Load Balancer가 ALB 또는 NLB와 같은 ELB 리소스를 제어하려면 AWS API를 호출할 수 있는 적절한 IAM 권한이 필요하다. 이를 위해 Kubernetes의 ServiceAccount와 IAM Role을 연결하는 IRSA(IAM Role for Service Account) 방식을 사용한다. IRSA는 클러스터 내 리소스가 AWS 리소스에 안전하게 접근할 수 있도록 도와주는 메커니즘으로 이를 위해 EKS 클러스터에는 OIDC(OpenID Connect) 공급자가 설정되어 있어야 한다. 이 OIDC 공급자는 Kubernetes에서 발급한 서비스 계정의 토큰을 신뢰할 수 있도록 AWS가 검증하는 역할을 한다.
AWS CloudFormation으로 EKS 클러스터 블로그 글을 통해 EKS 클러스터를 생성한 경우, 해당 템플릿에서 OIDC 공급자도 함께 설정되어 있다. 이때 IRSA를 완성하기 위해 OIDC 공급자와 함께 AWS Load Balancer Controller에 필요한 IAM 정책을 연결한 IAM Role을 생성하고, 이를 컨트롤러의 ServiceAccount에 바인딩해야 한다. 이 과정을 통해 Load Balancer Controller는 쿠버네티스 환경 안에서 IAM 권한을 위임받아 안전하게 AWS 리소스를 제어할 수 있다.


위 사진에서 정의된 권한을 보면 EC2나 ELB 리소르를 읽고 쓰는 권한 등이 포함되어 있는 것을 확인할 수 있다. 이런 IAM 정책을 AWS Load Balancer Controller의 권한에 위힘하면 컨트롤러가 필요한 리소스를 직접 생성하거나 관리할 수 있다. 그리고 생성된 IRSA에서도 IAM 역할과 연결된 IAM 정책을 확인할 수 있다.
AWS Load Balancer Controller 설치
# Helm Chart Repository 추가 및 업데이트
helm repo add eks https://aws.github.io/eks-charts
helm repo update
# AWS Load Balancer Controller 설치
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
-n kube-system \
--set clusterName=$CLUSTER_NAME \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller
# AWS Load Balancer Controller 확인
kubectl get deployment -n kube-system aws-load-balancer-controller
kubectl describe deploy -n kube-system aws-load-balancer-controller
helm install 명령어를 사용해 AWS Load Balancer Controller를 설치한다. 설치할 때 클러스터 이름을 지정하고, 서비스 어카운트 생성을 비활성화하고, 대신 위에서 생성해둔 IRSA를 직정 지정한다.
Service NLB 배포
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-echo
spec:
replicas: 2
selector:
matchLabels:
app: deploy-websrv
template:
metadata:
labels:
app: deploy-websrv
spec:
terminationGracePeriodSeconds: 0
containers:
- name: akos-websrv
image: k8s.gcr.io/echoserver:1.5
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-nlb-ip-type
annotations:
service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: ip
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
service.beta.kubernetes.io/aws-load-balancer-healthcheck-port: "8080"
service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
spec:
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: LoadBalancer
loadBalancerClass: service.k8s.aws/nlb
selector:
app: deploy-websrv
kubectl apply를 통해 Deployment를 통해 파드 2대를 생성하고, 서비스의 LoadBalancer 타입으로 NLB를 생성한다.
# Pod의 IP 확인
kubectl get pod -owide
# NLB에 연결된 타겟그룹 바인딩 정보 확인
kubectl get targetgroupbindings

위 명령어를 통해 파드 두 개와 NLB에 연결된 타겟그룹 바인딩 정보를 확인할 수 있다.
AWS 리소스 확인


AWS의 로드 밸런서를 확인하면 AWS Load Balancer Controller가 자동으로 NLB가 생성하고, TCP 80 포트가 열려있는 것을 확인할 수 있다. 그리고 연결된 대상 그룹 정보와 TargetGroupBindings 정보가 일치한 것을 확인할 수 있다. 마지막으로 등록된 대상 IP가 파드의 IP와 일치한 것도 확인할 수 있다.
인그레스(Ingress)

인그레스는 L7 로드 밸런싱을 제공하는 리소스로, 클러스터 외부에서 내부 서비스로 HTTP와 HTTPS 경로를 외부에 노출하는데 사용할 수 있다. AWS 환경에서 보면 ALB에 대응된다고 할 수 있다. Ingress를 활용하면 HTTP나 HTTPS 기반의 URL 라우팅이 가능해지기 때문에 하나의 로드 밸런서로 여러 서비스를 처리할 수 있다는 장점이 있다.
위 그림처럼 K8S의 Service와 Ingress는 공통적으로 NodePort를 통해 인입되고, ClusterIP를 통해 파드를 분산한다. 이 과정에서 리눅스 커널의 conntrack과 iptables가 사용하여 CPU나 메모리 자원을 소모된다.
출처
CloudNet@와 함께하는 Amazon EKS 기본 강의
EKS에 CLB, NLB, ALB 만들기, AWS Load Balancer Controller 사용
Helm을 사용하여 AWS Load Balancer Controller 설치
'기타 > AWS' 카테고리의 다른 글
| Terraform (1) | 2025.08.15 |
|---|---|
| AWS CloudFormation으로 EKS 클러스터 생성 (2) | 2025.07.22 |
| EKS 기본 아키텍처 (2) | 2025.07.17 |
| Amazon ELB (0) | 2025.07.08 |
| Amazon VPC (3) | 2025.07.03 |
- Total
- Today
- Yesterday
- 구름톤 챌린지
- jvm 메모리 구조
- 구름톤챌린지
- Redisson
- 넥스트스탭
- socket
- transaction
- 카프카
- pessimistic lock
- 비관적 락
- EKS
- annotation
- 분산 락
- sql
- mdcfilter
- mysql
- Synchronized
- nginx configuration
- spring session
- Java
- spring webflux
- Kafka
- 낙관적 락
- 트랜잭션
- postgresql
- NeXTSTEP
- redis session
- nginx
- 람다
- TDD
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
