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

빌드노트

같은 컨테이너를 다시 시작했는데 tmpfs 파일이 없다

컨테이너를 삭제하지 않고 stop/start해도 tmpfs 파일은 사라진다. 같은 ID에서 tmpfs와 writable layer 파일을 비교하고, Mounts와 HostConfig.Tmpfs로 실제 저장 위치를 확인해 임시 데이터와 재시작 뒤 필요한 데이터를 나누는 기준을 설명한다.

Docker 컨테이너를 삭제하지 않고 stop 뒤 start만 했는데 파일이 사라졌다. 저장한 경로가 tmpfs라면 컨테이너 ID가 같아도 일어나는 동작이다. tmpfs의 파일은 컨테이너가 중지될 때 사라진다. 먼저 그 파일의 경로에 어떤 마운트가 연결돼 있는지 확인한다.

Docker의 tmpfs 문서 (새 탭에서 열림)는 tmpfs를 컨테이너의 writable layer 밖에 두는 임시 저장소로 설명한다. writable layer는 마운트되지 않은 일반 경로에 파일을 쓸 때 사용하는 컨테이너별 쓰기 영역이다. 같은 컨테이너 안에서 읽히는 파일이라도 이 두 곳의 수명은 다르다.

파일 경로와 마운트 목적지를 맞춰 본다

다음 명령은 조사할 컨테이너의 ID와 마운트 설정을 읽는다. Docker CLI가 연결된 PowerShell에서 $container에 실제 이름이나 ID를 넣는다. 파일이 사라진 컨테이너가 중지된 상태여도 설정 조회는 가능하다. 명령이 실패하면 출력 대신 Docker context와 대상 이름부터 확인한다.

$container = 'my-container'
docker container inspect --format 'id={{.Id}} status={{.State.Status}}' $container
docker container inspect --format '{{json .Mounts}}' $container
docker container inspect --format '{{json .HostConfig.Tmpfs}}' $container

--format으로 필요한 필드를 고르는 방법은 container inspect 문서 (새 탭에서 열림)에 나와 있다. 재시작 전 ID를 기록해 뒀다면 첫 줄과 비교한다. 이름은 재사용할 수 있으므로 이름이 같다는 사실만으로 같은 컨테이너라고 판단하지 않는다.

이 글의 별도 실험에서는 /scratch/marker.txt를 tmpfs에 썼다. --mount type=tmpfs,dst=/scratch,tmpfs-size=1048576으로 만든 컨테이너의 두 번째 조회 결과는 다음과 같았다.

[{"Type":"tmpfs","Source":"","Destination":"/scratch","Mode":"","RW":true,"Propagation":""}]

Type이 tmpfs이고 Destination이 /scratch이므로 그 아래 marker.txt도 tmpfs에 저장된다. Source가 빈 것은 연결이 실패했다는 뜻이 아니다. tmpfs는 volume이나 bind처럼 연결할 원본 경로를 지정하지 않는다. 실제 파일이 /scratch/jobs/result.txt라면 /scratch 같은 상위 디렉터리의 마운트까지 찾아야 한다. 마운트가 중첩돼 있으면 파일 경로에 가장 구체적으로 적용되는 항목을 본다.

Mounts가 빈 배열이라고 곧바로 writable layer로 결론 내리지는 않는다. 같은 Linux Engine 29.8.0에서 --tmpfs /scratch:size=1m으로 만든 별도 컨테이너는 두 번째와 세 번째 조회가 각각 이렇게 나왔다.

[]
{"/scratch":"size=1m"}

이 경우 /scratch 설정은 HostConfig.Tmpfs에서 확인됐다. 두 생성 방식 모두 컨테이너 안의 /proc/mounts에서도 /scratch의 파일시스템 종류가 tmpfs인 것을 확인했다. 사용하는 버전에서 출력 구성이 다르다면 두 설정 필드와 실제 경로를 함께 확인한다. 파일 경로가 심볼릭 링크라면 링크가 가리키는 위치도 확인해야 한다.

ID와 설정은 남았지만 파일은 남지 않았다

