
Docker 빌드 인자와 실행 환경변수가 예상과 다르게 동작하면 값을 사용하는 시점, ARG의 선언 범위, 변수를 확장하는 주체를 순서대로 확인하세요. --build-arg는 빌드에 값을 전달하고, docker run -e는 실행할 컨테이너의 환경변수를 설정합니다. 실행 환경변수를 바꿔도 빌드 중에 만들어 둔 파일이나 바이너리가 다시 생성되지는 않습니다.
먼저 값이 필요한 단계를 구분하기
| 설정 | 적용 단계 | 컨테이너 실행 시 의미 |
|---|---|---|
ARG NAME=value | 선언된 범위의 빌드 단계 | ARG 자체는 실행 환경변수로 자동 전달되지 않음 |
ENV NAME=value | 선언 이후 빌드 단계와 이미지 설정 | 컨테이너 환경변수의 기본값이 됨 |
docker build --build-arg NAME=value | Dockerfile에 선언한 ARG에 값 전달 | 이미지에 반영되는 방식은 Dockerfile의 사용처에 달림 |
docker run -e NAME=value | 새 컨테이너 실행 | 같은 이름의 이미지 ENV 기본값을 덮어씀 |
예를 들어 프런트엔드 번들 생성 명령이 빌드 중에 API 주소를 읽어 파일에 넣었다면, 실행할 때 같은 이름의 환경변수를 바꿔도 그 파일 내용은 바뀌지 않습니다. 실행 시 설정을 바꾸려면 애플리케이션이나 시작 스크립트가 실행 환경변수를 읽도록 설계되어 있어야 합니다.
진단 순서: 전달한 값부터 실제 사용처까지 따라가기
- 실제로 사용한 빌드·실행 명령을 확인합니다. 빌드 값은
--build-arg NAME=value, 실행 값은-e NAME=value로 명시해 비교합니다. 호스트 셸에 변수가 있다는 사실만으로 Dockerfile에 자동 전달되지는 않습니다. - ARG 선언 위치를 확인합니다. 값을 사용하는 빌드 단계에 ARG가 선언되어 있어야 합니다. 특히
FROM이전 ARG와 단계 내부 ARG를 구분합니다. - ARG가 어디에 반영되는지 찾습니다. RUN 명령의 입력인지, ENV에 복사되는지, 생성 파일에 들어가는지에 따라 실행 시 바꿀 수 있는 범위가 달라집니다.
- 동일한 이름의 ENV가 있는지 확인합니다. 같은 이름의 ARG와 ENV가 겹치면 이후 RUN에서 ENV 값이 사용될 수 있습니다. 두 설정을 같은 것으로 취급하지 마세요.
- 프로세스가 값을 읽는 방법을 확인합니다. 셸이 변수를 확장하는지, 애플리케이션이 환경변수를 직접 읽는지, 설정 파일이 우선하는지 확인합니다.
재현 예제: 빌드 결과와 실행 환경을 따로 출력하기
Docker CLI와 실행 중인 Docker 데몬이 필요합니다. 아래 예제는 Alpine 이미지를 사용하므로 로컬에 이미지가 없다면 가져올 수 있는 네트워크 환경도 필요합니다. 빈 디렉터리에 다음 내용을 Dockerfile로 저장합니다.
FROM alpine:3
ARG BUILD_LABEL=dev
ENV APP_MODE=${BUILD_LABEL}
RUN printf '%s\n' "$BUILD_LABEL" > /build-label.txt
CMD ["sh", "-c", "printf 'built=%s runtime=%s arg=%s\\n' \"$(cat /build-label.txt)\" \"$APP_MODE\" \"${BUILD_LABEL:-unset}\""]BUILD_LABEL은 빌드 인자입니다. 이 값을 파일에 기록하고, ENV를 통해 APP_MODE의 기본값으로도 저장합니다. CMD는 컨테이너가 시작될 때 파일 내용과 환경변수를 출력합니다.
docker build --build-arg BUILD_LABEL=staging -t arg-env-demo .
docker run --rm arg-env-demo
docker run --rm -e APP_MODE=production arg-env-demo위 Dockerfile과 명령에서 기대하는 출력은 각각 다음과 같습니다.
built=staging runtime=staging arg=unset
built=staging runtime=production arg=unsetbuilt=staging: 빌드 중 생성한 파일의 내용입니다. 실행 환경변수를 바꿔도 유지됩니다.runtime=production: 두 번째 실행에서는-e가 이미지의 APP_MODE 기본값을 덮어씁니다.arg=unset: ARG 선언만으로 BUILD_LABEL이 컨테이너 환경에 남지는 않습니다.
실행 시 BUILD_LABEL도 직접 지정하면 차이가 더 분명해집니다.
docker run --rm -e BUILD_LABEL=runtime-only arg-env-demo기대 출력은 built=staging runtime=staging arg=runtime-only입니다. 실행 환경에 같은 이름의 변수를 추가했지만, 파일과 APP_MODE 기본값은 그대로입니다. ENV APP_MODE=${BUILD_LABEL}은 빌드 시 값을 저장하는 동작이며 두 변수 사이의 지속적인 연결을 만들지 않습니다.
ARG 범위와 CMD 확장에서 자주 놓치는 부분
FROM 이전 ARG는 단계 내부에서 다시 선언하기
ARG BASE_TAG=3
FROM alpine:${BASE_TAG}
ARG BASE_TAG
RUN printf 'base=%s\n' "$BASE_TAG"첫 ARG는 FROM의 이미지 태그를 정하는 데 사용합니다. 단계 내부 RUN에서도 그 값을 쓰려면 FROM 뒤에서 ARG를 다시 선언해야 합니다. 다단계 빌드에서도 별도의 FROM으로 시작한 독립 단계에 ARG가 자동으로 공유된다고 가정하면 안 됩니다. 값을 쓰는 각 단계의 선언과 상속 관계를 확인하세요.
exec 형식의 CMD는 셸 변수 확장을 수행하지 않음
CMD ["echo", "$APP_MODE"]이 명령은 셸을 거치지 않으므로 $APP_MODE라는 문자열을 그대로 출력합니다. 앞의 재현 예제는 sh -c를 명시했기 때문에 실행 시 환경변수가 확장됩니다. 실제 서비스에서는 애플리케이션이 환경변수를 직접 읽게 하면 별도의 셸 확장이 필요하지 않습니다.
설정을 바꾸기 전에 남겨야 할 확인 기준
먼저 예제처럼 빌드로 고정된 결과와 실행 시 읽는 값을 따로 관찰하세요. 빌드 결과가 달라져야 한다면 이미지를 다시 빌드하고, 실행 환경만 달라져야 한다면 새 컨테이너를 생성해 확인합니다. 출력이 예상과 다르면 애플리케이션의 설정 우선순위와 시작 스크립트까지 이어서 확인하면 됩니다.
단, 비밀번호나 토큰으로 이 진단을 수행하지 마세요. ARG는 비밀 저장소가 아니며 사용 방식에 따라 빌드 기록이나 결과물 등에 값이 남을 수 있습니다. ENV도 이미지 설정에 저장되므로 비밀값을 이미지에 넣는 용도로 사용하지 않아야 합니다.