Irbisa · Шпаргалка

Docker и контейнеры

Образы, слои и кэш сборки, multi-stage Dockerfile для Go, scratch и distroless, Compose для локальной разработки, тома и сети, сигналы и PID 1, безопасность образа, multi-arch сборки

Middle-разработчик · 18 вопросов

  1. 01

    Что такое контейнер, чем он отличается от виртуальной машины и что дают ему namespaces и cgroups?

    Контейнер представляет собой обычный процесс Linux, изолированный ядром: namespaces дают ему собственный взгляд на систему, а cgroups ограничивают, сколько CPU и памяти он может занять.

  2. 02

    Чем отличаются образ, контейнер и реестр (registry), и что такое слой образа?

    Образ (image) состоит из read-only стопки слоёв файловой системы и конфига (команда, переменные окружения, пользователь); контейнер является запущенным экземпляром образа с тонким записываемым слоем сверху; реестр (registry) хранит образы по имени и тегу.

    # one layer per build step that changed files; ENTRYPOINT only sets config$ docker image history --format 'table {{.CreatedBy}}\t{{.Size}}' registry.example.com/api:1.4.0CREATED BY                            SIZEENTRYPOINT ["/api"]                   0BCOPY /out/api /api # buildkit         10.7MBbazel build //common:cacerts_deb...   319kB…
  3. 03

    Что делают RUN, CMD и ENTRYPOINT в Dockerfile, и почему важна exec-форма ["/api"]?

    RUN выполняется при сборке и сохраняет слой; ENTRYPOINT (программа) и CMD (её аргументы по умолчанию, их заменяет всё, что стоит после имени образа в docker run) срабатывают при старте контейнера.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./# RUN: executed at build time; its file changes are saved as a layerRUN go mod download…
  4. 04

    Чем EXPOSE отличается от -p 8080:8080, и почему Go-сервер, который слушает 127.0.0.1:8080, недоступен через опубликованный порт?

    EXPOSE только документирует порт, который слушает приложение, и ничего не открывает.

    package main import (	"log"	"net/http")…
  5. 05

    Чем named volume отличается от bind mount, где Postgres должен хранить данные в Compose и что удаляет docker compose down -v?

    Оба хранят данные вне записываемого слоя контейнера, поэтому данные переживают docker rm.

    services:  postgres:    image: postgres:17-alpine    environment: {POSTGRES_USER: app, POSTGRES_PASSWORD: app, POSTGRES_DB: app}    volumes:      # named volume: managed by Docker, survives down, removed by down -v…
  6. 06

    Как написать production-Dockerfile для Go-сервиса и что ломается, когда бинарник запускается на scratch?

    Используйте multi-stage сборку: соберите статический бинарник с CGO_ENABLED=0 в образе golang, а затем скопируйте только его в distroless/static или scratch.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod download                 # cached until go.mod/go.sum changeCOPY . .…
  7. 07

    Найдите баг: после правки одной строки в хендлере docker build заново скачивает все модули и перекомпилирует всё дерево зависимостей.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcCOPY . .RUN go mod downloadRUN CGO_ENABLED=0 go build -o /out/api ./cmd/api FROM gcr.io/distroless/static-debian12:nonrootCOPY --from=build /out/api /apiENTRYPOINT ["/api"]

    COPY . . стоит перед go mod download, поэтому изменение любого файла инвалидирует этот слой и все слои после него, включая скачивание.

  8. 08

    Что такое контекст сборки Docker и что ломается, если в Go-репозитории нет .dockerignore?

    Контекст сборки представляет собой каталог, переданный в docker build (та самая . в конце), и только эти файлы хоста может читать COPY.

    # .dockerignore at the root of the build context, allowlist style:# exclude everything...*# ...then re-include only what the Go build reads!go.mod!go.sum…
  9. 09

    Образ, собранный на Mac с Apple Silicon, падает на amd64-сервере с exec /api: exec format error. Почему, и как собрать один образ под обе архитектуры?

    Go-бинарник содержит машинный код для одной архитектуры процессора, а на Mac с M-чипом docker build по умолчанию собирает образ linux/arm64, который ядро amd64 запустить не может.

    # syntax=docker/dockerfile:1# The build stage runs natively on the builder's own platform: no emulation.FROM --platform=$BUILDPLATFORM golang:1.25 AS buildWORKDIR /srcCOPY go.mod go.sum ./RUN go mod download…
  10. 10

    Почему деплоить :latest плохо, и чем тег образа отличается от digest?

    Тег представляет собой подвижное имя в реестре, а latest означает лишь тег по умолчанию, а не «самый новый»: то, на что он указывает, может измениться между двумя pull.

    # syntax=docker/dockerfile:1# wrong: a moving tag, a different image after every Go patch or Debian update#   FROM golang:1.25 AS build# right: the tag is for humans; the digest decides what is pulledFROM golang:1.25.14@sha256:699337d620559a59b4a2bb298ad59611e535d2ee755a34cf2d2a98f37578dc80 AS buildWORKDIR /src…
  11. 11

    Контейнер на базе Alpine стартует через entrypoint.sh, который читает секрет и запускает /app/server. Теперь поды останавливаются все 30 с и не пишут лог shutdown. Почему?

    PID 1 занимает скрипт, а Go-бинарник работает как его дочерний процесс, поэтому SIGTERM от kubelet получает sh, который его не пересылает и, будучи PID 1 без обработчика, даже не умирает от него.

    #!/bin/sh# entrypoint.shset -euDB_PASSWORD="$(cat /run/secrets/db-password)"export DB_PASSWORD…
  12. 12

    Почему Go-контейнер должен работать не от root и как это настроить на scratch или distroless?

    Без user namespaces UID 0 в контейнере остаётся UID 0 на хосте, которого сдерживают только урезанные capabilities и seccomp, так что взломанный процесс под root отделяет от ноды один баг в ядре или рантайме.

    # Dockerfile: USER 65532:65532 (numeric, so runAsNonRoot can verify it)spec:  securityContext:    runAsNonRoot: true    runAsUser: 65532    runAsGroup: 65532…
  13. 13

    Почему секреты нельзя зашивать в образ контейнера и как Go-сервис должен получать их вместо этого: при сборке и в рантайме?

    Любой слой образа может прочитать каждый, кто может его скачать, а удаление файла или переопределение переменной в следующем слое не убирает их из предыдущего.

    # syntax=docker/dockerfile:1FROM golang:1.25 AS buildWORKDIR /srcENV GOPRIVATE=github.com/acme/*COPY go.mod go.sum ./# wrong: ARG GITHUB_TOKEN, then RUN git config ... $GITHUB_TOKEN…
  14. 14

    Как настроить локальную разработку Go-сервиса с Postgres и Redis в Docker Compose, и как заставить сервис ждать готовности базы?

    Один compose.yaml: сервис собирается из своего Dockerfile, Postgres и Redis берутся из официальных образов, данные лежат в named volume.

    services:  api:    build: .    ports: ["8080:8080"]    environment:      DATABASE_URL: postgres://app:app@postgres:5432/app?sslmode=disable…
  15. 15

    Найдите баг: API подключается к Postgres при запуске через go run, а в Compose падает с dial tcp 127.0.0.1:5432: connect: connection refused.

    services:  api:    build: .    ports:      - "8080:8080"    environment:      DATABASE_URL: postgres://app:app@localhost:5432/app?sslmode=disable    depends_on:      postgres:        condition: service_healthy  postgres:    image: postgres:17-alpine    environment: {POSTGRES_USER: app, POSTGRES_PASSWORD: app, POSTGRES_DB: app}    ports:      - "5432:5432"    healthcheck:      test: ["CMD-SHELL", "pg_isready -U app -d app"]      interval: 2s

    Внутри контейнера localhost указывает на сам контейнер, а в контейнере api порт 5432 никто не слушает.

  16. 16

    Как защитить образ Go-сервиса помимо запуска не от root: сканирование, SBOM, подпись и ограничения в рантайме?

    Нужно поставлять как можно меньше, знать, что внутри, вовремя патчить, доказывать происхождение и ограничивать то, что может запущенный контейнер.

    # 1. scan: OS packages plus the Go modules and stdlib recorded in the binary$ trivy image --severity HIGH,CRITICAL --ignore-unfixed --exit-code 1 \    registry.example.com/api:1.4.0 # 2. Go-aware triage: which vulnerable functions are linked into the binary#    (needs symbols: scan a build made without -ldflags=-s)…
  17. 17

    Сервис работает на distroless, поэтому docker exec -it api sh падает с executable file not found. Как отлаживать запущенный контейнер?

    Инструменты приносят снаружи, а не кладут в образ: отладочный контейнер подключается к пространствам имён процессов и сети целевого.

    $ docker exec -it api shOCI runtime exec failed: exec failed: unable to start container process:exec: "sh": executable file not found in $PATH # Docker: a toolbox container in the same PID and network namespaces$ docker run --rm -it --pid=container:api --network=container:api \…
  18. 18

    Чем отличаются Docker, containerd, runc и Podman, и что изменилось для ваших образов, когда Kubernetes удалил dockershim в 1.24?

    Это слои одного стека: dockerd собирает и управляет, containerd скачивает образы и запускает контейнеры, runc создаёт процесс с пространствами имён и cgroups.