본문 바로가기
System Engineering/Kubernetes (쿠버네티스)

Embedded iPaaS 아키텍처를 위한 멀티 테넌시 격리 전략과 쿠버네티스 리소스 제어

by 코딩하는 동현 2025. 12. 27.

[Intro] B2B SaaS의 진화: Embedded iPaaS와 멀티 테넌시 아키텍처

최근 B2B SaaS(Software as a Service) 시장의 패러다임이 변화하고 있다. 과거의 SaaS가 공급자가 정의한 고정된 기능만을 제공하는 데 그쳤다면, 현재는 사용자가 직접 워크플로우를 정의하고 외부 시스템과 유연하게 연동할 수 있는 플랫폼 형태로 진화하고 있다.

업계에서는 이러한 확장을 Embedded iPaaS (Integration Platform as a Service)라고 정의한다.

 

비즈니스 관점에서 이는 '고객 경험의 확장'이지만, 엔지니어링 관점에서는 아키텍처의 근본적인 변화를 의미한다.

사용자가 정의한 임의의 로직이나 스크립트를 내부 인프라에서 실행해야 한다는 요구사항은, SaaS 공급자에게 사실상 내부적으로 PaaS(Platform as a Service) 혹은 FaaS(Function as a Service) 수준의 실행 환경을 구축할 것을 요구하기 때문이다.

 

이러한 아키텍처 변화의 핵심에는 멀티 테넌시(Multi-tenancy)가 있다. 멀티 테넌시는 단일 인스턴스로 복수의 고객(Tenant)을 수용하여 리소스 효율성을 극대화하는 전략이지만, 실행 로직을 예측할 수 없는 iPaaS 환경에서는 치명적인 리스크로 작용한다. 특정 테넌트의 과도한 리소스 점유가 전체 시스템의 가용성을 위협하는 Noisy Neighbor 문제는 더 이상 단순한 성능 저하가 아닌, 시스템 전체의 장애로 이어질 수 있다.

 

따라서 현대의 SaaS 데브옵스 엔지니어에게는 단순히 애플리케이션을 배포하는 것을 넘어, 신뢰할 수 없는 워크로드를 공유된 인프라 위에서 완벽하게 격리(Isolation)하고 제어(Restriction)하는 고도화된 아키텍처 설계 역량이 필수적이다.

본 포스팅에서는 이러한 Embedded iPaaS 환경을 안전하게 구축하기 위한 쿠버네티스 기반의 멀티 테넌시 격리 전략과, 네임스페이스(Namespace)의 한계를 극복하는 심층적인 리소스 제어 방안에 대해 다룬다.


1. 핵심 개념: 멀티 테넌시(Multi-tenancy)의 본질

멀티 테넌시란 하나의 소프트웨어 인스턴스로 여러 고객사를 동시에 수용하는 아키텍처를 의미한다. 이를 직관적으로 이해하기 위해 주거 형태에 비유할 수 있다.

  • 싱글 테넌시 (Single-tenancy)
    • 구조: 고객마다 별도의 서버, 별도의 DB, 별도의 OS를 구축해준다.
    • 장점: 완벽한 프라이버시, 타 고객의 부하 영향 없음.
    • 단점: 인프라 비용이 고객 수만큼 증가함. 유지보수가 어려움.
  • 멀티 테넌시 (Multi-tenancy)
    • 구조: 거대한 하나의 인프라(서버/DB) 안에 여러 고객사가 입주한다. CPU, Memory 등 리소스를 공유한다.
    • 장점: 비용 효율성 극대화, 관리 포인트 일원화.
    • 단점: Noisy Neighbor(이웃 간 소음) 및 데이터 침해 위험.

2. 데이터 격리(Data Isolation)의 3가지 구현 전략

멀티 테넌시 환경에서 가장 중요한 것은 데이터를 어떻게 나눌 것인가이다. 격리 수준과 비용 효율성에 따라 크게 3가지 레벨로 구분된다.

  • Level 1: 물리적 분리 (Database per Tenant)
    • 방식: 각 테넌트에게 별도의 DB 인스턴스를 할당한다.
    • 특징: 보안성은 최고 수준이나, 비용이 가장 비싸고 리소스 낭비가 심하다. 주로 금융/엔터프라이즈 전용 플랜에 사용된다.
  • Level 2: 논리적 분리 (Schema per Tenant)
    • 방식: 하나의 DB 인스턴스 내에서 스키마(Schema)나 네임스페이스를 분리한다.
    • 특징: 물리적 자원은 공유하되, 논리적인 데이터 공간을 분리하여 적절한 타협점을 찾는다.
  • Level 3: 완전 공유 (Shared Schema)
    • 방식: 하나의 DB, 하나의 테이블을 모든 테넌트가 공유한다.
    • 구분: 테이블 내에 tenant_id 컬럼을 추가하여 데이터를 구분한다.
    • 리스크: 대부분의 Modern SaaS가 채택하는 방식으로 효율성은 높지만, 애플리케이션 로직 실수 한 번에 타 고객의 데이터가 유출될 수 있는 위험을 안고 있다.

