Практика и деплой: registry, теги, best practices
Связь с предыдущими темами
Ранее вы научились запускать контейнеры, собирать образы через Dockerfile, подключать данные (volumes и bind mounts), настраивать сеть и запускать несколько сервисов через Docker Compose.
Теперь логичный шаг к практике и деплою: научиться
правильно именовать и версировать образы (теги)
хранить и распространять образы через registry
безопасно и повторяемо обновлять приложение на сервере
следовать базовым best practices, чтобы не усложнять себе жизньЧто такое registry и зачем он нужен
Registry — это хранилище Docker-образов. Оно решает задачу доставки:
вы собираете образ один раз
загружаете его в registry
на другом компьютере или сервере скачиваете (pull) и запускаетеСамый известный публичный registry — Docker Hub: Docker Hub
Также существуют другие варианты:
GitHub Container Registry: GitHub Container Registry
GitLab Container Registry: GitLab Container RegistryКак устроено имя образа
Имя образа обычно выглядит так:
nginx:1.27
username/myapp:1.0.0Более общий вид:
registry-host/namespace/name:tagПояснение частей:
registry-host — адрес registry (часто опускается, тогда подразумевается Docker Hub)
namespace — пользователь или организация
name — имя репозитория образа
tag — версия (или иной ярлык)Примеры:
postgres:16 означает Docker Hub + официальный образ postgres с тегом 16
ghcr.io/acme/api:2.3.1 означает registry ghcr.io, namespace acme, имя api, тег 2.3.1Теги: что это и почему latest опасен
Тег — это метка версии образа. Новички часто воспринимают теги как “магическое обновление”, но важно понимать:
тег может указывать на разное содержимое в разные моменты времени
если вы не фиксируете тег, вы не фиксируете окружениеПочему не стоит полагаться на latest
Тег latest не означает “самое новое” по строгим правилам. Это просто обычный тег, который кто-то должен поддерживать.
Практические проблемы:
сегодня latest работает, завтра — ломается из-за обновления
при расследовании инцидента трудно понять, какой именно образ был запущенРекомендация для обучения и работы:
для базовых сервисов используйте фиксированные версии: postgres:16, python:3.12-slim
для своих образов используйте явные версии: myapp:1.0.0, myapp:1.1.0Мини-стратегия версионирования для учебных и небольших проектов
Удобный минимум:
1.0.0 как первая стабильная версия
1.0.1 для багфиксов
1.1.0 для новых фич без ломающих изменений
2.0.0 если вы сломали обратную совместимостьВам не обязательно глубоко изучать семантическое версионирование прямо сейчас, но привычка фиксировать версии резко снижает количество сюрпризов.
Практика: собрать, затегировать и загрузить образ в Docker Hub
Ниже — типовой сценарий доставки своего приложения.
!Общая картинка жизненного цикла образа от сборки до запуска на сервере
Предусловия
у вас есть аккаунт в Docker Hub: Docker Hub
вы создали репозиторий, например username/myapp (через интерфейс Docker Hub)Шаг 1. Логин в registry
Команда:
Документация: docker login
Шаг 2. Сборка образа
Допустим, у вас есть Dockerfile в текущей папке.
Шаг 3. Тегирование под имя Docker Hub
Локальный тег myapp:1.0.0 удобен, но для загрузки в Docker Hub нужно имя вида username/myapp:1.0.0.
Документация: docker image tag
Шаг 4. Загрузка (push) в Docker Hub
Документация: docker image push
Шаг 5. Проверка: скачать образ на другом компьютере
На другой машине (или на сервере):
Документация: docker image pull
Шаг 6. Запуск
Если это веб-приложение и оно слушает порт 8000 внутри контейнера:
Практика деплоя через Docker Compose
В реальности удобно деплоить не одиночный контейнер, а набор сервисов.
Пример compose.yaml для сервера
Идея:
app запускается из готового образа (не build на сервере)
db хранит данные в volume
наружу публикуется только то, что нужноЗапуск и обновление обычно выглядят так:
Если вы выпустили новую версию, например username/myapp:1.0.1, вы меняете тег в compose.yaml и повторяете команды.
Best practices, которые реально помогают
Ниже набор практик, которые чаще всего отличают “работает сейчас” от “нормально поддерживается”.
Официальная подборка: Dockerfile best practices
Фиксируйте версии базовых образов
Плохо:
FROM python:latestЛучше:
FROM python:3.12-slimПочему:
повторяемость сборок
меньше внезапных поломокДелайте образы меньше
Практические методы:
используйте *-slim, если подходит
не ставьте лишние пакеты
чистите временные файлы после установки пакетов (особенно в Debian-based образах)
используйте мультистейдж-сборку, если есть компиляцияМеньший образ обычно означает:
быстрее загрузка на сервер
меньше поверхность атаки
быстрее старт в CIНе запускайте приложение от root без необходимости
По умолчанию многие образы работают от root. В production это риск.
Типовой подход в Dockerfile:
Почему это важно:
если приложение уязвимо, атакующий получает меньше прав внутри контейнераСекреты не должны попадать в образ
Плохие варианты:
пароли в Dockerfile через ENV
.env с секретами внутри Git-репозитория
копирование секретов внутрь образа через COPYРабочий минимум для старта:
передавайте секреты как переменные окружения на этапе запуска (Compose environment, .env вне репозитория)
для серьёзных окружений используйте специализированные хранилища секретов (это отдельная тема)Разделяйте конфигурацию и образ
Образ должен быть максимально “универсальным”, а окружение задаётся при запуске:
порты
адреса баз данных
режим работыТак вы сможете запускать один и тот же образ:
локально
на тестовом сервере
в productionменяя только переменные окружения.
Логи пишите в stdout и stderr
Хорошая практика для контейнеров:
приложение пишет логи в stdout и stderr
Docker собирает их, а вы смотрите через docker logs или docker compose logsТак проще:
мониторинг
диагностика
централизованный сбор логовНе публикуйте лишние порты
Правило:
наружу публикуйте только то, к чему вы реально подключаетесь с хоста или извнеНапример, базу данных часто не нужно публиковать на хост, если к ней ходит только app внутри сети Compose.
Используйте healthcheck для зависимых сервисов, когда это важно
depends_on в Compose задаёт порядок старта, но не гарантирует “готовность” сервиса.
Если ваша система чувствительна к старту (например, app должен ждать db), рассмотрите healthcheck.
Пример идеи (упрощённо):
Это не единственный способ, но сам принцип важен: порядок старта и готовность сервиса — разные вещи.
Проверяйте уязвимости образов
Минимум, который стоит знать:
образы могут содержать уязвимые пакеты
полезно регулярно сканировать образыВ Docker-экосистеме для этого есть Docker Scout: Docker Scout
Мини-шпаргалка команд для доставки
| Задача | Команда |
|---|---|
| Собрать образ | docker build -t myapp:1.0.0 . |
| Присвоить тег под registry | docker tag myapp:1.0.0 username/myapp:1.0.0 |
| Загрузить в registry | docker push username/myapp:1.0.0 |
| Скачать на сервер | docker pull username/myapp:1.0.0 |
| Запустить | docker run -d ... username/myapp:1.0.0 |
| Обновить Compose-проект | docker compose pull && docker compose up -d |
Итог
На этом этапе вы умеете выстраивать полный путь:
собрать образ из Dockerfile
корректно проставить тег версии
отправить образ в registry
скачать и запустить на сервере
использовать Compose для повторяемого деплояЭто база, на которую дальше легко накладываются CI, автоматические сборки, тестирование, безопасное хранение секретов и более продвинутые практики эксплуатации.