본문 바로가기
포트폴리오/개인 자작

AWS EKS & Ingress Controller를 활용한 MSA 아키텍처 구축

by jamong1014 2025. 4. 16.
반응형

목차

  1. Ingress Controller 개요
  2. VPC 설계
  3. EKS 클러스터 구축
  4. MSA 서비스 배포 설계
  5. CI/CD 파이프라인 구축 (GitHub Actions)
  6. 모니터링 구성 (CloudWatch Agent)
  7. 개선할 점

인프라 설계

EKS & Ingress Controller를 활용한 MSA 아키텍처


1. Ingress Controller

쿠버네티스 클러스터 외부에서 내부 서비스와 통신하기 위해 사용할 수 있는 서비스 타입은 NodePort, LoadBalancer, Ingress 이렇게 세 가지 방식이 있다. 이 중에서 Ingress 방식을 선택한 이유는 운영 효율성과 확장성 면에서 가장 유리하기 때문이다.

 

먼저, NodePort 방식부터 간단히 살펴보자.

NodePort

 

NodePort는 클러스터의 각 노드에 외부에서 접근 가능한 포트를 열어주는 방식이다. 이때 포트는 30000~32767 사이에서 할당되며, 외부 트래픽은 이 포트를 통해 Service → Pod로 전달된다.

 

하지만 이 방식은 다음과 같은 제한이 있어 실제 운영 환경에서는 잘 사용되지 않는다.

  1. 서비스당 하나의 포트만 할당 가능
  2. 포트 범위가 제한적
  3. 노드의 Public IP 변경 시 접속 주소가 바뀜

그렇기에 NodePort는 간단한 테스트나 임시 서비스용으로 사용하는 것이 적합하다.

 

LoadBalancer

LoadBalancer 타입은 외부와 통신이 가능할 뿐만 아니라, 트래픽 분산 기능까지 제공하므로 운영 환경에서 가장 널리 사용되는 일반적인 방식이다. 이 방식은 ExternalIP를 통해 외부에서 접근 가능한 고정 IP를 하나 할당받아 클러스터 외부와의 통신을 가능하게 한다.

 

다만 쿠버네티스에서 LoadBalancer는 내부적으로 단독 실행이 불가능하며, 외부에 별도의 Load Balancer 리소스(AWS NLB, MetalLB 등)를 프로비저닝해야 동작한다.

 

또한, 서비스마다 별도로 로드밸런서를 생성해야 하므로, 규모가 커질수록 비용적인 측면에서 비효율적이다.

 

Ingress

Ingress는 앞서 설명한 두 방식과는 다르게 Service의 타입이 아니라, 별도의 Kind 리소스로 동작한다.

 

위 그림처럼 Ingress는 Ingress Controller를 통해 도메인 경로에 따라 트래픽을 라우팅 하는 방식이다.

즉, Ingress는 외부와의 통신, LoadBalancer 역할, 도메인 기반 라우팅, SSL 인증서 처리와 같은 기능을 정의하는 객체이고, 이러한 기능을 실제로 수행하는 주체는 Ingress Controller다.

 

퍼블릭 클라우드 환경에서는 클라우드 벤더가 제공하는 ALB를 Ingress Controller로 사용할 수 있어 손쉽게 Ingress 구성이 가능하다. 반면, 온프레미스 또는 일반 VM 환경에서는 보통 nginx-ingress와 같은 오픈소스 컨트롤러를 사용한다.

이번 실습에서는 AWS EKS 환경에서 ALB를 Ingress Controller로 설정하여 Ingress로 구성하였다.

 

Ingress를 구성하기 위해서는 몇 가지 사전 준비가 필요하다.

IAM & OIDC 연동과 Ingress Controller 설치는 'EKS 클러스터 구축' 부분에서 다루겠다.

 

Ingress는 아래와 같이 annotations을 사용하여 리소스를 정의한다. 

apiVersion: networking.k8s.io/v1         # Ingress 리소스가 사용하는 API 버전
kind: Ingress                            # 리소스 타입: Ingress

