Skip to main content

Docker는 훌륭한 도구지만, 소규모 서비스나 빠른 배포가 필요한 상황에서는 오히려 오버엔지니어링이 될 수 있습니다. AWS Lightsail에서 systemd + uvicorn + nginx 조합으로 FastAPI를 직접 배포하면 Docker 없이도 안정적이고 빠른 운영이 가능합니다. 이 글에서는 실제 프로젝트에 적용한 전체 과정을 공유합니다.

Docker 없이 AWS Lightsail 배포하는 실전 방법

왜 Docker를 안 쓰는가?

Docker가 제공하는 환경 격리와 이식성은 분명히 가치 있습니다. 그러나 다음과 같은 상황에서는 오히려 복잡도를 높입니다:

  • $5~$10 Lightsail 인스턴스: RAM 512MB~1GB에서 Docker 오버헤드가 부담
  • 단일 서비스 배포: 컨테이너 오케스트레이션이 필요 없는 경우
  • 빠른 디버깅: 컨테이너 레이어 없이 직접 로그 접근이 편함
  • systemd 네이티브 기능: 자동 재시작, 의존성 관리가 이미 OS에 내장

1단계: Lightsail 인스턴스 초기 설정

Ubuntu 22.04 LTS 기반 인스턴스를 생성한 후, 기본 패키지와 Python 환경을 세팅합니다.

# 시스템 업데이트
sudo apt update && sudo apt upgrade -y

# Python 3.11 + pip + venv
sudo apt install -y python3.11 python3.11-venv python3-pip

# nginx 설치
sudo apt install -y nginx

# 방화벽 설정
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

Lightsail 콘솔에서도 인바운드 규칙에 80(HTTP), 443(HTTPS) 포트를 허용해야 합니다. 방화벽 설정은 OS 레벨과 Lightsail 네트워킹 레벨 양쪽 모두 확인하세요.

2단계: FastAPI 앱 배포 준비

애플리케이션은 /srv/myapp 경로에 배포합니다. 전용 시스템 유저를 만들어 보안을 강화합니다.

# 배포 경로 생성
sudo mkdir -p /srv/myapp
sudo useradd -r -s /bin/false myapp
sudo chown myapp:myapp /srv/myapp

# 코드 배포 (Git 사용 예시)
cd /srv/myapp
sudo -u myapp git clone https://github.com/yourorg/yourrepo.git .

# 가상환경 생성 및 패키지 설치
sudo -u myapp python3.11 -m venv venv
sudo -u myapp venv/bin/pip install --upgrade pip
sudo -u myapp venv/bin/pip install -r requirements.txt

환경 변수는 /srv/myapp/.env 파일에 관리하고, 파일 권한을 600으로 제한합니다. python-dotenv로 FastAPI 앱에서 로드하거나, systemd 서비스 파일에 EnvironmentFile로 지정할 수 있습니다.

3단계: systemd 서비스 파일 작성

systemd 서비스를 등록하면 서버 재부팅 시 자동 시작, 크래시 시 자동 재시작이 보장됩니다.

# /etc/systemd/system/myapp.service
[Unit]
Description=MyApp FastAPI Application
After=network.target

[Service]
Type=exec
User=myapp
Group=myapp
WorkingDirectory=/srv/myapp
EnvironmentFile=/srv/myapp/.env
ExecStart=/srv/myapp/venv/bin/uvicorn main:app     --host 127.0.0.1     --port 8000     --workers 2     --no-access-log
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myapp

[Install]
WantedBy=multi-user.target

--workers 2는 vCPU 수에 맞게 조정하세요. Lightsail $10 플랜(2vCPU)이라면 2~4개가 적당합니다. 서비스 등록 및 시작:

sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp

# 상태 확인
sudo systemctl status myapp
sudo journalctl -u myapp -f  # 실시간 로그

4단계: Nginx 리버스 프록시 설정

uvicorn은 localhost:8000에서 실행하고, Nginx가 외부 트래픽을 받아 프록시합니다.

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;

    # 정적 파일 직접 서빙
    location /static/ {
        alias /srv/myapp/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_read_timeout 120s;
        proxy_connect_timeout 10s;
    }
}

# sites-enabled 심볼릭 링크
# sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
# sudo nginx -t && sudo systemctl reload nginx

5단계: SSL 인증서 설치 (Let’s Encrypt)

Certbot으로 무료 SSL 인증서를 발급하고 자동 갱신까지 설정합니다.

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

# 자동 갱신 테스트
sudo certbot renew --dry-run

Certbot이 Nginx 설정을 자동으로 수정해 HTTPS 리다이렉트와 SSL 설정을 추가합니다. 갱신 크론은 자동 등록되며, /etc/cron.d/certbot에서 확인할 수 있습니다.

6단계: 무중단 배포 스크립트

코드 업데이트 시 짧은 다운타임도 없애려면 다음 배포 스크립트를 활용합니다. uvicorn의 graceful shutdown을 이용합니다.

#!/bin/bash
# /srv/myapp/deploy.sh

set -e

APP_DIR="/srv/myapp"
SERVICE_NAME="myapp"

echo "=== 배포 시작: $(date) ==="

# 코드 업데이트
cd $APP_DIR
git pull origin main

# 의존성 업데이트
./venv/bin/pip install -r requirements.txt --quiet

# Alembic 마이그레이션 (DB 변경 있을 때)
# ./venv/bin/alembic upgrade head

# 서비스 재시작 (systemd가 graceful shutdown 처리)
sudo systemctl reload-or-restart $SERVICE_NAME

# 헬스체크 (30초 대기)
sleep 5
for i in {1..6}; do
    if curl -sf http://127.0.0.1:8000/health > /dev/null; then
        echo "✅ 헬스체크 성공"
        break
    fi
    echo "⏳ 대기 중... ($i/6)"
    sleep 5
done

echo "=== 배포 완료: $(date) ==="

모니터링과 로그 관리

systemd journal이 로그를 자동 관리합니다. 디스크 사용량이 커지지 않도록 로그 보존 정책을 설정합니다.

# /etc/systemd/journald.conf 에 추가
[Journal]
SystemMaxUse=500M
MaxRetentionSec=30day

# 설정 적용
sudo systemctl restart systemd-journald

# 유용한 로그 명령
sudo journalctl -u myapp --since "1 hour ago"
sudo journalctl -u myapp --since "2026-03-01" --until "2026-03-01 23:59"
sudo journalctl -u myapp -n 100 --no-pager

추가로 Nginx 액세스 로그/var/log/nginx/access.log에, 에러는 /var/log/nginx/error.log에 기록됩니다. logrotate가 기본 설치되어 있어 자동으로 로그를 교체합니다.

Docker 배포 대비 실전 비교

항목Docker 방식systemd 방식
초기 설정 시간30~60분15~30분
메모리 오버헤드~100MB+~10MB
배포 속도이미지 빌드 필요git pull + restart
로그 접근docker logsjournalctl (네이티브)
자동 재시작–restart=alwaysRestart=always
환경 격리완전 격리OS 공유 (venv 격리)

코드벤터는 글로벌 협력 네트워크를 기반으로 다양한 배포 환경과 인프라 요구사항에 대응하며, 각 프로젝트 특성에 맞는 최적의 배포 전략을 적용합니다. Docker가 필요한 대규모 마이크로서비스부터 이 글에서 소개한 경량 직접 배포까지, 오버엔지니어링 없이 목적에 맞는 기술 선택이 곧 좋은 엔지니어링입니다.

코드픽 - 외주 전문 AI 바이브 코딩 글로벌 진출

댓글 남기기