2026년 10월 23일, Windows 11의 Docker Desktop 4.92.0에서 Linux/amd64 Engine 29.8.0으로 비교했다. 컨테이너는 Alpine 3.22.7이었고, 위 PowerShell 조회도 실제 실행했다. Windows 호스트에서 Linux 컨테이너를 실행한 결과이며 Windows 컨테이너를 시험한 것은 아니다.

새 컨테이너의 /scratch에만 tmpfs를 연결하고, 다른 마운트가 없는 /layer를 대조 경로로 뒀다. 두 파일이 처음에는 없는 것을 확인한 뒤 /scratch/marker.txt에는 tmpfs-marker, /layer/marker.txt에는 layer-marker와 줄바꿈을 각각 기록했다. 기록 직후 읽은 내용은 다음과 같았다.

/scratch/marker.txt=tmpfs-marker
/layer/marker.txt=layer-marker

이 컨테이너에 docker stop --timeout 2를 실행하고, 종료 상태를 확인한 다음 docker start로 다시 시작했다. 파일을 다시 만드는 시작 스크립트는 없었다. 같은 두 경로를 읽자 결과가 달라졌다.

/scratch/marker.txt=MISSING
/layer/marker.txt=layer-marker

MISSING은 실험의 조회 코드가 파일이 없을 때 출력한 표시다. 전체 ID를 비교했으며 중지 전후 모두 b4adea8ad5fb로 시작하는 같은 컨테이너였다. Mounts의 /scratch 항목도 앞의 JSON과 같았다. 마운트 설정이 유지됐다는 사실은 그 안의 파일까지 유지됐다는 뜻이 아니었다. writable layer의 파일은 내용 그대로 남았다.

--tmpfs 방식으로 만든 대조 컨테이너에서도 자신의 ID를 유지한 stop/start 뒤 같은 파일 차이를 관찰했다. 이 비교에는 앱의 파일 정리나 볼륨 교체가 없었다. 다만 실제 앱은 기동할 때 파일을 다시 만들 수 있으므로, 자기 서비스에서 파일이 보인다는 사실만으로 tmpfs 내용이 살아남았다고 판단할 수는 없다.

재시작 뒤에도 필요한 파일은 어디에 둘까

다시 계산하거나 외부 원본에서 다시 받을 수 있는 중간 파일이라면 tmpfs를 유지할 수 있다. 대신 앱은 시작할 때 그 경로가 비어 있어도 동작해야 한다. 업로드 원본이나 처리 완료 기록처럼 재시작 뒤에도 필요하다면 tmpfs에만 저장하는 구성을 바꿔야 한다.

Docker 저장소 문서 (새 탭에서 열림)에 따르면 volume은 컨테이너가 삭제돼도 데이터를 보존하며, bind mount는 지정한 호스트 경로를 연결한다. Docker가 관리할 데이터라면 volume을, 호스트에서도 직접 다룰 경로라면 bind mount를 검토한다. 앱의 임시 경로와 영속 데이터 경로를 나누고, 영속 경로가 실제로 해당 저장소에 연결됐는지 inspect로 확인한다.

이번에 /layer/marker.txt가 남았다고 해서 writable layer를 장기 저장소로 고르면 안 된다. 같은 컨테이너의 stop/start에서 남은 결과이며, 컨테이너를 삭제하면 그 쓰기 영역도 보존되지 않는다. 저장소 전환에는 실행 설정 변경과 컨테이너 재생성이 필요하므로, 아직 남아 있는 데이터의 이전 계획을 먼저 잡는다. 이미 사라진 tmpfs 파일이 새 volume을 연결하는 것만으로 돌아오지는 않는다.

이 실험은 소량의 합성 텍스트 파일과 수동 stop/start에 한정된다. DB의 저장·복구, 호스트 재부팅, 다른 Linux 엔진 버전은 검증하지 않았다. 실제 경로가 volume이나 bind로 확인된다면 이 글의 tmpfs 수명만으로 파일 소실을 설명할 수 없으므로, 연결한 저장소가 바뀌었는지와 앱이 파일을 지우는지부터 조사한다.

← 전체 글 보기