DevOps
Docker: основы
2013 год. Heroku инженер Solomon Hykes за 5 минут демонстрирует Docker на PyCon. Аудитория аплодирует стоя. Через год GitHub имеет 14 000 звёзд, Docker становится самым быстрорастущим open-source проектом в истории. Идея проста: если приложение работает в контейнере на ноутбуке разработчика - оно точно так же работает на production сервере. 'Works on my machine' исчезает как класс проблемы.
- **Netflix** запускает 700+ типов микросервисов в Docker - каждый деплоится независимо по тысячи раз в день
- **GitHub Actions** каждый CI/CD pipeline - это контейнер с известным окружением, не 'флакирующие тесты из-за версии Python'
- **Cloudflare Workers** isolation model основан на той же namespace-изоляции что и Docker, только без overhead image слоёв
Solomon Hykes и рождение Docker
Docker Inc. выросла из dotCloud - PaaS компании, основанной Solomon Hykes в 2008 году. Внутренний инструмент для изоляции клиентских приложений был представлен публике в марте 2013. Идея не была революционной технически: Linux containers (LXC) существовали с 2008, namespaces с 2002, cgroups с 2006. Революцией стал UX: Dockerfile + image registry + простой CLI превратили сложную системную технологию в доступный инструмент для любого разработчика. К 2016 Docker стал де-факто стандартом контейнеризации, что привело к созданию OCI (Open Container Initiative) - нейтрального стандарта runtime и image format.
Dockerfile
**Dockerfile** - текстовый файл с инструкциями для сборки Docker image. Каждая инструкция создаёт отдельный слой в union filesystem; слои кэшируются и переиспользуются при повторных сборках. Цель - reproducible build: один и тот же Dockerfile даёт идентичный image на любом хосте в любое время.
| Инструкция | Создаёт слой | Назначение |
|---|---|---|
| FROM | Да | Базовый image - отправная точка |
| COPY / ADD | Да | Копирование файлов в image |
| RUN | Да | Выполнение команд при сборке |
| ENV | Да | Переменные окружения |
| EXPOSE | Нет | Документирование порта (не открывает!) |
| CMD / ENTRYPOINT | Нет | Команда запуска контейнера |
**Порядок слоёв критичен для кэша**: инструкции с редко меняющимися данными (установка зависимостей) должны идти перед инструкциями с часто меняющимися (копирование исходников). Один изменённый слой инвалидирует все последующие.
Почему в Dockerfile рекомендуют копировать package.json до копирования исходного кода?
Images
**Docker image** - read-only шаблон для создания контейнеров. Структура: union filesystem из наложенных слоёв. Каждый слой - дельта изменений поверх предыдущего. Идентификация: content-addressable SHA256 digest, тег (name:tag) - мутабельный указатель на digest.
**Базовые images по размеру**: Ubuntu ~77MB, Debian Slim ~80MB, Alpine ~7MB, Distroless ~2MB, Scratch 0MB. Меньший image = быстрее pull в CI/CD, меньше attack surface. Alpine + musl libc подходит для большинства Go и Node приложений.
| Реестр | Назначение | Особенности |
|---|---|---|
| Docker Hub | Публичные official images | По умолчанию в docker pull |
| GitHub Container Registry | Images из GitHub Actions | ghcr.io, интеграция с GHCR |
| AWS ECR / GCR / ACR | Private cloud registry | IAM интеграция, geo-репликация |
| Self-hosted Harbor | On-premises registry | Vulnerability scanning, RBAC |
Два Docker image имеют одинаковый контент (одинаковые слои), но разные теги. Как хранится этот контент в Docker?
Containers
**Docker container** - запущенный экземпляр image. Технически: Linux namespace изоляция (pid, net, mnt, uts, ipc, user) + cgroups для ограничения ресурсов + тонкий writable слой поверх read-only image слоёв. Не виртуальная машина - процессы видны в host OS через `ps`, нет гипервизора.
**OOM Killer**: при достижении memory limit Linux OOM Killer убивает процесс в контейнере (exit code 137). Без `--memory` контейнер может съесть всю память хоста, убив соседние контейнеры. Всегда устанавливай memory limits в production.
Контейнер завершился с exit code 137. Что произошло?
Volumes
**Docker volumes** решают фундаментальную проблему: writable слой контейнера эфемерен - данные исчезают при удалении контейнера. Volumes - механизм persistent хранилища, управляемого Docker daemon вне файловой системы контейнера. Три типа монтирования с разными trade-offs.
| Тип | Синтаксис | Где хранится | Когда использовать |
|---|---|---|---|
| Named Volume | -v mydata:/data | /var/lib/docker/volumes/ | Production: БД, uploads, любые персистентные данные |
| Bind Mount | -v /host/path:/container/path | Произвольный путь на хосте | Разработка: hot reload исходников |
| tmpfs Mount | --tmpfs /tmp | Только RAM | Секреты, временные файлы, тесты |
**Secrets vs Volumes**: пароли и ключи НЕ стоит передавать через `-e` (видны в `docker inspect`). Docker Secrets (Swarm) или Kubernetes Secrets монтируются как tmpfs - в памяти, не на диске, не видны в метаданных контейнера.
Docker volumes - это просто папки на хосте, Docker здесь лишний посредник
Named volumes управляются Docker daemon: портируемы между хостами через volume drivers (NFS, cloud storage), имеют встроенный lifecycle management, поддерживают backup через `docker run --volumes-from`
Bind mounts действительно просто папки на хосте. Named volumes - абстракция поверх: driver может хранить данные на S3, NFS, iSCSI. При переносе контейнера на другой хост данные следуют за ним через volume driver.
PostgreSQL запущен в Docker без volumes. Что произойдёт с данными при `docker rm postgres-container`?
Ключевые идеи
- **Dockerfile** - reproducible build: инструкции слой за слоем, кэш инвалидируется сверху-вниз - порядок инструкций критичен для скорости
- **Image** - read-only шаблон из union filesystem слоёв, идентифицируется SHA256 digest, тег - мутабельный указатель
- **Container** - изолированный процесс через Linux namespaces + cgroups, не VM. Exit 137 = SIGKILL (OOM или принудительный kill)
- **Volume** - persistent хранилище вне ephemeral слоя контейнера: named volumes для production, bind mounts для dev, tmpfs для секретов
Вопросы для размышления
- Компания переходит с bare-metal серверов на Docker. Разработчик предлагает упаковать весь монолит (nginx + app + PostgreSQL) в один контейнер для простоты. Какие конкретные проблемы создаёт такой подход и как их решить?