Docker с нуля: образы, контейнеры и docker compose
Docker позволяет упаковать приложение вместе со всем окружением: нужной версией языка, библиотеками и настройками. Такой пакет одинаково запускается на ноутбуке разработчика, на тестовом сервере и в продакшене. Фраза «у меня работает» перестаёт быть проблемой.
Три понятия, без которых дальше никуда:
- Образ (image) — шаблон, доступный только для чтения: файловая система и инструкция запуска. Собирается по
Dockerfile. - Контейнер (container) — запущенный экземпляр образа. Из одного образа можно запустить сколько угодно контейнеров.
- Том (volume) — хранилище данных, которое живёт дольше контейнера. Без него данные БД пропадут при пересоздании контейнера.
Установить Docker и проверить, что всё работает
Установите Docker и запустите тестовый контейнер.
- Windows и macOS — Docker Desktop с официального сайта. На Windows нужен включённый WSL 2.
- Linux — Docker Engine из официального репозитория (не из стандартного репозитория дистрибутива, там часто старая версия).
docker --version
docker compose version
docker run --rm hello-world
Показать решение Скрыть решение
Если hello-world вывел приветствие, Docker скачал образ из Docker Hub, создал из него контейнер, запустил и (благодаря флагу --rm) удалил после завершения.
На Linux, чтобы не писать sudo перед каждой командой, добавьте пользователя в группу docker и перелогиньтесь:
sudo usermod -aG docker $USER
Запустить готовый образ и пробросить порт
Запустите веб-сервер Nginx так, чтобы он открывался в браузере по адресу http://localhost:8080.
docker run -d --name web -p 8080:80 nginx:alpine
-d— запуск в фоне (detached);--name web— имя контейнера, чтобы не запоминать ID;-p 8080:80— порт 8080 на компьютере → порт 80 внутри контейнера;nginx:alpine— образnginxс тегомalpine(лёгкая сборка).
Показать решение Скрыть решение
Полезные команды для работы с запущенным контейнером:
docker ps # запущенные контейнеры
docker logs -f web # логи в реальном времени
docker exec -it web sh # зайти внутрь контейнера
docker stop web # остановить
docker rm web # удалить
Написать свой Dockerfile
Есть простое PHP-приложение из одного файла. Упакуйте его в образ.
<?php
echo 'Привет из контейнера! PHP ' . PHP_VERSION;
Показать решение Скрыть решение
# Базовый образ: PHP со встроенным Apache
FROM php:8.3-apache
# Копируем код в корень веб-сервера
COPY index.php /var/www/html/
# Документируем порт (на публикацию не влияет)
EXPOSE 80
docker build -t my-php-app .
docker run -d --name app -p 8081:80 my-php-app
Каждая инструкция в Dockerfile создаёт слой. Docker кэширует слои, поэтому то, что меняется редко (установка пакетов), пишут выше, а то, что меняется часто (код), — ниже. Тогда пересборка занимает секунды.
Сохранить данные между перезапусками
Запустите MySQL так, чтобы база не пропадала после удаления контейнера.
Показать решение Скрыть решение
docker volume create mysql-data
docker run -d --name db \
-e MYSQL_ROOT_PASSWORD=secret \
-e MYSQL_DATABASE=app \
-v mysql-data:/var/lib/mysql \
-p 3306:3306 \
mysql:8.0
Теперь docker rm -f db удалит контейнер, но том mysql-data останется. При следующем запуске с тем же -v база будет на месте.
Есть два вида монтирования:
| Вид | Пример | Когда использовать |
|---|---|---|
| Именованный том | -v mysql-data:/var/lib/mysql |
Данные БД, кэши |
| Bind mount (папка с диска) | -v ./src:/var/www/html |
Код при разработке: правки видны сразу |
Поднять несколько сервисов через docker compose
Запускать каждый контейнер длинной командой неудобно. Опишите PHP-приложение и MySQL в одном файле.
Показать решение Скрыть решение
services:
app:
build: .
ports:
- "8081:80"
volumes:
- ./:/var/www/html
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: secret
MYSQL_DATABASE: app
volumes:
- mysql-data:/var/lib/mysql
volumes:
mysql-data:
docker compose up -d # собрать и запустить всё
docker compose ps # статус сервисов
docker compose logs -f app # логи одного сервиса
docker compose down # остановить и удалить контейнеры (тома остаются)
Внутри compose-сети сервисы видят друг друга по имени: из PHP к базе подключаемся по хосту db, а не localhost.
Почистить диск
Через пару месяцев работы Docker занимает десятки гигабайт. Освободите место.
Показать решение Скрыть решение
docker system df # сколько места занято
docker image prune # удалить «висячие» образы
docker container prune # удалить остановленные контейнеры
docker system prune -a # удалить всё неиспользуемое (кроме томов)
docker volume prune удаляет неиспользуемые тома вместе с данными. Запускайте осознанно.
Итог
- Образ — шаблон, контейнер — запущенный экземпляр, том — данные, которые переживают контейнер.
Dockerfileописывает сборку образа,compose.yaml— набор сервисов.- Сервисы в compose обращаются друг к другу по имени сервиса.
Следующий шаг — собрать полноценное окружение для Laravel: PHP-FPM, Nginx, MySQL и Redis (статья «Docker для сайта на Laravel»).