# Docker и образы

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

## Цель

Понять **контейнеры и Docker**: image vs container, Dockerfile, слои, registry, базовые команды и **типичные ошибки безопасности**.

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

- [Linux и shell](linux-i-shell.md)

## Время

**8–10 часов**

---

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

| | VM | Container |
|---|-----|-----------|
| Изоляция | Полная ОС + hypervisor | Общее ядро, namespaces/cgroups |
| Размер | GB | MB (обычно) |
| Старт | Минуты | Секунды |
| Аналогия | Отдельный дом | Квартира в одном здании |

**Image (образ)** — **шаблон** (read-only слои). **Container** — **запущенный экземпляр** образа.

---

## Основные команды

```bash
docker pull nginx:1.25-alpine
docker images
docker run -d -p 8080:80 --name web nginx:1.25-alpine
docker ps
docker logs web
docker exec -it web sh
docker stop web && docker rm web
```

| Флаг | Значение |
|------|----------|
| `-d` | Detached (фон) |
| `-p 8080:80` | Порт host:container |
| `-it` | Interactive terminal |

---

## Dockerfile (учебный пример)

```dockerfile
FROM node:20-alpine

WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .

USER node
EXPOSE 3000
CMD ["node", "server.js"]
```

| Строка | SecOps note |
|--------|-------------|
| `alpine` | Меньше surface, но проверять CVE |
| `npm ci` | Reproducible vs `npm install` |
| `USER node` | ✅ не root |
| `COPY . .` | Проверить `.dockerignore` — не COPY `.env` |

---

## Слои и кэш

Каждая инструкция Dockerfile → **слой**. Меняете код → пересборка только верхних слоёв.

**Плохо для security:** старые слои с CVE остаются, если не rebuild base.

---

## Registry

| Registry | Назначение |
|----------|------------|
| Docker Hub | Публичные образы |
| GHCR / GitLab Registry | Private для CI |
| Harbor | Self-hosted enterprise |

**Правила:**

- Pin версии (`nginx:1.25-alpine`, не `latest`)
- Scan images в CI (Trivy)
- Sign images (Cosign) — продвинутый уровень

---

## docker-compose (локально)

```yaml
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://user:YOUR_PASSWORD@db:5432/app
  db:
    image: postgres:16-alpine
    volumes:
      - pgdata:/var/lib/postgresql/data
volumes:
  pgdata:
```

**Не** деплойте compose as-is в prod без hardening.

---

## Типичные уязвимости образов

| Проблема | Риск |
|----------|------|
| Root user | Escape → host |
| Secrets в ENV/COPY | Leak в registry/layers |
| Fat base (ubuntu full) | Больше CVE |
| `:latest` | Непредсказуемые updates |
| Socket mount `/var/run/docker.sock` | Root на host |

---

## Multi-stage builds (кратко)

```dockerfile
FROM golang:1.22 AS builder
WORKDIR /src
COPY . .
RUN go build -o /app

FROM gcr.io/distroless/static
COPY --from=builder /app /app
USER nonroot
ENTRYPOINT ["/app"]
```

В финальном образе **нет** компилятора и исходников — меньше attack surface.

---

## Практика

1. Напишите Dockerfile для static HTML (nginx alpine).
2. Build + run, откройте в браузере.
3. Зайдите `docker exec` и проверьте `whoami`.
4. Запустите `trivy image YOUR_IMAGE` (если установлен).

---

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

1. Чем image отличается от container?
2. Зачем директива `USER`?
3. Почему `:latest` — плохая практика?
4. Где хранятся образы после `docker push`?

---

## Дальше

→ [Kubernetes — введение](kubernetes-vvedenie.md)
