Написано: 09.01.2023

Часть 7. Мульти-контейнерные приложения

До этого момента мы работали с приложениями с одним контейнером. Но теперь мы хотим добавить MySQL в стек приложений. Часто возникает следующий вопрос - “Где будет работать MySQL? Установите его в тот же контейнер или запустите отдельно?” В общем, каждый контейнер должен делать что-то одно, и делать это хорошо.

Причин несколько:

  • Есть большая вероятность, что придется масштабировать API и интерфейсы иначе, чем базы данных

  • Отдельные контейнеры позволяют вам создавать версии и обновлять версии изолированною

  • Хотя вы можете использовать контейнер для базы данных локально, вы можете захотеть использовать управляемую службу для базы данных в рабочей среде. Тогда вы не захотите отправлять свой движок базы данных вместе с вашим приложением.

  • Для запуска нескольких процессов потребуется диспетчер процессов (контейнер запускает только один процесс), что усложняет запуск / завершение работы контейнера

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

Fig7-1

Сеть контейнера

Помните, что контейнеры по умолчанию выполняются изолированно и ничего не знают о других процессах или контейнерах на том же компьютере. Итак, как мы можем позволить одному контейнеру общаться с другим? Ответ заключается в создании сетей. Теперь вам не обязательно быть сетевым инженером (ура!). Просто запомните это правило…

Обратите внимание
Если два контейнера находятся в одной сети, они могут взаимодействовать друг с другом. Если не находятся в сети, то не могут.

Запуск MySQL

Есть два способа поместить контейнер в сеть: 1) Назначить его при запуске или 2) подключить существующий контейнер.

На данный момент мы сначала создадим сеть и подключим контейнер MySQL при запуске.

1. Создайте сеть.

docker network create todo-app

2. Запустите контейнер MySQL и подключите его к сети. Мы также собираемся определить несколько переменных среды, которые база данных будет использовать для инициализации базы данных (см. раздел “Переменные среды” в списке MySQL Docker Hub).

Если вы используете Windows, примените такую команду в PowerShell.

