빌드노트
Docker export/import 뒤 파일은 남고 CMD·ENV는 사라지는 이유
Docker export/import 뒤 파일은 남았는데 기본 명령과 환경변수가 사라졌다면 이미지 Config를 비교한다. 합성 이미지의 CMD·ENV·WORKDIR와 컨테이너 추가 파일을 save/load 결과와 대조해 파일 추출과 이미지 보존 중 필요한 방식을 고르는 기준을 설명한다.
컨테이너를 docker export로 내보낸 뒤 docker import로 이미지를 만들었는데, 실행하면 no command specified가 나거나 환경변수와 작업 디렉터리가 달라질 수 있다. export가 담는 것은 컨테이너의 파일시스템이다. CMD·ENV·WORKDIR 같은 이미지의 실행 기본값까지 옮기려면 save/load를 사용한다. tar 파일이라는 확장자만 같을 뿐, 보존하는 대상이 다르다. Docker export 문서 (새 탭에서 열림), Docker save 문서 (새 탭에서 열림)
파일보다 먼저 이미지 설정을 확인한다
원본 이미지와 import한 이미지가 모두 남아 있다면 다음 조회로 실행 설정이 유지됐는지 비교한다. Docker의 Linux 컨테이너 엔진에 연결된 PowerShell에서 실행하는 예다. $before와 $after에는 자신의 원본·import 결과 이미지 이름을 넣는다.
$before = 'myrepo/demo:original'
$after = 'myrepo/demo:imported'
docker image inspect --format 'CMD={{json .Config.Cmd}} ENV={{json .Config.Env}} WORKDIR={{json .Config.WorkingDir}}' $before $after
별도 실험에서는 BusyBox 1.37.0을 바탕으로 ENV LAB_MESSAGE=from-image, WORKDIR /lab, CMD ["/bin/sh", "/lab/report.sh"]를 지정한 작은 이미지를 만들었다. /lab/message.txt에는 from-image를 넣었다. 이 이미지에서 만든 컨테이너를 export/import한 뒤 위 조회를 실행했다. 출력의 첫 줄이 원본, 두 번째 줄이 import 결과다.
CMD=["/bin/sh","/lab/report.sh"] ENV=["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","LAB_MESSAGE=from-image"] WORKDIR="/lab"
CMD=null ENV=null WORKDIR=""
import 결과에는 기본 명령도, 이미지에 선언했던 환경변수도 없었다. 작업 디렉터리 설정도 비어 있었다. 이 상태에서 명령을 생략하고 docker container create를 실행하자 종료 코드 1과 Error response from daemon: no command specified가 나왔다.
반면 /bin/sh로 같은 합성 스크립트를 직접 지정해 실행하면 /lab/message.txt는 여전히 from-image로 읽혔다. 그때 작업 디렉터리는 /, LAB_MESSAGE는 미설정이었다. 파일이 남아 있는 것과 그 파일을 어떤 명령·환경·위치에서 실행할지 기억하는 것은 별개였다. Dockerfile의 CMD와 ENV, WORKDIR는 이런 실행 기본값을 설정한다. Dockerfile CMD (새 탭에서 열림), ENV (새 탭에서 열림), WORKDIR (새 탭에서 열림)
자신의 출력에서도 원본에 있던 설정이 import 결과에서 비었다면 이 차이가 실행 실패를 설명할 수 있다. 조회 자체가 실패하면 먼저 이미지 이름과 Docker 연결 대상을 확인한다. 세 설정이 모두 같은데 증상이 다르면 실제 실행 옵션 등 다른 차이를 확인해야 한다. 원본 이미지도 정의 파일도 없다면 export한 tar만으로 원래 값을 알아냈다고 볼 수 없다.
같은 실행 기본값이 필요하면 save/load를 쓴다
이미지를 다른 Docker 환경에서도 같은 기본 명령으로 실행하려면 원본 이미지를 save한다. 앞에서 지정한 $before가 대상이다. 덮어쓸 파일이 없는 작업 폴더에서 첫 명령을 실행하고, 생성된 image.tar를 대상 머신으로 옮겨 두 번째 명령을 실행한다. 각 명령이 성공한 것을 확인한 뒤 다음 단계로 간다.
docker image save --output ./image.tar $before
docker image load --input ./image.tar
save는 지정한 이미지의 레이어와 태그를 아카이브로 만들고, load는 그 이미지와 태그를 읽어 들인다. load의 성공 문구를 확인한 뒤, 위 inspect 조회를 대상 머신에서 원본 태그에 실행해 CMD·ENV·WORKDIR를 대조한다. Docker save (새 탭에서 열림), Docker load (새 탭에서 열림)
이번 실험에서는 같은 엔진에서 실습용 원본 컨테이너와 이미지 태그를 제거한 다음 load했다. load 뒤에는 원본 이미지의 전체 Config와 RootFS가 같았고, 기본 명령으로 실행한 스크립트도 작업 디렉터리 /lab, 환경변수 from-image, 파일 내용 from-image를 출력했다. 단순히 기존 이미지가 남아 있어서 정상으로 보인 결과는 아니었다.
다만 save의 입력은 이미지다. 실험 중 컨테이너에만 추가한 /lab/runtime-only.txt는 load한 이미지에는 없었다. 컨테이너에서 바뀐 파일까지 이미지 백업에 자동으로 들어간다고 생각하면 안 된다. 컨테이너 생성 때 별도로 준 환경변수·포트·마운트 설정도 이미지 이동과 함께 다시 준비해야 한다.
현재 컨테이너의 파일이 필요하면 export를 고른다
컨테이너 파일시스템 자체를 꺼내려는 목적이라면 export가 맞다. 다음 첫 명령은 $sourceContainer에 넣은 기존 컨테이너의 파일을 저장한다. 두 번째 명령은 그 파일시스템으로 새 이미지를 만들 필요가 있을 때만 실행한다. $after에는 기존 작업과 겹치지 않는 새 태그를 쓰고, filesystem.tar를 덮어쓰지 않을 위치에서 실행한다.
$sourceContainer = 'my-demo-container'
docker container export --output ./filesystem.tar $sourceContainer
docker image import ./filesystem.tar $after
앞의 합성 컨테이너에만 추가한 파일은 이 경로를 거치자 from-container라는 내용으로 남았다. 원래 이미지의 message.txt도 함께 남았지만 실행 기본값은 빠졌다. 이미지 보존과 현재 컨테이너의 파일 추출 중 무엇이 필요한지에 따라 명령을 고를 이유가 여기에 있다.
이미 export한 tar만 남았고 새 이미지를 만들어야 한다면, 알고 있는 설정을 직접 지정할 수는 있다. import의 --change는 CMD·ENV·WORKDIR 같은 메타데이터 설정을 지원한다. 별도 비교에서 세 값을 다시 지정하니 기본 명령으로 실행됐고 /lab과 LAB_MESSAGE=from-image도 적용됐다. 이는 알고 있던 값을 새 이미지에 넣은 결과다. 잃어버린 설정이나 원래 이미지의 레이어 이력을 되찾은 것은 아니다. 원래 Dockerfile이 있다면 그 정의로 다시 빌드하는 방법도 고려한다. Docker import의 --change (새 탭에서 열림)
볼륨은 이 실험의 파일 비교에 포함하지 않았다. 공식 export 문서에 따르면 연결된 볼륨 내용은 내보내지 않으며, 볼륨이 기존 디렉터리를 가리면 그 아래 컨테이너 디렉터리의 내용이 대상이 된다. 볼륨 데이터 보관은 별도로 계획해야 한다. Docker export의 포함 범위 (새 탭에서 열림)
직접 확인한 환경은 Windows 11의 Docker Desktop 4.92.0, Docker CLI·Engine 29.8.0, Linux/amd64 한 플랫폼과 overlay2 저장소다. 서로 다른 머신·이미지 저장소 간 이동, 다른 아키텍처의 실행과 다중 플랫폼 아카이브는 검증하지 않았다. 공식 문서상 save는 로컬 저장소에 존재하는 플랫폼 변형을 대상으로 하고, load는 아카이브에 들어 있는 변형을 읽는다. 문서의 --platform 선택 옵션은 API 1.48 이상 조건이다. 이 단일 플랫폼 관찰을 다른 플랫폼 실행 보장으로 확대할 수는 없다. save의 플랫폼 범위 (새 탭에서 열림), load의 플랫폼 범위 (새 탭에서 열림)