MySQL в контейнере Docker - часть I
Тема MySQL казалось бы, уже сто раз обсосана, но все-таки она является одной из важных, поскольку MySQL хранит все наши данные уже не один десяток лет, она одна
из ведущих систем управления базами данных, и я думаю что вам понравится тема, которую я хочу предложить.
На первый взгляд, создание контейнера MySQL для Docker довольно легко, но если вы хотите подключиться (не уверены, что сервер mysql, который не позволил бы этого, был бы хорош) и отделить ваши базы данных от вашего контейнера (я предполагаю, что Вы не хотите, чтобы они уходили с вашим контейнером), тогда есть несколько проблем.
Я начинаю эту статью с упрощенного примера (с эфемерным хранилищем базы данных и без возможности подключения). Вы можете перейти на gist (в котором есть файлы для сборки контейнера, а также некоторые скрипты для его сборки и запуска), если все станет слишком скучным или запутанным.
mysql слушает на адресе 127.0.0.1, таким образом, мы можем только соединиться изнутри контейнера;
у нас есть только пользователь root, и ему разрешено входить только из контейнера;
поскольку наши данные записываются внутри контейнера, если мы теряем контейнер или нам нужно что-то изменить (например, применить Обновление безопасности), мы теряем наши данные.
Для этого нам нужно обновить bind-адрес в /etc/mysql/my.cnf от 127.0.0.1 до 0.0.0.0 (то есть сделаем привязку mysqld к каждой доступной сети вместо просто localhost.)
Мы могли бы просто добавить поддержку файлика /etc/mysql/my.cnf и добавить его в наш контейнер. а именно в Dockerfile:
Виновник торжества — тот самый дельфин :)
Нам нужно добавить учетную запись администратора для администрирования баз из-за пределов контейнера. Для того, чтобы добавить учетную запись, нам нужно, чтобы сервер mysql работал. Так как отдельные строки в Dockerfile создают различные коммиты, и коммиты сохраняют только состояние файловой системы (не состояние памяти), нам нужно втиснуть обе команды в один коммит:
Таким образом, чтобы сохранить базы данных из нашего контейнера в /data/mysql на вашем хост-компьютере, мы обновляем нашу команду run:
Чтобы решить эту проблему, давайте создадим скрипт, который для замены просто вызывает /usr/bin/mysqld_safe.
Во-первых, давайте напишем наш сценарий ( startup.sh) для инициализации только в том случае, если каталог данных еще не заполнен.
И теперь мы обновим Dockerfile, чтобы добавить startup.sh к контейнеру и вызвать его вместо mysqld_safe:
из ведущих систем управления базами данных, и я думаю что вам понравится тема, которую я хочу предложить.
На первый взгляд, создание контейнера MySQL для Docker довольно легко, но если вы хотите подключиться (не уверены, что сервер mysql, который не позволил бы этого, был бы хорош) и отделить ваши базы данных от вашего контейнера (я предполагаю, что Вы не хотите, чтобы они уходили с вашим контейнером), тогда есть несколько проблем.
Я начинаю эту статью с упрощенного примера (с эфемерным хранилищем базы данных и без возможности подключения). Вы можете перейти на gist (в котором есть файлы для сборки контейнера, а также некоторые скрипты для его сборки и запуска), если все станет слишком скучным или запутанным.
1. Делаем основу
Сейчас мы наметим Dockerfile на основе Ubuntu.FROM ubuntu
RUN dpkg-divert --local --rename --add /sbin/initctl
RUN ln -s /bin/true /sbin/initctl
RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get -y install mysql-server
EXPOSE 3306
CMD ["/usr/bin/mysqld_safe"]
затем создадим и пометим его:docker build -t mysql-ubu .
Теперь у нас есть полностью функционирующий контейнер, который мы можем запустить так:docker run -d -p 3306:3306 mysql-ubu
Он будет работать, но такой подход не полезен, потому что:mysql слушает на адресе 127.0.0.1, таким образом, мы можем только соединиться изнутри контейнера;
у нас есть только пользователь root, и ему разрешено входить только из контейнера;
поскольку наши данные записываются внутри контейнера, если мы теряем контейнер или нам нужно что-то изменить (например, применить Обновление безопасности), мы теряем наши данные.
2. Обновим адресную привязку
Первый шаг должен заставить наш сервер mysql слушать больше, чем localhost так, чтобы мы могли соединиться извне с нашим контейнером.Для этого нам нужно обновить bind-адрес в /etc/mysql/my.cnf от 127.0.0.1 до 0.0.0.0 (то есть сделаем привязку mysqld к каждой доступной сети вместо просто localhost.)
Мы могли бы просто добавить поддержку файлика /etc/mysql/my.cnf и добавить его в наш контейнер. а именно в Dockerfile:
ADD ./my.cnf /etc/mysql/my.cnf
Или мы можем обновить это свойство. Я предпочитаю этот способ, потому что я получаю самую последнюю конфигурацию из своей установки и просто обновляю то, что мне нужно. Мы можем добавить соответствующую команду sed в наш Dockerfile после установки mysql-server.RUN sed -i -e"s/^bind-address\s*=\s*127.0.0.1/bind-address = 0.0.0.0/" /etc/mysql/my.cnf
Даже при том, что mysqld сейчас слушает все, мы все еще не можем войти, потому что пользователь root только имеет доступ от localhost.Виновник торжества — тот самый дельфин :)
Нам нужно добавить учетную запись администратора для администрирования баз из-за пределов контейнера. Для того, чтобы добавить учетную запись, нам нужно, чтобы сервер mysql работал. Так как отдельные строки в Dockerfile создают различные коммиты, и коммиты сохраняют только состояние файловой системы (не состояние памяти), нам нужно втиснуть обе команды в один коммит:
RUN /usr/sbin/mysqld & \
sleep 10s &&\
echo "GRANT ALL ON *.* TO admin@'%' IDENTIFIED BY 'changeme' WITH GRANT OPTION; FLUSH PRIVILEGES" | mysql
EXPOSE 3306
CMD ["/usr/bin/mysqld_safe"]
А теперь давайте все соберем и запустим.docker build -t mysql-ubu .
docker run -d -p 3306:3306 mysql-ubu
Теперь попробуем подключиться. Для того, чтобы сделать это, нам нужно выяснить ip контейнера, и чтобы найти это, нам нужен идентификатор нашего контейнера. Это достаточно легко сделать вручную с помощью docker ps и docker inspect, но вы также можете написать сценарий:CONTAINER_ID=$(docker ps | grep mysql | awk '{print $1}')
IP=$(docker inspect $CONTAINER_ID | python -c 'import json,sys;obj=json.load(sys.stdin);print obj[0]["NetworkSettings"]["IPAddress"]')
mysql -u admin -p -h $IP
Теперь у нас есть полнофункциональный контейнер mysql! Это здорово, но мы вкладываем много доверия в этот контейнер, полагаясь на него, чтобы отслеживать наши базы данных, не говоря уже о том, что мы бы облажались, если мы когда-нибудь захотели обновить что-нибудь. Почему? Да потому, что данные непостоянны. Как их сделать постоянными — читайте далее.3. Делаем данные постоянными
Нам нужно удалить нашу зависимость от этого конкретного контейнера, и для этого нам нужно экстернализировать (попросту говоря, сделать внешним) наш каталог данных. Это легко, но вызывает проблемы. При запуске нашего контейнера мы просто посылаем в контейнер команду -v /host/path:/container/path, и поставляемый каталог на вашем хост-компьютере используется в контейнере везде, где мы указываем.Таким образом, чтобы сохранить базы данных из нашего контейнера в /data/mysql на вашем хост-компьютере, мы обновляем нашу команду run:
docker run -d -p 3306:3306 -v /data/mysql:/var/lib/mysql mysql-ubu
Проблема в том, что мы просто уничтожим наши системные таблицы, когда мы заменим /var/lib/mysql нашим пустым каталогом. Это также означает, что мы потеряли нашего администратора. Это сложно объяснить, потому что мы не можем инициализировать каталог (или добавить нашего администратора), пока каталог данных не будет виден контейнеру (во время выполнения), но мы не хотим инициализировать каталог каждый раз, когда мы запускаем либо. Весь смысл экстернализации каталога данных заключается в том, чтобы контейнер мог «приходить» и «уходить» без потери данных.Чтобы решить эту проблему, давайте создадим скрипт, который для замены просто вызывает /usr/bin/mysqld_safe.
Во-первых, давайте напишем наш сценарий ( startup.sh) для инициализации только в том случае, если каталог данных еще не заполнен.
#!/bin/bash
if [ ! -f /var/lib/mysql/ibdata1 ]; then
mysql_install_db
fi
/usr/bin/mysqld_safe
Это будет искать файл «ibdata1» в наших данных как способ определить, нужно ли нам инициализировать каталог или нет. После того, как каталог данных был инициализирован (или определен уже инициализирован), мы можем продолжить запуск сервера.И теперь мы обновим Dockerfile, чтобы добавить startup.sh к контейнеру и вызвать его вместо mysqld_safe:
ADD ./startup.sh /opt/startup.sh
CMD ["/bin/bash", "/opt/startup.sh"]
Мы также можем добавить в нашего admin пользователя со скриптом следующее:#!/bin/bash
if [ ! -f /var/lib/mysql/ibdata1 ]; then
mysql_install_db
/usr/bin/mysqld_safe &
sleep 10s
echo "GRANT ALL ON *.* TO admin@'%' IDENTIFIED BY 'changeme' WITH GRANT OPTION; FLUSH PRIVILEGES" | mysql
killall mysqld
sleep 10s
fi
/usr/bin/mysqld_safe
И, конечно, мы должны также удалить строку запуска из Dockerfile, который делал то же самое, но получал отмену как только мы расширили каталог данных.
Похожие публикации
MySQL в контейнере Docker - часть II
Облегчаем себе работу при помощи Docker и Plesk Panel
Docker заблокировал доступ из-за санкций
Как удалить старые пакеты Docker в CentOS
Как обновить MySQL 5.5 до 5.6/5.7 или MariaDB 5.5 до 10.x в Linux
Нет комментариев