docker run -d `
     --network todo-app --network-alias mysql `
     -v todo-mysql-data:/var/lib/mysql `
     -e MYSQL_ROOT_PASSWORD=secret `
     -e MYSQL_DATABASE=todos `
     mysql:8.0

Вы также увидите, что мы указали флаг --network-alias. Мы вернемся к этому довольно скоро.

Совет
Вы заметите, что здесь мы используем том с именем todo-mysql-data и монтируем его в /var/lib/mysql, где MySQL хранит свои данные. Однако мы никогда не запускали команду docker volume create. Docker распознает, что мы хотим использовать именованный том, и автоматически создает его для нас.

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

docker exec -it <mysql-container-id> mysql -u root -p

Когда появится запрос на ввод пароля, введите secret. В командной строке MySQL перечислите базы данных и убедитесь, что вы видите базу данных todos.

mysql> SHOW DATABASES;

Вы должны увидеть вывод, который выглядит следующим образом:

+--------------------+
 | Database           |
 +--------------------+
 | information_schema |
 | mysql              |
 | performance_schema |
 | sys                |
 | todos              |
 +--------------------+
 5 rows in set (0.00 sec)

Выйдите из командной строки MySQL, чтобы вернуться к командной строке на нашем компьютере.

mysql> exit

Ура! У нас есть наша база данных todos, и она готова к использованию!

Подключение к MySQL

Теперь, когда мы знаем, что MySQL запущен, давайте его использовать! Но вопрос в том… как? Если мы запустим другой контейнер в той же сети, как мы найдем контейнер (помните, что у каждого контейнера есть свой собственный IP-адрес)?

Чтобы разобраться в этом, мы собираемся использовать контейнер nicolaka/netshoot, который поставляется с множеством инструментов, полезных для устранения неполадок или отладки сетевых проблем.

1. Запустите новый контейнер, используя изображение nicolaka/netshoot. Убедитесь, что вы подключили его к той же сети.

docker run -it --network todo-app nicolaka/netshoot

2. Внутри контейнера мы собираемся использовать команду dig, которая является полезным инструментом DNS. Мы собираемся найти IP-адрес для имени хоста mysql.

dig mysql

И вы получите результат, подобный этому…

; <<>> DiG 9.18.8 <<>> mysql
 ;; global options: +cmd
 ;; Got answer:
 ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162
 ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0

 ;; QUESTION SECTION:
 ;mysql.				IN	A

 ;; ANSWER SECTION:
 mysql.			600	IN	A	172.23.0.2

 ;; Query time: 0 msec
 ;; SERVER: 127.0.0.11#53(127.0.0.11)
 ;; WHEN: Tue Oct 01 23:47:24 UTC 2019
 ;; MSG SIZE  rcvd: 44

В “ANSWER SECTION” вы увидите запись A для mysql, которая принимает значение 172.23.0.2 (ваш IP-адрес, скорее всего, будет иметь другое значение). Хотя mysql обычно не является допустимым именем хоста, Docker смог преобразовать его в IP-адрес контейнера, у которого был этот сетевой псевдоним (помните флаг --network-alias, который мы использовали ранее?).

Что это означает, так это то, что… нашему приложению достаточно просто подключиться к хосту с именем mysql, и оно будет взаимодействовать с базой данных! Это не намного проще, чем это!

Запуск приложения совместно с MySQL

Приложение todo поддерживает настройку нескольких переменных среды для указания параметров подключения к MySQL. Вот они:

  • MYSQL_HOST – имя хоста для запущенного сервера MySQL
  • MYSQL_USER – имя пользователя, используемое для подключения
  • MYSQL_PASSWORD – пароль, используемый для подключения
  • MYSQL_DB – база данных, которую нужно использовать после подключения

Настройка параметров подключения через Env Vars
Хотя использование переменных env для установки параметров подключения, как правило, приемлемо для разработки, это КРАЙНЕ НЕ РЕКОМЕНДУЕТСЯ при запуске приложений в рабочей среде. Диого Моника, бывший руководитель службы безопасности Docker, написал фантастический пост в блоге, объясняющий почему.
Более безопасный механизм заключается в использовании секретной поддержки, предоставляемой вашей платформой оркестровки контейнеров. В большинстве случаев эти секреты монтируются в виде файлов в запущенном контейнере. Вы увидите, что многие приложения (включая MySQL image и приложение todo) также поддерживают переменные env с суффиксом _FILE, указывающим на файл, содержащий переменную.
В качестве примера, установка переменной MYSQL_PASSWORD_FILE приведет к тому, что приложение будет использовать содержимое файла, на который ссылается ссылка, в качестве пароля для подключения. Docker ничего не делает для поддержки этих переменных среды. Вашему приложению нужно будет знать, как искать переменную и получать содержимое файла.

Теперь, когда все это объяснено, давайте запустим наш готовый к разработке контейнер!

1. Внимание: для версий MySQL 8.0 и выше обязательно включите в mysql следующие команды

mysql> ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY 'secret';
mysql> flush privileges;

2. Мы укажем каждую из приведенных выше переменных среды, а также подключим контейнер к нашей сети приложений.

Если вы используете Windows, то используйте эту команду в PowerShell.

docker run -dp 3000:3000 `
   -w /app -v "$(pwd):/app" `
   --network todo-app `
   -e MYSQL_HOST=mysql `
   -e MYSQL_USER=root `
   -e MYSQL_PASSWORD=secret `
   -e MYSQL_DB=todos `
   node:18-alpine `
   sh -c "yarn install && yarn run dev"

3. Если мы посмотрим на журналы для контейнера (docker logs -f <container-id>), мы должны увидеть сообщение, указывающее, что он использует базу данных mysql.

nodemon src/index.js
 [nodemon] 2.0.20
 [nodemon] to restart at any time, enter `rs`
 [nodemon] watching dir(s): *.*
 [nodemon] starting `node src/index.js`
 Connected to mysql db at host mysql
 Listening on port 3000

4. Откройте приложение в своем браузере и добавьте несколько пунктов в свой список дел.

5. Подключитесь к базе данных mysql и докажите, что элементы записываются в базу данных. Помните, что паролем является слово secret.

docker exec -it <mysql-container-id> mysql -p todos

И в командной строке mysql запустите следующее:

mysql> select * from todo_items;
 +--------------------------------------+--------------------+-----------+
 | id                                   | name               | completed |
 +--------------------------------------+--------------------+-----------+
 | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! |         0 |
 | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome!        |         0 |
 +--------------------------------------+--------------------+-----------+

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

Следующие шаги

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

Но есть большая вероятность, что вы начинаете чувствовать себя немного перегруженным всем, что вам нужно сделать, чтобы запустить это приложение. Вам нужно создать сеть, запустить контейнеры, указать все переменные среды, предоставить порты и многое другое! Это многое нужно запомнить, и это, безусловно, затрудняет передачу информации кому-то другому.

В следующем разделе вы узнаете о Docker Compose. С помощью Docker Compose вы можете гораздо проще делиться стеками своих приложений и позволять другим запускать их с помощью одной (и простой) команды!