클라우드 플랫폼 (AWS, GCP) 배포
지난 절에서는 NestJS 애플리케이션을 위한 CI/CD 파이프라인을 구축하여 자동화된 빌드, 테스트, 배포 프로세스를 만드는 방법을 알아보았습니다. 이제 11장의 세 번째 절로, 컨테이너화된 NestJS 애플리케이션을 클라우드 플랫폼에 배포하는 다양한 전략에 대해 심층적으로 살펴보겠습니다.
클라우드 컴퓨팅은 확장성, 유연성, 고가용성, 비용 효율성을 제공하며 현대 소프트웨어 배포에서 널리 사용됩니다.
클라우드 배포 전략 개요
NestJS 애플리케이션을 클라우드에 배포하는 방식은 크게 다음과 같이 나눌 수 있습니다.
IaaS (Infrastructure as a Service) 기반 VM 배포: 가상 머신(VM)을 직접 프로비저닝하고, 그 위에 Docker 환경을 수동으로 설정하여 컨테이너를 실행합니다. 가장 유연하지만 관리 부담이 큽니다.
PaaS (Platform as a Service) 기반 컨테이너 배포: 미리 설정된 플랫폼에 컨테이너 이미지를 배포하면, 플랫폼이 인프라 관리, 스케일링, 로드 밸런싱 등을 자동 처리합니다. 간편하지만 유연성이 제한될 수 있습니다.
컨테이너 오케스트레이션 기반 배포 (Kubernetes): Docker 컨테이너를 대규모로 관리하고 자동화하는 복잡한 시스템입니다. 높은 확장성과 유연성을 제공하지만 학습 곡선이 높습니다.
서버리스 컨테이너 배포: 컨테이너 이미지를 기반으로 하지만, 서버를 직접 관리할 필요 없이 이벤트에 따라 코드가 실행되고 자동으로 스케일링됩니다.
주요 클라우드 플랫폼별 NestJS 배포 전략
각 클라우드 플랫폼은 유사한 목적의 다양한 서비스를 제공합니다. 여기서는 AWS를 중심으로 설명하고, 다른 플랫폼의 대응 서비스도 간략히 언급하겠습니다.
IaaS: 가상 머신 (VM)에 직접 배포
- AWS: EC2 (Elastic Compute Cloud)
- GCP: Compute Engine
- Azure: Azure Virtual Machines
개념: EC2 인스턴스(가상 서버)를 생성하고, 해당 인스턴스에 SSH로 접속하여 Docker를 설치한 다음, 직접 Docker 이미지를 풀(pull)하고 컨테이너를 실행하는 방식입니다.
장점- 가장 높은 유연성과 제어권을 가집니다.
- 이미 Docker에 익숙하다면 쉽게 시작할 수 있습니다.
- 운영 부담: 서버 패치, 보안 업데이트, 스케일링, 로드 밸런싱, 고가용성 구성 등을 모두 직접 관리해야 합니다.
- 비용 효율성 감소: 유휴 리소스가 발생할 수 있습니다.
EC2 인스턴스 생성: Ubuntu LTS, Amazon Linux 2023 등의 AMI(Amazon Machine Image)를 선택하고, 인스턴스 타입, 키 페어, 보안 그룹(애플리케이션 포트와 SSH 22번 포트는 필요한 출발지로만 제한) 등을 설정합니다.
Docker 설치: 인스턴스에 SSH로 접속하여 Docker Engine을 설치하고 설정합니다. 아래는 Ubuntu LTS 예시이며, Amazon Linux 2023은 AWS의 dnf/yum 기반 절차를 따릅니다. 운영에서는 OS 표준 이미지나 Docker 공식 설치 절차를 팀 기준으로 고정해 재현성을 확보합니다.
sudo apt update
sudo apt install docker.io
sudo systemctl start docker
sudo systemctl enable docker
sudo usermod -aG docker ubuntu # 현재 사용자에게 docker 권한 부여
newgrp docker # 그룹 변경 적용docker 그룹은 사실상 root급 권한이므로 운영 서버에서는 전용 배포 사용자와 최소 권한 원칙을 함께 적용해야 합니다.
Docker 이미지 풀: Docker Hub 또는 ECR(Elastic Container Registry)에 푸시된 NestJS Docker 이미지를 가져옵니다.
docker pull your-docker-username/nestjs-app:<git-sha>컨테이너 실행: 단일 인스턴스에서는 앱 포트를 열 수 있지만, 운영에서는 80/443을 컨테이너에 직접 공개하기보다 ALB나 프록시 앞단에서 TLS를 종료하고 인스턴스 보안 그룹은 필요한 출발지만 허용합니다. 운영 비밀값은 명령줄 -e로 직접 흘리기보다 서버의 제한된 env 파일이나 비밀 저장소에서 주입합니다.
docker run -d -p 3000:3000 \
--name nestjs-prod-app \
--restart unless-stopped \
--env-file /etc/nestjs-app/production.env \
your-docker-username/nestjs-app:<git-sha>로드 밸런싱 및 오토 스케일링: 운영에서는 여러 EC2 인스턴스를 두고, 앞단에 ALB (Application Load Balancer)를 배치해 HTTPS 종료와 트래픽 분산을 맡깁니다. SSH는 고정된 관리 IP나 Session Manager처럼 감사 가능한 경로로 제한합니다.
또한 Auto Scaling Group (ASG)을 설정하면 트래픽에 따라 인스턴스 수를 자동 조절할 수 있습니다.
(이 경우 컨테이너 직접 관리는 복잡해지므로 ECS/EKS 같은 오케스트레이션으로 넘어가는 것이 일반적입니다.)
PaaS: 컨테이너 이미지 배포
- AWS: ECS on Fargate, AWS Elastic Beanstalk. App Runner는 기존 고객/레거시 선택지로만 검토합니다.
- GCP: Cloud Run, App Engine Flex
- Azure: Azure Container Apps, Azure App Service
개념: 개발자가 컨테이너 이미지만 제공하면, 클라우드 플랫폼이 이미지 빌드, 배포, 스케일링, 로드 밸런싱, SSL/TLS 처리, 로깅 등을 자동으로 처리해주는 서비스입니다. 서버 관리에 대한 부담을 크게 줄여줍니다.
장점- 관리 용이성: 인프라 관리 부담이 거의 없습니다.
- 빠른 배포: 코드만 푸시하거나 이미지 경로만 지정하면 배포가 자동으로 이루어집니다.
- 자동 스케일링: 트래픽에 따라 자동으로 스케일 아웃/인 됩니다.
- 비용 효율성: 사용한 만큼만 지불합니다.
- 제어권 제한: 인프라에 대한 세부적인 제어가 어렵습니다.
- 벤더 종속성: 특정 클라우드 서비스에 종속될 수 있습니다.
컨테이너 이미지 준비: NestJS 애플리케이션의 Docker 이미지를 ECR, Google Artifact Registry, Azure Container Registry 또는 Docker Hub 같은 레지스트리에 푸시합니다. 운영 배포에는 latest 단독 태그보다 버전 태그나 digest를 사용합니다.
- AWS에서는 ECS on Fargate, GCP에서는 Cloud Run, Azure에서는 Container Apps처럼 팀이 쓰는 관리형 컨테이너 실행 환경을 선택합니다.
- Source: 컨테이너 레지스트리의 이미지 URI와 tag/digest를 지정합니다.
- Deployment settings: 새 이미지 버전이 push될 때 자동 배포할지, 승인 후 수동 배포할지 선택합니다.
-
Service settings
- Service name: 서비스 이름 지정.
- Port: NestJS 앱이 수신 대기하는 포트(예: 3000)를 지정합니다.
- CPU and memory: 필요한 리소스 크기를 선택합니다.
- Environment variables: 공개 설정과 민감 정보를 분리합니다. DB 비밀번호와 JWT Secret은 평문 환경 변수보다 Secrets Manager, SSM Parameter Store, Secret Manager, Key Vault 같은 비밀 저장소 참조로 주입합니다.
- Auto scaling: 최소/최대 인스턴스 수 및 동시성(Concurrency)을 설정합니다.
- 헬스 체크: 컨테이너의 상태를 확인하기 위한 헬스 체크 경로(예:
/health)를 설정합니다.
- Networking: VPC 접속이 필요한 경우 설정합니다.
배포: 서비스 생성 후 플랫폼이 이미지를 풀하고 컨테이너를 실행하며, 공개 URL 또는 내부 엔드포인트를 제공합니다. 운영에서는 도메인, TLS, 로그 수집, 롤백 경로까지 함께 확인합니다.
커스텀 도메인 설정 (선택 사항): Route 53, Cloud DNS, Azure DNS 등을 통해 커스텀 도메인을 서비스에 연결합니다.
컨테이너 오케스트레이션
- AWS: EKS (Elastic Kubernetes Service)
- GCP: GKE (Google Kubernetes Engine)
- Azure: AKS (Azure Kubernetes Service)
개념: 컨테이너화된 애플리케이션의 배포, 확장, 관리, 자동 복구 등을 자동화하는 오픈 소스 플랫폼인 Kubernetes를 클라우드에서 관리형 서비스로 제공합니다. 복잡하지만 컨테이너 관리 범위가 넓습니다.
장점- 확장성 및 고가용성: 대규모, 복잡한 마이크로서비스 아키텍처에 최적화되어 있습니다.
- 유연성: 다양한 워크로드 유형과 배포 전략(롤링 업데이트, 블루/그린, 카나리)을 지원합니다.
- 클라우드 벤더 중립성: Kubernetes 자체가 오픈 소스이므로, 특정 클라우드에 덜 종속적입니다.
- 높은 학습 곡선: Kubernetes 개념과 YAML 설정에 대한 이해가 필요합니다.
- 복잡성: 설정 및 관리가 PaaS 서비스보다 훨씬 복잡합니다.
- 비용: 관리형 서비스라도 클러스터 유지 비용이 발생합니다.
EKS 클러스터 생성: AWS 콘솔 또는 eksctl CLI 도구를 사용하여 EKS 클러스터를 생성합니다.
Kubernetes Manifests 작성: NestJS 애플리케이션을 배포하기 위한 Deployment, Service, Ingress 등의 YAML 파일을 작성합니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nestjs-app
labels: { app: nestjs-app }
spec:
replicas: 3 # NestJS 인스턴스 3개 실행
selector: { matchLabels: { app: nestjs-app } }
template:
metadata: { labels: { app: nestjs-app } }
spec:
containers:
- name: nestjs-container
image: your-ecr-repo/nestjs-app:1.0.0 # 운영에서는 immutable tag나 digest 사용
ports: [{ containerPort: 3000 }]
env: # 환경 변수 (Kubernetes Secrets 또는 ConfigMaps 활용)
- name: DATABASE_HOST
valueFrom: { secretKeyRef: { name: 'app-secrets', key: 'DATABASE_HOST' } }
- name: JWT_SECRET
valueFrom: { secretKeyRef: { name: 'app-secrets', key: 'JWT_SECRET' } }
readinessProbe:
httpGet: { path: /health, port: 3000 }
initialDelaySeconds: 10
periodSeconds: 10
livenessProbe:
httpGet: { path: /health, port: 3000 }
initialDelaySeconds: 30
periodSeconds: 20
resources: # 리소스 요청 및 제한 설정 (매우 중요)
requests: { cpu: "100m", memory: "128Mi" }
limits: { cpu: "500m", memory: "256Mi" }
securityContext:
runAsNonRoot: true
runAsUser: 10001
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities: { drop: ["ALL"] }
seccompProfile: { type: RuntimeDefault }# service.yaml
apiVersion: v1
kind: Service
metadata:
name: nestjs-app-service
spec:
selector: { app: nestjs-app }
ports: [{ protocol: TCP, port: 80, targetPort: 3000 }]
type: LoadBalancer # L4 로드 밸런서. HTTP 라우팅은 Ingress와 ALB/Nginx 컨트롤러를 함께 검토Kubernetes Secrets/ConfigMaps 설정: 민감한 환경 변수는 Kubernetes Secrets에, 민감하지 않은 설정은 ConfigMaps에 저장합니다. 단, Kubernetes Secret은 기본적으로 운영 비밀 저장소 전체를 대체하지 않으므로 암호화 at rest, RBAC, External Secrets 또는 Secrets Store CSI 같은 연동을 함께 검토합니다.
kubectl로 배포: kubectl apply -f deployment.yaml -f service.yaml 명령어를 사용하여 Kubernetes 클러스터에 배포합니다.
Helm (선택 사항): 복잡한 애플리케이션의 Kubernetes 배포를 패키징하고 관리하기 위한 도구인 Helm을 사용할 수 있습니다.
서버리스 컨테이너 배포
- AWS: AWS Fargate (ECS 또는 EKS와 연동), AWS Lambda (컨테이너 이미지 패키징 지원)
- GCP: Cloud Run (컨테이너 이미지를 직접 실행하는 서버리스 플랫폼)
- Azure: Azure Container Apps, Azure Functions (컨테이너 이미지 지원)
개념: 개발자가 컨테이너 이미지를 제공하면, 클라우드 공급자가 서버 프로비저닝, 스케일링, 인프라 관리를 크게 줄여줍니다. 다만 장시간 떠 있는 웹 서비스와 이벤트 기반 함수의 과금·실행 모델은 다릅니다.
장점- 관리 부담 감소: 서버 OS와 용량 계획 부담이 줄어듭니다.
- 비용 효율성: Cloud Run처럼 scale-to-zero가 가능한 서비스는 유휴 비용을 줄일 수 있고, Fargate 서비스는 실행 중인 태스크 단위로 과금됩니다.
- 자동 스케일링: 트래픽이 0에서부터 수백만 요청까지 자동으로 확장됩니다.
- 콜드 스타트(Cold Start): 유휴 상태에서 첫 요청이 들어올 때 컨테이너가 시작되는 지연 시간이 발생할 수 있습니다.
- 제한 사항: Lambda의 컨테이너 이미지는 패키징 방식일 뿐 일반적인 장기 실행 웹 컨테이너와 다릅니다. API Gateway/Function URL/어댑터가 필요하고 실행 시간 제한도 고려해야 합니다.
- 복잡한 상태 관리: 서버리스 환경에서는 상태 비저장(Stateless) 아키텍처가 권장됩니다.
ECR에 이미지 푸시: NestJS Docker 이미지를 AWS ECR에 푸시합니다.
- ECS 클러스터 생성: Fargate 런치 타입으로 ECS 클러스터를 생성합니다.
- 태스크 정의(Task Definition): NestJS 컨테이너 이미지, CPU/메모리, 환경 변수, 포트 매핑 등을 정의합니다.
networkMode: awsvpc로 설정하여 ENI(Elastic Network Interface)를 통해 VPC 네트워크에 직접 연결되도록 합니다. - 서비스(Service) 생성: 태스크 정의를 사용하여 ECS 서비스(
Fargate런치 타입)를 생성하고, desired count, 최소/최대 태스크 수, 로드 밸런서(ALB) 연동 등을 설정합니다.
ALB (Application Load Balancer) 연동: ECS 서비스와 ALB를 연동하여 외부 트래픽을 NestJS 컨테이너로 라우팅합니다.
자동 스케일링: CloudWatch 메트릭(CPU 사용량, 요청 수)에 따라 ECS 서비스의 태스크 수를 자동으로 조절하도록 설정합니다.
NestJS 애플리케이션의 클라우드 배포 전략은 규모, 복잡성, 팀 운영 역량, 예산에 따라 달라집니다.
소규모 프로젝트나 빠른 시작이 필요하면 Cloud Run, Azure Container Apps, ECS on Fargate 같은 관리형 컨테이너 서비스가 적합하고, 높은 제어권이 필요하지만 운영 부담을 감수할 수 있다면 IaaS(EC2)가 맞습니다. App Runner는 신규 AWS 프로젝트의 기본 선택지로 두기보다 기존 고객/레거시 맥락에서만 검토합니다.
대규모 마이크로서비스와 복잡한 요구사항에는 컨테이너 오케스트레이션(EKS, GKE)을 선택지로 검토합니다.
비용 효율과 서버 관리 최소화를 원한다면 서버리스 컨테이너(Fargate, Cloud Run)도 고려할 만합니다. 단, scale-to-zero와 항상 실행되는 서비스의 비용 모델은 구분해서 봐야 합니다.
각 클라우드 플랫폼의 장단점을 비교하고, 프로젝트 요구사항에 맞는 배포 전략을 선택해야 합니다. 이 절에서는 NestJS 애플리케이션을 클라우드에 배포하고 운영할 때 확인할 기준을 정리했습니다.
아래 다이어그램은 클라우드 배포 전에 확인해야 할 운영 준비 흐름을 정리한 것입니다.
클라우드 배포 방식은 유행보다 운영 책임의 크기로 고르는 것이 안전합니다. 아래 보드는 EC2, Cloud Run/ECS Fargate, Kubernetes, 서버리스 컨테이너를 선택할 때 비교해야 할 기준을 모읍니다.