3. 인프라 레벨에서의 고충

Embedded iPaaS 아키텍처에서 우리는 Level 3(완전 공유) 환경 위에서, 신뢰할 수 없는 사용자 코드를 실행해야 한다.

이는 마치 벽이 얇은 아파트에서 입주민이 집 안에서 과도한 전력을 사용하여 전체 정전을 일으키거나, 벽을 뚫으려는 시도를 하는 상황과 같다. 따라서 단순한 애플리케이션 레벨의 방어 로직을 넘어, 인프라 레벨(Kubernetes)에서의 강력한 강제 격리 수단이 필수적이다.


4. 쿠버네티스 리소스 제어: LimitRange와 ResourceQuota

 

멀티 테넌시 환경에서 자원 제어는 개별 컨테이너 레벨(LimitRange)과 테넌트 총량 레벨(ResourceQuota)의 이중 방어선으로 구축해야 한다.

LimitRange: 개별 워크로드의 수직적 제한

LimitRange는 네임스페이스 내에서 생성되는 단일 Pod 또는 Container가 사용할 수 있는 리소스의 상한/하한을 강제한다. 특히 사용자가 resources 필드를 누락하더라도 default 값을 주입하여 예측 불가능한 스케줄링을 방지하는 것이 핵심이다.

  • 적용 대상: 단일 Task의 비정상적 자원 독점 방지.
apiVersion: v1
kind: LimitRange
metadata:
  name: task-limit-range
  namespace: tenant-a
spec:
  limits:
  - type: Container
    # 1. Max: 이 값을 초과하는 Pod 생성 요청은 즉시 거절(Reject)됨
    max:
      cpu: "1"
      memory: "1Gi"
    # 2. Min: 너무 작은 자원 할당으로 인한 스케줄링 오버헤드 방지
    min:
      cpu: "100m"
      memory: "128Mi"
    # 3. Default: 사용자가 리소스를 명시하지 않을 경우 자동 주입되는 값
    default:
      cpu: "500m"
      memory: "512Mi"

 

 

ResourceQuota: 테넌트 총량의 수평적 제한

ResourceQuota는 네임스페이스 전체의 자원 총량을 제한한다. 개별 Pod가 작더라도, 수천 개의 Pod를 동시에 생성하여 클러스터 전체를 마비시키는 공격을 방어한다.

  • 적용 대상: 테넌트의 과도한 수평적 확장(Scale-out) 제한 및 오브젝트 쿼터 관리.
apiVersion: v1
kind: ResourceQuota
metadata:
  name: tenant-quota
  namespace: tenant-a
spec:
  hard:
    # 1. Compute Resource 총량 제한
    requests.cpu: "4"         # 테넌트 전체 CPU 예약 총량 4코어
    requests.memory: "10Gi"
    limits.cpu: "8"           # 순간적인 버스트 허용 총량 8코어
    limits.memory: "16Gi"

    # 2. Object Count 제한 (좀비 프로세스 증식 방지)
    pods: "20"                # 동시 실행 가능한 Pod 개수
    persistentvolumeclaims: "5"

 

5. 쿠버네티스 네임스페이스(Namespace)의 한계

흔히 네임스페이스를 완벽한 격리 공간으로 오해하지만, 쿠버네티스에서 네임스페이스는 API 리소스의 논리적 그룹일 뿐, 물리적/보안적 격리 경계가 아니다. PaaS 구축 시 반드시 인지해야 할 네임스페이스의 3가지 맹점은 다음과 같다.

1. Network Isolation의 부재 (Flat Network Model)

쿠버네티스의 기본 네트워크 모델은 Flat Network이다. 이는 클러스터 내 모든 Pod가 NAT 없이 서로 고유 IP로 통신 가능함을 의미한다.

  • Risk: Namespace A의 워크로드는 기본적으로 Namespace B의 DB나 내부 서비스에 접근할 수 있다.
  • Solution: CNI 레벨의 NetworkPolicy 도입 필수.

