Skip to main content

Методическая работа 1. Контейнеризованные базы данных

Методическая работа 1. Контейнеризованные базы данных

MySQL • сети • volumes • Dockerfile • Docker Compose • резервное копирование

Параметр Описание
Дисциплина / модуль Системное администрирование. Контейнеризация
Формат Лабораторная работа с пояснениями и заданиями
Ориентировочное время Базовая часть — ориентировочно 8–10 академических часов; профессиональный кейс — ещё 2–3 часа. Один академический час — 45 минут.
Основа Docker Docs: Use containerized databases; материал дополнен

Об учебной среде: Учебные пароли в работе намеренно простые. Их нельзя использовать в реальных системах. Цель лабораторной - понять механизмы Docker, а не настроить production-сервер MySQL.

Редакция от 8 октября 2026 года

1. Цель работы

После первых двух лекций уже известны запуск контейнера, сети, хранилища и сборка образа. Здесь добавляется вводное объяснение Docker Compose, поэтому предварительно выполненный Compose-проект не требуется. В этой работе эти навыки объединяются на примере базы данных MySQL. База данных - хороший пример stateful-сервиса: нам важно не только запустить процесс, но и сохранить данные, обеспечить сетевой доступ, выполнить начальную настройку, а затем корректно остановить и пересоздать контейнер без потери информации.

После выполнения работы студент должен уметь:

  • запускать MySQL из Docker Official Image и читать журналы запуска
  • подключаться к СУБД из контейнера и через другой контейнер; дополнительно — с хостовой системы в необязательном разделе 6
  • объяснять разницу между портом контейнера и опубликованным портом хоста
  • создавать пользовательскую Docker-сеть и использовать встроенное разрешение имен
  • хранить файлы базы данных в named volume и проверять сохранность данных после пересоздания контейнера
  • создавать собственный образ MySQL с SQL-скриптом инициализации
  • описывать MySQL и phpMyAdmin в compose.yaml
  • использовать healthcheck и понимать, почему depends_on без проверки готовности недостаточно
  • создавать резервную копию базы данных и выполнять базовое восстановление
  • называть основные риски при контейнеризации СУБД.

2. Что потребуется

Компонент Требование
Docker Windows с Docker Desktop в режиме Linux containers; встроенный Docker Compose v2
Доступ в Интернет Для загрузки mysql:8.4 и phpmyadmin:5.2.3-apache
Терминал PowerShell 5.1 или 7; Linux-оболочка и SQL-клиент отмечаются отдельно
Браузер Для проверки phpMyAdmin
Свободные порты 8080; для самостоятельного задания — 8081; для стенда инцидента — 8082 либо порт, выданный преподавателем. Порт 3307 нужен только при выполнении необязательного раздела 6.
docker version
docker compose version

Пояснение: docker version должен показывать клиент и сервер; docker compose version — версию Compose. Дополнительно docker info --format '{{.OSType}}' должен вывести linux. Команды ниже выполняются в PowerShell Windows; sudo не добавляется. Ответ о версии ещё не проверяет свободные порты и готовность MySQL.

Ресурсы учебного компьютера

Ресурс Ориентир
Оперативная память Windows Не менее 8 ГБ по требованиям Docker Desktop; для этой работы рекомендуется 16 ГБ.
Процессор и виртуализация 64-разрядный процессор с поддержкой аппаратной виртуализации; виртуализация включена. Для учебной работы рекомендуется 4 логических процессора или больше.
Свободное место Планировать не менее 20 ГБ на диске хранения Docker Desktop для образов, томов и сборок; это учебный запас, а не фиксированный размер работы. Желателен SSD.
Среда Linux containers Поддерживаемая текущим Docker Desktop версия Windows и работоспособный backend, например WSL 2. Установка и обновление среды выполняются до работы.
Нагрузка Обычно одновременно работает один MySQL. Только при проверке восстановления нужны два; остальные учебные серверы останавливаются.

Требования к установке сверяются с Docker Desktop для Windows. Рекомендации к процессору, 16 ГБ RAM и запасу диска относятся к этой лабораторной. Диспетчер задач Windows показывает общую загрузку CPU, память и диск; docker stats --no-stream — использование ресурсов работающими контейнерами. При нехватке памяти сначала останавливаются неиспользуемые учебные серверы; тома для освобождения RAM удалять не требуется.

Как проверить занятый порт средствами Windows

  1. Нажмите Win+R, введите resmon и откройте «Монитор ресурсов».
  2. На вкладке «Сеть» раскройте «Прослушиваемые порты» и найдите нужный порт: 8080, 8081, 8082 или необязательный 3307. Посмотрите имя процесса и PID.
  3. Если это backend Docker Desktop, сопоставьте публикацию порта с выводом docker ps или списком Containers в Docker Desktop. Если это приложение Windows, определите его назначение.
  4. Остановите только известное ненужное учебное приложение либо выберите другой свободный хостовый порт и измените адрес проверки. Не завершайте неизвестные процессы ради освобождения порта.

Дополнительная диагностика в PowerShell Windows:

Get-NetTCPConnection -State Listen | Where-Object { $_.LocalPort -in 3307, 8080, 8081, 8082 } | Select-Object LocalAddress, LocalPort, OwningProcess

OwningProcess — PID процесса Windows. Эта проверка показывает слушающие TCP-порты; отсутствие строки не доказывает, что Docker сможет выполнить публикацию при любых настройках Windows. При отказе bind сначала проверяются полный текст ошибки и настройки публикации, а не изменяются внутренние порты MySQL. Справка Microsoft.

Подготовка Windows и правила выполнения

Сначала создайте только папки. Выберите удобное место на диске Windows и в Проводнике создайте папку docker-db-lab. Все перечисленные ниже объекты на этом шаге — папки, а не файлы:

  1. Внутри docker-db-lab создайте четыре папки: backup, mysql-custom, compose и assignment.
  2. Откройте папку mysql-custom и внутри неё создайте папку scripts.

Файлы создаются позже, по мере выполнения соответствующих разделов. Содержимое файлов приведено в этих разделах; заранее создавать пустые файлы не требуется. SQL — язык запросов, а не название папки или файла.

Имя файла В какой папке находится Когда и как появляется
Dockerfile — файл без расширения docker-db-lab\mysql-custom Сохранить редактором в разделе 10.2.
01-init.sql — файл SQL docker-db-lab\mysql-custom\scripts Сохранить редактором в разделе 10.3.
.env — файл с точным именем .env docker-db-lab\compose Сохранить редактором в разделе 11.1.
compose.yaml — файл YAML docker-db-lab\compose Сохранить редактором в разделе 11.2.
dockerlab-backup.sql — файл SQL-дампа docker-db-lab\backup Создаётся командами резервного копирования в разделе 9.1. Вручную не заполняется.
Файлы самостоятельного проекта docker-db-lab\assignment Создать в разделе 12: отдельные compose.yaml и .env; дополнительные файлы зависят от выбранного решения.

Итоговая структура основной части работы. Это схема для чтения, а не команды. При подготовке создаются только строки с пометкой «папка»; строки «файл» появятся на следующих этапах.

docker-db-lab/                    [папка]
├── backup/                       [папка]
│   └── dockerlab-backup.sql      [файл; раздел 9.1]
├── mysql-custom/                 [папка]
│   ├── Dockerfile                [файл; раздел 10.2]
│   └── scripts/                  [папка]
│       └── 01-init.sql           [файл; раздел 10.3]
├── compose/                      [папка]
│   ├── .env                      [файл; раздел 11.1]
│   └── compose.yaml              [файл; раздел 11.2]
└── assignment/                   [папка; отдельный проект раздела 12]

В Проводнике включите отображение расширений имён файлов. В Windows 11: «Вид → Показать → Расширения имён файлов»; в Windows 10: вкладка «Вид → Расширения имён файлов». При сохранении редактором выберите тип «Все файлы», если редактор предлагает добавить .txt. Проверьте точные имена: Dockerfile, а не Dockerfile.txt; .env, а не .env.txt; compose.yaml, а не compose.yaml.txt. Кодировка файлов — UTF-8 без BOM; отступы YAML — пробелы, а не табуляция. Справка Microsoft о расширениях имён файлов.

Где открыть PowerShell. Для резервного копирования и восстановления — в docker-db-lab; для сборки образа — в docker-db-lab\mysql-custom; для основного Compose-проекта — в docker-db-lab\compose; для самостоятельного проекта — в docker-db-lab\assignment. Откройте нужную папку в Проводнике, введите powershell в адресной строке и нажмите Enter. Проверьте, что путь в приглашении терминала заканчивается нужной папкой. У команд с относительными путями ./backup/... и docker build ... . результат зависит от текущей папки.