metadata:
  name: vote-result-ingress              # Ingress 리소스의 이름
  namespace: default                     # 해당 리소스가 존재하는 네임스페이스
  annotations:
    alb.ingress.kubernetes.io/scheme: internet-facing             # ALB를 퍼블릭(외부 공개용)으로 설정 (internal이면 사설망용)
    alb.ingress.kubernetes.io/target-type: ip                     # ALB가 직접 Pod IP로 트래픽을 전달하도록 설정 (ip 또는 instance)
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}]'      # ALB가 리스닝할 포트 (여기서는 HTTP 80)

spec:
  ingressClassName: alb                   # 사용할 Ingress Controller 클래스 이름 (AWS ALB Ingress Controller 사용 시 'alb'로 지정)
  rules:                                  # 도메인 기반 트래픽 라우팅 규칙 정의
    - host: vote.foolblack.net            # 첫 번째 도메인 요청 처리
      http:
        paths:
          - path: /                       # 루트 경로로 들어온 요청
            pathType: Prefix              # 경로가 해당 prefix로 시작하면 매칭
            backend:
              service:
                name: vote                # vote라는 이름의 서비스로 트래픽 전달
                port:
                  number: 8080            # 해당 서비스가 리스닝하는 포트

    - host: result.foolblack.net          # 두 번째 도메인 요청 처리
      http:
        paths:
          - path: /                       # 마찬가지로 루트 경로
            pathType: Prefix
            backend:
              service:
                name: result              # result라는 이름의 서비스로 트래픽 전달
                port:
                  number: 8081            # 서비스가 리스닝하는 포트

 

Route 53 → CloudFront → ALB → vote/result 도메인에 따라 각각 vote, result 서비스로 라우팅 하는 것이다.


2. VPC 설계

 

EKS 클러스터를 구성하기 위해 가장 먼저 진행한 작업은 VPC 구성이다. EKS는 퍼블릭과 프라이빗 서브넷을 나누어 배포하는 것이 일반적인 구조인데, 이는 보안성과 확장성 측면에서 매우 중요한 부분이다.

 

VPC 이름: vote-result-vpc

CIDR 블록: 10.0.0.0/16

서브넷: 총 4개

  • Public Subnet1 (10.0.0.0/24) - ap-northeast-2a
  • Private Subnet1 (10.0.1.0/24) - ap-northeast-2a
  • Public Subnet2 (10.0.2.0/24) - ap-northeast-2b
  • Private Subnet2 (10.0.3.0/24) - ap-northeast-2b

이렇게 두 개의 가용 영역(AZ)에 퍼블릭/프라이빗 서브넷을 나눠 배치함으로써 고가용성과 이중화를 고려한 설계가 가능해진다.

라우팅 테이블 구성

  • vote-result-Public
    • 인터넷 게이트웨이(IGW)를 통해 외부와 통신한다.
    • 주로 ALB, Bastion Host 등 외부 접근이 필요한 리소스들이 배치된다.
  • vote-result-Private
    • NAT Gateway를 통해 외부와 통신하되, 직접적으로 외부에서 접근할 수 없음
    • EKS 워커 노드는 여기에 배치 됨.

네트워크 연결

  • vote-result-igw (Internet Gateway): 퍼블릭 서브넷에 연결되어 외부 인터넷과 통신 가능
  • vote-result-ngw (NAT Gateway): 프라이빗 서브넷에서 인터넷으로 나가는 통신을 담당

 

Bastion Host에 대한 필요성?

프라이빗 서브넷에 위치한 워커 노드에 직접 액세스 하기 위해서는, 일반적으로 퍼블릭 서브넷에 위치한 Bastion Host를 통해 SSH 접속하는 방식이 전통적으로 많이 사용된다.

 

하지만 Bastion Host는 별도의 인스턴스를 항상 켜두어야 하기 때문에 비용 측면에서 부담이 될 수 있다. 이를 대체할 수 있는 방식으로는 AWS Systems Manager(SSM)을 활용한 세션 매니저 접속 방식이 있으며, 비용 효율성과 보안 측면에서 훨씬 유리하다.

 

