К содержимому

Docker с нуля: образы, контейнеры и docker compose

Docker позволяет упаковать приложение вместе со всем окружением: нужной версией языка, библиотеками и настройками. Такой пакет одинаково запускается на ноутбуке разработчика, на тестовом сервере и в продакшене. Фраза «у меня работает» перестаёт быть проблемой.

Три понятия, без которых дальше никуда:

  • Образ (image) — шаблон, доступный только для чтения: файловая система и инструкция запуска. Собирается по Dockerfile.
  • Контейнер (container) — запущенный экземпляр образа. Из одного образа можно запустить сколько угодно контейнеров.
  • Том (volume) — хранилище данных, которое живёт дольше контейнера. Без него данные БД пропадут при пересоздании контейнера.
Задача 1 Лёгкая

Установить 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
Задача 2 Лёгкая

Запустить готовый образ и пробросить порт

Запустите веб-сервер Nginx так, чтобы он открывался в браузере по адресу http://localhost:8080.

Запуск Nginx в фоне
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             # удалить
Задача 3 Лёгкая

Написать свой Dockerfile

Есть простое PHP-приложение из одного файла. Упакуйте его в образ.

index.php
<?php
echo 'Привет из контейнера! PHP ' . PHP_VERSION;
Показать решение Скрыть решение
Dockerfile
# Базовый образ: 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 кэширует слои, поэтому то, что меняется редко (установка пакетов), пишут выше, а то, что меняется часто (код), — ниже. Тогда пересборка занимает секунды.

Задача 4 Лёгкая

Сохранить данные между перезапусками

Запустите MySQL так, чтобы база не пропадала после удаления контейнера.

Показать решение Скрыть решение
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 Код при разработке: правки видны сразу
Задача 5 Лёгкая

Поднять несколько сервисов через docker compose

Запускать каждый контейнер длинной командой неудобно. Опишите PHP-приложение и MySQL в одном файле.

Показать решение Скрыть решение
compose.yaml
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:
Основные команды compose
docker compose up -d        # собрать и запустить всё
docker compose ps           # статус сервисов
docker compose logs -f app  # логи одного сервиса
docker compose down         # остановить и удалить контейнеры (тома остаются)

Внутри compose-сети сервисы видят друг друга по имени: из PHP к базе подключаемся по хосту db, а не localhost.

Задача 6 Лёгкая

Почистить диск

Через пару месяцев работы 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»).