기술 · 실용 가이드
Docker exec format error: 다시 빌드하기 전에 이미지·바이너리·진입점 확인
exec format error는 이미지 태그만으로 진단할 수 없다. 캐시 삭제, QEMU 추가, 재빌드 전에 선택된 매니페스트와 최종 파일시스템, 실행 파일 아키텍처, 진입점 인터프리터를 추적한다.

Linux 컨테이너의 ‘exec format error’는 커널이 전달받은 파일을 실행하지 못했다는 뜻이다. CPU 아키텍처는 가능한 원인 중 하나이며 유효한 인터프리터 헤더가 없는 스크립트나 다른 잘못된 실행 형식도 원인이 될 수 있다. 먼저 정확한 파일과 실패 단계를 확인한 뒤 수정 방법을 선택한다.
실패한 이미지와 오류 출력을 보존한다. 유용한 장애 기록은 실행 호스트, 선택된 이미지 매니페스트, 최종 파일시스템의 실제 실행 파일, 이를 시작하려던 명령이라는 네 연결 고리를 따른다. 아키텍처 라벨 일치만으로 이 네 가지 확인을 대신할 수 없다.
1. 재빌드 전에 실패한 실행 기록하기
전체 오류, 이미지 참조와 다이제스트, 존재하는 컨테이너 ID, 요청 플랫폼, 사용한 Docker context를 저장한다. Dockerfile만 보지 말고 실패한 컨테이너의 설정과 마운트를 검사한다. 실행 시 진입점 재정의나 마운트된 스크립트가 실제 실행 파일을 바꿀 수 있다. docker container inspect CONTAINER로 설정을, docker image inspect IMAGE로 로컬 이미지 메타데이터를 확인한다.
관련 호스트는 실제 프로세스를 실행하는 Docker 데몬이나 빌드 워커다. 원격일 수도, Docker Desktop의 Linux VM 내부일 수도 있으므로 노트북 CPU만으로 판단하지 않는다. Dockerfile의 RUN 단계 실패와 완성된 컨테이너의 시작 실패도 구분한다. RUN 오류가 /bin/sh를 지목하면 빌드 단계 환경을, 앱 진입점이 실패하면 최종 런타임 파일시스템을 조사한다.
2. 레지스트리 변형과 로컬 이미지 비교하기
docker buildx imagetools inspect IMAGE_REFERENCE로 레지스트리 참조의 매니페스트와 플랫폼별 변형을 확인하고, 정확한 로컬 이미지 및 실패한 실행이 요청한 플랫폼과 비교한다. 변경 가능한 태그는 실패 이후 새 이미지를 가리킬 수 있다. 현재 태그를 과거 증거로 취급하지 말고 원래 다이제스트를 보존한다.
필요한 플랫폼 변형이 없으면 올바른 변형을 선택하거나 빌드한다. 존재하지만 다른 변형이 선택됐다면 명시적 플랫폼 지정과 배포 설정을 조사한다. --platform=linux/amd64는 대상을 선택할 뿐, 이미 복사된 임의의 arm64 파일을 변환하지 않는다. 호환 매니페스트가 없으면 실행 이전 선택 단계에서 오류가 날 수 있으며 이는 ENOEXEC와 다른 단계다.
3. 실패 명령을 시작하지 않고 파일 검사하기
중지된 실패 컨테이너가 남아 있으면 docker cp CONTAINER:/path/to/file ./artifact-to-inspect로 의심 파일을 복사할 수 있다. Docker 문서는 중지된 컨테이너에서 복사하는 기능을 지원한다. 이미지만 있다면 docker create IMAGE로 시작하지 않는 컨테이너를 만들고 반환된 ID를 기록해 검사한다. 실행 시 제공된 파일은 이미지 내용과 다를 수 있으므로 마운트는 별도로 확인한다.
복사한 파일은 file 같은 신뢰할 수 있는 검사 도구로 분석한다. ELF 바이너리는 readelf -h로 머신 유형을, readelf -l로 요구하는 프로그램 인터프리터를 확인할 수 있다. 아키텍처를 알아보겠다고 파일을 실행하지 않는다. 로컬에 도구가 없다면 기존의 신뢰할 수 있는 분석 환경을 사용한다. 최소 구성 또는 distroless 이미지에 shell, file, readelf가 있다고 가정하지 않는다.
4. 복사된 바이너리를 빌드 단계까지 추적하기
최종 이미지로 복사되는 실행 파일을 생성하거나 다운로드하는 모든 단계를 나열한다. BUILDPLATFORM은 BuildKit 워커를, TARGETPLATFORM은 요청한 출력을 나타낸다. 빌더 플랫폼에 고정된 단계에서 네이티브 컴파일러를 실행할 수 있지만 컴파일 설정은 여전히 대상용 결과물을 만들어야 한다.
Docker의 사전 정의 대상 인수는 전역에서 사용 가능하지만 단계 안에서 사용하려면 ARG로 선언해야 한다. TARGETOS, TARGETARCH와 필요한 TARGETVARIANT를 확인한 뒤 언어 도구 체인의 실제 결과물도 확인한다. 인수 선언만으로 모든 컴파일러가 자동 교차 컴파일하는 것은 아니다. 최종 FROM을 대상 호환 기반 이미지로 바꾸어도 다른 단계에서 복사한 비호환 실행 파일이 변환되지 않는다.
5. 스크립트·인터프리터 누락·권한 분리하기
직접 실행하는 스크립트는 첫 바이트와 인터프리터 줄을 검사한다. #! 헤더가 의도한 인터프리터를 가리키고 그 인터프리터가 런타임 파일시스템에 존재해야 한다. 헤더 앞의 예상치 못한 바이트와 Windows 방식 줄바꿈도 확인한다. 명시적으로 shell을 거치면 실행 방식이 바뀌므로 원래 직접 실행 경로가 고쳐졌다는 증거는 아니다.
정확한 오류 범주를 유지한다. Linux에서 인식 불가능한 형식이나 잘못된 아키텍처는 ENOEXEC, 경로나 스크립트·ELF 인터프리터 누락은 ENOENT, 실행 권한 거부나 noexec 마운트 같은 권한 문제는 EACCES다. 파일이 보여도 인터프리터가 없으면 실패할 수 있다. chmod는 잘못된 CPU 아키텍처를 고치지 않으며 에뮬레이션 추가가 누락된 인터프리터를 설치하지도 않는다.
6. 원인 하나를 고친 뒤 원래 경로 검증하기
증거에 맞는 분기를 선택한다. 변형 누락이면 이미지 선택이나 빌드 출력, 내부 바이너리 오류면 해당 결과물 재빌드·교체, 스크립트 형식 문제면 헤더·인코딩, 인터프리터 누락이면 필요한 호환 런타임, 권한 거부면 사용자·파일 모드·마운트 정책을 확인한다. 이미지 정리로 원인 분류를 대신하지 않는다.
에뮬레이션은 비네이티브 실행을 의도적으로 선택할 때의 대안이다. 호스트 전체 binfmt 등록을 바꾸기 전에 실제 워커의 지원을 확인한다. Docker Desktop과 공식 BuildKit에 이미 지원이 포함될 수 있다. 수정 후 일회용 비운영 환경에서 원래 의도한 명령, 플랫폼, 사용자와 관련 마운트로 시험한다. 새 다이제스트와 달라진 확인 항목을 기록한다. 여전히 실패하면 새 오류를 보존하고 해당 계층부터 이어가며 같은 재빌드를 반복하지 않는다.