다만, 이번 인프라 구성은 Ingress Controller 중심 아키텍처 구현에 중점을 두고 있는 만큼, 접근 방식은 간단히 Bastion Host 기반으로 구성하였다.


3. EKS 클러스터 구축

VPC가 준비되었다면, 이제 본격적으로 Amazon EKS 클러스터를 생성해 볼 차례다.
이번 구성은 프라이빗 서브넷 기반의 보안 중심 구조로, 퍼블릭으로 직접 노출되는 노드는 없다. 대신 Ingress Controller와 ALB를 통해 외부 요청을 받아 내부 서비스로 전달되도록 설계하였다.

 

클러스터 생성

클러스터 이름: vote-result-eks

EKS 버전: 1.29

VPC 선택: 앞서 구성한 vote-result-vpc 사용

서브넷 선택:

  • Private Subnet 1 (ap-northeast-2a)
  • Private Subnet 2 (ap-northeast-2b)

퍼블릭 서브넷은 ALB용으로만 사용되며, 클러스터 노드는 프라이빗 서브넷에만 배포되었다.

 

노드 그룹(Managed Node Group) 구성

클러스터 생성 후, 워커 노드를 배포하기 위해 Managed Node Group을 추가했다.
이 노드 그룹은 AWS에서 자동으로 Auto Scaling Group(ASG)을 생성해 관리하며, 다음과 같은 설정을 적용하였다.

 

인스턴스 타입: t3.medium

노드 수 설정: 최소 1개, 최대 3개, 원하는 수 2개

AMI 타입: Amazon Linux 2 (EKS Optimized)

서브넷: Private Subnet 1, 2 (AZ 분산 배치)

 

실습 당시 각 서브넷에 2개의 인스턴스가 생성되었으며, 실제 kubectl get nodes로 확인 시 정상적으로 2개의 노드가 등록된 것을 확인할 수 있다.

 

IAM & OIDC 연동

EKS 클러스터에서는 기본적으로 IAM과 직접 연동되지 않기 때문에, AWS 리소스(AWS Load Balancer Controller 등)를 사용하기 위해서는 OIDC 설정과 서비스 계정 연결이 필요하다.

 

1. OIDC Provider 연결

eksctl utils associate-iam-oidc-provider \
  --region ap-northeast-2 \
  --cluster vote-result-eks \
  --approve

 

2. 서비스 계정 생성 및  IAM 정책 연결

AWS Load Balancer Controller는 ALB, Target Group, Listener 등 AWS 리소스를 생성해야 하기 때문에, 해당 권한을 가진 IAM Role이 필요하다. 하지만 쿠버네티스는 IAM을 직접 인식하지 못하므로, ServiceAccount와 IAM Role을 OIDC를 통해 연결해 주는 과정이 필요하다.

 

* IAM 정책 생성 ( AWS 제공 정책 다운로드)

curl -o iam-policy.json \
  https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/main/docs/install/iam_policy.json

aws iam create-policy \
  --policy-name AWSLoadBalancerControllerIAMPolicy \
  --policy-document file://iam-policy.json

 

* 서비스 계정 생성 (eksctl 사용)

eksctl create iamserviceaccount \
  --cluster vote-result-eks \
  --namespace kube-system \
  --name aws-load-balancer-controller \
  --attach-policy-arn arn:aws:iam::<your-account-id>:policy/AWSLoadBalancerControllerIAMPolicy \
  --approve

 

서비스 계정은 단순한 Kubernetes 내부 계정이 아니라, AWS 리소스에 안전하게 접근하기 위한 연결 고리이자 인증 주체이다. 간단히 말하면, 쿠버네티스 안에서 Pod와 연결되는 계정이라 할 수 있다.

 

하지만 쿠버네티스는 AWS IAM을 직접 인식할 수 없기 때문에, ServiceAccount에 IAM Role을 연결함으로써 해당 Pod가 IAM 권한을 위임받아 AWS 리소스를 사용할 수 있게 된다.

 

