Docker Compose를 사용해 Linux 서버에 멀티 컨테이너 애플리케이션을 배포하는 실전 단계별 가이드입니다 — Docker 설치, 프로덕션 수준의 compose 파일 작성, HTTPS를 처리하는 리버스 프록시 추가, 그리고 Skyline Cloud 국내 인프라에서 안정적으로 운영하는 방법까지 다룹니다.
소개
Docker Compose를 사용하면 멀티 컨테이너 애플리케이션 — 웹 앱, 데이터베이스, 캐시, 리버스 프록시 — 을 하나의 선언적 파일에 정의한 뒤 단 하나의 명령으로 전체를 기동할 수 있습니다. 긴 docker run 명령을 손수 실행하는 대신, 원하는 상태를 한 번만 기술하면 Compose가 그 상태에 맞춰 조정해 줍니다.
이 튜토리얼에서는 Linux 서버에 작지만 현실적인 스택을 배포합니다: 웹 애플리케이션, PostgreSQL 데이터베이스, 그리고 HTTPS를 자동으로 종단 처리하는 Caddy 리버스 프록시입니다. 끝까지 마치면 여러분의 도메인에서 TLS로 접근 가능한 실행 중인 앱을 갖게 됩니다.
배포는 Skyline Cloud VPS에서 진행합니다. Skyline은 사우디아라비아 국내 인프라에서 운영되므로, 여러분과 고객의 데이터가 PDPL, NCA, SDAIA 관할권 아래에 머무릅니다 — 이는 KSA 및 GCC 조직의 워크로드를 호스팅할 때 중요한 부분입니다. 아직 서버가 없다면 몇 분 만에 만들 수 있습니다.
사전 준비
- 공인 IP를 가진 Linux 서버(Ubuntu 22.04/24.04 또는 Debian 12). 1–2 vCPU / 2 GB RAM VPS면 시작하기에 충분합니다.
sudo권한을 가진 비루트(non-root) 사용자, 그리고 서버에 대한 SSH 접근 권한.- 서버의 공인 IP를 가리키는 A 레코드가 설정된 도메인 이름. Skyline의 .sa 도메인 및 관리형 DNS를 사용한다면
app.example.sa → 203.0.113.10과 같은 레코드를 생성하면 됩니다.
1단계 — Docker Engine 및 Compose 플러그인 설치
최신 Docker는 Compose를 플러그인 형태(docker compose, 레거시 docker-compose가 아님)로 제공합니다. 공식 편의 스크립트가 엔진과 플러그인을 함께 설치합니다:
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo 없이 Docker를 실행할 수 있도록 사용자를 docker 그룹에 추가한 뒤 세션을 다시 엽니다:
sudo usermod -aG docker $USER
newgrp docker
엔진과 Compose 플러그인이 모두 설치되었는지 확인합니다:
docker --version
docker compose version
2단계 — 프로젝트 구조 잡기
스택을 위한 디렉터리를 생성하고 그 안으로 이동합니다:
mkdir -p ~/myapp && cd ~/myapp
여기에 세 개의 파일을 둡니다: 앱 이미지를 위한 Dockerfile, 서비스들을 서로 연결하는 compose.yaml, 그리고 비밀 정보와 구성을 담는 .env 파일입니다.
3단계 — 애플리케이션 Dockerfile 작성
일반적인 Node.js 앱이라면 작은 멀티 스테이지 빌드로 최종 이미지를 가볍게 유지할 수 있습니다. 여러분의 스택(Python, Go, PHP 등)에 맞게 명령을 조정하세요:
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
EXPOSE 3000
CMD ["node", "server.js"]
4단계 — .env 파일에 비밀 정보 정의
자격 증명을 compose 파일에 하드코딩하지 마세요. Compose가 자동으로 읽어 들이는 .env에 넣으세요:
POSTGRES_USER=appuser
POSTGRES_PASSWORD=change-me-to-a-strong-secret
POSTGRES_DB=appdb
APP_DOMAIN=app.example.sa
권한을 제한하고 버전 관리에서 제외합니다:
chmod 600 .env
echo ".env" >> .gitignore
5단계 — Compose 파일 작성
compose.yaml을 생성합니다. 이 파일은 세 개의 서비스, 하나의 프라이빗 네트워크, 그리고 재시작 후에도 데이터와 TLS 인증서가 유지되도록 이름 있는 볼륨을 정의합니다:
services:
app:
build: .
restart: unless-stopped
environment:
DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}
depends_on:
db:
condition: service_healthy
expose:
- "3000"
db:
image: postgres:16-alpine
restart: unless-stopped
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
caddy:
image: caddy:2-alpine
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
depends_on:
- app
volumes:
db-data:
caddy-data:
caddy-config:
주목할 만한 몇 가지 세부 사항:
- 데이터베이스 포트는
ports:로 공개되지 않습니다. 내부 Compose 네트워크에서만 접근 가능하므로 인터넷에 절대 노출되지 않습니다. condition: service_healthy가 포함된depends_on은 앱이 PostgreSQL 컨테이너가 시작되는 시점이 아니라 실제로 연결을 수락할 때까지 기다리도록 만듭니다.restart: unless-stopped는 재부팅이나 크래시 이후에도 서비스를 자동으로 다시 살려 줍니다.
6단계 — 리버스 프록시 구성
compose 파일 옆에 Caddyfile이라는 이름의 파일을 생성합니다. Caddy는 Let's Encrypt 인증서를 자동으로 획득하고 갱신하므로, 수동 인증서 처리 없이 HTTPS를 사용할 수 있습니다:
app.example.sa {
reverse_proxy app:3000
}
app.example.sa를 여러분의 실제 도메인으로 교체하세요. Caddy는 내부 네트워크를 통해 app을 여러분의 애플리케이션 컨테이너로 해석하고 요청을 3000번 포트로 프록시합니다.
7단계 — 스택 기동
이미지를 빌드하고 모든 것을 백그라운드에서 시작합니다:
docker compose up -d --build
모든 서비스가 정상(healthy) 상태인지 확인합니다:
docker compose ps
로그를 따라가며 앱이 시작되고 Caddy가 인증서를 발급하는 과정을 지켜봅니다:
docker compose logs -f
브라우저에서 https://app.example.sa를 엽니다. Caddy가 이미 유효한 TLS 인증서를 프로비저닝해 두었을 것입니다.
8단계 — 운영 및 업데이트
일상적으로 자주 쓰는 명령들:
| 작업 | 명령 |
|---|---|
| 실행 중인 서비스 보기 | docker compose ps |
| 한 서비스의 로그 따라가기 | docker compose logs -f app |
| 한 서비스 재시작 | docker compose restart app |
| 스택 중지(데이터 유지) | docker compose down |
| 최신 베이스 이미지 가져오기 | docker compose pull |
| 재빌드 및 재배포 | docker compose up -d --build |
코드의 새 버전을 배포하려면, 변경 사항을 가져온 뒤 docker compose up -d --build를 실행하세요. Compose는 변경된 컨테이너만 다시 생성하고 데이터베이스는 그대로 두므로, 상태 비저장(stateless) 서비스에 대해 거의 무중단 배포를 제공합니다.
볼륨을 포함하여 모든 것을 제거하려면(이 명령은 데이터베이스 데이터를 삭제합니다) docker compose down -v를 실행하세요 — 신중하게 사용하세요.
9단계 — 데이터 백업
애플리케이션 데이터는 db-data 볼륨에 저장됩니다. 정기적으로 덤프를 받아 서버 외부에 보관하세요. 간단한 PostgreSQL 백업 예시:
docker compose exec db pg_dump -U appuser appdb > backup-$(date +%F).sql
이를 cron으로 스케줄링하고 덤프를 Skyline Cloud 오브젝트 스토리지나 클라우드 백업으로 푸시해 두면, 단일 서버 장애가 결코 여러분의 데이터를 앗아가지 못합니다.
마치며
이제 여러분은 Docker Compose로 배포되고, 자동 HTTPS 리버스 프록시를 앞단에 둔 멀티 컨테이너 애플리케이션을 여러분이 직접 통제하는 서버에서 실행하게 되었습니다. 여기서부터 Redis 캐시를 추가하거나, 프록시 뒤에서 docker compose up -d --scale app=3으로 상태 비저장 서비스를 확장하거나, 오케스트레이션 단계로 발전시킬 수 있습니다 — 단일 호스트를 넘어설 때를 대비한 사우디아라비아 관리형 Kubernetes 가이드를 참고하세요.
앱과 함께 도메인용 전문 메일박스도 필요하다면, Skyline 비즈니스 이메일 호스팅이 여러분의 메일 또한 국내에 유지해 줍니다.
배포할 준비가 되셨나요? Skyline Cloud 서버를 만들고 시작하세요.
Comments
0 total · 0 threads