블로그

멀티테넌트 Docker 격리를 위한 단계별 청사진

호스팅 비용 부담을 통제하면서 Docker에서 멀티테넌트 워크로드를 격리하세요. 네임스페이스, cgroups, 네트워크 정책 및 런타임 하드닝 구성 방법을 알아봅니다.

요약

여러 클라이언트 캠페인이나 사내 웹 자산을 위한 공유 인프라를 관리하다 보면 호스팅 비용과 데이터 보안 문제를 두고 경영진과 마찰이 생기기 쉽습니다. Docker 컨테이너는 전용 가상 머신(VM)을 대체할 수 있는 가벼운 대안이지만, 기본 설정 상태에서는 심각한 격리 취약점이 존재합니다. 진정한 멀티테넌시를 구현하려면 커널, 프로세스, 네트워크 및 스토리지 레벨에서 명확한 경계를 설정해야 합니다. 이 가이드는 리눅스 네이티브 격리 프리미티브(primitives)를 사용하여 멀티테넌트 Docker 배포를 안전하게 구축하는 실용적인 5단계 프레임워크를 제공합니다. 리소스 할당량을 적용하고, 프로세스 권한을 제한하며, 컨테이너 네트워크를 분할하고, 적절한 격리 계층을 선택하는 방법을 배우게 됩니다. 이 청사진을 따르면 테넌트 환경을 안전하게 보호하면서 비기술 직군 이해관계자들에게 인프라 예산의 타당성을 설득할 수 있습니다.

비개발자 관리자가 지난달 클라우드 호스팅 청구서 인쇄물을 들고 찾아옵니다. 비용은 급증했는데, 동시 제품 출시 기간 동안 주요 랜딩 페이지 여러 곳에서 지연 시간 급증 현상이 발생했습니다. 마케팅 자산이 왜 서버를 공유하는지, 고객 데이터가 노출될 위험은 없는지, 왜 모든 캠페인마다 값비싼 전용 가상 머신을 띄울 수 없는지 설명해야 하는 상황입니다.

모든 디지털 자산에 개별 가상 머신(VM)을 할당하면 '시끄러운 이웃(noisy neighbor)' 문제는 해결되지만, 운영 예산이 빠르게 소진됩니다. 표준 Docker 배포는 단일 운영체제 커널에서 여러 사이트를 실행함으로써 비용 문제를 해결하지만, 기본 설정에는 위험한 격리 공백이 존재합니다. 한 테넌트 애플리케이션에서 폭주 스크립트가 실행되거나 악의적인 침해가 발생하면 동일한 호스트에 있는 모든 공동 호스팅 애플리케이션이 위험에 처하게 됩니다.

이 단계별 기술 청사진을 활용하여 Docker에서 철저한 멀티테넌트 격리를 구성해 보세요. 시스템 안정성을 보호하고, 테넌트 데이터를 격리하며, 기술 인프라 선택을 경영진을 위한 명확한 비즈니스 가치로 전환할 수 있는 5가지 운영 단계를 구현하십시오.


1. 컨트롤 그룹(cgroups)을 활용한 엄격한 리소스 할당량 적용

모든 컨테이너에 CPU, 메모리, 디스크 I/O 제한을 즉시 명시적으로 설정하십시오. 여러 테넌트가 기본 호스트를 공유할 때, 제한이 없는 컨테이너는 시스템 리소스를 두고 경쟁하게 됩니다. 단 하나의 폭주 데이터베이스 쿼리나 트래픽이 몰리는 캠페인이 호스트의 전체 메모리 풀을 고갈시켜, 리눅스 OOM(Out-Of-Memory) 킬러가 임의의 시스템 프로세스를 강제 종료하도록 유발할 수 있습니다.

리눅스 컨트롤 그룹(cgroups)은 각 컨테이너가 소비할 수 있는 컴퓨팅 용량을 제어합니다. 배포 정의에 이러한 제한 경계를 직접 적용하십시오:

