← [Раздел](README.md) · [Главная](../README.md)

# Контейнеры и Docker

## Цель

Понять **контейнеризацию**: чем контейнер отличается от VM, как собрать **безопасный образ**, работать с **registry** и подготовить приложение к запуску в Kubernetes.

## Предварительно

- [README раздела 10](README.md)
- Установлен Docker или совместимый runtime (Podman)

## Время

~75 минут + сборка одного учебного образа

---

## Контейнер vs виртуальная машина

| | VM | Контейнер |
|---|-----|-----------|
| Изоляция | Гипервизор, своё ядро | Общее ядро, namespaces/cgroups |
| Старт | Минуты | Секунды |
| Размер | ГБ | МБ–сотни МБ |
| Use case | Разные ОС | Микросервисы, одинаковый runtime |

Контейнер — **упакованный процесс**, не мини-сервер. Один контейнер — одна основная задача (не systemd с десятью сервисами).

---

## Образ и слои

```text
[app jar 50MB]
[runtime JRE   200MB]  ← слой
[base distro   80MB]    ← слой
```

Слои **кэшируются**. Порядок в Dockerfile влияет на скорость сборки: редко меняющееся — вниз, код — вверх.

---

## Минимальный Dockerfile (пример)

```dockerfile
# build stage
FROM golang:1.22-alpine AS build
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server ./cmd/api

# run stage
FROM gcr.io/distroless/static-debian12
COPY --from=build /app/server /server
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
```

| Практика | Зачем |
|----------|-------|
| **Multi-stage** | Маленький финальный образ |
| **non-root USER** | Снижение риска при escape |
| **distroless / alpine** | Меньше CVE |
| **COPY до RUN** | Кэш зависимостей |

---

## .dockerignore

```text
.git
node_modules
*.md
.env
```

Не копируйте секреты и мусор в build context.

---

## Конфигурация и секреты

| Плохо | Хорошо |
|-------|--------|
| `ENV API_KEY=sk_live_xxx` в Dockerfile | Secret из vault / k8s Secret |
| Конфиг в образе | ConfigMap / env на deploy |

Образ **один** для staging и prod; отличается только конфигурация при запуске.

---

## Health в контейнере

```dockerfile
HEALTHCHECK --interval=30s --timeout=3s \
  CMD wget -qO- http://localhost:8080/health/ready || exit 1
```

В Kubernetes healthcheck задаётся в манифесте (предпочтительнее).

---

## Container registry

```text
docker build -t registry.example.com/shop-api:1.0.0 .
docker push registry.example.com/shop-api:1.0.0
```

| Практика | Описание |
|----------|----------|
| Сканирование CVE | Trivy, Grype в CI |
| Immutable tags | Не перезаписывать `1.0.0` |
| Retention policy | Удалять старые untagged |

Для учебных проектов подойдёт GitHub Container Registry или локальный registry.

---

## docker-compose для разработки

```yaml
services:
  api:
    build: .
    ports: ["8080:8080"]
    environment:
      DATABASE_URL: postgres://app:example@db:5432/shop
    depends_on: [db]
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: example  # только local!
```

Compose — **не** production orchestrator; для прода — Kubernetes (следующая страница).

---

## Лимиты ресурсов

Задавайте **requests** и **limits** (в k8s) или `deploy.resources` (compose v3+):

- без limits pod съедает ноду;
- без requests scheduler не знает, куда ставить.

---

## Самопроверка

1. Чем контейнер отличается от VM?
2. Зачем multi-stage build?
3. Почему нельзя класть API-ключи в образ?
4. Для чего docker-compose vs Kubernetes?

---

## Дальше

→ [Kubernetes базово](kubernetes-bazovo.md)  
← [README раздела 10](README.md)