이때 쿠버네티스의 ServiceAccount와 AWS의 IAM Role을 연결하기 위해 필요한 것이 바로 OIDC Provider이다.

 

VPC 서브넷 태그 설정

ALB Ingress Controller가 올바르게 작동하려면, EKS 클러스터가 사용할 서브넷에 필수적인 태그를 추가해 주는 작업이 꼭 필요하다. 특히, ALB가 생성되거나 TargetGroup이 연결될 때 서브넷에 특정 태그가 없으면 자동 연결이 되지 않거나 생성 자체가 실패할 수 있다.

 

퍼블릭 서브넷 (ALB가 생성될 위치)

  • Key : kubernetes.io/role/elb
  • Value : 1

프라이빗 서브넷 (ALB TargetGroup이 연결될 위치)

  • Key : kubernetes.io/role/internal-elb
  • Value: 1

* 이 작업은 편하게 VPC 구성할 때 같이 해두자.

 

ALB Ingress Controller 설치 (Helm)

이제 ServiceAccount와 IAM Role 연결까지 완료되었으니, 본격적으로 ALB Ingress Controller를 클러스터에 설치할 차례다. EKS에서 Ingress 리소스를 사용하려면, 이를 실제로 처리해 줄 Ingress Controller가 필수다.

 

이번 구성에서는 AWS에서 공식적으로 지원하는 AWS Load Balancer Controller(ALB Ingress Controller)를 Helm을 통해 설치한다.

 

1. Helm 리포지토리 등록

helm repo add eks https://aws.github.io/eks-charts
helm repo update

 

2. Helm 설치

helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
  -n kube-system \
  --set clusterName=vote-result-eks \
  --set serviceAccount.create=false \
  --set serviceAccount.name=aws-load-balancer-controller \
  --set region=ap-northeast-2 \
  --set vpcId=vpc-xxxxxxxxxxxxxxxxx \
  --set ingressClass=alb

 

3. 설치 완료 후 확인

kubectl get deployment -n kube-system

4. MSA 서비스 배포 설계

MSA 설계

 

이번 프로젝트는 단일 애플리케이션이 아닌, MSA 기반으로 설계되었다.

즉, 하나의 서비스가 아닌 여러 개의 독립적인 마이크로서비스 POD가 각자 배포되고, Ingress Controller를 통해 외부에서 하나의 도메인으로 접근할 수 있도록 구성했다.

  • voting-app (투표 기능 앱)
  • redis (캐시)
  • worker (redis에서 투표 정보를 읽어와 db에 저장)
  • db (PostgreSQL)
  • result-app (투표 결과 조회 앱)

 

* /vote, /result 방식으로 트래픽을 분기하려 했지만 ALB의 URL 구조 제약상 힘들어서 도메인 기반 라우팅 구조로 전환함.

  • vote.foolblack.net → vote 서비스로 전달
  • result.foolblack.net → result 서비스로 전달

해당 Ingress 정의 코드는 Ingress Controller 섹션에서 확인할 수 있다.


5. CI/CD 파이프라인 구축 (GitHub Actions)

EKS 클러스터에 지속적 배포를 자동화하기 위해 GitHub Actions를 활용한 CI/CD 파이프라인을 구축했다.

배포 대상은 폴더 내에 정의된 Kubernetes manifest(.yaml) 파일들이다.

파이프라인 구성 개요

  • GitHub Actions로 .yml 워크플로우 파일 작성
  • main 브랜치에 커밋(push)될 때마다 자동으로 배포 트리거
  • kubectl apply -f 명령어를 통해 EKS에 매니페스트 반영

* GitHub Actions Workflow