services:
  tenant_app:
    image: nginx:alpine
    deploy:
      resources:
        limits:
          cpus: '0.75'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M
  • 메모리 제한 (limits.memory): 엄격한 상한선을 설정합니다. 컨테이너가 512MB를 초과하면 커널은 인접 테넌트의 성능 저하 없이 해당 컨테이너 내부의 프로세스만 종료합니다.
  • 메모리 예약 (reservations.memory): 기준 메모리 할당을 보장하여 트래픽이 적은 애플리케이션도 원활하게 응답할 수 있도록 합니다.
  • CPU 제한 (limits.cpus): 컨테이너가 사용할 수 있는 최대 CPU 코어 비율을 제한하여 단일 테넌트로 인한 CPU 고갈 현상을 방지합니다.

비기술직 경영진에게 이 아키텍처를 설명할 때는 cgroups를 자동화된 '디지털 개별 계량기'로 비유해 보세요. 사무용 건물의 입주자(테넌트)가 메인 차단기에 과부하를 주지 않고 각자 사용한 전기 요금만 지불하는 것처럼, cgroups는 트래픽이 급증한 한 랜딩 페이지가 다른 고객의 리드 생성 포털을 다운시키지 않도록 보장합니다. 아키텍처 트레이드오프에 대한 자세한 내용은 멀티테넌트 아키텍처 설계 가이드를 확인하세요.


2. 네임스페이스 및 Non-Root 사용자로 테넌트 프로세스 분리

컨테이너 프로세스를 기본 root 사용자로 실행하지 마십시오. 표준 리눅스 컨테이너 환경에서는 명시적으로 재매핑(remapping)하지 않는 한, 컨테이너 내부의 루트는 호스트 커널의 루트와 동일합니다. 공격자가 루트로 실행 중인 웹 애플리케이션을 침해하면 공유 호스트에 대한 높은 권한을 얻게 됩니다.

사용자 네임스페이스와 명시적인 non-root 실행을 통해 프로세스 격리를 강제하십시오:

  1. 권한 없는 런타임 사용자 정의: Dockerfile 내에 전용 저권한 서비스 사용자를 생성합니다.
    FROM php:8.2-fpm-alpine
    RUN addgroup -g 10001 tenantgroup && \
        adduser -u 10001 -D -G tenantgroup tenantuser
    USER tenantuser
    
  2. 사용자 네임스페이스(userns-remap) 활성화: Docker 데몬(/etc/docker/daemon.json)을 구성하여 컨테이너 사용자 ID를 호스트의 권한 없는 범위로 매핑합니다.
    {
      "userns-remap": "default"
    }
    

리눅스 네임스페이스는 시스템 가시성을 분할합니다. 프로세스 ID(PID) 네임스페이스는 테넌트 A가 테넌트 B의 프로세스를 확인하거나 신호를 보내거나 종료할 수 없도록 보장합니다. 마운트(MNT) 네임스페이스는 각 테넌트에 독립된 파일 시스템 뷰를 제공하며, IPC 네임스페이스는 승인되지 않은 프로세스 간 통신을 차단합니다.

사용자 네임스페이스 재매핑은 컨테이너 탈출(escape) 공격 경로를 무력화합니다. 컨테이너 내부에서 자신이 root(UID 0)라고 인식하는 프로세스가 호스트 머신에서는 권한이 없는 ID(예: UID 165536)로 매핑됩니다. 만약 익스플로잇이 컨테이너 장벽을 우회하더라도 공격자는 호스트 구성을 수정하거나 인접 테넌트 디렉터리에 접근할 수 없는 권한 없는 셸에 갇히게 됩니다.


3. 커널 권한 축소 및 읽기 전용 파일 시스템 강제

사용 가능한 리눅스 기능(capabilities)을 최소화하고 부팅 시 컨테이너 루트 파일 시스템을 불변(immutable) 상태로 만드십시오. 기본 컨테이너 런타임은 약 10여 가지의 리눅스 커널 기능을 부여하지만, 웹 애플리케이션에 필요한 기능은 거의 없습니다. 과도한 권한은 공격자에게 네트워크 라우팅 조작, 호스트 시계 변경, 파일 접근 제어 우회 등의 수단을 제공합니다.

모든 기본 기능을 제거하고 필수 운영 플래그만 다시 추가하여 런타임 컨테이너를 안전하게 잠그십시오:

