본문 바로가기
빌드노트 검색

빌드노트

서비스 env_file에 값이 있는데 Compose의 ${VAR}가 비는 이유

서비스 env_file에 값이 있는데 Compose 치환 결과가 비는 이유를 다룬다. 최소 설정에서 치환 입력·최종 설정·컨테이너 값을 비교하고, 중복된 environment 대입을 지울지 별도의 치환 입력을 줄지 판단한다. 미설정과 빈 문자열의 기본값 차이도 실제 관찰로 구분한다.

service.env에는 값이 있는데 compose.yaml의 ${LAB_MODE}는 빈 문자열이 된다. 혹은 파일에 쓴 값 대신 내 PC의 값이 들어간다. 서비스의 env_file은 컨테이너에 넣을 환경변수이고, Compose 파일의 ${...}를 채우는 입력은 별도이기 때문이다.

컨테이너에 값을 전달하려는 목적이라면 같은 키를 environment에서 다시 대입하는 줄부터 확인한다. LAB_MODE: "${LAB_MODE}"의 치환 결과가 빈 문자열이어도 env_file의 값을 덮는다. Compose 파일 자체의 값을 만들려는 목적이라면 셸·.env·CLI의 --env-file을 확인한다. 서비스 env_file과 environment의 우선순위 (새 탭에서 열림)

서비스 파일은 읽혔는데 값이 비는 경우

다음처럼 같은 키를 두 곳에 지정한 구성을 보자. 자신의 설정에서는 서비스 이름과 변수 이름을 바꿔 해당 부분을 확인하면 된다. 아래 작은 재현을 따로 실행하려면 Linux 컨테이너 엔진과 Compose가 필요하고, BusyBox 이미지를 가져올 수 있어야 한다. 새 빈 폴더에 두 파일만 UTF-8로 저장한다. 아직 .env는 만들지 않는다.

service.env:

LAB_MODE=service-blue
LAB_SERVICE_ONLY=service-only

compose.yaml:

services:
  probe:
    image: busybox:1.37.0
    network_mode: none
    env_file: service.env
    environment:
      LAB_MODE: "${LAB_MODE}"

PowerShell에서 같은 폴더로 이동해 실행한다. 고유 프로젝트 이름은 별도 재현용이다. 자신의 구성에서 진단할 때는 원래 사용하던 프로젝트·파일 옵션과 서비스 이름을 유지한다.

$labProject = 'envinterp-' + [guid]::NewGuid().ToString('N').Substring(0,12)
Remove-Item Env:LAB_MODE -ErrorAction SilentlyContinue
docker compose -p $labProject -f compose.yaml config --environment | Select-String '^LAB_MODE='
$cfg = docker compose -p $labProject -f compose.yaml config --format json | ConvertFrom-Json
'config LAB_MODE=<' + $cfg.services.probe.environment.LAB_MODE + '>'
docker compose -p $labProject -f compose.yaml run --rm --no-deps -T probe env | Select-String '^LAB_(MODE|SERVICE_ONLY)=' | Sort-Object

config --environment는 치환에 쓰는 입력을 보여 준다. 여기에는 LAB_MODE 행이 없었고, 미설정 변수를 빈 문자열로 바꾼다는 경고가 나왔다. 이어서 확인한 최종 설정과 새 컨테이너의 값은 다음과 같았다. 생성·다운로드 안내는 생략했다.

config LAB_MODE=<>

LAB_MODE=
LAB_SERVICE_ONLY=service-only

LAB_SERVICE_ONLY가 남아 있으므로 서비스 파일을 못 읽은 것이 아니다. 치환할 입력이 없어 environment에 빈 값이 만들어졌고, 그 값이 파일의 service-blue를 덮었다. config 결과는 서비스 환경 파일까지 합친 모델이므로, 그 안에 파일의 다른 키가 보이는 것과 그 파일이 ${...}의 입력이라는 주장은 구분해야 한다. config와 --environment의 역할 (새 탭에서 열림)

실제 프로젝트에서도 치환 입력의 해당 키, 최종 설정의 해당 키, 컨테이너가 받은 값을 대조한다. 전체 환경이나 전체 설정을 로그에 붙이지 않는다. 명령이 실패했다면 빈 출력으로 판단하지 말고 종료 코드와 오류부터 확인한다. 위 run은 새 일회성 컨테이너를 검사하므로 이미 실행 중인 앱의 값까지 확인한 것은 아니다.

컨테이너 값만 필요하면 중복 대입을 지운다

앱이 컨테이너 안에서 LAB_MODE를 읽기만 한다면 environment의 LAB_MODE: "${LAB_MODE}"를 지우고 env_file: service.env를 남긴다. 다른 환경변수는 유지한다. 위 예제에서는 유일한 항목을 지운 뒤 빈 environment 블록도 제거하면 된다.

