# [Docker Compose 개념] 환경 변수와 시작 순서 설계

Compose 파일에서 가장 많이 헷갈리는 부분은 환경 변수다. .env가 있고, env_file도 있고, environment도 있다. 셋 다 비슷해 보이지만 역할이 다르다.

예시 프로젝트에서는 이 구분이 특히 중요하다. DB 비밀번호, JWT secret, AWS S3 key, Admin 계정 정보가 모두 .env에 들어 있다. 이 값을 아무 컨테이너에나 통째로 넣으면 "실행은 되지만 관리하기 불편한 상태"가 된다.

.env는 Compose 파일을 채우는 값 저장소다

Compose는 기본적으로 프로젝트 디렉터리의 .env 파일을 읽어 ${...} 자리에 값을 넣을 수 있다.

environment:
  SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE}
  SPRING_DATASOURCE_URL: ${SPRING_DATASOURCE_URL}

이때 .env는 Compose 파일을 해석하는 시점에 쓰인다. 이것을 보간이라고 부른다. .env에 값이 있다고 해서 모든 값이 자동으로 컨테이너 안에 들어가는 것은 아니다. 컨테이너 안으로 넣으려면 environmentenv_file을 통해 전달해야 한다.

정리하면 다음과 같다.

위치역할컨테이너 안에 자동 주입되는가
.envCompose 파일의 ${...} 값을 채움아니다
environment해당 서비스 컨테이너에 환경 변수 주입그렇다
env_file파일의 변수를 해당 서비스 컨테이너에 통째로 주입그렇다

env_file은 편하지만 넓다

현재 Compose 파일은 appdb에 모두 .env를 연결한다.

app:
  env_file: .env

db:
  env_file:
    - .env

이렇게 하면 .env의 모든 값이 두 컨테이너에 들어간다. 그 결과 DB 컨테이너에도 AWS key, JWT secret, Admin 계정 정보 같은 값이 들어간다. PostgreSQL 컨테이너는 이 값들을 사용할 필요가 없다.

실행에는 큰 문제가 없어 보일 수 있지만, 좋은 경계는 아니다. 컨테이너마다 필요한 환경 변수만 좁게 전달해야 한다.

db:
  environment:
    POSTGRES_DB: ${POSTGRES_DB}
    POSTGRES_USER: ${POSTGRES_USER}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}

이렇게 하면 DB 컨테이너에는 PostgreSQL 초기화에 필요한 값만 들어간다.

Spring Boot 설정은 환경 변수로 덮어쓸 수 있다

현재 application-prod.yml에는 DB URL이 다음처럼 들어 있다.

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/discodeit_prod

이 값은 컨테이너 환경에서는 맞지 않다. 앱 컨테이너의 localhost는 DB 컨테이너가 아니기 때문이다. Compose에서는 환경 변수로 이 값을 덮어쓴다.

app:
  environment:
    SPRING_DATASOURCE_URL: jdbc:postgresql://db:5432/${POSTGRES_DB}
    SPRING_DATASOURCE_USERNAME: ${POSTGRES_USER}
    SPRING_DATASOURCE_PASSWORD: ${POSTGRES_PASSWORD}

Spring Boot는 SPRING_DATASOURCE_URLspring.datasource.url 설정으로 해석할 수 있다. 그래서 이미지 안의 설정 파일을 다시 빌드하지 않고도 실행 환경에 맞는 DB 주소를 주입할 수 있다.

이 방식은 컨테이너에서 특히 중요하다. 이미지는 최대한 환경 독립적으로 만들고, 실제 주소와 비밀번호는 실행 시점에 주입하는 편이 낫다.

depends_on은 시작 순서이고 healthcheck는 준비 상태다

현재 Compose 파일에는 좋은 설정이 하나 있다.

app:
  depends_on:
    db:
      condition: service_healthy

db:
  healthcheck:
    test: [ "CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}" ]
    interval: 10s
    timeout: 5s
    retries: 5
    start_period: 10s

depends_on만 쓰면 DB 컨테이너를 먼저 시작한 뒤 앱 컨테이너를 시작한다. 하지만 PostgreSQL 프로세스가 실제로 연결을 받을 준비가 되었는지는 보장하지 않는다.

condition: service_healthy를 쓰면 DB의 healthcheck가 통과한 뒤 앱 컨테이너가 생성된다. Spring Boot가 시작하자마자 DB에 연결하려다 실패하는 상황을 줄일 수 있다.

완전한 장애 복구는 아니다

depends_on은 시작 시점의 순서를 제어한다. 실행 중 DB가 죽었다가 살아나는 상황까지 앱의 연결 복구를 완벽하게 보장하지는 않는다. 애플리케이션의 커넥션 풀, 재시도 정책, 로그 확인도 함께 필요하다.

현재 프로젝트에 맞는 환경 변수 분리 기준

예시 프로젝트의 환경 변수는 용도별로 나누면 이해하기 쉽다.

묶음변수 예시전달 대상
PostgreSQL 초기화POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORDdb
Spring DB 접속SPRING_DATASOURCE_URL, SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORDapp
Spring 프로필과 JPASPRING_PROFILES_ACTIVE, SPRING_JPA_HIBERNATE_DDL_AUTO, SPRING_JPA_SHOW_SQLapp
AWS S3AWS_S3_ACCESS_KEY, AWS_S3_SECRET_KEY, AWS_S3_REGION, AWS_S3_BUCKETapp
인증과 관리자JWT_SECRET, ADMIN_EMAIL, ADMIN_USERNAME, ADMIN_PASSWORDapp
JVMJVM_OPTSapp

이 표를 기준으로 Compose 파일을 보면 db에 들어갈 값과 app에 들어갈 값이 자연스럽게 갈린다.

참고한 공식 문서