Имена учебных объектов начинаются с db-lab-. До первого запуска проверяется, что они свободны. Найденные объекты не удаляются автоматически: сначала определяется их назначение. Порты могут занимать и обычные программы Windows. Если 8080 занят контейнером из лекций, этот известный учебный контейнер останавливается, либо выбирается свободный порт с изменением адреса проверки.

docker ps -a --filter name=db-lab-
docker network ls --filter name=db-lab-
docker volume ls --filter name=db-lab-

Основной маршрут — PowerShell. Команды Docker записаны одной строкой без Bash-переносов. После docker exec -it … bash команды выполняются в Linux-оболочке контейнера. При интерактивном запуске mysql -u root -p появляется приглашение mysql>: SQL вводится в нём. Команды docker exec … mysql … -e "..." запускаются из PowerShell, выполняют указанный запрос и завершают клиент автоматически. exit; закрывает интерактивный SQL-клиент, exit — оболочку контейнера. Для сборки и Compose терминал открывается в соответствующей папке через Проводник. Блоки вывода и структуры файлов не являются командами.

Каждый этап включает задачу, подготовку, команды, ожидаемый результат и объяснение. Работа продолжается после проверки предыдущего этапа. Имена, объёмы, порты и время запуска могут отличаться; при продолжении другого занятия сохраняются учебные данные и исходные файлы.

3. Теория перед началом

3.1. Почему база данных отличается от обычного веб-контейнера

Контейнеры удобно пересоздавать. Для stateless-приложения это обычно не проблема: новый контейнер стартует из того же образа и получает состояние из внешних сервисов. База данных, наоборот, хранит изменяемое состояние - таблицы, индексы, журналы транзакций и служебные файлы. Если сохранить эти файлы только в writable layer контейнера, удаление контейнера удалит и его writable layer. Поэтому для СУБД почти всегда требуется отдельное постоянное хранилище.

Важно: Перезапуск контейнера и удаление контейнера - разные операции. После docker restart данные в writable layer остаются, потому что контейнер тот же. После docker rm writable layer удаляется. В лабораторной мы специально удалим контейнер, чтобы проверить работу volume.

3.2. Образ, контейнер и каталог данных

Объект Назначение Пример в работе
Image Неизменяемый шаблон для создания контейнера mysql:8.4
Container Созданный экземпляр образа, который может работать или быть остановленным db-lab-mysql
Writable layer Изменяемый слой конкретного контейнера временные файлы контейнера
Named volume Хранилище, жизненный цикл которого отделен от контейнера db-lab-data
Docker network Изолированная виртуальная сеть контейнеров db-lab-net

3.3. Почему в работе используется mysql:8.4, а не mysql:latest

Тег latest удобен для знакомства, но он может начать указывать на новую основную версию. Для лабораторной работы важна воспроизводимость: одинаковые команды должны давать одинаковый результат у всей группы. Поэтому далее используется ветка mysql:8.4. Она фиксирует ветку, но не конкретную сборку: исправления внутри 8.4 могут обновлять тег. Для точной воспроизводимости фиксируется проверенный digest. Это не означает, что тег latest запрещен; это означает, что в учебных и особенно в производственных конфигурациях версию образа лучше выбирать осознанно.

3.4. Переменные окружения официального образа MySQL

Переменная Что делает
MYSQL_ROOT_PASSWORD Задает пароль встроенной учетной записи root при первичной инициализации.
MYSQL_DATABASE Создает указанную базу данных при первичной инициализации.
MYSQL_USER Создает дополнительного пользователя. Используется вместе с MYSQL_PASSWORD.
MYSQL_PASSWORD Пароль дополнительного пользователя.

Важно: Эти параметры инициализации не предназначены для изменения уже существующей БД. Если /var/lib/mysql содержит ранее созданную базу, entrypoint сохраняет существующие данные и не переинициализирует их.

3.5. Порты и сети

MySQL внутри контейнера слушает TCP-порт 3306. Этот порт существует в сетевом пространстве контейнера и сам по себе не обязан быть доступен с хоста. Публикация порта создается отдельно параметром -p или ports в Compose.

127.0.0.1:3307:3306
^^^^^^^^^ ^^^^ ^^^^
IP хоста   |    |
порт хоста |
           порт контейнера

В этой методичке для локального доступа используется привязка к 127.0.0.1. Это значит, что опубликованный порт предназначен только для локальной машины. Если приложение работает в другом контейнере той же Docker-сети, публиковать порт базы данных на хост вообще не требуется: контейнеры обращаются друг к другу по имени и внутреннему порту.

3.6. Термины базы данных и Compose

СУБД — система управления базами данных; MySQL включает сервер mysqld и клиент mysql. Сервер хранит данные и выполняет запросы; клиент подключается к серверу. База данных объединяет таблицы, таблица содержит строки и столбцы. SQL — язык запросов; SELECT читает строки, CREATE TABLE создаёт таблицу, INSERT добавляет записи. Учетная запись MySQL отличается от пользователя Windows и задаёт права доступа к данным. В работе приложение подключается под labuser; root используется для административных операций.

Логический дамп — файл SQL с описанием объектов и данными для восстановления, а не копия каталога /var/lib/mysql. Проверка резервной копии включает загрузку дампа в отдельный сервер и сравнение результатов SELECT.

Docker Compose описывает несколько сервисов в YAML-файле. Сервис — описание контейнера и его параметров; проект — группа сервисов, сетей и томов одного приложения. Compose создаёт общую сеть, в которой имя сервиса становится адресуемым DNS-именем. .env предоставляет значения для подстановки ${…}; сам файл не передаёт все переменные контейнеру без соответствующей настройки environment или env_file. Имя проекта влияет на имена создаваемых объектов.

Особенность официального MySQL image. Образ объявляет VOLUME /var/lib/mysql. Если источник не задан при run, Docker создаёт анонимный том с автоматически сгенерированным именем. Поэтому данные MySQL в первых этапах уже находятся вне writable layer. Новый run без подключения прежнего тома создаёт новый том; старые данные не видны новому контейнеру. В этапах 1–4 временный анонимный том удаляется явно через rm -v после stop. В этапе 5 используется именованный том, который сохраняется отдельно. Dockerfile официального MySQL.

3.7. Как читать SQL и команды клиента

Элемент Значение в этой работе
PRIMARY KEY Первичный ключ: значение однозначно определяет строку и не повторяется. Здесь это столбец id.
AUTO_INCREMENT MySQL автоматически назначает очередное значение id, когда оно не указано в INSERT. После удаления строки её номер не обязан использоваться повторно.
NOT NULL Столбец не может содержать NULL — отсутствие значения. Пустая строка и NULL различаются.
INT, VARCHAR(n) Целое число и строка длиной до n символов.
-D dockerlab Выбирает базу данных для SQL-клиента.
-N Не выводит строку с названиями столбцов; SELECT 1 показывает только результат.
-e "..." Выполняет SQL из аргумента и завершает клиент; интерактивное приглашение mysql> не открывается.
ORDER BY id Сортирует результат по id для удобного сравнения. Без ORDER BY порядок строк не гарантирован.

4. Этап 1. Запуск контейнера MySQL

Сначала загрузите выбранный образ:

docker pull mysql:8.4

Запустите MySQL в фоновом режиме:

docker run --name db-lab-mysql  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_DATABASE=dockerlab  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  -d mysql:8.4

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-mysql. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-mysql mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"

Ожидаемый результат: создаётся db-lab-mysql без публикации порта и с временным анонимным томом. Дальше проверяются его состояние, точки подключения и готовность СУБД.

docker inspect -f '{{json .Mounts}}' db-lab-mysql

4.1. Разбор команды

Фрагмент Назначение
--name db-lab-mysql Читаемое имя контейнера.
-e ... Передает переменную окружения внутрь контейнера.
-d Detached mode: контейнер работает в фоне.
mysql:8.4 Образ и выбранная ветка версии.

Проверьте состояние:

docker ps

Если контейнер не отображается, покажите в том числе остановленные контейнеры:

docker ps -a

Посмотрите журнал запуска MySQL:

docker logs db-lab-mysql

Пояснение: На первом запуске MySQL требуется время на создание системных таблиц и настройку каталога данных. Строка о запуске контейнера в docker ps не всегда означает, что СУБД уже готова принимать соединения. Для диагностики сначала смотрите docker logs.

5. Этап 2. Работа внутри контейнера

Откройте интерактивную оболочку контейнера:

docker exec -it db-lab-mysql bash

Обратите внимание: вы не запускаете новый контейнер. docker exec запускает дополнительный процесс внутри уже работающего контейнера db-lab-mysql.

hostname
pwd
mysql --version
cat /etc/os-release