services:
  tenant_web:
    image: custom-nginx:latest
    read_only: true
    cap_drop:
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
      - seccomp=default.json
    tmpfs:
      - /tmp:rw,noexec,nosuid,size=64m
      - /var/run:rw,noexec,nosuid,size=16m
  • cap_drop: - ALL: 컨테이너 프로세스에서 모든 커널 기능을 박탈합니다.
  • cap_add: - NET_BIND_SERVICE: 원시(raw) 네트워크 소켓 조작은 차단하면서 특권 포트(80, 443 등) 바인딩만 명시적으로 허용합니다.
  • read_only: true: 컨테이너 루트 파일 시스템 전체를 읽기 전용으로 마운트합니다. 공격자가 악성 바이너리를 다운로드하거나 PHP 스크립트를 수정하거나 웹 서버 구성 파일을 변경할 수 없습니다.
  • tmpfs: 필수 임시 파일(예: /tmp)을 위해 휘발성 인메모리 디렉터리를 할당하는 동시에 바이너리 실행(noexec) 및 권한 상승(nosuid)을 차단합니다.

seccomp(보안 컴퓨팅 모드) 필터와 AppArmor 또는 SELinux 같은 보안 모듈을 적용하여 공유 호스트 커널로 전달되는 시스템 호출을 가로채고 제한하십시오. 팀에서 커스텀 웹 애플리케이션 빌드를 관리하는 경우, 배포 파이프라인 전반에 걸친 도커 컨테이너 보안 강화(하드닝) 단계별 가이드를 따르십시오.


4. 테넌트 환경 간 네트워크 분할

기본 브리지 네트워킹을 비활성화하고 테넌트 스택마다 격리된 커스텀 소프트웨어 정의 브리지 네트워크를 구축하십시오. 기본적으로 표준 Docker 브리지 네트워크에 배치된 컨테이너들은 내부 IP 주소를 통해 서로를 검색하고 통신할 수 있습니다. 한 테넌트의 마케팅 마이크로서비스에 취약점이 생기면 해당 호스트의 다른 모든 내부 데이터베이스와 애플리케이션으로의 횡적 이동(lateral movement)이 가능해집니다.

테넌트별로 독립적인 네트워크 브리지를 선언하여 테넌트 트래픽을 완벽히 격리하십시오:

networks:
  tenant_alpha_net:
    driver: bridge
    internal: true
  tenant_beta_net:
    driver: bridge
    internal: true
  public_gateway_net:
    driver: bridge

services:
  alpha_app:
    image: tenant_a_app:latest
    networks:
      - tenant_alpha_net
      - public_gateway_net

  alpha_db:
    image: mariadb:10.11
    networks:
      - tenant_alpha_net

  beta_app:
    image: tenant_b_app:latest
    networks:
      - tenant_beta_net
      - public_gateway_net

  beta_db:
    image: mariadb:10.11
    networks:
      - tenant_beta_net
  • 테넌트 격리: alpha_appalpha_dbtenant_alpha_net을 통해서만 통신합니다. 공격자가 내부 서브넷을 스캔하더라도 beta_appalpha_db에 접근할 수 없습니다.
  • 내부 플래그 (internal: true): 데이터베이스 네트워크가 외부 인터넷으로 직접 트래픽을 라우팅하지 못하도록 차단하여, 인바운드 및 아웃바운드 접근을 애플리케이션 컨테이너로만 엄격히 제한합니다.
  • 리버스 프록시 게이트웨이: 인그레스 프록시만 public_gateway_net에 연결되어 수신된 HTTP/HTTPS 요청을 호스트 이름에 따라 지정된 테넌트 컨테이너로 라우팅합니다.

보다 향상된 환경을 원한다면 Docker의 ECI(Enhanced Container Isolation) 모드나 Sysbox 같은 런타임을 고려해 보세요. 복잡한 수동 네트워크 스크립팅 없이도 더욱 엄격한 사용자 네임스페이스 경계와 가상화된 /proc/sys 파일 시스템을 자동으로 적용할 수 있습니다.


5. 객관적인 멀티테넌트 의사결정 매트릭스 수립

