Docker Compose 운영형 실행
앞 절에서 만든 image만 실행해도 프로세스는 뜰 수 있지만 Study Log API는 PostgreSQL 없이는 업무 요청을 처리할 수 없습니다. Compose는 두 컨테이너를 편하게 시작하는 도구인 동시에, 비밀번호·네트워크 이름·영속 volume 같은 운영 경계를 로컬에서 재현하는 문서가 됩니다.
여기서 목표는 운영 환경을 완전히 흉내 내는 것이 아니라 애플리케이션과 DB가 서로 어떤 이름과 상태에 의존하는지 눈에 보이게 만드는 것입니다.
로컬 통합 환경도 운영 경계를 모사한다
애플리케이션은 localhost가 아니라 Compose 서비스 이름 postgres로 DB를 찾습니다. PostgreSQL의 데이터는 컨테이너 파일 시스템이 아니라 이름 있는 volume에 저장하고, 비밀번호는 image나 YAML 값에 직접 넣지 않고 secret 파일로 주입합니다.
app과 DB의 의존·health·volume을 작성한다
아래 compose.yaml에서 DB healthcheck는 “PostgreSQL이 연결을 받을 준비가 되었는가”만 판단합니다. depends_on은 이 최초 준비를 기다린 뒤 앱을 시작하지만, 실행 중 DB가 끊겼을 때의 재연결까지 대신하지는 않습니다.
services:
postgres:
image: postgres:18-alpine
environment:
POSTGRES_DB: studylog
POSTGRES_USER: studylog
POSTGRES_PASSWORD_FILE: /run/secrets/db-password
secrets:
- db-password
volumes:
- postgres-data:/var/lib/postgresql
healthcheck:
test: ["CMD-SHELL", "pg_isready -U studylog -d studylog"]
interval: 5s
timeout: 3s
retries: 20
app:
image: study-log-api:1.0.0
ports:
- "8080:8080"
environment:
SPRING_DATASOURCE_URL: jdbc:postgresql://postgres:5432/studylog
SPRING_DATASOURCE_USERNAME: studylog
SPRING_CONFIG_IMPORT: optional:configtree:/run/secrets/
secrets:
- spring.datasource.password
depends_on:
postgres:
condition: service_healthy
secrets:
db-password:
file: ./secrets/db-password.txt
spring.datasource.password:
file: ./secrets/db-password.txt
volumes:
postgres-data:SPRING_CONFIG_IMPORT=configtree:는 secret 파일 이름을 Spring 설정 키로 읽습니다. 그래서 서비스에 연결한 spring.datasource.password라는 파일 이름과 애플리케이션 설정 키가 정확히 일치해야 합니다.
재시작과 데이터 보존을 확인한다
컨테이너가 다시 준비됐다는 사실과 데이터가 보존됐다는 사실은 따로 확인합니다. 아래 명령은 Flyway 이력 행 수를 DB 재시작 전후에 비교하고, PostgreSQL과 애플리케이션 readiness가 다시 정상으로 돌아올 때까지 제한된 횟수만 기다립니다.
: "${STUDYLOG_DB_PASSWORD:?set STUDYLOG_DB_PASSWORD for the local Compose stack}"
mkdir -p secrets
umask 077
printf '%s' "$STUDYLOG_DB_PASSWORD" > secrets/db-password.txt
cleanup() {
docker compose down
rm -f secrets/db-password.txt
}
trap cleanup EXIT
docker compose up -d --wait
before="$(docker compose exec -T postgres \
psql -U studylog -d studylog -Atc 'select count(*) from flyway_schema_history')"
docker compose restart postgres
for attempt in {1..30}; do
docker compose exec -T postgres pg_isready -U studylog -d studylog >/dev/null && \
curl -fsS http://localhost:8080/actuator/health/readiness >/dev/null && break
test "$attempt" -lt 30
sleep 2
done
after="$(docker compose exec -T postgres \
psql -U studylog -d studylog -Atc 'select count(*) from flyway_schema_history')"
test "$before" = "$after"애플리케이션 readiness가 다시 성공하고 Flyway 이력 행 수가 재시작 전후에 같아야 합니다. 이 비교는 volume이 유지됐다는 최소 증거이며, 업무 데이터의 내용까지 보존됐는지는 별도 조회로 확인해야 합니다.depends_on은 애플리케이션 복구 전략을 대신하지 않는다
DB가 늦게 준비되거나 잠시 끊겨도 앱은 제한된 재연결과 명확한 readiness로 상태를 알려야 합니다.
DB 장애를 liveness에 넣어 컨테이너까지 계속 재시작하면 이미 불안정한 DB에 연결 폭주를 만들 수 있습니다. 프로세스 자체는 살아 있되 readiness만 실패하도록 두고, 연결 풀이 회복하지 못하는 경우에만 운영자가 원인을 보고 재시작을 선택합니다.
Compose에서 확인한 health와 외부 설정 계약은 다음 절의 Kubernetes probe와 ConfigMap·Secret으로 그대로 확장됩니다.