Запустите клиент MySQL:

mysql -u root -p

Введите пароль: lab-root-pass. Затем выполните:

SHOW DATABASES;
SELECT VERSION();
SELECT CURRENT_USER();
USE dockerlab;
SHOW TABLES;

Предскажите результат: какие значения id получат две строки, если INSERT не указывает id? После запроса сопоставьте предположение с выводом SELECT и объясните роль AUTO_INCREMENT.

Создадим небольшую таблицу, которая понадобится далее:

CREATE TABLE students (
  id INT PRIMARY KEY AUTO_INCREMENT,
  name VARCHAR(100) NOT NULL,
  group_name VARCHAR(20) NOT NULL
);
INSERT INTO students (name, group_name) VALUES
('Ivan Ivanov', 'LAB'),
('Petr Petrov', 'LAB');
SELECT * FROM students;

Примечание: SQL здесь нужен только на базовом уровне. Главное в лабораторной - не синтаксис СУБД, а связь между процессом MySQL, контейнером, сетью и постоянным хранилищем.

Для выхода из клиента выполните exit;, затем еще раз exit для выхода из bash контейнера.

Для самопроверки: Почему exec не создал новую базу, а запустил дополнительный процесс? Чем SQL-клиент отличается от сервера MySQL?

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
db-lab-mysql запущен; students содержит две строки Временный анонимный том Порт MySQL не опубликован

6. Этап 3. Подключение к базе данных с хоста — необязательно

Необязательный этап. Выполняется для знакомства с клиентом MySQL на Windows. Если клиент не установлен или этот способ подключения не требуется, пропустите раздел 6 целиком и переходите к разделу 7. Контейнер db-lab-mysql после раздела 5 остаётся запущенным; команды в начале раздела 7 подходят для обоих маршрутов. Пропуск раздела 6 не препятствует выполнению основной работы и не снижает оценку.

Текущий контейнер не имеет опубликованного порта. Убедитесь в этом:

docker port db-lab-mysql

Ожидается отсутствие сопоставления 3306 с портом хоста. Для изменения публикации создаётся новый контейнер. Таблица students, созданная на этапе 2, намеренно удаляется вместе с временным анонимным томом командой rm -v. Её содержимое предварительно фиксируется в отчёте. Именованный том для сохраняемых данных появится в этапе 5.

docker stop --timeout 60 db-lab-mysql
docker rm -v db-lab-mysql
docker run --name db-lab-mysql  -p 127.0.0.1:3307:3306  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_DATABASE=dockerlab  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  -d mysql:8.4

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-mysql. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-mysql mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"
docker port db-lab-mysql
docker ps

6.1. Что означает 3307:3306

Клиент на хостовой системе подключается к 127.0.0.1:3307. Docker перенаправляет соединение на порт 3306 внутри контейнера. Мы используем 3307 на хосте, чтобы не конфликтовать с MySQL, который может быть установлен локально и использовать 3306.

6.2. Проверка клиентом MySQL

Если на хосте установлен клиент mysql, выполните:

mysql -h 127.0.0.1 -P 3307 -u labuser -p

Пароль: lab-user-pass. После входа:

USE dockerlab;
SHOW TABLES;

Примечание: В новом контейнере таблица students отсутствует: предыдущий временный анонимный том был явно удалён, а run создал новый. Это не пример хранения MySQL в writable layer. Без rm -v старый анонимный том мог бы остаться неиспользуемым, но новый run автоматически к нему не подключается.

6.3. Проверка через графический клиент

В HeidiSQL, DBeaver, MySQL Workbench или другом графическом клиенте используйте следующие параметры. Достаточно одного клиента; установка нескольких не требуется. В HeidiSQL создайте новое подключение к MySQL через TCP/IP, укажите хост, порт, пользователя и пароль из таблицы. После подключения выберите базу dockerlab:

Параметр Значение
Host 127.0.0.1
Port 3307
Database dockerlab
User labuser
Password lab-user-pass

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
Если раздел 6 выполнен: db-lab-mysql запущен; прежняя students удалена Новый анонимный том 127.0.0.1:3307 → 3306
Если раздел 6 пропущен: db-lab-mysql остался после раздела 5 Анонимный том со students Порт не опубликован

7. Этап 4. Связь двух контейнеров через Docker network

В реальном приложении база данных часто должна быть доступна веб-приложению, но не обязана быть опубликована на сетевых интерфейсах хоста. Для этого сервисы помещают в одну пользовательскую Docker-сеть.

docker stop --timeout 60 db-lab-mysql
docker rm -v db-lab-mysql
docker network create db-lab-net

Запустите MySQL без -p, но подключите его к созданной сети:

docker run --name db-lab-mysql  --network db-lab-net  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_DATABASE=dockerlab  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  -d mysql:8.4

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-mysql. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-mysql mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"

Теперь запустите phpMyAdmin в той же сети:

docker run --name db-lab-admin  --network db-lab-net  -p 127.0.0.1:8080:80  -e PMA_HOST=db-lab-mysql  -e PMA_PORT=3306  -d phpmyadmin:5.2.3-apache

7.1. Почему PMA_HOST равен db-lab-mysql

В пользовательской Docker-сети контейнеры получают встроенное разрешение имен. Имя контейнера db-lab-mysql становится сетевым именем, которое другой контейнер этой сети может разрешить в IP-адрес. phpMyAdmin обращается к db-lab-mysql:3306 напрямую внутри Docker-сети. Порт 3306 при этом не опубликован на хост.

docker network inspect db-lab-net

Браузер Windows обращается к порту 8080, Docker передаёт HTTP на порт 80 phpMyAdmin; phpMyAdmin подключается к db-lab-mysql:3306 внутри db-lab-net.

Рисунок 1. Маршрут соединения на этапе 4. Браузер работает с веб-интерфейсом phpMyAdmin, а phpMyAdmin — с MySQL. Для открытия интерфейса используйте адрес из следующего пункта.

7.2. Проверка через браузер

Откройте в браузере http://127.0.0.1:8080. Для входа используйте labuser и lab-user-pass. Убедитесь, что база dockerlab отображается.

Важно: Не используйте адрес localhost:3306 между двумя контейнерами. Для процесса внутри phpMyAdmin localhost означает сам контейнер phpMyAdmin, а не контейнер MySQL.

Для самопроверки: Как phpMyAdmin получил доступ к MySQL без -p у базы? Почему PMA_HOST использует имя контейнера и порт 3306?

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
db-lab-mysql и db-lab-admin запущены в db-lab-net У MySQL временный анонимный том phpMyAdmin: 127.0.0.1:8080; MySQL не опубликован

8. Этап 5. Постоянное хранение данных в volume

Теперь перейдём от временного анонимного тома к именованному db-lab-data. Данные уже хранились вне writable layer; именованный том проще найти и явно подключить к новому контейнеру. Проверим это пересозданием контейнера.

docker stop --timeout 60 db-lab-admin db-lab-mysql
docker rm -v db-lab-admin db-lab-mysql
docker volume create db-lab-data
docker volume ls

Запустите MySQL с named volume. В официальном образе каталог данных MySQL - /var/lib/mysql.

docker run --name db-lab-mysql  --network db-lab-net  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_DATABASE=dockerlab  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  --mount type=volume,src=db-lab-data,dst=/var/lib/mysql  -d mysql:8.4

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-mysql. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-mysql mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"

Пояснение: В Docker существуют две популярные формы записи mounts: -v и --mount. Здесь используется --mount, потому что она более явная: видно тип, источник и точку назначения.

Ожидаемый результат: MySQL принимает SELECT 1. Проверьте подключение volume:

docker inspect db-lab-mysql

В разделе Mounts найдите Type: volume, Name: db-lab-data и Destination: /var/lib/mysql.

8.1. Создаем данные

Запрос создания и наполнения выполняется один раз на новом учебном томе. При повторной проверке выполняется только SELECT: повторный INSERT добавит новые строки.

docker exec db-lab-mysql mysql  -u labuser -plab-user-pass  -D dockerlab  -e "CREATE TABLE IF NOT EXISTS inventory (id INT PRIMARY KEY AUTO_INCREMENT, item VARCHAR(100)); INSERT INTO inventory (item) VALUES ('router'), ('switch'); SELECT * FROM inventory;"

Важно: Передача пароля прямо в командной строке удобна только для лабораторной. MySQL может вывести предупреждение о небезопасном использовании пароля в CLI. В production секреты не следует хранить в истории shell и compose.yaml в открытом виде.

Предскажите результат: исчезнут ли router и switch после удаления контейнера? Что изменится, если новый контейнер подключить к другому пустому тому? После раздела 8.3 объясните результат через источник mount, а не только через имя контейнера.