2. Kernel Sharing (Shared Kernel Issue)

동일 노드에 배치된 서로 다른 네임스페이스의 Pod들은 호스트 OS의 커널을 공유한다.

  • Risk: 특정 테넌트의 코드가 시스템 콜 취약점을 공격하거나 커널 패닉을 유발할 경우, 해당 노드 자체가 다운되어 타 테넌트의 서비스까지 중단된다. 네임스페이스는 이를 방어하지 못한다.
  • Solution:
    • 샌드박스 런타임: gVisorKata Containers를 사용하여 커널 레벨 격리 구현.
    • 물리적 격리: Taint/Toleration을 통한 노드 분리.

컨테이너 샌드박스 런타임에 대해서는 따로 블로그글의 "4. K8s 커널 취약 방어 (샌드박스 컨테이너)" 목차에다가 이미 정리를 해놨다.

https://konkukcodekat.tistory.com/362

 

[DevOps 기술 면접 회고] TLB, K8s 내부 동작과 커널 격리, TLS 핸드셰이크

최근 3학년 2학기 시험기간에 DevOps 엔지니어 기술 면접을 보았다.나름대로 OS(프로세스, 메모리)와 네트워크 기초는 깊게 팠다고 자부했지만, 쿠버네티스 내부 통신 원리, HTTPS의 핸드쉐이크에 대

konkukcodekat.tistory.com

3. Cluster-Scoped Resources

네임스페이스 삭제 명령은 해당 네임스페이스에 속한 리소스(Pod, Service, PVC)만 삭제한다.

  • Risk: PersistentVolume(PV), Node, StorageClass 등은 클러스터 전역 리소스이므로 삭제되지 않는다. 특히 PV의 ReclaimPolicy가 Retain인 경우, 네임스페이스가 삭제되어도 실제 스토리지 비용이 계속 청구되거나 데이터 보안 문제가 발생할 수 있다.

6. Advanced Network Isolation: NetworkPolicy & CNI

네임스페이스의 네트워크 격리 한계를 극복하기 위해 NetworkPolicy를 사용하여 L3/L4 레벨의 트래픽 제어를 수행해야 한다.

작동 원리와 CNI 의존성

NetworkPolicy 리소스 자체는 API 객체일 뿐, 직접 트래픽을 차단하지 않는다. 실제 패킷 필터링은 CNI Plugin(Calico, Cilium, Weave Net 등)이 수행한다.

  • Implementation: CNI는 주로 iptables 룰을 동적으로 수정하거나, 최근에는 eBPF를 통해 커널 레벨에서 고성능 패킷 드롭을 수행한다.
  • AWS VPC CNI: 과거에는 지원하지 않았으나, 현재는 별도 설정 혹은 추가 데몬을 통해 지원 여부를 반드시 확인해야 한다.

Implicit Deny와 Whitelist 모델

NetworkPolicy가 특정 Pod(podSelector)를 선택하는 순간, 해당 Pod의 트래픽은 Default Deny 상태로 전환된다. 이후 명시된 규칙(Allow Rule)만 허용된다.

YAML Logic Trap: AND vs OR

NetworkPolicy 작성 시 가장 빈번한 실수가 발생하는 지점은 namespaceSelector와 podSelector의 결합 논리이다.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: isolate-tenant-db
  namespace: tenant-a
spec:
  podSelector:
    matchLabels:
      app: db  # Target Pod
  policyTypes:
  - Ingress
  ingress:
  - from:
    # [Trap] 리스트 아이템(-)의 구분에 따른 논리 변화 주의

    # Case 1: OR 조건 (별도 아이템)
    # 의미: "IP 대역이 172.17.0.0/16 인 곳" OR "라벨이 project:myproject인 네임스페이스의 모든 Pod"
    - ipBlock:
        cidr: 172.17.0.0/16
    - namespaceSelector:
        matchLabels:
          project: myproject

    # Case 2: AND 조건 (하나의 아이템 내 병기)
    # 의미: "라벨이 project:myproject인 네임스페이스 내부의" AND "라벨이 role:frontend인 Pod"
    - namespaceSelector:
        matchLabels:
          project: myproject
      podSelector:
        matchLabels:
          role: frontend

    ports:
    - protocol: TCP
      port: 5432

 

DNS 트래픽 허용 (Port 53)

Egress 트래픽을 모두 차단할 경우, CoreDNS와의 통신(UDP/TCP 53)도 차단되어 도메인 조회가 불가능해진다. 반드시 kube-system 네임스페이스의 DNS 포트 허용 규칙을 명시적으로 추가해야 한다.


