Методическая работа 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
- Нажмите Win+R, введите
resmonи откройте «Монитор ресурсов». - На вкладке «Сеть» раскройте «Прослушиваемые порты» и найдите нужный порт: 8080, 8081, 8082 или необязательный 3307. Посмотрите имя процесса и PID.
- Если это backend Docker Desktop, сопоставьте публикацию порта с выводом
docker psили списком Containers в Docker Desktop. Если это приложение Windows, определите его назначение. - Остановите только известное ненужное учебное приложение либо выберите другой свободный хостовый порт и измените адрес проверки. Не завершайте неизвестные процессы ради освобождения порта.
Дополнительная диагностика в 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. Все перечисленные ниже объекты на этом шаге — папки, а не файлы:
- Внутри
docker-db-labсоздайте четыре папки:backup,mysql-custom,composeиassignment. - Откройте папку
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
Рисунок 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 также приведет к потере информации. Поэтому администратор должен уметь создавать логические дампы.
Рисунок 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
- Открыть в Проводнике папку docker-db-lab/compose.
- Редактором сохранить в ней .env и compose.yaml с содержимым ниже.
- Открыть 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). Каждая папка содержит только одну неисправность. Решения и исправная конфигурация находятся в отдельном комплекте преподавателя.
- Средствами Windows сохранить исходную конфигурацию отдельно. Зафиксировать симптом, состояние сервисов и имя тома, подключённого к /var/lib/mysql, до исправления.
- Самостоятельно выбрать проверки. Отделить состояние контейнера, готовность MySQL, соединение между сервисами и доступ к интерфейсу с Windows. Сформулировать причину на основе фактов, проверить и отклонить другую гипотезу.
- Внести минимальное исправление и применить изменённую конфигурацию. Не отключать healthcheck или ожидание готовности ради обхода ошибки; имя проекта, учётные записи и SQL инициализации сохраняются.
- Подтвердить вход через phpMyAdmin обычным пользователем и все id/значения обеих таблиц с сортировкой по id.
- Добавить в 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.
- Контейнер MySQL имеет статус Up, но phpMyAdmin ещё не подключается. Какие факты позволят отличить первичную инициализацию базы от ошибки сети или учётных данных?
- После изменения MYSQL_PASSWORD и пересоздания контейнера со старым томом новый пароль не принимается. Почему? Как исправить ситуацию, сохранив таблицы?
- После изменения Compose база запускается, но inventory исчезла. Какие сведения о подключениях хранилища нужно сохранить и проверить до любых изменений? Почему создание похожей таблицы не устраняет причину?
- 01-init.sql изменён, образ пересобран, но база с прежним томом не изменилась. Как проверить новую версию скрипта, не уничтожая существующую базу?
- SQL-дамп имеет ненулевой размер, но восстановление одной таблицы не подтверждено. Какие проверки нужны, чтобы считать учебную копию пригодной к восстановлению? Почему одного количества строк недостаточно?
16.2. Теоретические вопросы для самопроверки
Ниже сохранены 17 вопросов как справочный банк для устного обсуждения и самопроверки. Обязательные письменные ответы — только пять задач раздела 16.1.
- Почему хранить данные только в writable layer рискованно? Почему официальный mysql:8.4 без явного --mount использует анонимный том?
- Чем docker restart отличается от удаления и повторного создания контейнера с точки зрения данных?
- Что означает запись 127.0.0.1:3307:3306?
- Почему контейнер phpMyAdmin может подключиться к MySQL без публикации порта 3306 на хост?
- Что означает имя db в PMA_HOST: db при использовании Compose?
- Чем named volume отличается от каталога внутри writable layer контейнера?
- Сохраняется ли named volume после docker rm контейнера?
- Что делает каталог /docker-entrypoint-initdb.d в официальном MySQL image?
- Почему init SQL-скрипт может не выполниться после пересборки образа?
- Почему хранить пароль в Dockerfile хуже, чем передавать его при запуске?
- Зачем базе данных healthcheck?
- Почему обычный depends_on не всегда означает, что СУБД уже готова принимать подключения?
- Что произойдет с данными после docker compose down? А после docker compose down -v?
- Почему volume не заменяет резервное копирование?
- Для чего используется mysqldump в этой работе?
- В каком случае имеет смысл публиковать порт MySQL на хост, а когда лучше этого не делать?
- Почему выбран тег ветки 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: контейнеризованные базы данных
- Тома Docker
- Сеть Docker
- Порядок запуска сервисов Compose
- Официальный образ MySQL
- Описание сервисов Compose
- MySQL 8.4: mysqldump
- MySQL 8.4: ограничения mysqladmin ping
Методическая работа основана на официальной лабораторной Docker по контейнеризованным базам данных, но дополнена пояснениями по сетям, постоянному хранению, особенностям первичной инициализации MySQL, healthcheck, резервному копированию, диагностике и безопасному учебному использованию.