8.2. Удаляем контейнер, но сохраняем volume

docker stop --timeout 60 db-lab-mysql
docker rm db-lab-mysql
docker volume ls

Контейнера больше нет, но db-lab-data должен остаться. Это и есть отделение данных от контейнера.

8.3. Создаем новый контейнер с тем же volume

docker run --name db-lab-mysql  --network db-lab-net  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_DATABASE=dockerlab  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  --mount type=volume,src=db-lab-data,dst=/var/lib/mysql  -d mysql:8.4

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-mysql. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-mysql mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"
docker exec db-lab-mysql mysql -u labuser -plab-user-pass -D dockerlab -e "SELECT * FROM inventory;"

Если вы видите router и switch, опыт выполнен правильно. Таблица хранится не в пересозданном контейнере, а в volume.

Пояснение: Переменные MYSQL_DATABASE, MYSQL_USER и другие переданы повторно для читаемости команды. Поскольку volume уже содержит инициализированную БД, официальный entrypoint не создает ее заново и не меняет существующие учетные данные.

Для самопроверки: Что сохранилось после удаления db-lab-mysql и почему новый контейнер увидел inventory? Почему повторное MYSQL_PASSWORD не меняет старый пароль?

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
db-lab-mysql пересоздан; inventory содержит router и switch; db-lab-admin удалён db-lab-data подключён к /var/lib/mysql; сеть db-lab-net сохранена Порты не опубликованы; phpMyAdmin на этом этапе не запущен

9. Резервное копирование и проверка восстановления

Volume повышает живучесть данных при пересоздании контейнера, но volume не является резервной копией. Ошибочное удаление таблицы, повреждение данных или удаление самого volume также приведет к потере информации. Поэтому администратор должен уметь создавать логические дампы.

SQL-дамп исходной базы сохраняется в Windows, восстанавливается в отдельный контейнер с отдельным томом; строки inventory сравниваются запросами SELECT.

Рисунок 2. На схеме показана inventory; в практической проверке дампа дополнительно сравнивается lab_notes. Проверка резервной копии: исходный том db-lab-data и том восстановления db-lab-restore-data — разные хранилища. Оба контейнера работают в одном Docker Desktop; на схеме они разделены для показа источника и восстановления. Между ними передаётся SQL-дамп, а результат проверяется сравнением строк.

9.1. Создание SQL-дампа без перенаправления PowerShell

В резервной копии проверяются две таблицы: inventory с двумя строками и lab_notes с одной контрольной записью. На этом этапе исходный сервер db-lab-mysql работает; PowerShell открыт в docker-db-lab. Следующий блок подготовки выполняется один раз:

docker exec db-lab-mysql mysql -ulabuser -plab-user-pass -D dockerlab -e "CREATE TABLE lab_notes (id INT PRIMARY KEY, note VARCHAR(120) NOT NULL); INSERT INTO lab_notes VALUES (1, 'backup verification'); SELECT * FROM inventory ORDER BY id; SELECT * FROM lab_notes ORDER BY id;"

Если этап подготовки уже выполнен, повторяются только SELECT; повторный CREATE TABLE здесь не нужен. Зафиксируйте все id и значения обеих таблиц до дампа.

Подготовка: db-lab-mysql работает, inventory содержит router и switch, lab_notes — запись backup verification; PowerShell открыт в корне docker-db-lab, папка backup создана в Проводнике. Дамп записывается mysqldump внутри контейнера, затем docker cp копирует готовый файл в Windows. Это сохраняет байты файла и избегает текстового перенаправления PowerShell 5.1, которое может изменить кодировку. Файл backup/dockerlab-backup.sql создаётся командой резервного копирования, а не вводится редактором вручную.

docker exec db-lab-mysql sh -c 'exec mysqldump -uroot -p$MYSQL_ROOT_PASSWORD --single-transaction --no-tablespaces --databases dockerlab > /tmp/dockerlab-backup.sql'
if ($LASTEXITCODE -ne 0) { throw 'Backup failed; do not continue' }
docker cp db-lab-mysql:/tmp/dockerlab-backup.sql ./backup/dockerlab-backup.sql
if ($LASTEXITCODE -ne 0) { throw 'Copy failed; do not continue' }
Get-Item ./backup/dockerlab-backup.sql
Get-Content -Encoding UTF8 ./backup/dockerlab-backup.sql -TotalCount 20

Ожидаемый результат: дамп создан, файл имеет ненулевой размер и содержит SQL. Предупреждение MySQL о пароле в командной строке возможно; успех проверяется кодом завершения. Операторы > и $MYSQL_ROOT_PASSWORD находятся внутри аргумента sh -c и обрабатываются Linux-оболочкой контейнера. На хосте не используется > для SQL-дампа.

--single-transaction подходит для согласованной копии транзакционных таблиц InnoDB; во время копирования не выполняются изменения структуры таблиц. Это не универсальная гарантия для любого движка, всех баз, пользователей или настроек сервера. --databases включает создание выбранной базы, --no-tablespaces исключает сведения tablespaces. Восстанавливается dockerlab, а учётные записи исходного сервера этим дампом не переносятся. mysqldump.

Предскажите результат: будет ли новая база dockerlab доступна на сервере восстановления до импорта? Перенесёт ли дамп пользователя labuser? После восстановления объясните, почему здесь используются отдельный том и учётная запись root нового сервера.

9.2. Восстановление в отдельный контейнер

Имена db-lab-restore и db-lab-restore-data должны быть свободны. Источник и его том не изменяются. Создаётся отдельный сервер с собственным паролем root.

docker volume create db-lab-restore-data
docker run --name db-lab-restore -e MYSQL_ROOT_PASSWORD=restore-root-pass --mount type=volume,src=db-lab-restore-data,dst=/var/lib/mysql -d mysql:8.4

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-restore. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-restore mysql --protocol=TCP -h 127.0.0.1 -uroot -prestore-root-pass -N -e "SELECT 1;"

После успешной проверки готовности выполняется восстановление. PowerShell остаётся в корне docker-db-lab.

docker cp ./backup/dockerlab-backup.sql db-lab-restore:/tmp/dockerlab-backup.sql
if ($LASTEXITCODE -ne 0) { throw 'Copy failed; do not continue' }
docker exec db-lab-restore sh -c 'exec mysql -uroot -p$MYSQL_ROOT_PASSWORD < /tmp/dockerlab-backup.sql'
if ($LASTEXITCODE -ne 0) { throw 'Restore failed; do not continue' }
docker exec db-lab-restore mysql -uroot -prestore-root-pass -D dockerlab -e "SELECT * FROM inventory ORDER BY id; SELECT * FROM lab_notes ORDER BY id;"
docker exec db-lab-mysql mysql -ulabuser -plab-user-pass -D dockerlab -e "SELECT * FROM inventory ORDER BY id; SELECT * FROM lab_notes ORDER BY id;"

Ожидаемый результат: Все id и значения обеих таблиц совпадают на исходном и восстановленном сервере: inventory — две строки, lab_notes — одна. Сам факт наличия непустого файла не доказывает успешное восстановление. Оператор < в этом варианте исполняется внутри контейнера; вставлять его непосредственно в PowerShell не требуется.

Для самопроверки: почему dump сохраняется отдельно от volume, а восстановление выполняется в новый том? Как сравнение SELECT подтверждает восстановление учебных данных?

9.3. Дополнительный опыт: потеря таблицы и восстановление — необязательно

Опыт выполняется только после успешного сравнения обеих таблиц и наличия проверенного файла backup/dockerlab-backup.sql. Удаление моделируется исключительно на сервере восстановления db-lab-restore, подключённом к db-lab-restore-data. Исходный db-lab-mysql и его db-lab-data не изменяются. Сначала выполните inspect и проверьте имя контейнера и тома:

docker inspect -f '{{.Name}} {{json .Mounts}}' db-lab-restore

Продолжайте только если имя — /db-lab-restore, а источник /var/lib/mysql — db-lab-restore-data. Затем намеренно удалите inventory на этом учебном сервере:

docker exec db-lab-restore mysql -uroot -prestore-root-pass -D dockerlab -e "DROP TABLE inventory; SHOW TABLES;"

Ожидается отсутствие inventory при сохранении lab_notes. Не создавайте inventory вручную: восстановите её из ранее проверенного дампа тем же способом, что в разделе 9.2:

В этом опыте повторно загружается полный дамп базы данных. Это приводит к восстановлению таблиц в состояние на момент создания копии и может заменить изменения, сделанные после резервного копирования. В реальной эксплуатации перед восстановлением необходимо оценить последствия для актуальных данных.

