Docker-образы без Dockerfile: dockerTools в Nix
В посте про VoiceLM я пообещал объяснить, почему мой контейнер собирается через Nix, а не через Dockerfile. Объясняю по официальному туториалу nix.dev и своему опыту.
Что не так с Dockerfile
Типичный Dockerfile выглядит так:
FROM python:3.12
RUN apt-get update && apt-get install -y ffmpeg
RUN pip install -r requirements.txtПроблема в том, что результат зависит от момента сборки. Сегодня apt-get update даст одну версию ffmpeg, через месяц другую. Два человека, собравшие «один и тот же» Dockerfile, получат разные образы. Плюс в образ попадает весь базовый Debian с shell, пакетным менеджером и кучей ненужных утилит.
Как это делает Nix
В Nix образ — это просто ещё одна сборка, описанная декларативно:
pkgs.dockerTools.buildImage {
name = "hello-docker";
config.Cmd = [ "${pkgs.hello}/bin/hello" ];
}Обратите внимание: здесь нет базового образа. Nix сам знает точное замыкание зависимостей программы: какие библиотеки ей нужны, вплоть до хешей. В образ попадает ровно это и больше ничего.
Сборка и запуск:
nix-build hello-docker.nix # получаем tar.gz с образом
docker load < result # загружаем в Docker
docker run hello-docker:<тег>buildLayeredImage
Для реальных проектов лучше buildLayeredImage: он раскладывает зависимости по отдельным слоям. Если поменялся только код приложения, а Python и ffmpeg остались прежними, Docker перекачает один маленький слой вместо всего образа.
Нюанс
Образ Docker — это Linux. На macOS придётся настроить удалённую сборку на Linux-машине или кросс-компиляцию. На NixOS этой проблемы нет.
Мой комментарий
Для меня главный плюс даже не размер, а честность. Если nix build прошёл у меня, он пройдёт и на сервере, и через год, потому что все версии закреплены в flake.lock. Образ VoiceLM — distroless по сути: внутри нет shell, нет apt, только Python, ffmpeg, CUDA-библиотеки и мой код. Злоумышленнику, попавшему в такой контейнер, даже ls запустить нечем.
Этот блог, кстати, собирается так же: nix build выдаёт папку с HTML.