В чем разница Docker образа и Docker контейнера
Что проверяют
Понимаете ли вы, что образ это неизменяемый шаблон, а контейнер это запущенный из него изолированный процесс со своим слоем записи. Для Go-разработчика следом почти всегда идёт вопрос, как собрать маленький образ сервиса и как приложение корректно завершается при остановке.
Как отвечать
Удобная аналогия: образ похож на скомпилированный бинарник, контейнер на процесс, который его выполняет. Из одного образа запускают сколько угодно контейнеров.
- образ: слои только для чтения плюс конфигурация (
ENTRYPOINT,CMD, переменные окружения, пользователь). Слои дают инструкции Dockerfile, меняющие файлы:RUN,COPY,ADD. Образ однозначно определяется дайджестомsha256, а тег вродеlatestэто подвижная ссылка на него; - контейнер: слои образа плюс тонкий слой записи сверху (copy-on-write, драйвер
overlay2), а также namespaces, cgroups, сеть и процесс. Остановленный контейнер существует (docker ps -a) вместе со своим слоем, пока его не удалят черезdocker rm.
docker build создаёт образ, docker run создаёт и запускает контейнер. docker commit делает образ из контейнера, но это антипаттерн: сборка должна воспроизводиться из Dockerfile.
Для Go покажите multi-stage сборку: в стадии на golang выполняется CGO_ENABLED=0 go build, а в финальный scratch или distroless/static копируется только бинарник. go.mod и go.sum копируют раньше исходников и выполняют go mod download, чтобы слой зависимостей кешировался. В scratch нет корневых сертификатов и часовых поясов: их копируют отдельно или встраивают через import _ "time/tzdata".
docker stop шлёт процессу SIGTERM, а через 10 секунд SIGKILL, поэтому сервис ловит сигнал через signal.NotifyContext и вызывает Shutdown у http.Server.
Где спотыкаются
Называют контейнер лёгкой виртуальной машиной. Контейнер работает на ядре хоста, изоляцию дают namespaces и cgroups.
Хранят данные в слое записи контейнера и теряют их при docker rm. Для состояния нужны тома.
Пишут ENTRYPOINT в shell-форме. Тогда PID 1 становится оболочка, SIGTERM до Go-процесса не доходит, и контейнер убивают по таймауту. Нужна exec-форма: ENTRYPOINT ["/app"].