docker cp ./backup/dockerlab-backup.sql db-lab-restore:/tmp/dockerlab-backup.sql
if ($LASTEXITCODE -ne 0) { throw 'Copy failed; do not continue' }
docker exec db-lab-restore sh -c 'exec mysql -uroot -p$MYSQL_ROOT_PASSWORD < /tmp/dockerlab-backup.sql'
if ($LASTEXITCODE -ne 0) { throw 'Restore failed; do not continue' }
docker exec db-lab-restore mysql -uroot -prestore-root-pass -D dockerlab -e "SELECT * FROM inventory ORDER BY id; SELECT * FROM lab_notes ORDER BY id;"

Сравните результат с исходными SELECT: восстановлены две строки inventory и одна строка lab_notes, включая прежние id и значения. Объясните, почему наличие именованного тома само по себе не защитило от DROP TABLE.

9.4. Освобождение ресурсов после проверки дампа

Сохраните выводы SELECT и результаты опыта. Серверы источника и восстановления дальше не нужны; корректно остановите их, сохранив контейнеры и тома:

docker stop --timeout 60 db-lab-mysql db-lab-restore

Если результат нужно перепроверить, запускается только нужный существующий контейнер через docker start и ожидается готовность MySQL. Повторять создание таблиц и импорт ради продолжения работы не требуется.

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
db-lab-mysql и db-lab-restore остановлены после проверки; SELECT обеих таблиц сохранены Отдельные db-lab-data и db-lab-restore-data; SQL-дамп в папке backup Windows Порты обоих серверов не опубликованы

10. Этап 6. Собственный образ MySQL с инициализацией

Официальный образ MySQL умеет выполнять скрипты из каталога /docker-entrypoint-initdb.d при первичной инициализации пустого каталога данных. Перед этапом в Проводнике открывается mysql-custom/scripts, SQL сохраняется как 01-init.sql, а Dockerfile — в mysql-custom; затем PowerShell открывается в mysql-custom. Используем это, чтобы получить образ, который автоматически создает таблицу.

10.1. Структура проекта

mysql-custom/          [папка]
├── Dockerfile         [файл без расширения]
└── scripts/           [папка]
    └── 01-init.sql    [файл SQL]

10.2. Dockerfile

Редактором создайте файл с точным именем Dockerfile без расширения в папке docker-db-lab\mysql-custom и сохраните в нём следующий текст:

# syntax=docker/dockerfile:1
FROM mysql:8.4
# Это не секрет: задаем только имя БД.
ENV MYSQL_DATABASE=dockerlab
COPY ./scripts/ /docker-entrypoint-initdb.d/

Важно: Не записывайте MYSQL_ROOT_PASSWORD или MYSQL_PASSWORD в Dockerfile через ENV. Значение станет частью метаданных образа и будет распространяться вместе с ним.

10.3. SQL-скрипт 01-init.sql

Редактором создайте файл 01-init.sql в папке docker-db-lab\mysql-custom\scripts и сохраните в нём следующий SQL:

CREATE TABLE IF NOT EXISTS dockerlab.devices (
  id INT PRIMARY KEY AUTO_INCREMENT,
  hostname VARCHAR(100) NOT NULL,
  ip_address VARCHAR(45) NOT NULL
);
INSERT INTO dockerlab.devices (hostname, ip_address) VALUES
('srv-docker-01', '10.10.10.10'),
('gw-lab-01', '10.10.10.1');

10.4. Сборка и запуск

docker build -t db-lab-mysql-image:1.0 .
docker volume create db-lab-custom-data
docker run --name db-lab-custom  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  --mount type=volume,src=db-lab-custom-data,dst=/var/lib/mysql  -d db-lab-mysql-image:1.0

Проверка готовности: следующая команда должна вывести 1. При ошибке соединения или ещё не созданной базе нужно подождать несколько секунд и повторить её; при постоянной ошибке проверяются логи db-lab-custom. К последующим запросам переходят после успешного результата. Для SQL-клиента предупреждение о пароле в CLI допустимо.

docker exec db-lab-custom mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"
docker exec db-lab-custom mysql -u labuser -plab-user-pass -D dockerlab -e "SELECT * FROM devices;"

10.5. Критически важная особенность init-скриптов

Скрипты из /docker-entrypoint-initdb.d выполняются при первичной инициализации новой базы. Если db-lab-custom-data уже содержит MySQL, удаление контейнера, пересборка образа и создание нового контейнера с тем же volume не заставят 01-init.sql выполняться повторно. Это защищает существующую базу от случайной переинициализации.

Опыт с прежним томом. Предскажите, добавятся ли ещё две строки из 01-init.sql, если создать новый контейнер из того же образа с тем же томом. Перед удалением убедитесь, что SELECT из devices показал две строки. Затем выполните:

docker stop --timeout 60 db-lab-custom
docker rm db-lab-custom
# Volume db-lab-custom-data НЕ удаляем - база уже инициализирована.

Пересоздайте контейнер, явно подключив прежний именованный том:

docker run --name db-lab-custom  -e MYSQL_ROOT_PASSWORD=lab-root-pass  -e MYSQL_USER=labuser  -e MYSQL_PASSWORD=lab-user-pass  --mount type=volume,src=db-lab-custom-data,dst=/var/lib/mysql  -d db-lab-mysql-image:1.0

Дождитесь результата 1 в проверке готовности; если MySQL ещё запускается, повторите только эту команду:

docker exec db-lab-custom mysql --protocol=TCP -h 127.0.0.1 -ulabuser -plab-user-pass -D dockerlab -N -e "SELECT 1;"

После успешной проверки прочитайте таблицу:

docker exec db-lab-custom mysql -u labuser -plab-user-pass -D dockerlab -e "SELECT * FROM devices ORDER BY id;"

Ожидаемый результат: остаются те же две строки и прежние id; повторного INSERT из init-скрипта не было. Объясните, почему новый контейнер не означает новую базу.

После фиксации результата остановите этот контейнер, сохранив его и том для отчёта:

docker stop --timeout 60 db-lab-custom

Дополнительный опыт — необязательно: измените начальные записи в 01-init.sql и соберите образ под новым тегом. Для проверки нового скрипта создайте другой контейнер и другой пустой именованный том. Прежний том db-lab-custom-data сохраните. Сравните данные двух баз: изменённый init-скрипт должен проявиться только при первичной инициализации нового тома.

Пояснение: Если во время лабораторной вы изменили 01-init.sql и хотите проверить скрипт с нуля, используйте новый volume или удалите учебный volume. Никогда не переносите эту привычку на production без резервной копии.

Для самопроверки: Когда выполняется 01-init.sql? Что нужно подготовить для проверки изменённого скрипта, не уничтожая прежний том?

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
db-lab-custom, db-lab-mysql и db-lab-restore остановлены; их тома и результаты опытов сохранены Три отдельных именованных тома; у db-lab-custom — db-lab-custom-data Порты MySQL не опубликованы; порт 8080 свободен, если его не заняло другое приложение

11. Этап 7. MySQL и phpMyAdmin через Docker Compose

Вручную запускать два контейнера удобно для понимания механики. Для повторяемого проекта параметры описываются декларативно в compose.yaml. Compose создаёт отдельный проект с собственными сетью и томом; данные из db-lab-data автоматически в него не переносятся. В этом этапе объекты имеют имя проекта db-lab-compose.

11.1. Подготовка папки и файла .env

  1. Открыть в Проводнике папку docker-db-lab/compose.
  2. Редактором сохранить в ней .env и compose.yaml с содержимым ниже.
  3. Открыть PowerShell в папке compose.

Создайте файл .env:

MYSQL_ROOT_PASSWORD=lab-root-pass
MYSQL_DATABASE=dockerlab
MYSQL_USER=labuser
MYSQL_PASSWORD=lab-user-pass

Важно: Файл .env удобен для лабораторной, но сам по себе не является защищенным хранилищем секретов. Его нельзя бездумно помещать в публичный Git-репозиторий. Для реальных систем применяют секрет-хранилища, Docker secrets или иные механизмы управления секретами.

11.2. Файл compose.yaml

name: db-lab-compose
services:
  db:
    image: mysql:8.4
    restart: unless-stopped
    environment:
      MYSQL_ROOT_PASSWORD: ${MYSQL_ROOT_PASSWORD}
      MYSQL_DATABASE: ${MYSQL_DATABASE}
      MYSQL_USER: ${MYSQL_USER}
      MYSQL_PASSWORD: ${MYSQL_PASSWORD}
    volumes:
      - mysql-data:/var/lib/mysql
    healthcheck:
      test: ["CMD-SHELL", "mysql --protocol=TCP -h127.0.0.1 -u$$MYSQL_USER -p$$MYSQL_PASSWORD -D$$MYSQL_DATABASE -N -e 'SELECT 1;' >/dev/null"]
      interval: 5s
      timeout: 5s
      retries: 20
      start_period: 40s
  phpmyadmin:
    image: phpmyadmin:5.2.3-apache
    restart: unless-stopped
    depends_on:
      db:
        condition: service_healthy
    environment:
      PMA_HOST: db
      PMA_PORT: 3306
    ports:
      - "127.0.0.1:8080:80"
