Ответ
Организация установки зависимостей и сборки в Docker — это критически важный аспект для создания эффективных, безопасных и воспроизводимых образов. Вот мой подход:
1. Многоступенчатая сборка (Multi-stage builds) Это основной паттерн, который я использую для разделения этапов сборки и выполнения.
Пример для Go приложения:
# Этап 1: Сборка зависимостей
FROM golang:1.21 AS deps
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
# Этап 2: Тестирование
FROM deps AS tester
COPY . .
RUN go test ./... -v
# Этап 3: Сборка бинарника
FROM deps AS builder
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-w -s" -o /app/main ./cmd/app
# Этап 4: Финальный образ
FROM gcr.io/distroless/static-debian11
COPY --from=builder /app/main /app/main
USER nonroot:nonroot
ENTRYPOINT ["/app/main"]
2. Оптимизация кэширования зависимостей Копирую файлы зависимостей отдельно от исходного кода, чтобы максимально использовать кэш Docker.
Для Node.js:
FROM node:18-alpine AS deps
WORKDIR /app
# Копируем только package файлы
COPY package.json package-lock.json* ./
# Устанавливаем зависимости
RUN npm ci --only=production &&
cp -R node_modules production_node_modules
# Устанавливаем dev зависимости отдельно
RUN npm ci
# Копируем исходный код и собираем
COPY . .
RUN npm run build
# Финальный образ
FROM node:18-alpine
WORKDIR /app
COPY --from=deps /app/production_node_modules ./node_modules
COPY --from=deps /app/dist ./dist
COPY --from=deps /app/package.json ./package.json
USER node
CMD ["node", "dist/index.js"]
3. Использование специфичных команд установки
- npm:
npm ciвместоnpm installдля предсказуемости и скорости - pip:
pip install --no-cache-dir -r requirements.txt - apt: Объединение update, install и clean в одной RUN-инструкции
4. Безопасность и сканирование зависимостей
# Этап сканирования уязвимостей
FROM aquasec/trivy:latest AS scanner
COPY --from=builder /app /app
RUN trivy filesystem --exit-code 1 --severity HIGH,CRITICAL /app
# Этап сканирования лицензий
FROM fossa/fossa-cli AS license-check
COPY --from=builder /app /app
WORKDIR /app
RUN fossa analyze --output
5. Пример для Python приложения:
# Этап сборки
FROM python:3.11-slim AS builder
WORKDIR /app
# Установка системных зависимостей
RUN apt-get update &&
apt-get install -y --no-install-recommends gcc build-essential &&
rm -rf /var/lib/apt/lists/*
# Копируем и устанавливаем Python зависимости
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Копируем исходный код
COPY . .
# Запускаем тесты
RUN python -m pytest tests/
# Финальный образ
FROM python:3.11-slim
WORKDIR /app
# Копируем зависимости из builder
COPY --from=builder /root/.local /root/.local
COPY --from=builder /app .
# Настраиваем PATH для Python пакетов
ENV PATH=/root/.local/bin:$PATH
# Создаем непривилегированного пользователя
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
# Запускаем приложение
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "wsgi:app"]
6. Использование BuildKit для оптимизации
# syntax=docker/dockerfile:1.4
# Используем BuildKit секреты для безопасной передачи ключей
RUN --mount=type=secret,id=npm_token
npm config set //registry.npmjs.org/:_authToken $(cat /run/secrets/npm_token) &&
npm ci
# Используем кэш для npm
RUN --mount=type=cache,target=/root/.npm
npm ci
# Используем кэш для apt
RUN --mount=type=cache,target=/var/cache/apt
apt-get update && apt-get install -y package
7. Интеграция с CI/CD В CI/CD пайплайне я дополнительно:
- Кэширую слои Docker между сборками
- Использую
docker buildxдля multi-arch сборок - Подписываю образы с помощью Cosign
- Сохраняю SBOM (Software Bill of Materials) с помощью Syft
Ключевые принципы, которых я придерживаюсь:
- Минимализм: Только необходимые зависимости в финальном образе
- Воспроизводимость: Фиксированные версии всех зависимостей
- Безопасность: Регулярное сканирование на уязвимости
- Эффективность: Максимальное использование кэширования
- Стандартизация: Единый подход для всех проектов в организации