모든 디지털 자산에 전용 가상 머신이 필요하다는 고정관념을 재검토하십시오. 마케팅 리더들은 하드웨어 수준의 VM 격리만이 유일하게 안전한 보안 모델이라고 생각하는 경우가 많습니다. 그러나 가벼운 랜딩 페이지나 단기 캠페인 사이트에 전용 VM을 프로비저닝하면 웹 애플리케이션 보안은 크게 개선되지 않으면서 막대한 비용 낭비와 운영 유지보수 부담만 초래됩니다.

다음 비교 매트릭스를 활용하여 워크로드 요구사항을 평가하고 의사결정권자에게 합리적인 배포 전략을 제시하십시오:

격리 계층기반 기술보안 경계리소스 오버헤드최적 활용 사례
공유 스택 컨테이너단일 OS 기반 네임스페이스 및 cgroups논리적 OS 수준 격리매우 낮음대량의 랜딩 페이지, 내부 스테이징, 임시 캠페인 사이트
하드닝된 컨테이너 (ECI / Sysbox)사용자 네임스페이스, AppArmor, 읽기 전용 루트고급 OS 수준 및 가상화낮음다중 클라이언트 에이전시 호스팅, 인증 포털, 민감한 마케팅 폼
전용 가상 머신 (VM)하이퍼바이저 하드웨어 가상화엄격한 하드웨어/커널 분리높음결제 처리, HIPAA/PCI 규제 데이터, 검증되지 않은 커스텀 코드 실행
하이브리드 (전용 VM 내 컨테이너)테넌트 전용 VM 내부의 하드닝된 컨테이너다층 하드웨어 및 OS 경계보통 ~ 높음전용 계약 준수(컴플라이언스)가 요구되는 최고 등급 엔터프라이즈 고객

인프라 예산을 배정하기 전에 엄격한 기준에 따라 각 프로젝트를 평가하십시오:

  1. 데이터 민감도: 프로젝트에서 규제 대상 데이터(예: 신용카드 정보, 의료 건강 정보)를 저장합니까? 그렇다면 전용 VM에 배포하십시오.
  2. 코드 출처(Provenance): 팀에서 검증한 표준화된 코드를 배포합니까, 아니면 검증되지 않은 서드파티 플러그인을 허용합니까? 표준 코드는 하드닝된 컨테이너에 적합하며, 테스트되지 않은 서드파티 코드는 하이퍼바이저 격리가 필요합니다.
  3. 예산 및 수명주기: 시즌성 랜딩 페이지나 핵심 기업 웹사이트의 경우, 하드닝된 컨테이너 멀티테넌시가 비용 대비 최고의 성능을 제공합니다.

경영진에게 인프라 계획을 보고할 때는 고객에게 전용 VM이 필요한 시점 평가 가이드를 참고하여 명확한 계층 기반 논리로 추천안을 뒷받침하십시오.


결론: 보안 통제를 비즈니스 ROI로 전환하기

멀티테넌트 Docker 환경을 보호하는 데 막대한 엔터프라이즈 클라우드 아키텍처 예산이 필요한 것은 아닙니다. 운영체제 통제 기능을 엄격하고 체계적으로 적용하는 것으로 충분합니다.

비기술직 경영진과 인프라를 검토할 때는 이러한 기술적 구성을 세 가지 핵심 비즈니스 지표를 중심으로 설명하십시오:

  • 비용 효율성: 멀티테넌트 컨테이너를 사용하면 개별 VM에 필요한 컴퓨팅 공간의 일부만으로도 수십 개의 마케팅 사이트를 호스팅할 수 있습니다.
  • 가동 시간 보호: 컨트롤 그룹을 통해 시즌 캠페인에 트래픽이 폭증하더라도 핵심 브랜드 웹사이트의 성능이 저하되지 않도록 보장합니다.
  • 피해 반경(Blast Radius) 격리: 읽기 전용 파일 시스템, 최소 권한 설정, 격리된 네트워크 브리지를 통해 단일 사이트가 침해되더라도 인접 고객의 데이터베이스나 호스트 제어 권한에 접근하지 못하도록 차단합니다.

컨테이너 템플릿 전반에 이러한 보호 장치를 체계적으로 구현하십시오. 엔지니어링 보안 표준과 경영진의 예산 제약을 모두 만족하는 고성능, 고비용 효율의 인프라를 제공할 수 있습니다.

Sources (5)