volumes:
  mysql-data:

11.3. Что здесь нового

  • У db нет секции ports. База доступна phpMyAdmin по внутренней сети Compose, но не опубликована на хост.
  • Compose автоматически создает общую сеть проекта. Имя сервиса db используется как DNS-имя.
  • Named volume mysql-data хранит /var/lib/mysql вне жизненного цикла контейнера.
  • healthcheck проверяет TCP-подключение обычного пользователя к базе и выполнение SELECT 1.
  • depends_on с condition: service_healthy задерживает запуск зависимого сервиса до успешной проверки здоровья базы.
  • Знак $$ нужен, чтобы передать символ $ внутрь команды контейнера, а не подставить переменную на этапе обработки Compose.

Healthcheck подключается по TCP обычным пользователем к выбранной базе и выполняет SELECT 1. Это проверяет доступ приложения, а не только наличие процесса. Временный сервер первичной инициализации MySQL не принимает такие TCP-соединения. Mysqladmin ping может вернуть успех даже при Access denied, поэтому он не используется как доказательство правильных учётных данных. $$ сохраняет доллар для обработки внутри контейнера. Учебные пароли не содержат пробелов и символов shell; шаблон не предназначен для произвольных секретов.

depends_on с service_healthy управляет порядком первоначального запуска. Он не гарантирует восстановление соединений приложения после позднего сбоя db; unhealthy сам по себе не вызывает restart policy. При медленной инициализации состояние и логи проверяются повторно, параметры ожидания при необходимости увеличиваются.

11.4. Проверка конфигурации и запуск

docker compose config --quiet
docker compose up -d
docker compose ps

Проверка config --quiet при успехе ничего не печатает; при ошибке файл исправляется до up. Она проверяет конфигурацию, но не доступность образов и БД. В выводе docker compose ps дождитесь состояния healthy для db. Затем откройте http://127.0.0.1:8080 и войдите под labuser.

docker compose logs -f db

Примечание: Для выхода из режима просмотра логов используйте Ctrl+C. Контейнеры при этом не останавливаются, потому что они запущены с -d.

Предскажите результат: сохранится ли compose_test после down/up? Как изменится результат при down -v? Проверьте первый вариант командами ниже; удаление тома здесь выполнять не требуется.

11.5. Создаем данные и проверяем сохранность

Создание compose_test выполняется один раз в новом проекте. После повторного up сначала проверяется healthy через compose ps, затем выполняется только запрос чтения; существующую таблицу создавать повторно не нужно.

docker compose exec db mysql -u labuser -plab-user-pass -D dockerlab -e "CREATE TABLE compose_test (id INT PRIMARY KEY, value_text VARCHAR(50)); INSERT INTO compose_test VALUES (1, 'persistent data'); SELECT * FROM compose_test;"
docker compose down
docker compose up -d

После повторного запуска проверьте таблицу:

docker compose exec db mysql -u labuser -plab-user-pass -D dockerlab -e "SELECT * FROM compose_test;"

Пояснение: docker compose down удаляет контейнеры и сеть проекта, но named volume по умолчанию сохраняет. Поэтому данные переживают down/up.

11.6. Чем опасен параметр -v у docker compose down

Следующую команду сейчас выполнять не нужно - она приведена для сравнения:

docker compose down -v

Эта команда дополнительно удаляет named volumes, объявленные проектом. Для учебной среды это удобный способ начать с нуля. Для базы данных с ценными данными такая команда потенциально разрушительна.

Важно: Перед удалением volume всегда задайте себе два вопроса: что именно в нем хранится и существует ли проверенная резервная копия?

Для самопроверки: Чем отличаются down и down -v? Доказывает ли healthy доступность всех функций phpMyAdmin?

11.7. Освобождение ресурсов перед самостоятельным проектом

После проверки compose_test сохраните результаты. В PowerShell, открытом в docker-db-lab/compose, остановите основной Compose-проект:

docker compose stop --timeout 60
docker compose ps -a

Контейнеры остановлены, том и конфигурация сохранены. Docker compose stop не удаляет проект. Убедитесь, что старые db-lab-mysql, db-lab-restore и db-lab-custom тоже остановлены; для просмотра общей нагрузки используйте docker ps и docker stats --no-stream. Следующий сервер запускается только после освобождения ресурсов предыдущего этапа.

Состояние стенда перед следующим этапом

Что осталось Хранилище Доступ с Windows
Основной Compose-проект db-lab-compose остановлен командой stop; compose_test и результаты проверки down/up сохранены Собственный том Compose; данные db-lab-data в него не переносились После stop интерфейс на 8080 недоступен; порт освобождён. MySQL не опубликован

12. Самостоятельное задание

Базовый уровень: отдельный Compose-проект проверяет самостоятельное применение изученных механизмов. После него выполняется профессиональный кейс повышенного уровня, если требуется оценка «5».

В Проводнике используется отдельная папка assignment: решение создаётся на основе изученных принципов. Проект получает name: db-lab-assignment и новый собственный том. Изменение .env старого проекта с уже заполненным томом не переименует существующую базу и не изменит пользователей. Исходный Compose-проект и его данные сохраняются. Требования к новому проекту:

  • phpMyAdmin открывался на локальном порту 8081 вместо 8080
  • имя базы данных стало admin_lab
  • обычный пользователь назывался adminuser
  • при первичной инициализации нового тома автоматически создавалась таблица hosts с полями id, hostname и role
  • в hosts было не менее трех записей: router, web-server, db-server
  • после docker compose down и docker compose up -d данные сохранялись
  • порт MySQL не был опубликован на хост.

Примечание: Для автоматического создания таблицы можно использовать init SQL-скрипт, подключив его в /docker-entrypoint-initdb.d, либо собственный образ. Помните: init выполняется только для пустого каталога данных.

12.1. Проверка самостоятельного проекта

PowerShell открыт в папке docker-db-lab\assignment. После запуска проверьте готовность базы: дождитесь healthy, если предусмотрен healthcheck, либо выполните успешный SELECT 1 через клиент. Проверьте решение по следующим критериям:

Критерий Как подтвердить
Отдельный проект db-lab-assignment name в compose.yaml; вывод docker compose ps -a из assignment. Исходный проект db-lab-compose сохранён.
phpMyAdmin на локальном порту 8081 Открыть http://127.0.0.1:8081 и показать публикацию порта в docker compose ps.
База admin_lab и обычный пользователь adminuser Войти в phpMyAdmin под adminuser; в SQL-вкладке выполнить SELECT CURRENT_USER(); и SHOW DATABASES;. Доступ к admin_lab должен быть успешным.
Таблица hosts с id, hostname, role В базе admin_lab выполнить DESCRIBE hosts; и SELECT * FROM hosts ORDER BY id;. Показать не менее трёх строк с hostname router, web-server, db-server; role заполнить осмысленными значениями.
Автоматическое создание таблицы при первом запуске Приложить init SQL и способ его доставки в /docker-entrypoint-initdb.d: mount в Compose либо Dockerfile. После первого запуска нового тома таблица существует без ручного CREATE TABLE.
Данные сохраняются Зафиксировать SELECT до down; выполнить down и up -d без -v, дождаться готовности базы, повторить тот же SELECT. Строки и id совпадают.
MySQL не опубликован на хост У сервиса базы нет ports в compose.yaml; docker compose ps не показывает сопоставления хостового порта с 3306. Запись только 3306/tcp сама по себе не означает публикацию.

SQL-проверки выполняются во вкладке SQL phpMyAdmin, а команды Docker — в PowerShell. Использование root вместо adminuser не подтверждает выполнение требования об обычном пользователе.

12.2. Повышенный уровень. Диагностика неисправного Compose-проекта

Скачать четыре готовых варианта — ZIP

Преподаватель назначает один вариант. Студент распаковывает его папку средствами Windows. В ней находятся compose.yaml, .env, TASK.md и папка init с файлом 01-data.sql. Не изменять структуру папки. Стенд запускается на компьютере студента одной командой Docker Compose; запуск .ps1 и предварительная подготовка преподавателем не требуются.

Ситуация: организация передала конфигурацию контейнерного сервиса с одной ошибкой. Требуется ввести сервис в работу: определить причину недоступности, обоснованно исправить конфигурацию и доказать сохранность данных при пересоздании контейнеров. Симптом выбранного варианта указан в TASK.md. Готовый алгоритм диагностики не предоставляется. Это учебная диагностика при развёртывании, а не восстановление перенесённой производственной базы.

