배포란?
배포 환경의 유형
전통적인 배포 방식: 서버 직접 설정 (On-Premise)
최신 배포 방식: Docker, CI/CD, 클라우드 플랫폼을 활용한 자동화 배포 Cloud-based, Serverless
클라우드 서비스 | 장점 | 단점 | 추천 사용 사례 |
---|---|---|---|
AWS Free Tier | ● 다양한 서비스 제공 (EC2, Lambda 등) ● 1년간 무료 사용 ● 강력한 확장성 및 유연성 | ● 복잡한 설정 과정 ● 1년 후 요금 발생 | ● 대규모 프로젝트 ● 복잡한 마이크로서비스 및 확장 가능한 애플리케이션 |
Google Cloud Platform (GCP) | ● 강력한 데이터 분석 도구 ● Kubernetes와의 통합 지원 ● 무료 크레딧 제공 (최초 $300) | ● 설정 및 관리를 위한 학습 필요 ● 무료 크레딧 소진 후 유료 | ● 데이터 중심 프로젝트 ● AI/ML, 데이터 처리 및 분석 |
Microsoft Azure | ● Windows 기반 시스템에 강력한 지원 ● 다양한 지역에 데이터센터 제공 | ● 설정이 복잡하고 비용이 다소 높음 ● 무료 티어 제한적 | ● Windows 기반 프로젝트 ● 기업용 애플리케이션 |
클라우드 서비스 | 장점 | 단점 | 추천 사용 사례 |
---|---|---|---|
Heroku | ● 간편한 배포 및 사용 ● 개발자 친화적 ● 비교적 낮은 단가의 기본 플랜 | ● 기본 플랜에서 슬립 모드 (대기 상태) 발생 ● 유료 플랜 비용이 다소 높음 | ● 스타트업 및 개인 프로젝트 ● 간단한 웹 애플리케이션 및 API 서버 |
Vercel | ● 정적 사이트 및 서버리스 함수에 최적화 ● 무료 플랜 제공 ● 자동화된 배포 | ● 동적 애플리케이션에는 제약 ● 데이터베이스 연결 시 추가 설정 필요 | ● 정적 사이트 ● JAMstack 애플리케이션 (Next.js 등) |
Netlify | ● 정적 사이트 배포에 최적화 ● 무료 플랜 제공 ● CI/CD 파이프라인 내장 | ● 서버리스 기능 제한 ● 동적 애플리케이션에는 적합하지 않음 | ● 프론트엔드 중심 프로젝트 ● 간단한 마케팅 페이지 및 블로그 |
클라우드 서비스 | 장점 | 단점 | 추천 사용 사례 |
---|---|---|---|
Fly.io | ● Edge 서버를 통한 지리적 분산 배포 ● 무료 크레딧 제공 ● Docker 이미지 지원: 컨테이너 기반 배포 가능 | ● 복잡한 설정 필요: 초보자에게는 설정이 어려울 수 있음 ● 대규모 애플리케이션에 제한적: 높은 트래픽 처리에는 한계 | ● 글로벌 사용자 대상 애플리케이션 ● 소규모 API |
Render | ● 자동화된 CI/CD 파이프라인 ● 무료 티어 제공 ● 쉬운 설정 | ● 높은 사용량 시 유료 전환 필요 ● 제한된 무료 리소스 | ● 개인 프로젝트 ● API 서버, 웹 애플리케이션 |
Railway | ● 직관적인 UI 및 빠른 배포 ● 사용량 기반 청구 ● 여러 언어 및 데이터베이스 지원 | ● 무료 티어 사용량 제한 ● 높은 트래픽 시 요금 발생 | ● 개인 및 소규모 팀 프로젝트 ● 데이터베이스가 필요한 애플리케이션 |
프로젝트 규모 및 복잡도
예산
설정의 간편성
기술 스택 호환성
서비스 안정성 및 지원
성능 및 확장성
CI / CD 및 개발자 도구
DevOps: Development + Operations
CI/CD 파이프라인 예시
배포 시스템이 확장성을 가지려면?
배포 후 지속적인 모니터링
항목 | 가상머신 (VM) | Docker 컨테이너 |
---|---|---|
실행 속도 | 느림 | 빠름 |
운영체제 | 각 VM마다 필요 | 호스트 OS 공유 |
리소스 사용 | 많음 (RAM, CPU 차지) | 적음 |
실행 환경 | 무겁고 복잡 | 가볍고 단순 |
하드웨어 자원을 가상화하여 독립적인 운영체제를 실행할 수 있도록 만든 소프트웨어 환경을 의미해. 즉, 하나의 물리적인 컴퓨터 위에서 여러 개의 가상 컴퓨터를 실행
특징
VM의 대표적인 종류
가상머신(VM)은 언제 사용하면 좋을까?
핵심
주요 명령어
docker ps
docker ps -a
docker stop <container_id>
docker kill <container_id>
docker rm <container_id>
docker logs <container_id>
docker build -t <이미지명><태그>
, (docker build -t app .
)docker run -d -p <호스트포트>:<컨테이너포트> <이미지명>
docker images
docker rmi <image_id>
docker system prune -a
실행 환경을 최대한 가볍게 만들어야 함
FROM python:3.10 (922MB)
FROM python:3.10-alpine (25MB)
불필요한 캐시 저장 방지 & 빌드 속도 최적화
# 1. Python 3.10 기반 이미지 사용
FROM python:3.10-alpine (alpine사용 경량화)
# 2. 작업 디렉토리 생성
WORKDIR /app
# 3. 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 4. FastAPI 애플리케이션 복사
COPY . .
# 5. 컨테이너 실행 명령어 설정
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# 1. 빌드 스테이지 (의존성 설치만 수행)
FROM python:3.10-alpine (alpine사용 경량화)
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 2. 실행 스테이지 (필요한 파일만 복사)
FROM python:3.10-alpine (alpine사용 경량화)
WORKDIR /app
COPY --from==builder /app /app
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# main.py
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "FastAPI on Docker!"}
# requirements.txt
fastapi
uvicorn
# Dockerfile
# 1. Python 3.10 기반 이미지 사용
FROM python:3.10-alpine
# 2. 작업 디렉토리 생성
WORKDIR /app
# 3. 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 4. FastAPI 애플리케이션 복사
COPY . .
# 5. 컨테이너 실행 명령어 설정
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
docker-compose up -d
# compose.yaml
version: "3.9" # Docker Compose 버전
services:
db:
image: postgres: 13 # PostgreSQL 13 버전 사용
container_name: fastapi_postgres
restart: always # 컨테이너가 종료되면 자동 재시작
environment:
POSTGRES_USER: fastapi_user
POSTGRES_PASSWORD: fastapi_password
POSTGRES_DB: fastapi_db
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data # DB 데이터 저장
fastapi:
build: . # 현재 디렉토리의 Dockerfile를 사용해 이미지 빌드
container_name: fastapi_app
depends_on:
- db # db 서비스가 실행된 후 fastapi 서비스 실행
ports:
- "8000:8000"
environment:
DATABASE_URL: "postgresql://fastapi_user:fastapi_password@db:5432/fastapi_db"
volumes:
postgres_data: # PostgreSQL 데이터를 저장할 볼륨(컨테이너 재시작 시 데이터 유지)
# (1) 도커 이미지 빌드 (현재 디렉토리의 Dockerfile 사용)
docker build -t app .
# (2) 빌드된 이미지 확인
docker images
# (3) 컨테이너 실행 (-d: 백그라운드 실행, -p: 포트 매핑)
docker run -d -p 8000:8000 app
# (1) 실행 중인 컨테이너 확인
docker ps
# (2) 컨테이너 중지 (b = 컨테이너 ID or 이름)
docker stop b
# (3) 실행 중인 컨테이너가 있는지 다시 확인
docker ps -a
# (1) 특정 컨테이너 삭제 (중지된 상태여야 함)
docker rm b
# (2) 모든 컨테이너 삭제 (실행 중인 것도 포함, 강제)
docker rm $(docker ps -aq) -f
# (1) 실행 중인 컨테이너 확인
docker ps
# (2) 실행 중인 컨테이너 중지 (필수)
docker stop <container_id>
# (3) 실행 중지 후 이미지 삭제 (-f: 실행 중인 것도 삭제)
docker rmi 이미지명 -f
# (4) 사용하지 않는 이미지 전부 삭제 (dangling 이미지)
docker image prune -a
- 컨테이너를 클라우드에 올린다고 바로 실행되지 않음
- 로컬에서는 Docker 컨테이너 실행만으로 API에 접근 가능
- 클라우드에서는
- 컨테이너가 실행되더라도 네트워크 접근이 차단되어 있음
- 로드 밸런서, 방화벽, 인바운드 규칙 설정이 필요할 수 있음
- 게다가 여러 컨테이너를 운영하려면 오케스트레이션 필요
- 보통 Kubernetes(쿠버네티스) 사용
- 여러개를 관리하려면 필요한 개념
database.py
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker, declarative_base
import os
DB_DIR = "./data"
if not os.path.exists(DB_DIR):
os.makedirs(DB_DIR)
DATABASE_URL = "sqlite:///./data/test.db"
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()
class Ticket(Base):
__tablename__ = "tickets"
id = Column(Integer, primary_key=True, autoincrement=True)
name = Column(String, index=True)
age = Column(Integer)
price = Column(Integer)
Base.metadata.create_all(bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# cloudtype.yaml
app: fastapi-tickets
service: web
env: python
start: uvicorn main:app --host 0.0.0.0 --port 8000
# database.py
import os
DATABASE_URL = os.getenv("DATABASE_URL", "postgresql://user:password@dbhost:5432/dbname")
engine = create_engine(DATABASE_URL)
3.vercel.json
{
"builds": [
{ "src": "main.py", "use": "@vercel/python" }
],
"routes": [
{ "src": "/(.*)", "dest": "main.py" }
]
}