같은 컨테이너 확인 명령을 다시 실행했을 때 출력은 다음과 같았다.

LAB_MODE=service-blue
LAB_SERVICE_ONLY=service-only

이 구성에서는 .env에 LAB_MODE=dot-green을 두거나 셸에 LAB_MODE=host-purple를 두어도 service-blue가 들어갔다. 셸에 키가 있다는 사실만으로 컨테이너 환경을 덮는 것은 아니다. 앞선 구성에서 셸 값을 가져온 연결은 environment의 ${LAB_MODE}였다.

Compose 파일 자체에서 값이 필요하면 치환 입력을 준다

이미지 이름이나 포트처럼 Compose가 먼저 읽어야 하는 값에는 치환 입력이 필요하다. 셸·.env·CLI의 --env-file을 확인하고, 파일을 명시했어도 셸의 같은 키가 우선한다는 점을 확인한다. 치환 입력의 우선순위 (새 탭에서 열림)

서비스의 env_file과 CLI의 --env-file은 같은 파일을 가리킬 수도 있지만 역할은 다르다. 최초 예제처럼 environment에 치환이 있는 구성에서, 셸의 LAB_MODE를 지우고 서비스 파일을 CLI 입력으로도 지정해 비교했다.

docker compose -p $labProject -f compose.yaml --env-file service.env config --environment | Select-String '^LAB_MODE='
docker compose -p $labProject -f compose.yaml --env-file service.env run --rm --no-deps -T probe env | Select-String '^LAB_(MODE|SERVICE_ONLY)=' | Sort-Object

이번에는 치환 입력에도 LAB_MODE=service-blue가 나왔고, 컨테이너에도 service-blue가 들어갔다. 기존 프로젝트에 적용할 때는 컨테이너용 파일 전체를 치환 입력으로도 쓸지 판단하고 파일을 지정한다.

별도 비교에서는 .env의 LAB_MODE=dot-green이 치환을 거쳐 컨테이너로 들어갔다. 셸에 LAB_MODE=host-purple를 지정하면 .env와 CLI 파일 양쪽보다 우선해 host-purple가 들어갔다. .env에만 두고 서비스 설정에서 연결하지 않은 키는 컨테이너에 없었다. .env를 읽는 것과 그 모든 키를 컨테이너로 복사하는 것은 다르다. 컨테이너 환경변수 설정 방법 (새 탭에서 열림)

같은 빈 출력이어도 미설정과 빈 문자열은 다르다

치환 입력에 키가 없는 상태와 LAB_MODE=로 빈 값을 지정한 상태도 따로 비교했다. 두 경우 모두 직접 치환한 LAB_MODE는 비었지만 기본값 처리는 달랐다.

  • 키가 없으면 ${LAB_MODE-fallback}와 ${LAB_MODE:-fallback} 모두 fallback이 됐다. 직접 치환에서는 미설정 경고가 나왔다.
  • LAB_MODE=이면 ${LAB_MODE-fallback}는 빈 문자열, ${LAB_MODE:-fallback}는 fallback이 됐다. 미설정 경고는 없었다.

빈 값도 기본값으로 대체하려면 콜론이 있는 :-를 쓴다. 빈 값을 허용하면 안 되는 입력은 다음처럼 해석 단계에서 거부할 수 있다.

LAB_MODE: "${LAB_MODE:?LAB_MODE must be non-empty}"

이 설정의 config --quiet는 미설정과 빈 문자열에서 모두 종료 코드 1과 LAB_MODE must be non-empty 오류로 거부됐고, 비어 있지 않은 CLI 입력에서는 종료 코드 0이었다. 검사 대상은 Compose의 치환 입력이므로 서비스 파일에 값이 있다는 이유로 통과하지 않는다. 기본값·필수값 치환 문법 (새 탭에서 열림)

PowerShell 5.1에서 $env:LAB_MODE = ''는 변수를 제거했다. 위 빈 값 비교는 파일의 LAB_MODE=로 만들었다. 셸에서 빈 값을 만들 때는 사용하는 버전도 확인해야 한다. PowerShell 환경변수의 빈 문자열 지원 (새 탭에서 열림)

확인 환경은 Windows PowerShell 5.1, Docker Compose v5.5.1, Docker Engine 29.8.0의 Linux/amd64였다. 합성 값으로 새 일회성 컨테이너를 실행했고 네트워크·볼륨은 만들지 않았다. .env 탐색은 실행 폴더와 Compose 파일 폴더가 같은 조건만 확인했다. 기존 실행 컨테이너의 설정 갱신, 다른 폴더의 탐색 규칙, Swarm, 앱 프레임워크의 설정 해석은 검증하지 않았다. 새 컨테이너에는 올바른 값이 들어가는데 앱만 다르게 읽는다면 앱의 변수 이름과 설정 우선순위를 확인할 차례다.

← 목록으로 돌아가기