Файл в образе есть, но запуск даёт no such file: загрузчик и библиотеки после multi-stage
Стадия сборки завершилась успешно, COPY --from перенёс /out/app, но контейнер сразу падает с no such file or directory. Повторная проверка показывает: файл действительно существует. При запуске Linux открывает не только сам файл. Для динамического ELF нужен указанный в нём загрузчик, а для скрипта — интерпретатор из shebang. Ошибка может относиться к этому второму пути.
Сначала подтверждаем файл и точный запуск
Проверьте исходный ENTRYPOINT или CMD и абсолютный путь. Убедитесь, что файл скопирован из нужной стадии, имеет разрешение исполнения и соответствует платформе. Permission denied и exec format error направляют к другим причинам; здесь исследуется ENOENT при существующем пути. Если запуск выполняется через shell, сообщение может быть сформировано оболочкой, поэтому сохраните полную ошибку runtime.
В scratch нет shell, ls и обычных средств диагностики. Не меняйте рабочий образ установкой пакетов ради одного просмотра. Анализируйте артефакт на стадии сборки либо в отдельном диагностическом окружении. Для файла, полученного из недоверенного источника, не запускайте ldd: используйте статический разбор ELF-информации.
file ./app readelf -l ./app readelf -d ./app
Команды выполняются в Linux-окружении с file и binutils. В Program Headers ищите INTERP и строку Requesting program interpreter. В dynamic section — NEEDED. Например, путь /lib64/ld-linux-x86-64.so.2 указывает на загрузчик glibc для соответствующей архитектуры. Если его нет в финальной файловой системе, ядро не сможет начать выполнение, хотя /app присутствует.
Почему multi-stage оставил зависимость снаружи
Каждый FROM начинает отдельную стадию с собственной файловой системой. COPY --from переносит указанные файлы, но не вычисляет и не добавляет автоматически все их динамические зависимости. Бинарник, собранный в Debian, может зависеть от glibc, тогда как Alpine использует musl, а scratch вообще не содержит пользовательских библиотек.
Не копируйте один найденный loader наугад: следом потребуются совместимые shared libraries и, возможно, дополнительные данные runtime. Практичный вариант — выбрать совместимый runtime-образ и устанавливать зависимости штатным пакетным способом. Другой вариант — действительно статическая сборка, если приложение и его библиотеки поддерживают её. Проверяйте результат readelf и запуском, а не только именем флага компилятора.
Учебный Go-пример без cgo
Для простой программы только со стандартной библиотекой Go можно отключить cgo и использовать scratch. Сохраните main.go и Dockerfile в одном каталоге:
package main
import "fmt"
func main() {
fmt.Println("ready")
}FROM golang:1.26 AS build
WORKDIR /src
COPY main.go .
RUN CGO_ENABLED=0 go build -o /out/app main.go
FROM scratch
COPY --from=build /out/app /app
ENTRYPOINT ["/app"]docker build -t multistage-lab .
docker run --rm multistage-labОжидается ready, выведенное программой. Версия golang в примере должна быть доступна в используемом registry. CGO_ENABLED=0 подходит этому исходнику, но не заменяет cgo для пакета, которому необходима C-библиотека. У реального сервиса дополнительно могут потребоваться CA-сертификаты, часовые пояса, файл пользователей и другие данные. Их отсутствие обычно проявится уже после старта, поэтому тестируйте рабочий запрос приложения.
Скрипты проверяем отдельно
Если file показывает текстовый скрипт, прочитайте первую строку. Shebang #!/bin/bash требует /bin/bash в финальном образе. Запись #!/usr/bin/env python3 требует и env, и доступный python3. Переносы CRLF могут добавить символ к имени интерпретатора и привести к похожей ошибке. Исправьте формат файла или выберите образ с необходимым runtime; наличие скрипта не делает его самодостаточным.
Для ELF итоговая проверка включает архитектуру, существование interpreter и библиотек, затем реальный запуск в целевом образе. Для скрипта — shebang и доступность его интерпретатора. Ошибка ENOENT при execve относится и к отсутствующему интерпретатору. Сохранённый вывод readelf позволяет объяснить, какой именно файл отсутствовал после переноса между стадиями.
Ещё по теме
- Docker COPY не видит файл: контекст сборки, путь Dockerfile и .dockerignore
- Read-only file system в Docker: какие каталоги приложению нужны для записи
Продолжить изучение
Хотите разобраться в контейнеризации целиком, от образа до работающего окружения? Сборка, сети, данные и запуск приложений входят в программу «Docker и Docker Compose: контейнеризация приложений».
Продолжить на практике
- Docker и Docker Compose: контейнеризация приложений ↗
Упакуйте приложение в Docker и запустите его с PostgreSQL через Compose. Образы, сети, тома, диагностика поломок, обновление версий и восстановление данных — с практикой и итоговым проектом.
