빌드노트
Docker 이미지의 설정 파일이 안 보이면 bind mount부터 확인한다
이미지에 넣은 설정 파일이 컨테이너에서 안 보일 때 bind mount의 목적지부터 확인한다. 같은 이미지에서 마운트 없음, 빈 폴더의 설정 경로 마운트, 데이터 경로 마운트를 실제 비교하고 마운트 범위를 줄일 기준을 설명한다.
Docker 이미지에 /app/conf/app.properties를 넣었는데 실행한 컨테이너에서는 파일이 없거나 다른 내용이 보인다. 이때는 이미지를 다시 만들기 전에 그 파일이나 상위 디렉터리에 bind mount가 걸렸는지 확인한다. 빈 호스트 폴더를 /app/conf에 연결하면 이미지에 있던 설정 파일도 그 경로에서는 보이지 않는다.
bind mount는 호스트의 파일이나 폴더를 컨테이너 경로에 연결한다. Docker 공식 문서 (새 탭에서 열림)는 기존 내용이 있는 곳에 마운트하면 그 내용이 가려진다고 설명한다. 이미지의 파일이 삭제됐다는 뜻은 아니다. 같은 이미지로 새 컨테이너를 만들어도 같은 빈 폴더를 같은 위치에 연결하면 증상이 반복될 수 있다.
같은 이미지에서 설정이 보이는 조건을 비교했다
운영 장애 사례가 아니라 합성 설정 파일 하나로 만든 실습이다. app.properties에 origin=image 한 줄을 넣고, 아래 Dockerfile로 이미지의 /app/conf/app.properties에 복사했다. COPY 문서 (새 탭에서 열림)의 빌드 컨텍스트 파일 복사에 해당한다.
FROM busybox:1.37.0
COPY app.properties /app/conf/app.properties
CMD ["sh", "-c", "set -eu; printf 'FILES\n'; ls -A /app/conf; printf 'CONFIG\n'; if [ -f /app/conf/app.properties ]; then cat /app/conf/app.properties; else printf 'MISSING\n'; fi"]
하나의 이미지로 컨테이너 세 개를 만들었다. 첫 번째에는 마운트가 없고, 두 번째에는 새 빈 호스트 폴더를 /app/conf에 연결했다. 세 번째에는 같은 폴더를 /app/data에 연결했다. 세 컨테이너의 이미지 ID가 같은지도 별도로 확인했다.
마운트가 없을 때와 /app/data에 연결했을 때의 표준 출력은 같았다.
FILES
app.properties
CONFIG
origin=image
빈 폴더를 /app/conf에 연결한 컨테이너만 다음처럼 출력했다. FILES와 CONFIG 사이에 파일 이름이 없고, 설정 파일 검사에서는 MISSING이 나왔다.
FILES
CONFIG
MISSING
이 차이는 이미지 재빌드 없이 생겼다. 설정 경로와 겹치는 마운트에서는 파일이 가려졌고, 마운트 대상을 설정 경로 밖으로 옮긴 새 컨테이너에서는 다시 보였다. 이번 관찰은 이미지에 파일을 넣었는지와 실행할 때 그 파일을 볼 수 있는지가 별개의 확인이라는 근거다.
Mounts의 목적지가 설정 경로와 겹치는지 본다
컨테이너를 관리하는 Docker context에서 다음 명령을 실행한다. my-app은 조사할 컨테이너 이름으로 바꾼다. 종료된 컨테이너도 삭제하지 않았다면 조회할 수 있다. container inspect 문서 (새 탭에서 열림)의 --format으로 마운트 목록만 출력하는 명령이다.
docker container inspect --format '{{json .Mounts}}' my-app
먼저 Type이 bind인 항목의 Destination을 읽는다. 설정 파일이 /app/conf/app.properties라면 /app/conf나 /app 같은 상위 경로의 마운트도 확인 대상이다. 파일 자체를 연결했다면 Destination이 설정 파일 경로와 같을 수 있다. Source는 그 자리에 연결한 호스트 경로이므로, 실제로 필요한 설정이 그쪽에 있는지 확인한다.
실습의 마운트 없는 컨테이너에서는 Mounts가 []였다. 문제 조건에서는 Type이 bind, Destination이 /app/conf, RW가 false였다. 세 번째 컨테이너는 같은 Source를 사용하면서 Destination만 /app/data였다.
읽기 전용 마운트여도 기존 내용은 가려진다. RW: false와 readonly는 컨테이너가 연결한 호스트 경로에 쓰지 못하게 한다는 뜻이다. 이미지의 기본 파일을 함께 보여 주는 옵션으로 해석하면 안 된다. 읽기 전용 bind mount 설명 (새 탭에서 열림)
Source의 호스트는 Docker daemon이 실행되는 쪽이다. 원격 daemon을 쓰면서 내 PC의 같은 이름 폴더만 확인하면 다른 곳을 조사할 수 있다. 아래 재현 명령은 Windows의 로컬 Docker Desktop이 호스트 경로를 공유하는 경우를 전제로 한다.
직접 비교하려면 새 설정 파일과 빈 폴더만 사용한다
Windows PowerShell, 실행 중인 Docker Desktop Linux 엔진과 desktop-linux context가 필요하다. 처음에는 BusyBox 이미지를 받을 수 있어야 한다. 새 실습 폴더를 만들고, 그 안에 위 Dockerfile과 origin=image 한 줄을 담은 app.properties를 저장한다. 앱 소스나 실제 설정 파일은 필요 없다.
그 폴더에서 다음 명령을 하나씩 실행하고, Docker 명령이 종료 코드 0일 때만 다음으로 진행한다. 이미지 빌드 뒤 얻은 ID를 세 실행에 공통으로 사용한다. --rm을 생략한 것은 종료 후에도 Mounts를 비교하기 위해서다.
$prefix = 'bind-conf-' + [guid]::NewGuid().ToString('N')
$image = $prefix + ':lab'
$empty = Join-Path (Get-Location).Path ($prefix + '-empty')
New-Item -ItemType Directory -Path $empty | Out-Null
docker --context desktop-linux build --network none --tag $image .
$imageId = docker --context desktop-linux image inspect --format '{{.Id}}' $image
docker --context desktop-linux run --name "$prefix-none" --network none --read-only $imageId
docker --context desktop-linux run --name "$prefix-conf" --network none --read-only --mount "type=bind,source=$empty,target=/app/conf,readonly" $imageId
docker --context desktop-linux run --name "$prefix-data" --network none --read-only --mount "type=bind,source=$empty,target=/app/data,readonly" $imageId
docker --context desktop-linux container inspect --format '{{json .Mounts}}' "$prefix-none" "$prefix-conf" "$prefix-data"
파일 출력은 앞의 두 결과와 비교한다. 첫 번째 컨테이너에서도 파일이 없다면 마운트에 의한 가림을 판단할 기준이 성립하지 않는다. 빌드 입력, COPY의 목적지와 실제 실행 이미지부터 확인한다. bind source path does not exist는 파일을 가린 결과가 아니라 소스 경로 연결에 실패한 것이다. 이 예제는 폴더를 먼저 만들고 --mount를 사용한다. 기본 --mount는 없는 소스 경로에서 오류를 내지만 -v는 디렉터리를 만들 수 있다는 문법 차이 (새 탭에서 열림)가 있기 때문이다.
확인이 끝나면 같은 PowerShell 창에서 이 실습이 만든 컨테이너와 이미지 이름만 정리한다.
docker --context desktop-linux container rm "$prefix-none" "$prefix-conf" "$prefix-data"
docker --context desktop-linux image rm $image
재빌드보다 먼저 고칠 실행 설정
마운트가 없을 때 파일이 보이고 설정 경로를 마운트했을 때만 사라진다면, 실행 설정의 마운트 목적지를 먼저 고친다. 이미지의 기본 설정을 사용할 목적이면 해당 경로의 마운트를 제거한다. 호스트 데이터 폴더만 필요하면 /app 전체 대신 /app/data처럼 설정과 겹치지 않는 경로로 범위를 줄인다. 앱도 그 데이터 경로를 쓰도록 맞춰야 한다. 호스트 설정으로 대체하려는 목적이었다면 올바른 파일을 Source 쪽에 준비한다.
변경한 구성으로 컨테이너를 새로 만들어 다시 확인한다. 단순 재시작은 마운트 구성을 바꾸지 않는다. Docker 문서도 가려진 내용을 확인하는 방법으로 해당 마운트 없이 컨테이너를 다시 만드는 방식을 제시한다.
2026년 10월 21일 KST에 Windows 11, Docker Desktop 4.92.0의 Linux 엔진 29.8.0, BusyBox 1.37.0으로 세 조건을 실행했다. 본문의 PowerShell 순서도 새 실습 폴더에서 별도로 실행해 파일 출력과 마운트 정보를 대조했다. 앞선 첫 시도는 Docker 엔진 연결 실패로 빌드 전에 멈췄으며 그 실패를 파일 가림의 증거로 사용하지 않았다.
확인한 것은 빈 디렉터리 bind mount가 합성 설정 파일을 가리는 동작이다. 호스트의 다른 설정 파일로 대체하는 경우, Compose 구성, 원격 daemon과 앱의 설정 우선순위는 실행하지 않았다. 파일 내용은 정상인데 앱이 다른 설정을 적용한다면, 이 실습만으로 그 원인까지 확정할 수 없다.