on:
  push:
    branches: [ main ]  # main 브랜치 푸시 시 실행

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Setup kubectl
        uses: azure/setup-kubectl@v3
        with:
          version: latest

      - name: Update kubeconfig
        run: |
          aws eks update-kubeconfig \
            --region ${{ secrets.AWS_REGION }} \
            --name vote-result-eks

      - name: Deploy manifests to EKS
        run: |
          kubectl apply -f ./k8s-specifications/
  • GitHub Actions 내에서 kubectl 명령이 실행되지만, 실제 대상은 AWS EKS 클러스터
  • aws eks update-kubeconfig 명령을 통해 GitHub Actions Runner가 클러스터와 통신 가능하게 설정됨

6. 모니터링 구성 (CloudWatch Agent)

EKS에서 애플리케이션이 잘 동작하고 있는지 확인하려면, 리소스 사용량(Metrics), 로그 수집, 상태 모니터링이 필수다.
이를 위해 Amazon CloudWatch Agent를 EKS에 설치하여 Pod의 리소스 지표를 실시간으로 수집하도록 구성했다.

 

왜 CloudWatch Agent?

  • AWS에서 공식적으로 제공하고 EKS와 통합이 잘 됨
  • 외부 모듈(Prometheus, Grafana 등) 없이 빠르게 구성 가능
  • Pod 단위의 메모리, CPU 사용량을 바로 확인할 수 있음
  • CloudWatch Dashboard에서 시각화 가능

* CloudWatch Agent는 각 노드에 DaemonSet으로 배포되어 해당 노드 위에 실행 중인 Pod들의 리소스를 수집한다.

 

설치 방법

1. CloudWatch Agent Helm Chart 레포 추가

helm repo add eks https://aws.github.io/eks-charts
helm repo update

 

2. IAM 정책 연결 (metrics:PutMetricData 등)

eksctl create iamserviceaccount \
  --cluster vote-result-eks \
  --namespace amazon-cloudwatch \
  --name cloudwatch-agent \
  --attach-policy-arn arn:aws:iam::aws:policy/CloudWatchAgentServerPolicy \
  --approve

 

3. Helm으로 설치

helm install cloudwatch-agent eks/cloudwatch-agent \
  -n amazon-cloudwatch --create-namespace \
  -f cloudwatch-agent-values.yaml

 

4. values.yaml 예시

clusterName: vote-result-eks
logs:
  metrics_collected:
    kubernetes:
      cluster_name: vote-result-eks
      metrics_collection_interval: 60

 

구성 결과

  • 모든 노드(Pod 포함)에 Agent가 설치되어 상태 감시 가능
  • CloudWatch 콘솔에서 리소스 이상 탐지, 알림 설정(Alarm)까지 연동 가능
  • Prometheus 같은 외부 툴 없이도 AWS 콘솔만으로 기본적인 모니터링 가능

7. 개선할 점

1. Terraform 도입 (IaC로 전체 인프라 자동화)

현재는 VPC, Subnet, EKS, IAM 등 대부분 수동으로 구성됨

Terraform을 통해 다음 리소스를 코드로 선언하고 자동화 가능:

  • VPC, 서브넷, NAT Gateway, IGW
  • EKS 클러스터 및 노드 그룹
  • IAM 역할 및 정책
  • CloudWatch Agent, ALB Ingress Controller
  • Route 53 + ACM 인증서 + CloudFront 구성

→ 재현성 있는 인프라 구성 및 협업 효율성 극대화

2. HPA (Horizontal Pod Autoscaler) 적용

  • 노드 기준이 아닌 포드 기준으로 오토스케일링
  • 현재는 Deployment의 replicas 값을 수동으로 지정
  • CPU, 메모리 사용량 기준으로 Pod 수 자동 확장 가능
  • 실시간 트래픽 변화에 대응 가능한 유연한 구조 완성

5. 로깅 개선 (Fluent Bit 또는 CloudWatch Logs)

  • 현재는 로그 수집 구성없음
  • Fluent Bit DaemonSet을 구성하여 Pod 로그를 중앙 수집 및 필터링 가능
  • CloudWatch Logs로 전송하면 알람 및 로그 검색 기능과도 연계됨

6. Bastion Host 대체 방안 (비용 절감)

  • Bastion Host 대신 SSM Session Manager 도입 → 운영 노드에 SSH 없이 접속

 

반응형