Перед запуском: сохранить результаты базового задания и в папке docker-db-lab/assignment остановить его:

docker compose stop --timeout 60
docker compose ps -a

Открыть терминал в папке назначенного варианта и выполнить:

docker compose up -d

Compose создаёт отдельный именованный том. При первом запуске на пустом томе MySQL автоматически выполняет init/01-data.sql внутри контейнера: создаёт inventory с тремя строками и departments с двумя. При повторном запуске с заполненным томом инициализация не повторяется. Загрузка образов и создание базы требуют времени. В одном варианте команда запуска сообщает об ошибке зависимости — это предусмотренный симптом. Не удалять том ради повторного старта.

Адрес интерфейса — http://127.0.0.1:8082, либо согласованный свободный порт из .env. База incident_db; пользователь incidentuser, пароль incident-user-pass. Root-пароль из .env используется только для диагностики и администрирования. MySQL на Windows не публикуется. На компьютере одновременно работает только один вариант, предыдущие учебные серверы остановлены.

Контрольные записи inventory: (1, router), (2, switch), (3, server); departments: (1, IT), (2, Support). Каждая папка содержит только одну неисправность. Решения и исправная конфигурация находятся в отдельном комплекте преподавателя.

  1. Средствами Windows сохранить исходную конфигурацию отдельно. Зафиксировать симптом, состояние сервисов и имя тома, подключённого к /var/lib/mysql, до исправления.
  2. Самостоятельно выбрать проверки. Отделить состояние контейнера, готовность MySQL, соединение между сервисами и доступ к интерфейсу с Windows. Сформулировать причину на основе фактов, проверить и отклонить другую гипотезу.
  3. Внести минимальное исправление и применить изменённую конфигурацию. Не отключать healthcheck или ожидание готовности ради обхода ошибки; имя проекта, учётные записи и SQL инициализации сохраняются.
  4. Подтвердить вход через phpMyAdmin обычным пользователем и все id/значения обеих таблиц с сортировкой по id.
  5. Добавить в inventory собственную запись с id=100 и значением student-ВАШ_КОД (латинские буквы и цифры). Зафиксировать SELECT и имя тома, затем пересоздать контейнеры:
docker compose down --timeout 60
docker compose up -d

Подтвердить повторный вход, все пять исходных записей, собственную запись и тот же том. Собственная запись отсутствует в init/01-data.sql, поэтому её сохранность доказывает, что данные пережили пересоздание контейнеров. Не добавлять эту запись в файл инициализации и не воссоздавать таблицы для имитации результата.

Сдать исходную и исправленную конфигурацию, диагностические факты, SELECT до и после пересоздания, имя тома и отчёт: симптом → диагностика → причина → исправление → проверка. Не выполнять down -v, volume rm/prune и сброс Docker Desktop в процессе задания. Подробные требования — в TASK.md; критерии оценки — в разделе 14.1.

13. Что проверить перед сдачей

Проверки основной части приведены ниже. Самостоятельный проект базового уровня проверяется по всем критериям раздела 12.1; повышенный уровень — по доказательствам раздела 12.2. Результаты фиксируются по мере выполнения этапов; одновременно запускать все созданные серверы ради сдачи не требуется.

№ Проверка Команда / способ
1 Контейнеры проверяемого этапа доступны при демонстрации docker ps или docker compose ps. Серверы завершённых этапов остаются остановленными; их результаты подтверждаются сохранёнными SELECT. Для демонстрации запускается только нужный стенд.
2 MySQL принимает соединения mysql / phpMyAdmin / healthcheck
3 Контейнеры видят друг друга по имени phpMyAdmin подключается к db или db-lab-mysql
4 Данные хранятся в volume docker volume ls + пересоздание контейнера
5 Init-скрипт создал таблицу SELECT из созданной таблицы
6 Резервная копия восстановлена Дамп backup/dockerlab-backup.sql; SELECT inventory и lab_notes исходной и восстановленной базы совпадают
7 После down/up данные остаются SELECT после повторного запуска Compose

14. Что приложить к отчету

В отчет не нужно вставлять скриншот каждой команды. Нужны доказательства ключевых результатов и краткие выводы.

  • Титульная часть: ФИО, группа, название лабораторной работы.
  • Скриншот или текстовый вывод docker ps / docker compose ps с работающими сервисами.
  • Результат SELECT из таблицы, созданной вручную.
  • Результат SELECT после удаления и пересоздания контейнера с тем же volume.
  • Фрагмент docker network inspect или объяснение, почему phpMyAdmin подключается к db-lab-mysql/db по имени.
  • Содержимое собственного Dockerfile и SQL-скрипта инициализации.
  • Файлы основного Compose-проекта и самостоятельного проекта: два compose.yaml с указанием соответствующих папок; init SQL и Dockerfile самостоятельного решения, если он используется.
  • Доказательства всех критериев раздела 12.1: вход под adminuser, структура и строки hosts, интерфейс на 8081, сохранение строк после down/up и отсутствие публикации MySQL. Для каждого результата указать проект, к которому он относится.
  • Краткие объяснения результатов опытов: сохранение inventory, однократная инициализация devices, восстановление дампа и сохранение compose_test.
  • Результат подключения клиентом Windows из раздела 6 — только если необязательный этап выполнялся.
  • Доказательство создания резервной копии и успешного восстановления: SELECT inventory и lab_notes исходной и восстановленной базы с совпадающими id и значениями. Просмотр дампа сам по себе не заменяет проверку восстановления.
  • Краткие ответы на пять обязательных ситуационных задач раздела 16.1. Все 17 теоретических вопросов письменно переписывать не требуется.
  • При выполнении повышенного уровня: исходная и исправленная конфигурация, факты диагностики, SELECT inventory и departments, собственная запись до и после пересоздания контейнеров, имя исходного тома и отчёт по разделу 12.2.
  • Краткий вывод: 5-8 предложений о том, чем контейнеризация базы данных отличается от запуска stateless-контейнера.

14.1. Балльная система и условия оценок

Максимум — 100 баллов: 70 за базовую часть и 30 за профессиональный кейс. Баллы начисляются за проверяемый результат и объяснение, а не за число скриншотов. Рабочий результат без пояснения получает только баллы результата; пояснение без доказательства работы не заменяет практическую проверку.

Результат За работоспособность и доказательства За объяснение Всего
MySQL, проверка готовности, контейнерная сеть и phpMyAdmin 10 5 15
Именованный том и сохранение inventory после пересоздания 10 5 15
Собственный образ, devices и опыт однократной инициализации 7 3 10
Дамп и восстановление inventory и lab_notes с совпадением всех значений 10 5 15
Основной Compose и все требования самостоятельного базового проекта 7 3 10
Отчёт с привязкой результатов к этапам и пять ситуационных ответов По 1 баллу за обоснованный ответ; отсутствие проверяемых материалов учитывается в соответствующей строке выше 5
Кейс: факты диагностики и проверка гипотез Факты и связь с симптомом — до 7; проверка и отклонение другой гипотезы — до 3 10
Кейс: установленная причина Назван конкретный ошибочный параметр/подключение и объяснена причинная связь 5
Кейс: исправление и сохранность Минимальное исправление — до 3; доступ обычного пользователя через интерфейс — до 2; совпадение исходных записей — до 2; собственная запись после пересоздания — до 2; тот же том — до 1 10
Кейс: отчёт об инциденте Пять звеньев «симптом → диагностика → причина → исправление → проверка» — по 1 баллу 5

Внутри строки частичные баллы соответствуют реально подтверждённым требованиям. В строке Compose 7 баллов практики распределяются так: основной проект и сохранность compose_test — 2; самостоятельный проект с отдельными БД/пользователем и портом — 2; автоматическое создание hosts и три строки — 1; сохранность hosts после down/up — 1; отсутствие публикации MySQL — 1. Ещё по 1 баллу — за объяснение отдельного проекта/тома, однократной инициализации и различия внутреннего и хостового портов. Без соответствующих SELECT сохранность данных не считается подтверждённой.

Оценка Условия
«2» Менее 50 баллов либо не доказаны сохранение данных в именованном томе и восстановление базового дампа.
«3» 50–69 баллов; сохранность в томе и восстановление обеих таблиц базового дампа подтверждены.
«4» 70–84 балла и все практические требования базового задания выполнены. При большем числе баллов, но незавершённом кейсе работа также оценивается не выше «4».
«5» 85–100 баллов; базовая часть выполнена, за кейс получено не менее 20 из 30, исходные данные и том сохранены, доступ обычного пользователя восстановлен.