7. Compute Isolation: Taint, Toleration & Affinity

네트워크 격리를 넘어, 물리적인 컴퓨팅 자원을 완전히 격리해야 하는 경우(예: Premium 고객을 위한 전용 노드 할당, 고성능 GPU 노드 독점)에는 Node-centric Isolation 전략을 사용한다.

이때 중요한 점은 Taint(거부)Affinity(선호)라는 두 가지 메커니즘을 결합해야만 '완벽한 물리적 격리(Dedicated Node)'가 완성된다는 것이다.

Taint와 Toleration: 노드의 배타적 거부 (Repelling)

Taint는 노드가 "자격 없는 Pod"의 스케줄링을 거부하는 속성이다. 즉, 다른 잡음(Noisy Neighbor)이 내 전용 공간에 들어오지 못하게 막는 '방패' 역할을 수행한다.

  • NoSchedule: Toleration이 없는 Pod는 스케줄링되지 않음. (기존 실행 중인 Pod는 유지)
  • PreferNoSchedule: 최대한 피하지만, 다른 자원이 없으면 배치될 수 있음. (Soft Limit)
  • NoExecute (Critical): 스케줄링 차단은 물론, **이미 실행 중인 Pod 중 Toleration이 없는 워크로드를 즉시 축출(Eviction)**한다. 장애 노드 격리나 긴급 점검 시 유용하다.

Node Affinity: 특정 노드로의 유인 (Attracting)

Toleration만으로는 "들어갈 자격"만 얻을 뿐, 해당 노드로 "반드시 가야 한다"는 강제성은 없다. 즉, VIP 고객(Pod)이 전용 노드를 놔두고 일반 공용 노드에 스케줄링될 가능성이 여전히 존재한다. 따라서 Pod가 특정 노드를 의도적으로 찾아가도록 강제하는 '자석' 역할인 NodeAffinity 설정이 필수적이다.

Dedicated Node Pattern (완전한 물리적 격리 패턴)

완벽한 격리(Mutual Exclusivity)를 위해서는 위 두 가지 개념을 결합하여, "외부인은 못 들어오게 막고(Taint), 주인은 딴 데 못 가게 잡는(Affinity)" 구조를 확립해야 한다.

  • Step 1: Node Taint 설정 (외부인 차단)
    • 명령어: kubectl taint nodes node-1 tenant=a:NoSchedule
    • 효과: Tenant A의 Toleration이 없는 모든 일반 Pod의 진입을 원천 봉쇄한다.
  • Step 2: Pod Toleration 추가 (출입 권한 부여)
    • 설정: Tenant A의 Pod에 tolerations 필드 추가.
    • 효과: Tenant A의 Pod가 node-1에 스케줄링될 수 있는 자격(Permission)을 획득한다.
  • Step 3: Node Affinity 추가 (거주지 강제)
    • 설정: Tenant A의 Pod에 nodeAffinity (requiredDuringScheduling...) 추가.
    • 효과: 자격을 가진 Tenant A의 Pod가 다른 일반 노드로 분산되지 않고, 반드시 node-1에만 배치되도록 강제(Constraint)한다.

Taint 없이는 일반 Pod가 침범하고, Affinity 없이는 전용 Pod가 이탈한다. 두 설정이 모두 적용되어야 비로소 물리적으로 완벽하게 격리된 Dedicated Node가 구현된다.


8. 결론: Layered Defense Strategy

안전한 Embedded iPaaS 아키텍처는 단일 솔루션으로 완성되지 않는다. 다음과 같은 계층적 방어 전략(Layered Defense)이 통합되어야 한다.

  1. Application Layer: Headless Browser 등 고부하 작업은 Ephemeral Container나 서버리스로 격리하여 리소스 누수를 원천 차단.
  2. Resource Layer: LimitRange와 ResourceQuota를 통해 Noisy Neighbor의 자원 독점을 방지.
  3. Network Layer: CNI 기반의 NetworkPolicy를 통해 L3/L4 트래픽을 화이트리스트 기반으로 제어.
  4. Compute Layer: Taint/Toleration 및 샌드박스 기술을 통해 커널 레벨 및 물리적 노드 격리 구현.

이러한 아키텍처 설계는 사용자의 코드가 시스템을 위협할 수 있다는 전제하에, 가용성(Availability)보안(Security)을 동시에 확보하는 유일한 방법이다.

 

반응형

댓글