Senior Go Interview Prep - Core Go: https://go.vbloher.org/docs/01-core-go/ - Механика defer в Go: https://go.vbloher.org/docs/01-core-go/defer/ - Встраивание структур и интерфейсов (Embedding): https://go.vbloher.org/docs/01-core-go/embedding/ - Ошибки в Go: error, wrapping, errors.Is/As/Join: https://go.vbloher.org/docs/01-core-go/errors/ - Дженерики в Go (1.18+): https://go.vbloher.org/docs/01-core-go/generics/ - Интерфейсы в Go: https://go.vbloher.org/docs/01-core-go/interfaces/ - Устройство map в Go: https://go.vbloher.org/docs/01-core-go/maps/ - panic / recover: механика, раскрутка стека и runtime-паники: https://go.vbloher.org/docs/01-core-go/panic-recover/ - Указатели в Go: https://go.vbloher.org/docs/01-core-go/pointers/ - Рефлексия в Go (reflect): https://go.vbloher.org/docs/01-core-go/reflection/ - Внутреннее устройство слайсов в Go: https://go.vbloher.org/docs/01-core-go/slices/ - Строки, руны и байты в Go: https://go.vbloher.org/docs/01-core-go/strings-runes-bytes/ - Система типов Go: defined types, alignment, memory layout: https://go.vbloher.org/docs/01-core-go/type-system/ - Concurrency: https://go.vbloher.org/docs/02-concurrency/ - sync/atomic: https://go.vbloher.org/docs/02-concurrency/atomic/ - Буферизованные vs небуферизованные каналы: https://go.vbloher.org/docs/02-concurrency/buffered-unbuffered/ - Канал vs Mutex: когда что выбрать: https://go.vbloher.org/docs/02-concurrency/channel-vs-mutex/ - Каналы: устройство hchan: https://go.vbloher.org/docs/02-concurrency/channels/ - Утечки горутин, дедлоки, livelock, starvation: https://go.vbloher.org/docs/02-concurrency/common-leaks-deadlocks/ - sync.Cond: https://go.vbloher.org/docs/02-concurrency/cond/ - context: https://go.vbloher.org/docs/02-concurrency/context/ - Горутины: жизненный цикл, стоимость, стек: https://go.vbloher.org/docs/02-concurrency/goroutines-lifecycle/ - sync.Mutex и sync.RWMutex: https://go.vbloher.org/docs/02-concurrency/mutex-rwmutex/ - sync.Once: https://go.vbloher.org/docs/02-concurrency/once/ - Паттерны конкурентности: https://go.vbloher.org/docs/02-concurrency/patterns/ - Race Detector (гонки данных и -race): https://go.vbloher.org/docs/02-concurrency/race-detector/ - Планировщик GMP: https://go.vbloher.org/docs/02-concurrency/scheduler-gmp/ - select: https://go.vbloher.org/docs/02-concurrency/select/ - sync.WaitGroup: https://go.vbloher.org/docs/02-concurrency/waitgroup/ - Runtime и память: https://go.vbloher.org/docs/03-runtime-memory/ - Паттерны аллокаций и снижение давления на GC: https://go.vbloher.org/docs/03-runtime-memory/allocation-patterns/ - Escape Analysis: когда переменная убегает в кучу: https://go.vbloher.org/docs/03-runtime-memory/escape-analysis/ - Сборщик мусора Go: concurrent tri-color mark-sweep: https://go.vbloher.org/docs/03-runtime-memory/gc/ - Тюнинг GC: GOGC и GOMEMLIMIT: https://go.vbloher.org/docs/03-runtime-memory/gogc-gomemlimit/ - GOMAXPROCS: параллелизм планировщика и проблема контейнеров: https://go.vbloher.org/docs/03-runtime-memory/gomaxprocs/ - Утечки горутин (goroutine leaks): https://go.vbloher.org/docs/03-runtime-memory/goroutine-leaks/ - Утечки памяти в Go (несмотря на GC): https://go.vbloher.org/docs/03-runtime-memory/memory-leaks/ - Модель памяти Go (Go Memory Model): happens-before и синхронизация: https://go.vbloher.org/docs/03-runtime-memory/memory-model/ - pprof: профилирование CPU, памяти и блокировок в Go: https://go.vbloher.org/docs/03-runtime-memory/pprof/ - Execution Tracer и runtime/trace: тайминги вместо агрегатов: https://go.vbloher.org/docs/03-runtime-memory/runtime-tracing/ - Стек vs Куча: где живут данные в Go: https://go.vbloher.org/docs/03-runtime-memory/stack-vs-heap/ - Тестирование: https://go.vbloher.org/docs/04-testing/ - testify, assert/require и golden files: https://go.vbloher.org/docs/04-testing/assertions-testify/ - Бенчмарки в Go: https://go.vbloher.org/docs/04-testing/benchmarks/ - Покрытие, -race и флаки-тесты: https://go.vbloher.org/docs/04-testing/coverage-race/ - Нативный fuzzing в Go (1.18+): https://go.vbloher.org/docs/04-testing/fuzzing/ - Интеграционные тесты, testcontainers-go, TestMain: https://go.vbloher.org/docs/04-testing/integration-testcontainers/ - Моки, стабы и тестируемость: https://go.vbloher.org/docs/04-testing/mocks/ - Table-driven тесты, subtests и параллельность: https://go.vbloher.org/docs/04-testing/table-driven/ - Backend: https://go.vbloher.org/docs/05-backend/ - Аутентификация и авторизация: AuthN/AuthZ, сессии vs токены, RBAC/ABAC, API keys, mTLS, секреты: https://go.vbloher.org/docs/05-backend/auth-authz/ - Graceful Shutdown HTTP/gRPC сервера в Go: https://go.vbloher.org/docs/05-backend/graceful-shutdown/ - gRPC: типы RPC, интерсепторы, контекст, метаданные, error model: https://go.vbloher.org/docs/05-backend/grpc/ - JWT (JSON Web Token): https://go.vbloher.org/docs/05-backend/jwt/ - Middleware-паттерн в Go: https://go.vbloher.org/docs/05-backend/middleware/ - net/http: Server, Handler, ServeMux, таймауты, Client и контекст: https://go.vbloher.org/docs/05-backend/net-http/ - OAuth2: роли, grant types, OIDC, токены и типовые ошибки: https://go.vbloher.org/docs/05-backend/oauth2/ - OpenAPI/Swagger, code generation, contract-first vs code-first, валидация: https://go.vbloher.org/docs/05-backend/openapi/ - Protocol Buffers: схемы, wire format, эволюция и совместимость: https://go.vbloher.org/docs/05-backend/protobuf/ - REST: принципы, версионирование, идемпотентность, статусы, пагинация, ошибки: https://go.vbloher.org/docs/05-backend/rest/ - Сети и протоколы: https://go.vbloher.org/docs/06-networking/ - Пулы соединений: http.Transport, БД, утечки: https://go.vbloher.org/docs/06-networking/connection-pooling/ - DNS: записи, резолвинг, кэширование, DNS в Go: https://go.vbloher.org/docs/06-networking/dns/ - Версии HTTP: 1.1, 2, 3: https://go.vbloher.org/docs/06-networking/http-versions/ - TCP/IP: модель, транспорт и что важно бэкендеру: https://go.vbloher.org/docs/06-networking/tcp-ip/ - TLS: handshake, сертификаты, mTLS, производительность: https://go.vbloher.org/docs/06-networking/tls/ - UDP и надёжность поверх UDP: https://go.vbloher.org/docs/06-networking/udp/ - WebSocket: upgrade, фреймы, масштабирование: https://go.vbloher.org/docs/06-networking/websocket/ - Базы данных: https://go.vbloher.org/docs/07-databases/ - Пул соединений к PostgreSQL в Go: database/sql, pgx, pgxpool, PgBouncer: https://go.vbloher.org/docs/07-databases/connection-pooling-pgx/ - Взаимоблокировки (Deadlocks) в PostgreSQL: https://go.vbloher.org/docs/07-databases/deadlocks/ - Индексы в PostgreSQL: https://go.vbloher.org/docs/07-databases/indexes/ - Уровни изоляции транзакций в PostgreSQL: https://go.vbloher.org/docs/07-databases/isolation-levels/ - MVCC в PostgreSQL: версии строк, видимость, VACUUM и bloat: https://go.vbloher.org/docs/07-databases/mvcc/ - Обзор NoSQL и Redis: https://go.vbloher.org/docs/07-databases/nosql-redis/ - Партиционирование таблиц в PostgreSQL: https://go.vbloher.org/docs/07-databases/partitioning/ - Архитектура PostgreSQL: https://go.vbloher.org/docs/07-databases/postgresql-architecture/ - Планирование и оптимизация запросов в PostgreSQL: https://go.vbloher.org/docs/07-databases/query-planning/ - Репликация в PostgreSQL: https://go.vbloher.org/docs/07-databases/replication/ - Шардирование (горизонтальное масштабирование): https://go.vbloher.org/docs/07-databases/sharding/ - Транзакции в PostgreSQL и Go (database/sql, pgx): https://go.vbloher.org/docs/07-databases/transactions/ - Распределённые системы: https://go.vbloher.org/docs/08-distributed-systems/ - CAP теорема: https://go.vbloher.org/docs/08-distributed-systems/cap-theorem/ - Circuit Breaker: https://go.vbloher.org/docs/08-distributed-systems/circuit-breaker/ - Консенсус и Raft: репликация состояния в присутствии отказов: https://go.vbloher.org/docs/08-distributed-systems/consensus-raft/ - Модели согласованности: https://go.vbloher.org/docs/08-distributed-systems/consistency/ - Гарантии доставки сообщений: at-most-once / at-least-once / exactly-once: https://go.vbloher.org/docs/08-distributed-systems/delivery-guarantees/ - Eventual Consistency: https://go.vbloher.org/docs/08-distributed-systems/eventual-consistency/ - Идемпотентность в распределённых системах: https://go.vbloher.org/docs/08-distributed-systems/idempotency/ - Apache Kafka: https://go.vbloher.org/docs/08-distributed-systems/kafka/ - Transactional Outbox: https://go.vbloher.org/docs/08-distributed-systems/outbox/ - RabbitMQ: AMQP 0-9-1, маршрутизация, надёжность доставки и сравнение с Kafka: https://go.vbloher.org/docs/08-distributed-systems/rabbitmq/ - Ретраи: backoff, jitter, budgets и идемпотентность: https://go.vbloher.org/docs/08-distributed-systems/retries/ - Saga Pattern: https://go.vbloher.org/docs/08-distributed-systems/saga/ - Observability: https://go.vbloher.org/docs/09-observability/ - Grafana: https://go.vbloher.org/docs/09-observability/grafana/ - Метрики: RED, USE, Golden Signals: https://go.vbloher.org/docs/09-observability/metrics/ - OpenTelemetry: https://go.vbloher.org/docs/09-observability/opentelemetry/ - Prometheus: https://go.vbloher.org/docs/09-observability/prometheus/ - SLI / SLO / SLA: https://go.vbloher.org/docs/09-observability/slo-sli/ - Структурированное логирование (slog): https://go.vbloher.org/docs/09-observability/structured-logging/ - Distributed Tracing: https://go.vbloher.org/docs/09-observability/tracing/ - System Design: https://go.vbloher.org/docs/10-system-design/ - Analytics Pipeline: https://go.vbloher.org/docs/10-system-design/analytics-pipeline/ - Chat System: https://go.vbloher.org/docs/10-system-design/chat/ - Фреймворк System Design интервью: https://go.vbloher.org/docs/10-system-design/framework/ - Notification Service: https://go.vbloher.org/docs/10-system-design/notification-service/ - Order Service: https://go.vbloher.org/docs/10-system-design/order-service/ - Payment Service: https://go.vbloher.org/docs/10-system-design/payment-service/ - Rate Limiter: https://go.vbloher.org/docs/10-system-design/rate-limiter/ - URL Shortener: https://go.vbloher.org/docs/10-system-design/url-shortener/ - DevOps: https://go.vbloher.org/docs/11-devops/ - CI/CD: пайплайны, стадии, стратегии деплоя: https://go.vbloher.org/docs/11-devops/cicd/ - Облака (AWS / GCP) для бэкендера: https://go.vbloher.org/docs/11-devops/cloud-aws-gcp/ - Docker для Go-разработчика: https://go.vbloher.org/docs/11-devops/docker/ - GitHub Actions и GitLab CI: https://go.vbloher.org/docs/11-devops/github-gitlab-ci/ - Kubernetes для Go-разработчика: https://go.vbloher.org/docs/11-devops/kubernetes/ - Terraform / Infrastructure as Code: https://go.vbloher.org/docs/11-devops/terraform/ - Алгоритмы: https://go.vbloher.org/docs/12-algorithms/ - Типовые алгоритмические задачи и паттерны: https://go.vbloher.org/docs/12-algorithms/common-problems/ - Асимптотическая сложность (Big-O): https://go.vbloher.org/docs/12-algorithms/complexity/ - Структуры данных в Go: https://go.vbloher.org/docs/12-algorithms/data-structures/ - Специфика live-coding на Go: https://go.vbloher.org/docs/12-algorithms/go-specifics/ - Behavioral: https://go.vbloher.org/docs/13-behavioral/ - Конфликты, разногласия и работа со стейкхолдерами: https://go.vbloher.org/docs/13-behavioral/conflicts/ - Как проходит senior-интервью: этапы, оценка, оффер: https://go.vbloher.org/docs/13-behavioral/interview-flow/ - Лидерство и менторство: https://go.vbloher.org/docs/13-behavioral/leadership-mentoring/ - Типовые поведенческие вопросы для Senior: https://go.vbloher.org/docs/13-behavioral/senior-questions/ > Модуль: DevOps · Уровень: Middle+/Senior ## TL;DR - Docker-образ — это набор **read-only слоёв** (union filesystem, OverlayFS) + JSON-манифест. Каждая инструкция `RUN`/`COPY`/`ADD` создаёт новый слой; `ENV`/`WORKDIR`/`CMD` создают слои-метаданные нулевого размера. - Для Go используйте **multistage build**: stage сборки на `golang:1.x`, финальный образ — `scratch` или `gcr.io/distroless/static`. Итог: 5–15 МБ вместо 800+ МБ. - Статическая линковка: `CGO_ENABLED=0` даёт полностью статический бинарь, который работает в `scratch`. С CGO нужен glibc/musl в рантайме. - Кэш слоёв ломается на первом изменившемся слое и всех последующих. Поэтому `COPY go.mod go.sum` и `go mod download` идут ДО `COPY . .`. - `.dockerignore` обязателен: исключает `.git`, `node_modules`, локальные бинарники из build context (ускоряет билд и уменьшает риск утечки секретов). - Безопасность: non-root `USER`, минимальный базовый образ, никаких секретов в слоях, multi-stage чтобы не тащить toolchain. ## Теория ### Образы и слои Образ состоит из: - **слоёв (layers)** — tar-архивы дельт файловой системы, адресуемые по SHA256 (content-addressable); - **манифеста** — список digest'ов слоёв + указатель на config; - **config** — метаданные (`Env`, `Cmd`, `Entrypoint`, `WorkingDir`, `User`, история). Слои переиспользуются между образами по digest. Если два образа имеют один и тот же базовый слой, на диске он хранится один раз. При `docker pull` тянутся только отсутствующие слои. Контейнер при запуске добавляет тонкий **writable layer** поверх read-only слоёв (copy-on-write). Все изменения в нём эфемерны и теряются при удалении контейнера. ```bash # посмотреть слои и их размер docker history myimage:tag docker image inspect myimage:tag --format '{{json .RootFS.Layers}}' # dive — лучший инструмент для анализа слоёв и "впустую" занятого места dive myimage:tag ``` ### Multistage build для Go Классический паттерн: тяжёлый builder + лёгкий runtime. ```dockerfile # --- builder stage --- FROM golang:1.22 AS builder WORKDIR /src # 1) сначала зависимости — слой кэшируется, пока не изменились go.mod/go.sum COPY go.mod go.sum ./ RUN go mod download # 2) потом исходники COPY . . # статическая сборка ARG VERSION=dev RUN CGO_ENABLED=0 GOOS=linux go build \ -trimpath \ -ldflags="-s -w -X main.version=${VERSION}" \ -o /out/app ./cmd/app # --- runtime stage --- FROM gcr.io/distroless/static-debian12:nonroot COPY --from=builder /out/app /app USER nonroot:nonroot ENTRYPOINT ["/app"] ``` Что делают флаги: - `-ldflags="-s -w"` — убирает таблицу символов и DWARF debug info, бинарь меньше на ~25–30%. Минус: хуже стектрейсы/профилирование. Для прод-сервиса обычно оставляют (символы Go-функций для panic-трейсов остаются и без `-s -w`, теряется только отладочная информация для gdb/delve). - `-trimpath` — убирает абсолютные пути сборки из бинаря (репродьюсибилити + не светим структуру FS машины сборки). - `-X main.version=...` — инъекция версии на этапе компиляции (вместо чтения из файла в рантайме). ### scratch vs distroless vs alpine | База | Размер | libc | shell | non-root | CA-сертификаты | tzdata | |------|--------|------|-------|----------|----------------|--------| | `scratch` | ~0 | нет | нет | руками | нет (надо копировать) | нет | | `distroless/static` | ~2 МБ | нет | нет | образ `:nonroot` есть | да | да | | `distroless/base` | ~20 МБ | glibc | нет | да | да | да | | `alpine` | ~5 МБ | musl | sh | руками | `apk add ca-certificates` | руками | - **scratch** — абсолютный минимум. Подходит только для `CGO_ENABLED=0`. Нужно вручную добавить CA-сертификаты (иначе HTTPS-запросы упадут с `x509: certificate signed by unknown authority`) и tzdata, если используете таймзоны. ```dockerfile FROM scratch # CA для исходящего HTTPS COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ # таймзоны, если нужны time.LoadLocation COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=builder /out/app /app ENTRYPOINT ["/app"] ``` Альтернатива tzdata: собрать с `go build -tags timetzdata` — Go вкомпилирует базу таймзон в бинарь. - **distroless** (от Google) — нет shell, нет пакетного менеджера, есть CA + tzdata, есть тег `:nonroot`. Рекомендуемый дефолт для прода: безопасность scratch + готовые сертификаты. Минус для дебага: нет shell, `kubectl exec` бесполезен (используйте ephemeral debug containers или `:debug` теги). - **alpine** — есть shell (удобно дебажить), но musl libc: при CGO возможны несовместимости с glibc-сборками, и иногда DNS-резолвинг ведёт себя иначе. ### CGO и статическая линковка - `CGO_ENABLED=0` → Go использует **чистый Go-резолвер DNS** и net-стек, бинарь полностью статический, работает в `scratch`. Это дефолт для контейнеров. - `CGO_ENABLED=1` (дефолт при наличии C-компилятора и cross-зависимостей вроде `net`/`os/user` в некоторых конфигурациях) → бинарь динамически линкуется с glibc. В `scratch` упадёт с `no such file or directory` (нет `ld-linux.so`). Когда CGO реально нужен: SQLite (`mattn/go-sqlite3`), некоторые крипто/ML-библиотеки, libpq напрямую. Тогда: ```dockerfile # статическая линковка с musl FROM golang:1.22-alpine AS builder RUN apk add --no-cache build-base ENV CGO_ENABLED=1 RUN go build -ldflags="-linkmode external -extldflags '-static'" -o /out/app . # рантайм: alpine (musl) или distroless/base (glibc) ``` Проверка статичности: `ldd app` → `not a dynamic executable`, либо `file app` → `statically linked`. ### Кэширование слоёв Docker кэширует каждый слой. Кэш инвалидируется, если: - изменилась инструкция в Dockerfile; - для `COPY`/`ADD` — изменилось содержимое копируемых файлов (по checksum); - инвалидирован любой предыдущий слой (каскад). Главный приём — **отделить зависимости от кода**, т.к. код меняется на каждом коммите, а `go.mod` — редко: ```dockerfile COPY go.mod go.sum ./ RUN go mod download # кэшируется надолго COPY . . # инвалидируется на каждом коммите RUN go build ... ``` BuildKit + cache mounts — переиспользование Go build/module cache между билдами без раздувания слоёв: ```dockerfile # syntax=docker/dockerfile:1 RUN --mount=type=cache,target=/go/pkg/mod \ --mount=type=cache,target=/root/.cache/go-build \ go build -o /out/app ./cmd/app ``` Включается через `DOCKER_BUILDKIT=1` или `docker buildx`. В CI кэш можно экспортировать: `--cache-to type=registry,...` / `--cache-from`. ### .dockerignore Build context (всё в каталоге) отправляется демону. Большой context = медленный билд + риск утечки. ``` .git .gitignore *.md bin/ dist/ node_modules/ .env *.local testdata/large/ Dockerfile .dockerignore ``` Без него `COPY . .` затащит `.git` (история + потенциальные секреты) и локальные бинарники в кэш-чувствительный слой. ### Безопасность образа ```dockerfile FROM gcr.io/distroless/static-debian12:nonroot COPY --from=builder /out/app /app USER 65532:65532 # nonroot uid в distroless ENTRYPOINT ["/app"] ``` Чек-лист: - **non-root**: `USER` с числовым uid (k8s `runAsNonRoot: true` проверяет именно числовой uid, а не имя). - **минимальная база**: меньше пакетов = меньше CVE. - **никаких секретов** в слоях: даже если удалить файл в следующем `RUN`, он остаётся в предыдущем слое. Используйте BuildKit secrets: `RUN --mount=type=secret,id=token ...`. - **read-only rootfs** в рантайме (`securityContext.readOnlyRootFilesystem: true`) — статический Go-бинарь обычно не пишет в FS. - **сканирование**: `trivy image myimage:tag`, `grype`, `docker scout`. - **pin версий**: `golang:1.22.3` или по digest `golang@sha256:...`, не `latest`. ## Подводные камни / gotchas - **HTTPS падает в scratch** — забыли скопировать `ca-certificates.crt`. Симптом: `x509: certificate signed by unknown authority`. - **CGO неожиданно включён** — на машине есть gcc, забыли `CGO_ENABLED=0`. Бинарь становится динамическим и не запускается в scratch. Всегда явно ставьте `CGO_ENABLED=0` для контейнеров. - **Таймзоны не работают** — `time.LoadLocation("Europe/Moscow")` возвращает ошибку в scratch без zoneinfo. Решение: `-tags timetzdata` или копирование `/usr/share/zoneinfo`. - **GOOS/GOARCH при cross-build на ARM Mac** — собираете на M1/M2, деплоите на amd64. Нужно `GOARCH=amd64` или `docker buildx build --platform linux/amd64`. - **Слой с `RUN apt-get update` без очистки** — раздувает образ. Объединяйте: `apt-get update && apt-get install -y X && rm -rf /var/lib/apt/lists/*` в одном RUN. - **`ADD` вместо `COPY`** — `ADD` распаковывает архивы и качает URL, что неочевидно. Для копирования файлов всегда `COPY`. - **`latest` тег** — невоспроизводимые билды. Пинуйте версии. - **Кэш не инвалидируется по go.sum** — если копируете только `go.mod`, новые зависимости из обновлённого `go.sum` не подтянутся корректно при verify. Копируйте оба файла. - **PID 1 и сигналы** — `ENTRYPOINT ["/app"]` (exec form) делает приложение PID 1, и оно само должно обрабатывать SIGTERM. Shell form (`ENTRYPOINT /app`) запускает через `/bin/sh -c`, который не форвардит сигналы (в scratch shell вообще нет). Всегда exec form. ## Вопросы на собеседовании **В:** Почему multistage build, а не просто `golang:1.22` как финальный образ? **О:** Финальный образ не должен содержать toolchain (компилятор, кэши модулей, исходники). Это ~800 МБ против ~10 МБ, плюс лишняя attack surface (компилятор, shell, пакеты с CVE) и утечка исходников. Multistage оставляет в рантайме только бинарь. **В:** Что такое слой образа и когда переиспользуется кэш? **О:** Слой — это content-addressable (по SHA256) дельта файловой системы, создаваемая инструкциями `RUN`/`COPY`/`ADD`. Кэш переиспользуется, если инструкция и её входные данные (для COPY — checksum файлов) не изменились и не инвалидирован ни один предыдущий слой. **В:** В чём разница между scratch, distroless и alpine для Go? **О:** scratch — пусто, нужно вручную добавлять CA/tzdata, только для CGO_ENABLED=0. distroless — нет shell/пакетов, но есть CA+tzdata+nonroot, лучший дефолт для безопасности. alpine — есть shell (удобный дебаг), но musl libc может конфликтовать с CGO/glibc-сборками. **В:** Почему статический бинарь падает в scratch с ошибкой про сертификаты? **О:** Это не про линковку — Go-резолвер работает, но для верификации TLS нужны корневые CA-сертификаты, которых в scratch нет. Надо скопировать `/etc/ssl/certs/ca-certificates.crt` из builder. **В:** Как ускорить сборку Go-образа в CI? **О:** (1) Отделить `COPY go.mod go.sum` + `go mod download` от `COPY . .` для кэша слоёв. (2) BuildKit cache mounts для `/go/pkg/mod` и `/root/.cache/go-build`. (3) Хороший `.dockerignore`. (4) `--cache-from`/`--cache-to` для переиспользования кэша между раннерами CI. **В:** Как сделать образ безопасным? **О:** Минимальная база (distroless/scratch), non-root с числовым uid, никаких секретов в слоях (BuildKit secrets вместо ARG/ENV), pin версий по digest, read-only rootfs, сканирование trivy/grype, multistage чтобы не тащить toolchain. **В:** Почему `ENTRYPOINT` лучше писать в exec-форме? **О:** Exec form (`["/app"]`) делает приложение PID 1 напрямую и доставляет ему сигналы (SIGTERM при остановке). Shell form оборачивает в `/bin/sh -c`, который перехватывает сигналы и не форвардит их, ломая graceful shutdown; к тому же в scratch/distroless нет shell. **В:** Как уменьшить размер Go-бинаря? **О:** `-ldflags="-s -w"` (убрать debug-инфу), `-trimpath`, UPX-сжатие (осторожно, замедляет старт и ловится антивирусами), отключить ненужные фичи через build tags. Главное — multistage с минимальной базой. ## На что копают на senior+ - **Reproducible builds**: pin базовых образов по digest, `-trimpath`, фиксированные версии Go, `SOURCE_DATE_EPOCH`. Понимание, почему два билда одного коммита должны давать идентичный digest. - **Supply chain security**: подпись образов (cosign/sigstore), SBOM (syft), provenance attestations (SLSA), сканирование на каждом этапе пайплайна. - **BuildKit изнутри**: чем отличается от legacy builder (параллельный граф сборки, cache mounts, secrets, frontend `# syntax=`), как устроен экспорт/импорт кэша в registry. - **OCI vs Docker image spec**: что такое manifest list (multi-arch), как `docker buildx` собирает образы под несколько платформ одновременно. - **Layer squashing trade-offs**: когда стоит схлопывать слои (меньше overhead) и когда нет (теряется переиспользование кэша между образами). - **Глубокое понимание CGO**: когда runtime принудительно включает CGO (например для `os/user` и cgo-резолвера), `-tags netgo,osusergo` для чистого Go, `linkmode external` со static-extldflags. - **Эфемерность writable layer и persistent state**: почему данные в контейнере нельзя хранить, как это связано с volumes и stateless-дизайном сервисов.