Docker 설치부터 Compose v2로 서비스 올리기(Nginx+PostgreSQL, Ubuntu 24.04)
컨테이너는 배포를 단순화하고 환경 차이를 줄여줍니다. 이 글에서는 Ubuntu 24.04 서버에서 Docker Engine과 Docker Compose v2를 설치한 뒤, Nginx(리버스 프록시)와 PostgreSQL(데이터베이스)를 compose.yaml 하나로 올려보겠습니다. 설치→권한→네트워크/볼륨→헬스체크→백업→점검까지 운영 관점으로 정리합니다.
1) Docker Engine + Compose v2 설치
# 1) 패키지 준비
sudo apt update
sudo apt install -y ca-certificates curl gnupg
# 2) Docker 공식 GPG 키/리포지토리
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu noble stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 3) Docker 엔진/CLI/Compose 플러그인 설치
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 4) 서비스 활성화 & 버전 확인
sudo systemctl enable --now docker
docker --version
docker compose version
옵션: 현재 사용자로 sudo 없이 쓰려면:
sudo usermod -aG docker $USER
# 로그아웃/로그인 또는
newgrp docker
2) 프로젝트 디렉터리 & 기본 구조
sudo mkdir -p /opt/stack/nginx/conf
sudo mkdir -p /opt/stack/nginx/html
sudo mkdir -p /opt/stack/pg/data
sudo chown -R $USER:$USER /opt/stack
cd /opt/stack
테스트용 index 페이지:
echo "<h1>It works (Docker + Nginx)!</h1>" > /opt/stack/nginx/html/index.html
3) Nginx 기본 설정(리버스 프록시 예시 포함)
정적 파일 서빙 + 백엔드 프록시(있다면) 템플릿:
# /opt/stack/nginx/conf/default.conf
server {
listen 80;
server_name _;
# 정적 테스트 페이지
root /usr/share/nginx/html;
index index.html;
# (선택) 백엔드 프록시
# location /api/ {
# proxy_pass http://backend:8080;
# 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;
# }
}
4) compose.yaml 작성: Nginx + PostgreSQL
# /opt/stack/compose.yaml
name: app-stack
services:
nginx:
image: nginx:1.27-alpine
container_name: nginx
ports:
- "80:80"
# HTTPS를 붙일 계획이면 "443:443" 매핑(추가 설정 필요)
volumes:
- ./nginx/html:/usr/share/nginx/html:ro
- ./nginx/conf/default.conf:/etc/nginx/conf.d/default.conf:ro
healthcheck:
test: ["CMD-SHELL", "wget -qO- http://localhost:80 || exit 1"]
interval: 10s
timeout: 3s
retries: 5
depends_on:
- db
restart: unless-stopped
networks:
- appnet
db:
image: postgres:16-alpine
container_name: postgres
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: strongpassword
POSTGRES_DB: appdb
volumes:
- ./pg/data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d appdb"]
interval: 10s
timeout: 5s
retries: 10
restart: unless-stopped
networks:
- appnet
# (선택) 백엔드 앱 컨테이너 예시
# backend:
# build: ./app
# container_name: backend
# environment:
# SPRING_PROFILES_ACTIVE: prod
# DB_URL: jdbc:postgresql://db:5432/appdb
# DB_USER: app
# DB_PASS: strongpassword
# depends_on:
# db:
# condition: service_healthy
# restart: unless-stopped
# networks:
# - appnet
networks:
appnet:
driver: bridge
포인트:
volumes로 데이터 영속성 보장(PostgreSQL), Nginx 설정/정적파일은:ro로 읽기전용 마운트healthcheck로 서비스 준비 상태 확인 →depends_on.condition로 의존성 제어(예시 주석)restart: unless-stopped로 장애 시 자동 재기동
5) 기동 & 동작 확인
cd /opt/stack
docker compose up -d
docker compose ps
웹 확인(서버에서):
curl -I http://127.0.0.1
# HTTP/1.1 200 OK
DB 접속 확인(PostgreSQL 클라이언트):
docker exec -it postgres psql -U app -d appdb -c "\l"
6) 운영 옵션(로그·리소스·보안)
- 로그:
docker logs -f nginx,docker logs -f postgres - 리소스 제한 (고급):
주의: Swarm/Compose v2 환경에 따라 적용 범위가 다릅니다(테스트 후 사용).deploy: resources: limits: cpus: '1.0' memory: 512M - 환경변수/비밀: 민감값은 .env 또는 secrets 매커니즘 사용(레포에 커밋 금지)
- 네트워크: 외부에 직접 노출할 서비스만
ports사용. 내부 통신은networks로 격리
7) 백업/복구(필수 루틴)
DB 볼륨을 주기적으로 덤프합니다(로컬/원격 저장소에 보관).
# 백업
docker exec postgres pg_dump -U app -d appdb | gzip > /opt/stack/pg/backup/appdb_$(date +%F).sql.gz
# 복원(예시: 새 DB에)
gunzip -c /opt/stack/pg/backup/appdb_2025-10-14.sql.gz | \
docker exec -i postgres psql -U app -d appdb
정기 자동화는 crontab -e로 예약(예: 매일 새벽 3시):
0 3 * * * docker exec postgres pg_dump -U app -d appdb | gzip > /opt/stack/pg/backup/appdb_$(date +\%F).sql.gz
8) HTTPS 적용(요약)
컨테이너 앞단에 호스트 Nginx(또는 프록시 전용 컨테이너)를 두고 Certbot으로 인증서를 받아 443을 개방하세요. 호스트 Nginx에서 proxy_pass http://127.0.0.1:80 → 컨테이너 Nginx로 라우팅하면 관리가 수월합니다.
9) 트러블슈팅
- 80 포트 충돌: 호스트에서 다른 데몬이 점유(
ss -lntup | grep :80). 기존 서비스 포트 변경 또는 compose의 포트를8080:80으로 매핑 - 파일 권한 문제: Nginx/PG 볼륨의 소유권/권한 확인(
chown,chmod) - 헬스체크 실패:
docker inspect --format='{{json .State.Health}}' <컨테이너> | jq로 상세 원인 확인 - 이미지 업데이트:
docker compose pull && docker compose up -d
10) 배포 전/후 체크리스트
- Docker/Compose 버전 확인 & 서비스 자동 시작
compose.yaml에 볼륨 영속성·헬스체크·재시작 정책 반영- 외부 노출 포트 최소화(80/443만), 내부 통신은 네트워크 격리
- 백업 크론 동작 확인, 복원 리허설 1회
- 로그/리소스 모니터링 확보(예: Promtail+Loki, node-exporter 등)
여기까지면 Nginx+PostgreSQL 구성의 컨테이너 기반 운영이 가능합니다. 다음 단계로는 백엔드 앱 컨테이너를 추가하고, HTTPS와 관찰성 스택(Prometheus/Grafana)을 붙여 운영 품질을 높이세요.
'서버 인프라 실무' 카테고리의 다른 글
| 사용자·그룹·sudoers 운영 표준: 최소 권한과 감사 로그(Ubuntu 24.04) (0) | 2025.10.22 |
|---|---|
| 리눅스 파일 권한 이해: chmod/chown/umask 한 번에 끝내기(실무 예제·함정·점검 루틴) (0) | 2025.10.21 |
| 무료 SSL(HTTPS) 적용: Certbot 자동 갱신 + 자주 나는 오류 해결(Ubuntu 24.04, Nginx) (0) | 2025.10.20 |
| Nginx 서버 블록(server_name)로 도메인 연결: WWW/비WWW 선택과 301 리다이렉트, HTTP→HTTPS까지 (0) | 2025.10.20 |
| UFW 방화벽 실전: 웹(80/443)만 열고 나머지 닫기 + 포트스캔 방어(IPv4/IPv6, 로그, 테스트까지) (0) | 2025.10.19 |