Если получено 70 и более баллов, но не выполнено обязательное практическое требование базовой части, оценка не выше «3» при наличии доказанной сохранности и восстановления. Потеря или подмена исходных данных кейса обнуляет баллы за его исправление и сохранность и исключает оценку «5». Необязательные разделы 6 и 9.3, дополнительный опыт с новым томом и справочные теоретические вопросы не нужны для получения любой оценки; повышенный кейс нужен для «5».

15. Типичные ошибки и диагностика

Симптом Вероятная причина Что проверить
Container name is already in use Контейнер с таким именем уже существует docker ps -a; проверить принадлежность контейнера этой работе. Продолжить нужный этап с существующим учебным контейнером либо после сохранения данных остановить и удалить именно его.
Bind for 127.0.0.1:3307 failed Порт 3307 занят выбрать другой хостовый порт, например 3308
phpMyAdmin не подключается сразу после запуска MySQL еще инициализируется CLI: docker logs db-lab-mysql; Compose: docker compose logs db; дождаться готовности
Access denied for user Неверный пароль/пользователь либо volume создан со старыми учетными данными проверить переменные и понять, был ли volume пустым
Изменил MYSQL_PASSWORD, но старый пароль продолжает работать База уже была инициализирована в volume переменные init не меняют существующую БД
Изменил 01-init.sql, но новая таблица не появилась Используется старый volume init-скрипты выполняются только при первичной инициализации
После docker compose down данные остались Это нормальное поведение named volume docker volume ls; down без -v volume не удаляет
После docker compose down -v данные исчезли Volume был удален вместе с проектом восстанавливать из backup; не использовать -v без понимания последствий
localhost не соединяет два контейнера localhost внутри контейнера указывает на него самого использовать имя сервиса/контейнера в общей Docker-сети

16. Контрольные вопросы и ситуационные задачи

16.1. Пять обязательных ситуационных задач

На каждую задачу достаточно краткого ответа: наблюдение, первая проверка, обоснованная гипотеза и способ подтверждения результата. Не требуется перечислять все команды Docker.

  1. Контейнер MySQL имеет статус Up, но phpMyAdmin ещё не подключается. Какие факты позволят отличить первичную инициализацию базы от ошибки сети или учётных данных?
  2. После изменения MYSQL_PASSWORD и пересоздания контейнера со старым томом новый пароль не принимается. Почему? Как исправить ситуацию, сохранив таблицы?
  3. После изменения Compose база запускается, но inventory исчезла. Какие сведения о подключениях хранилища нужно сохранить и проверить до любых изменений? Почему создание похожей таблицы не устраняет причину?
  4. 01-init.sql изменён, образ пересобран, но база с прежним томом не изменилась. Как проверить новую версию скрипта, не уничтожая существующую базу?
  5. SQL-дамп имеет ненулевой размер, но восстановление одной таблицы не подтверждено. Какие проверки нужны, чтобы считать учебную копию пригодной к восстановлению? Почему одного количества строк недостаточно?

16.2. Теоретические вопросы для самопроверки

Ниже сохранены 17 вопросов как справочный банк для устного обсуждения и самопроверки. Обязательные письменные ответы — только пять задач раздела 16.1.

  1. Почему хранить данные только в writable layer рискованно? Почему официальный mysql:8.4 без явного --mount использует анонимный том?
  2. Чем docker restart отличается от удаления и повторного создания контейнера с точки зрения данных?
  3. Что означает запись 127.0.0.1:3307:3306?
  4. Почему контейнер phpMyAdmin может подключиться к MySQL без публикации порта 3306 на хост?
  5. Что означает имя db в PMA_HOST: db при использовании Compose?
  6. Чем named volume отличается от каталога внутри writable layer контейнера?
  7. Сохраняется ли named volume после docker rm контейнера?
  8. Что делает каталог /docker-entrypoint-initdb.d в официальном MySQL image?
  9. Почему init SQL-скрипт может не выполниться после пересборки образа?
  10. Почему хранить пароль в Dockerfile хуже, чем передавать его при запуске?
  11. Зачем базе данных healthcheck?
  12. Почему обычный depends_on не всегда означает, что СУБД уже готова принимать подключения?
  13. Что произойдет с данными после docker compose down? А после docker compose down -v?
  14. Почему volume не заменяет резервное копирование?
  15. Для чего используется mysqldump в этой работе?
  16. В каком случае имеет смысл публиковать порт MySQL на хост, а когда лучше этого не делать?
  17. Почему выбран тег ветки mysql:8.4? Фиксирует ли он конкретную сборку так же, как digest?

17. Краткий конспект

Понятие Главная мысль
Stateful container Сервис хранит изменяемое состояние, поэтому жизненный цикл данных нужно отделить от контейнера.
MySQL port 3306 Внутренний порт СУБД. Для связи контейнер–контейнер публикация на хост не обязательна.
Published port Сопоставляет IP/порт хоста с портом контейнера.
User-defined network Дает контейнерам изоляцию и разрешение имен.
Named volume Постоянное хранилище, управляемое Docker и существующее отдельно от контейнера.
/var/lib/mysql Каталог, в котором официальный MySQL image хранит данные.
/docker-entrypoint-initdb.d Каталог init-скриптов, выполняемых при первичной инициализации пустой БД.
healthcheck Проверяет готовность/здоровье сервиса, а не только факт существования процесса.
docker compose down Удаляет контейнеры и сеть проекта, но обычно сохраняет named volumes.
docker compose down -v Дополнительно удаляет named volumes проекта.
mysqldump Создает логическую резервную копию SQL.

18. Дополнительные замечания по безопасности и эксплуатации

Лабораторная работа показывает базовые механизмы, но production-развертывание базы данных требует большего. Минимальный набор правил, который следует унести из этой работы:

  • не публиковать порт СУБД наружу, если доступ нужен только другим контейнерам
  • не использовать root как учетную запись приложения
  • не хранить реальные пароли в Dockerfile, публичном Git и общедоступных compose-файлах
  • закреплять версии образов и планировать обновления, а не обновляться случайно через latest
  • делать резервные копии и периодически проверять восстановление
  • контролировать свободное место на хосте и размер volume
  • следить за логами, healthcheck и рестартами контейнера
  • перед удалением volume понимать последствия
  • для критичных систем отдельно проектировать отказоустойчивость, репликацию, мониторинг и управление секретами.

Примечание: Docker решает задачу упаковки и запуска процесса, но не отменяет обязанности администратора по резервному копированию, обновлениям, мониторингу и защите СУБД.

19. Очистка учебной среды

Очистка выполняется после сохранения отчёта и проверенного дампа. Сначала проверяется принадлежность перечисленных объектов работе. Отсутствующие контейнеры можно пропустить. Stop даёт MySQL время корректно завершить запись; если службе требуется больше времени, увеличивается timeout.

docker stop --timeout 60 db-lab-mysql db-lab-restore db-lab-custom db-lab-admin
docker rm db-lab-mysql db-lab-restore db-lab-custom db-lab-admin
docker network rm db-lab-net

Следующая команда уничтожает файлы трёх именованных учебных томов; выполняется только при окончательном завершении работы:

docker volume rm db-lab-data db-lab-restore-data db-lab-custom-data

Для Compose-проекта PowerShell открывается в compose; для самостоятельного проекта — в assignment. В каждом из этих каталогов сначала проверяется проект:

docker compose ps -a
docker compose down --timeout 60

Down сохраняет данные. Если выбранный проект окончательно завершён и его учебный том также требуется удалить, отдельно выполняется:

docker compose down --volumes

Down --volumes удаляет тома, которыми управляет выбранный проект; external volumes он не удаляет. В базовых Compose-проектах и кейсе 12.2 тома управляются Compose. Команда down --volumes удаляет их данные; во время диагностики и проверки сохранности она запрещена. Не следует подставлять эту команду в каталог другого проекта. Общие system prune и volume prune для завершения работы не нужны.

Дамп в backup и исходные файлы Windows сохраняются. Собственный образ db-lab-mysql-image:1.0 при необходимости удаляется отдельно через docker image rm после удаления использующих его контейнеров.

Завершение стенда инцидента

В папке выданного проекта сохраните отчёт и остановите стенд через docker compose stop --timeout 60. Исходный именованный том сохраняется. Окончательное удаление данных выбранного проекта допускается по указанию преподавателя после проверки; общие prune, удаление всех томов и сброс Docker Desktop не применяются.

20. Источники и справочные материалы

Методическая работа основана на официальной лабораторной Docker по контейнеризованным базам данных, но дополнена пояснениями по сетям, постоянному хранению, особенностям первичной инициализации MySQL, healthcheck, резервному копированию, диагностике и безопасному учебному использованию.