MySQL 8 в контейнере Docker

Когда нужно сделать что-то быстро, это стоит сделать в docker контейнере. Так что для тестирования приложения с новой версией БД я сделаю именно это. Здесь следует ремарка, что контейнеры by-design предполагались stateless, что совершенно не вяжется с концепцией классической реляционной БД. Однако в моем случае задача носит скорее исследовательский характер: целью будет протестировать возможности MySQL 8, не мешая уже работающей на моем сервере MariaDB. Для описанных мною целей docker подойдет идеально: я получу изолированный от хостовой системы MySQL и легко удалю его, когда он больше не потребуется.

Итак, сегодня мы узнаем как:

  • Запустить MySQL в контейнере – просто
  • Настроить базу и подключиться к ней с хостовой системы – средне
  • Подключиться к базе данных из PhpStorm на домашнем компьютере – сложно

Запускаем контейнер

Предполагается, что Docker у вас уже стоит. Поэтому сразу перейдем к получению нужного образа: docker pull mysql/mysql-server:latest. Вообще вместо latest лучше использовать конкретную версию, но экспериментировать, так экспериментировать.

Далее мы запускаем контейнер docker run --name=mysql -d -p 33060:3306 mysql/mysql-server:latest – здесь я прокинул стандартный порт от MySQL на порт 33060 хостовой машины, так как 3306 у меня уже занят моей СУБД. Монтировать разделы физической машины в контейнер мы не будем, потому что целостность данных в данном случае маловажна – однако как правило при установке БД в контейнер это лучше сделать.

С помощью команды docker ps удостоверимся, что все работает. В списке должен появиться контейнер с именем mysql и image, который мы до этого указали.

Настройка контейнера

Теперь запущенный контейнер надо настроить, чтобы им можно было пользоваться. Для начала необходимо задать пароль для root. Мы его при создании не указывали и он сгенерировался, найти его можно в логах контейнера. Перейдем туда, набрав docker logs mysql.

Пароль будет выглядеть так.

Этот пароль впоследствии мы поменяем на свой – это можно сделать непосредственно внутри контейнера. В команде run мы назвали его mysql, так что теперь можем зайти в него используя это имя. Вводим docker exec -it mysql mysql -uroot -p.
Что тут происходит? Первая часть docker exec -it mysql является требованием исполнить команду в контейнере mysql в интерактивном режиме, вторая mysql -uroot -p является командой, которую контейнер исполняет. Здесь, как несложно понять, мы заходим в базу под рутом. Нужно будет ввести наш сгенерированный пароль и мы окажемся в консоли. Сразу поменяем пароль, для этого введем в консоль следующее: ALTER USER 'root'@'localhost' IDENTIFIED BY 'your_password';

В принципе на этом настройка закончена: мы имеем работающую БД, которая видна с хост-машины на порте 33060. Однако лично мне приятнее и удобнее работать с базой при помощи IDE от JetBrains, чем по-старинке через какой-нибудь adminer или, прости господи, из консоли. Поэтому я сразу настрою подключение оттуда к нашей докеризованной базе.

На самом деле настройка закончена не совсем – конкретно в моем случае необходимо также дать доступ пользователю root с ip адреса 172.17.0.1. Этого можно избежать, запуская контейнер с опцией
–net=host

Настройка подключения из PhpStorm

Подключаться к базе данных мы будем с использованием ssh туннеля, чтобы обезопасить ее от нежелательных подключений: доступ внутрь разрешается только с localhost. К счастью, в PhpStorm данный функционал доступен из коробки, ничего настраивать не надо. Переходим в меню Database->New->указываем тип источника данных и открывается окно настройки подключения.

Подключение к самой БД.
SSH туннель. Там, где 0.0.0.0 – ваш адрес.

Все очень просто, так что запускаем.
Не работает…

Host '172.17.0.1' is not allowed to connect to this MySQL server

Появляется резонный вопрос: а почему 172.17.0.1, когда мы явным образом указали локалхост? It’s Google time!
Собственно ответ нашелся довольно быстро (ссылка), так что внесем некоторые коррективы в наш контейнер. Открывать доступ всему миру идея плохая, поэтому давайте откроем доступ прямо этому ip. Насколько я понял, это что-то вроде служебного ip адреса докера, через который он линкует контейнеры.

Пятиминутка гугления и получаем такой скрипт для исполнения в консоли MySQL GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.17.0.1' IDENTIFIED BY 'your_password' WITH GRANT OPTION;

Ну почти… Чем дальше в лес, тем больше дров. (что не так?)
Модифицируем наш скрипт под MySQL 8:

CREATE USER 'root'@'172.17.0.1' IDENTIFIED BY 'root';
GRANT ALL PRIVILEGES ON *.* TO 'root'@'172.17.0.1' WITH GRANT OPTION;
Список доступных нам пар пользователь-хост.

По-прежнему ошибка, на этот раз IDE требует пароль. Задаем его явно таким же скриптом:
ALTER USER 'root'@'172.17.0.1' IDENTIFIED BY 'your_password';

Прогресс налицо!

Тут моя ошибка – я почему-то был уверен, что CREATE USER ‘root’@’172.17.0.1’ IDENTIFIED BY ‘root’; сделает пароль нового рута таким же, как у существующего. Однако это не так.

Ура, успех, Test Connection проходит!
Разумеется, все не может быть так просто…

Connection to docker failed.
[08S01] Communications link failure.
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.

По-видимому, ошибка из-за того, что у меня нет в конфиге такого:
bind-address 0.0.0.0 – в треде на гитхабе, ссылка на который была выше, это тоже упомянуто.
Заходим в контейнер, смотрим, что там в файле my.cnf:

bash-4.2# cat /etc/my.cnf
# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/8.0/en/server-configuration-defaults.html

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M

# Remove leading # to revert to previous value for default_authentication_plugin,
# this will increase compatibility with older clients. For background, see:
# https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_default_authentication_plugin
# default-authentication-plugin=mysql_native_password
skip-host-cache
skip-name-resolve
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
secure-file-priv=/var/lib/mysql-files
user=mysql

Да, действительно нет.
В этот момент я нагугливаю, что такие вещи надо делать с помощью DOCKERFILE еще на этапе запуска контейнера, но потери времени и сил слишком велики, поэтому хочется найти простой способ закончить это приключение. Собственно, вот и способ.

Надо ли говорить, что это не помогло? Поиски продолжались.

Развязка

Здесь стоит заметить, что на хостовой машине контейнерная база работала – я подключился к ней через php по адресу localhost:33060 и никаких ошибок не случилось. Возникло подозрение, что источник ошибки – сама IDE. Так оно и оказалось – в каком-то треде по DataGrip (это IDE’шка для работы с базами данных от JB, которая встроена в PhpStorm), я нашел описание похожей проблемы. Вкратце, там предлагалось во вкладке “advanced” поменять hostNameInCertificate. Правда, такого пункта не было.

Враг обнаружен.

Но был параметр verifyServerSertificate, установка которого в false решила проблему. В описании можно увидеть, что для более старых версий этот параметр выключен. Что ж, зато я узнал об этой опции – раньше вообще никогда не посещал данную закладку 🙂

После всех описанных священнодействий подключение к базе заработало. В принципе самой сложной частью оказалось именно подключение из IDE; включение же контейнера и его настройка сами по себе достаточно простые процедуры – здесь Docker показал свои положительные стороны: простота установки софта и независимость от физической машины.