В этой статье мы расскажем о переносе одного из компонентов монолитного SAAS-сервиса, а именно тестового интернет-магазина, в контейнеры Docker. Статья будет полезна тем, кто только приступил к изучению Docker.
Планируя перенос в Docker, мы ставили перед собой несколько задач.
Прежде всего, хотелось оценить возможность и трудоемкость переноса всего SAAS-сервиса интернет-магазинов в контейнеры. Предполагается, что такой перенос повлияет положительно на отказоустойчивость сервиса и масштабируемость при соответствующих изменениях в его архитектуре. После переноса появится возможность переработки технологии CI/CD с применением специально предназначенных для этого инструментов. Перенос в Docker создает предпосылки к переходу на микро-сервисную архитектуру.
Далее, возможность запуска магазинов в контейнерах упростит для программистов работы над программным обеспечением. Теперь они смогут очень быстро развернуть среду на своем домашнем компьютере без привлечения системного администратора.
И, конечно, использование контейнеров «модно и молодежно» и будет интересно программистам, занятым в сопровождении и развитии сервиса (рис. 1).
Так как раньше мы использовали Docker только для экспериментов и запуска отдельных готовых приложений, то прежде всего решили обновить свои познания в этой области.
В интернете можно найти немало курсов и статей на эту тему. Но наш SAAS-сервис написан на Perl, и довольно давно — работы были начаты примерно 20 лет назад. Чтобы было понятно, о чем идет речь, мы показали на рис. 2 портрет основателя сервиса и автора этой статьи, созданный Midjourney.
Большинство курсов, книг и статей, посвященных Docker, приводят примеры переноса в контейнеры приложений, написанных на других, более популярных сегодня языках программирования — Python, Go, JavaScript (NodeJS) и других.
В процессе переноса возникало множество вопросов. Мы читали документацию и различные статьи, искали ответы в интернете с помощью Google, YouTube, СhatGPT, а также различных групп Telegram.
Хотя СhatGPT не всегда подсказывал работающее решение, он сэкономил нам немало времени. Некоторые изображения для этой статьи были созданы при помощи нейросети Midjourney и фрагментов переписки с СhatGPT буквально за несколько минут.
Архитектура сервиса SAAS
Кратко расскажем о том, как устроена часть SAAS-сервиса, имеющая отношение к тестовым интернет-магазинам.
До переноса тестовые интернет-магазины создавались на виртуальных машинах Debian с установленной панелью управления ISPmanager или HestiaCP. Начальная подготовка такой виртуальной машины заключалась в установке OS Debian, одной из перечисленных выше панелей управления, а также в запуске скриптов начального развертывания среды SAAS-сервиса.
С учетом необходимой дополнительной ручной работы на подготовку такой виртуальной машины у системного администратора уходит 1–2 дня. Исторически такие инструменты, как Ansible, не использовались.
При переносе тестового интернет-магазина в контейнеры ставилась задача максимального облегчения работы программистов. В идеале они должны самостоятельно создавать и запускать на своих рабочих станциях нужные контейнеры, используя для работы с ПО те же самые инструменты, которыми пользовались раньше.
Кроме того, нужно было минимизировать изменения в ПО сайтов, связанные с переходом к работе в контейнерах Docker. Из этих соображений контейнеры должны были предоставлять совместимую среду выполнения для скриптов интернет-магазинов.
Тестовый интернет-магазин состоит из трех сайтов (рис. 3):
сайт витрины;
административный сайт;
сайт конфигуратора
Все эти сайты работают с одной и той же базой данных Mariadb. При этом скрипты сайта витрины и административного сайта должны иметь доступ к файлам ядра ПО сервиса, которые хранятся в каталогах специально выделенного для этого пользователя core.
Для кеширования сайты используют memcached, а для ускорения поиска — Sphinx. Кроме того, с помощью Sphinx организован поиск в базе данных KLADR.
Административный сайт позволяет менеджерам интернет-магазина управлять каталогом товаров, обрабатывать заказы, работать с покупателями с помощью встроенной CRM, получать данные статистики и так далее.
В процессе управления каталогом административный сайт загружает изображения товаров в каталог сайта витрины. Поэтому скрипты административного сайта должны иметь доступ к файлам и каталогам сайта витрины.
Сайт конфигуратора управляет настройками административного сайта и сайта витрины. Ему нужен доступ к той же базе данных, которая используется первыми двумя сайтами.
Сайты витрины и административный сайт работают вместе. Вначале мы разместили эти сайты в одном контейнере, чтобы для программистов все было похоже на работу с существующими виртуальными машинами. Однако потом было принято решение выделить для каждого из этих сайтов отдельный контейнер (рис. 4).
В файле docker-compose.yml проекта были созданы соответствующие сервисы, названия которых показаны на рис. 4.
Как видно из рисунка, сервис app реализует функциональность административного сайта, app_shop — сайта витрины интернет-магазина, а сервис app_config — сервис конфигуратора.
В рамках сервиса mariadb работает одноименная СУБД, а сервис memcached кэширует запросы к базе данных (и к некоторым другим сервисам) со стороны сайтов.
Для работы с базами данных в процессе отладки используются сервисы adminer и phpmyadmin. При этом программисты могут выбрать тот инструмент, к которому они больше привыкли.
И, наконец, сервис обратного прокси nginx-proxy необходим для обеспечения доступа ко всем трем сайтам с использованием портов 80 и 443. Дополнительно он позволяет подключать к сайтам сертификаты SSL, как бесплатные Let`s Encrypt, так и приобретенные для конкретных доменных имен.
При таком разделении на контейнеры с применением систем оркестрации Swarm или Kubernetes в рабочем окружении появится возможность запуска контейнеров на разных узлах кластера. Это даст возможность повышения отказоустойчивости и нагрузочной способности.
Если какой-то из узлов выйдет из строя, контейнеры будут перенесены на другие узлы. Если же в пике продаж возрастет нагрузка на сайт интернет-магазина, появится возможность запуска дополнительных сервисов app_shop на других узлах системы.
Что пришлось изменить в проекте при переносе
Оказалось, что при соответствующей настройке контейнеров для запуска интернет-магазинов в Docker с целью разработки и отладки потребовалось не так много изменений. Фактически все свелось к правке строк конфигурации, имеющих отношение к соединению с базой данных и memcached.
В файле конфигурации сайтов в DSN базы данных и в подключении к сервису Memcached строки localhost и 127.0.0.1 заменили именами соответствующих контейнеров из файла docker-compose.yml, описанного ниже:
$this->{ db_dsn } = "DBI:mysql:gift_db:mariadb";
$this->{ mcached_servers } = ["memcached:11211"];
Аналогичное изменение было сделано и в файле конфигурации Sphinx:
source cities
{
type = mysql
sql_host = mariadb
sql_user = kladr
sql_pass = *********
sql_db = kladr
sql_port = 3306
…
}
Больше никакой адаптации скриптов делать не пришлось. При переводе архитектуры монолита на микро-сервисы планируется внести множество изменений, однако это уже будет другая история.
Файл docker-compose.yml
Ниже мы представили файл docker-compose.yml, который запускает все необходимые нам контейнеры:
version: '3'
services:
app:
build:
context: app
env_file: .env
ports:
- 8077:80
depends_on:
- memcached
- mariadb
volumes:
- mariadb-socket:/run/mysqld/
- ./app/home:/home
networks:
- mynet
app_config:
build:
context: app_config
env_file: .env
ports:
- 8078:80
depends_on:
- memcached
- mariadb
- app
volumes:
- mariadb-socket:/run/mysqld/
- ./app/home:/home
networks:
- mynet
app_shop:
build:
context: app_shop
env_file: .env
ports:
- 8079:80
depends_on:
- memcached
- mariadb
- app
volumes:
- mariadb-socket:/run/mysqld/
- ./app/home:/home
networks:
- mynet
memcached:
image: memcached
ports:
- "11211:11211"
restart: always
networks:
- mynet
mariadb:
image: mariadb
restart: always
environment:
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
CONFIG_DB: ${CONFIG_DB}
CONFIG_USER: ${CONFIG_USER}
CONFIG_DB_PASSWORD: ${CONFIG_DB_PASSWORD}
GIFT_DB: ${GIFT_DB}
GIFT_DB_USER: ${GIFT_DB_USER}
GIFT_DB_PASSWORD: ${GIFT_DB_PASSWORD}
KLADR_DB: ${KLADR_DB}
KLADR_USER: ${KLADR_USER}
KLADR_DB_PASSWORD: ${KLADR_DB_PASSWORD}
volumes:
- ../mariadb:/var/lib/mysql
- mariadb-socket:/run/mysqld/
- ./app/mariadb_conf:/etc/mysql/conf.d
- ./app/initdb:/docker-entrypoint-initdb.d
networks:
- mynet
user: "root"
command: --init-file /docker-entrypoint-initdb.d/01-create-db.sh
adminer:
image: adminer
restart: always
ports:
- 8080:8080
networks:
- mynet
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- 8087:80
environment:
- PMA_ARBITRARY=1
depends_on:
- mariadb
networks:
- mynet
nginx_proxy:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- nginx_proxy_net
volumes:
mariadb-socket:
networks:
mynet:
nginx_proxy_net:
Сервисы для сайтов интернет-магазина
Так как контейнеры сайтов требуют индивидуальной настройки, например установки утилит и модулей Perl, то для каждого из них подготовлен файл Dockerfile. Эти файлы будут рассмотрены ниже в статье. Что касается сервисов Mariadb, Memcached и других, то они создаются на базе готовых образов, полученных с Docker Hub.
Блоки сервисов app, app_shop и app_config, определенные в файле docker-compose.yml, очень похожи. У них отличаются только пути к каталогу для сборки Docker-образа и отображение порта 80 контейнера на порты хоста :
app:
build:
context: app
env_file: .env
ports:
- 8077:80
depends_on:
- memcached
- mariadb
volumes:
- mariadb-socket:/run/mysqld/
- ./app/home:/home
networks:
- mynet
Для административного сайта app используется порт 8077, для сайта витрины app_shop — порт 8079, а для сайта конфигуратора app_config — порт 8078. Эти отображения заданы в соответствующих списках ports.
Скрипты сайтов исторически были написаны таким образом, что им нужен доступ к базе данных не только по сети через порт 3306, но и через сокет. Поэтому в разделе volumes указано, что в контейнере должен быть доступен сокет Mariadb, а также каталог app/home, содержащий файлы и скрипты трех сайтов:
volumes:
- mariadb-socket:/run/mysqld/
- ./app/home:/home
Доступность в контейнере каталога app/home
, отображающегося на каталог /home
контейнера, позволяет разработчикам менять файлы сайтов, не перезапуская контейнер.
Список depends_on определяет, что сервис app должен быть запущен после сервисов memcached и mariadb:
depends_on:
- memcached
- mariadb
Следует учесть, что на запуск зависимых сервисов, например, mariadb, может потребоваться некоторое время. Порядок запуска не гарантирует, что сервис будет запущен после завершения инициализации сервисов, от которых он зависит.
Для связи сервисов app, app_shop, app_config, memcached и mariadb мы используем список сетей networks, в котором определена сеть mynet:
networks:
- mynet
Параметр env_file указывает файл .env, содержащий пароли доступа к базам данных, пользователям и другую конфиденциальную информацию, определенную в виде переменных среды:
env_file: .env
Вот как может выглядеть такой файл (пароли заменены символами *****):
GIFT_PASSWORD=*****
CORE_PASSWORD=*****
KLADR_PASSWORD=*****
CONFIG_PASSWORD=*****
GIFT_DB=gift
GIFT_DB_USER=gift
GIFT_DB_PASSWORD=*****
KLADR_DB=kladr
KLADR_USER=kladr
KLADR_DB_PASSWORD=*****
CONFIG_DB=config
CONFIG_USER=config
CONFIG_DB_PASSWORD=*****
MARIADB_ROOT_PASSWORD=*****
По соображениям безопасности файл .env добавлен в файл .gitignore и не сохраняется в проекте Gitlab.
Сервис memcached
Сервис memcached используется сайтами для снижения нагрузки на базу данных и для сокращения запросов к различным сервисам. Он определен в файле docker-compose.yml следующим образом:
memcached:
image: memcached
ports:
- "11211:11211"
restart: always
networks:
- mynet
Здесь нет ничего необычного. Указан стандартный для memcached порт 11211, а также сеть mynet.
Сервис mariadb
Для сервиса mariadb мы использовали готовый одноименный образ:
mariadb:
image: mariadb
restart: always
environment:
MARIADB_ROOT_PASSWORD: ${MARIADB_ROOT_PASSWORD}
CONFIG_DB: ${CONFIG_DB}
CONFIG_USER: ${CONFIG_USER}
CONFIG_DB_PASSWORD: ${CONFIG_DB_PASSWORD}
GIFT_DB: ${GIFT_DB}
GIFT_DB_USER: ${GIFT_DB_USER}
GIFT_DB_PASSWORD: ${GIFT_DB_PASSWORD}
KLADR_DB: ${KLADR_DB}
KLADR_USER: ${KLADR_USER}
KLADR_DB_PASSWORD: ${KLADR_DB_PASSWORD}
volumes:
- ../mariadb:/var/lib/mysql
- mariadb-socket:/run/mysqld/
- ./app/mariadb_conf:/etc/mysql/conf.d
- ./app/initdb:/docker-entrypoint-initdb.d
networks:
- mynet
user: "root"
command: --init-file /docker-entrypoint-initdb.d/01-create-db.sh
Параметр environment задает список переменных среды, значения которых берутся из упомянутого выше файла .env и передаются в контейнер mariadb. Они используются там для инициализации баз данных.
Параметр volumes связывает локальный каталог mariadb, расположенный в каталоге пользователя, запускающего docker-compose, и каталог /var/lib/mysql контейнера, в котором находятся файлы баз данных.
Кроме того, этот параметр предоставляет доступ к базе данных через сокеты, позволяет управлять конфигурацией mariadb через каталог /etc/mysql/conf.d
, а также задает каталог app/initdb
для хранения скриптов инициализации баз данных:
volumes:
- ../mariadb:/var/lib/mysql
- mariadb-socket:/run/mysqld/
- ./app/mariadb_conf:/etc/mysql/conf.d
- ./app/initdb:/docker-entrypoint-initdb.d
Когда контейнер применяется для отладки (как в нашем случае), мы можем позволить себе хранить файлы баз данных в каталоге локального компьютера. Напомним, что если хранить их в контейнере, то каждый раз при перезапуске контейнера базы данных придется создавать заново.
Если же контейнер будет использован в рабочем окружении, то каталоги /var/lib/mysql
и /etc/mysql/conf.d
придется монтировать на сетевое дисковое устройство. Например, подойдет NFS, iSCSI или FC, в зависимости от ваших возможностей и требований к быстродействию.
В файле docker-compose.yml для создаваемой базы данных задается пароль пользователя root (параметр MARIADB_ROOT_PASSWORD). Таким же образом можно создать еще одну базу данных, указав ее пароль.
В нашем случае нужны сразу три базы данных (для магазина, конфигуратора и KLADR). Для каждой базы требуется задать имя пользователя, пароль, кодировку, а также сортировку.
Для создания баз данных мы сделали отображение каталога /docker-entrypoint-initdb.d
на локальный каталог app/initdb
с файлом 01-create-db.sh:
#!/bin/bash
mysql -uroot -p${MARIADB_ROOT_PASSWORD} <<MYSQL_SCRIPT
CREATE DATABASE IF NOT EXISTS config CHARACTER SET cp1251 COLLATE cp1251_general_ci;
CREATE USER IF NOT EXISTS 'config'@'%' IDENTIFIED BY '${CONFIG_DB_PASSWORD}';
GRANT ALL PRIVILEGES ON config.* TO 'config'@'%';
CREATE DATABASE IF NOT EXISTS kladr CHARACTER SET cp1251 COLLATE cp1251_general_ci;
CREATE USER IF NOT EXISTS 'kladr'@'%' IDENTIFIED BY '${KLADR_DB_PASSWORD}';
GRANT ALL PRIVILEGES ON kladr.* TO 'kladr'@'%';
CREATE DATABASE IF NOT EXISTS gift CHARACTER SET cp1251 COLLATE cp1251_general_ci;
CREATE USER IF NOT EXISTS 'gift'@'%' IDENTIFIED BY '${GIFT_DB_PASSWORD}';
GRANT ALL PRIVILEGES ON gift.* TO 'gift'@'%';
FLUSH PRIVILEGES;
MYSQL_SCRIPT
Этот файл должен быть доступен для выполнения (используйте chmod + x
).
Поле инициализации базы данных файл 01-create-db.sh будет запущен, для чего в файле docker-compose.yml предусмотрены параметры user и command:
user: "root"
command: --init-file /docker-entrypoint-initdb.d/01-create-db.sh
Если вам нужно заново создать базы данных, следует сначала удалить каталог mariadb, созданный в каталоге пользователя локального компьютера, а потом запустить контейнеры заново.
Но как задать параметры для mariadb, работающей в контейнере?
Для этого мы создали файл db.cnf и записали его в локальный каталог app/initdb, отображаемый на каталог /etc/mysql/conf.d
контейнера:
[mysqld]
skip-name-resolve = 1
key_buffer_size = 256M
max_allowed_packet = 16M
…
innodb_buffer_pool_size = 256M
innodb_buffer_pool_instances = 1
Если вы привыкли оценивать необходимые изменения в параметрах работы Mariadb с помощью утилиты mysqltuner.pl, то сможете использовать ее и при работе этой СУБД в контейнере.
Прежде всего, определите с помощью команды docker ps
имя или идентификатор контейнера mariadb:
$ docker ps | grep mariadb
88ea97e408f0 mariadb "docker-entrypoint.s…" 59 minutes ago Up 59 minutes 3306/tcp gift-docker_mariadb_1
В данном случае имя контейнера — gift-docker_mariadb_1.
Далее подключитесь к контейнеру, запустив bash:
$ docker exec -it gift-docker_mariadb_1 bash
root@88ea97e408f0:/#
Вы увидите системное приглашение root, находясь внутри контейнера. Заметим, что подобным образом можно подключиться к любому работающему контейнеру, например, чтобы посмотреть журналы, просмотреть или отредактировать файлы, установить какие-либо дополнительные модули или программы, а также выполнить другие аналогичные действия.
Теперь загрузите программу mysqltuner.pl и запустите ее:
# apt update && apt install wget -y
# wget http://mysqltuner.pl/ -O mysqltuner.pl
# perl mysqltuner.pl --user root --pass 'passwordroot'
Укажите пароль root базы данных, заданный в параметре MARIADB_ROOT_PASSWORD файла .env.
В результате вы получите необходимые рекомендации. Внесите изменения в файл app/initdb/db.cnf
и перезапустите контейнер.
Если что-то пошло не так, посмотрите журнал контейнера mariadb:
$ docker logs gift-docker_mariadb_1
Подобным образом можно просматривать журнал любого запущенного контейнера.
Сервисы adminer и phpmyadmin
Панели управления сервером, такие как ISPmanager и HestiaCP, устанавливают для работы с базами данных приложение phpMyAdmin. Что касается Adminer, то это приложение практически ни в чем не уступает phpMyAdmin, однако его образ занимает в памяти почти в два раза меньше места.
В файле docker-compose.yml сервис adminer определен следующим образом:
adminer:
image: adminer
restart: always
ports:
- 8080:8080
networks:
- mynet
Для тех разработчиков, кто привык к phpMyAdmin, в файле docker-compose.yml подготовлено определение соответствующего сервиса:
phpmyadmin:
image: phpmyadmin
restart: always
ports:
- 8087:80
environment:
- PMA_ARBITRARY=1
depends_on:
- mariadb
networks:
- mynet
Параметр PMA_ARBITRARY позволяет phpMyAdmin подключаться к любому серверу СУБД, а не только к тем, что определены в его файлах конфигурации.
В рабочем окружении сервисы adminer и phpmyadmin можно запускать только для отладки.
Сервис nginx_proxy
В файле docker-compose.yml мы определили три контейнера с сайтами, доступными на портах 8077, 8078 и 8079. Однако нам нужно сделать так, чтобы эти сайты открывались по своим доменным именам на портах 80 и 443.
Воспользуемся для этого обратным прокси Nginx Proxy Manager на базе готового образа jc21/nginx-proxy-manager, добавив в файл docker-compose.yml сервис nginx_proxy:
nginx_proxy:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '81:81'
- '443:443'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- nginx_proxy_net
В каталоге data сервис будет хранить базу данных SQLite и файлы конфигурации nginx.
Проект Nginx Proxy Manager представлен на сайте https://nginxproxymanager.com/ (рис. 5).
Детальное описание этого сервиса выходит за рамки данной статьи, однако в интернете есть различные руководства, дополняющие описание, представленное на сайте.
Отметим только, что с помощью Web-интерфейса Nginx Proxy Manager вы сможете легко настроить переадресацию для ваших доменных имен на нужные порты. Кроме этого, можно подключить к сайтам сертификаты SSL, а также настроить индивидуальные конфигурации Nginx для каждого из сайтов.
Сервис app
Для сборки контейнеров сайтов мы используем готовый образ debian:stable-slim. Этот образ выбран для лучшей совместимости, так как на виртуальных серверах разработчиков интернет-магазинов и на рабочих серверах установлена ОС Debian 11.
В файлах Dockerfile каждого из трех упомянутых выше сайтов выполняется установка необходимых программ и модулей Perl, а также другие действия.
В рамках сервиса app работает административный сайт интернет-магазина. Ниже мы привели сокращенный файл Dockerfile для этого сервиса:
FROM debian:stable-slim
RUN useradd -ms /bin/bash gift && useradd -ms /bin/bash core && useradd -ms /bin/bash kladr && \
DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y \
apt-utils \
build-essential \
net-tools \
curl \
wget \
sudo \
apache2 apache2-utils apache2-suexec-custom libapache2-mod-php \
git \
cron \
liblib-abs-perl libcache-memcached-perl \
…
libdbd-mysql-perl libmariadb-dev \
libimage-magick-perl \
imagemagick \
libgd-perl \
php-mysql php-curl php-gd php-json php-zip php && \
cpan -i App::cpanminus && cpan Net::SMTP::SSL && \
cpanm HTML::Entities Digest::SHA1 HTML::Template BSON::Time Class::Accessor LWP::UserAgent MIME::Lite Devel::SimpleTrace DBI \
…
Authen::Htpasswd Apache::Htaccess && \
apt clean && apt-get autoremove -y && rm -rf /var/lib/apt/lists/* && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
ADD https://cpan.metacpan.org/authors/id/B/BR/BROQ/Switch-Perlish-1.0.5.tar.gz /tmp/
RUN cd /tmp/ && tar -xzf Switch-Perlish-1.0.5.tar.gz && cd Switch-Perlish-1.0.5 && perl Makefile.PL && make install && make clean
…
WORKDIR /app
COPY ./data/itmatrix /app/itmatrix
WORKDIR /app/itmatrix
RUN tar xzf Installation.tar.gz && ./install.sh
WORKDIR /app
RUN rm -rf itmatrix
COPY ./httpd_conf/apache2.conf /etc/apache2/apache2.conf
COPY ./httpd_conf/000-default.conf /etc/apache2/sites-available/000-default.conf
COPY ./httpd_conf/admin.gift.shop2you.ru.conf /etc/apache2/sites-available/admin.gift.shop2you.ru.conf
RUN ln -s /etc/apache2/sites-available/admin.gift.shop2you.ru.conf /etc/apache2/sites-enabled/
WORKDIR /etc/apache2/mods-enabled
RUN ln -s ../mods-available/cgi.load cgi.load
COPY ./httpd_conf/suexec /etc/apache2/suexec/www-data
RUN a2enmod rewrite ssl suexec auth_digest && apachectl configtest
RUN mkdir /usr/local/sphinx/
COPY ./data/sphinx-3.5.1/bin /usr/local/sphinx
COPY ./data/sphinx-3.5.1/bin /usr/bin
RUN crontab -u gift -l | { cat; echo "* * * * * /usr/bin/perl /home/gift/data/www/admin.gift.shop2you.ru/cgi-bin/dbatch.pl"; } | crontab -u gift -
RUN cron
ADD start.sh ./start.sh
RUN chmod +x ./start.sh
EXPOSE 80
CMD ["./start.sh"]
В команде RUN файле Dockerfile сервиса app создаются пользователи для сайтов, устанавливаются утилиты и модули Perl. В частности, устанавливается Apache и все необходимое для него, а также PHP.
Что касается модулей Perl, то они устанавливаются пятью разными способами — из пакетов Debian, с помощью утилит cpan и cpanm, путем скачивания и распаковки архива с сайта cpan.metacpan.org, а также из приватного архива Installation.tar.gz. Некоторые модули устанавливаются без ошибок только из пакетов Debian, а для некоторых возможна установка только из дистрибутивов, загруженных с сайта cpan.metacpan.org (при этом нужна определенная версия модулей).
После завершения установки модулей Perl команда RUN копирует в каталог /etc/apache2/
файлы конфигурации для сайтов и добавляет ссылку на конфигурацию административного сайта в каталог /etc/apache2/sites-enabled/
.
На следующем этапе к конфигурации Apache добавляются необходимые модули, а также настраивается файл /etc/apache2/suexec/www-data
.
В ваших проектах только что перечисленные действия, скорее всего, будут другими. Они зависят от деталей реализации сайтов.
Для ускорения поиска сайты используют Nginx. В Dockerfile выполняется копирование предварительно загруженных бинарных файлов этого поискового сервера. Также выполняется настройка пакетного задания и запуск cron.
На финальной стадии Dockerfile с помощью команды CMD запускает пакетный файл start.sh.
Перед запуском Apache в файле start.sh устанавливаются пароли пользователей, заданные в файле .env, меняются владельцы файлов в каталогах сайтов и настраивается доступ к каталогу /home/core/
, содержащему модули Perl ядра интернет-магазина. Кроме этого, выполняется запуск Sphinx для поиска по базе данных KLADR:
#!/bin/bash
echo "gift:${GIFT_PASSWORD}" | chpasswd
echo "core:${CORE_PASSWORD}" | chpasswd
echo "kladr:${KLDAR_PASSWORD}" | chpasswd
chown -R gift:gift /home/gift
chown -R kladr:kladr /home/kladr
chown -R core:core /home/core
usermod -a -G core gift && chmod -R g+rx /home/core/
/usr/bin/indexer --config /home/kladr/data/kladr/sphinx/sphinx_linux.conf --all
/usr/bin/searchd --config /home/kladr/data/kladr/sphinx/sphinx_linux.conf
apache2ctl -D FOREGROUND
Сервис app_shop
Файл Dockerfile сервиса app_shop аналогичен только что рассмотренному файлу сервиса app. Отличия заключаются в блоках копирования конфигурации Apache:
COPY ./httpd_conf/gift.shop2you.ru.conf /etc/apache2/sites-available/gift.shop2you.ru.conf
RUN ln -s /etc/apache2/sites-available/gift.shop2you.ru.conf /etc/apache2/sites-enabled/
Кроме этого, в этом файле несколько другой состав устанавливаемых модулей Perl.
Содержимое файла start.sh показано ниже:
#!/bin/bash
echo "gift:${GIFT_PASSWORD}" | chpasswd
chown -R gift:gift /home/gift
/usr/bin/indexer --config /home/kladr/data/kladr/sphinx/sphinx_linux.conf --all
/usr/bin/searchd --config /home/kladr/data/kladr/sphinx/sphinx_linux.conf
apache2ctl -D FOREGROUND
Здесь перед запуском Apache устанавливается пароль пользователя gift, устанавливается владелец файлов каталога /home/gift
, а также запускается Sphinx для поиска в базе данных KLADR.
Сервис app_config
Для работы сайта конфигурации был подготовлен сервис app_config. В его Dockerfile копируются файлы конфигурации Apache этого сайта:
COPY ./httpd_conf/config.itmatrix.ru.conf /etc/apache2/sites-available/config.itmatrix.ru.conf
RUN ln -s /etc/apache2/sites-available/config.itmatrix.ru.conf /etc/apache2/sites-enabled/
В остальном Dockerfile аналогичен сайту витрины интернет-магазина.
Отладка
В процессе переноса монолита в контейнеры возникала необходимость отладки файлов Dockerfile, конфигурации Apache, Mariadb, а также Sphinx.
Как уже говорилось в статье, первоначально все три сайта работали в одном контейнере. Это упростило подготовку файла Dockerfile, так как все изменения, например добавление модулей Perl и настройку конфигурации Apache, можно было делать в одном месте.
Затем было принято решение перенести все эти сайты в отдельные контейнеры. Основная причина такого переноса — подготовка для системы оркестрации, такой как Swarm или Kubernetes. Размещение сайтов в отдельных контейнерах необходимо для обеспечения возможности масштабирования. Например, при возрастании нагрузки на сайт витрины интернет-магазина можно будет запустить для витрины дополнительные контейнеры.
Ошибки в docker-compose.yml и Dockerfile
Ошибки в файлах docker-compose.yml и Dockerfile можно увидеть при запуске. Для упрощения запуска подготовлен скрипт start.sh:
#!/bin/bash
sudo chown -R $USER:$USER $HOME/gift-docker
docker-compose up -d –build
Команда chown
здесь необходима из-за того, что при инициализации сервиса app происходит смена владельцев для каталогов из app/home.
Остановку контейнеров можно сделать скриптом stop.sh:
#!/bin/bash
docker-compose down
После того как контейнеры запустились, вы можете просмотреть их журналы с помощью команды docker logs:
docker logs gift-docker_app_1
В качестве параметра этой команде нужно передать имя контейнера. Имена всех работающих контейнеров легко определить с помощью команды “docker ps”.
Ошибки в конфигурации Apache
У сервиса Apache имеются свои журналы, путь к которым задается в файле конфигурации сервиса.
Чтобы посмотреть содержимое этих журналов, подключитесь к контейнеру следующим образом:
docker exec -it gift-docker_app_1 bash
Перейдите в каталог /var/log/apache2/
. Там находятся все нужные вам журналы:
access.log
error.log
other_vhosts_access.log
Вы можете посмотреть содержимое журналов, например, с помощью команды tail
:
# tail -f error.log
После обнаружения ошибки в файле конфигурации Apache перезапустите контейнер. Также в этот журнал могут попадать ошибки, связанные с работой скриптов ваших сайтов.
Ошибки в конфигурации Mariadb
Если сервис mariadb не запускается или с ним возникают проблемы, можно посмотреть журнал Mariadb следующим образом:
docker logs gift-docker_mariadb_1
Эта команда поможет вам при настройке файла конфигурации Mariadb.
Итоги
В результате проведенных работ были достигнуты все поставленные цели, а также намечен план дальнейших действий по переводу SAAS-сервис интернет-магазинов в Docker.
Теперь программисты могут самостоятельно разворачивать на своих рабочих станциях сайты интернет-магазинов, буквально за полчаса в автоматическом режиме и без привлечения системного администратора. Раньше на подготовку виртуальных машин разработчиков у системного администратора уходило 1-2 дня.
Отталкиваясь от достигнутых результатов, запланированы работы по разворачиванию тестового интернет-магазина сначала в Swarm, а затем в Kubernetes. На первом этапе будет подключено внешнее сетевое хранилище для размещения баз данных и файлов сайтов.
Если все работы завершатся успехом, планируется переход на микро-сервисную архитектуру. Это, однако, потребует внесения масштабных изменений в архитектуру, а также в исходные коды проекта.
Автор статьи: Александр Фролов.
НЛО прилетело и оставило здесь промокод для читателей нашего блога:
— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS.
Комментарии (9)
cadmi
00.00.0000 00:00После того как контейнеры запустились, вы можете просмотреть их журналы с помощью команды docker logs:
docker logs gift-docker_app_1
В качестве параметра этой команде нужно передать имя контейнера. Имена всех работающих контейнеров легко определить с помощью команды “docker ps”.Господи, у вас же compose, к чему все эти приседания с ps и суффиксами _1 ?
Ну и набирайте сразу docker compose logs -f app (если cli свежий, а если старьё, то docker-compose logs -f app)AlexandreFrolov
00.00.0000 00:00Let’s look at the logs using the docker compose logs -f command. You’ll see the logs from each of the services interleaved into a single stream.
Правильно ли я понимаю, что логи от всех контейнеров попадут в один поток? А нужно ли так? Все же там несколько контейнеров.
Iron_Butterfly
Правильное название панели ispmanager, а не ISPmanager: https://www.ispmanager.ru/materials
AlexandreFrolov
Панель меняла свое название, вот, например, тут она называлась ISPmanager:
https://habr.com/ru/company/ruvds/blog/472044/
https://www.isplicense.ru/services/ispsystem/ispmanager4/
https://www.reg.ru/hosting/ispmanager?utm_source=google.com&utm_medium=organic&utm_campaign=google.com&utm_referrer=google.com
Но при использовании Docker и систем оркестрации, таких как Swarm и Kubernetes, а также инструмента Ansible панели управления серверами не устанавливаются на узлы кластера. Просто незачем.
Я читал, что в ispmanager будут встроены средства управления контейнерами Docker:
https://docs.ispmanager.ru/ispmanager6-lite/docker
Но у меня пока нет ясности, как они будут соотноситься (если будут) с системами оркестрации.
Установить Docker на сервер и запустить docker-compose нетрудно и без панели. Но системы оркестрации позволяют реализовать основные преимущества контейнеров, такие как отказоустойчивость и горизонтальное масштабирование. Вот тут как раз интересно, что сможет предложить ispmanager.
Iron_Butterfly
С мая 2022 года, когда компания стала самостоятельной, она называется ispmanager. Старое название ISPmanager неверное.
Поддержка докера уже реализована. Там все на уровне управления контейнерами, образами. Монтирование томов, директорий, маппинг портов. Все из интерфейса. Все, что чаще всего востребовано обычными пользователями.
AlexandreFrolov
Правильно ли я понял, что контейнеры работают только в рамках одного хоста, где установлена панель, там же находится репозиторий, и никакие средстве оркестрации не используются, а интеграция со Swarm и Kubernetes не планируется?
В этом случае, на мой взгляд, теряется основное преимущество контейнеров — реализация отказоустойчивости и масштабирования для высоконагруженных проектов.
Что же касается монтирования томов и маппинга портов, то на мой взгляд, это куда удобнее делать через файл docker-compose.yml. Этот файл потом можно скачать из registry (GitHib, Harbor и т.п.) и развернуть перечисленные в нем контейнеры на любом хосте одной командой.
На самом деле было бы интересно почитать, для чего и как именно используются средства управления контейнерами ispmanager. То есть описания реальных кейсов с применением ispmanager.
Iron_Butterfly
Да, все верно вы поняли.
Реальные кейсы - например, поднятие контейнера с Redis для кэширования WordPress сайта. Или поднятие контейнера с MongoDB опять же для нужд сайта.
Докер файлом или docker-compose.yml никто не мешает пользоваться в командной строке хоста.
Iron_Butterfly
Кстати, фича реквест за интеграцию с Kubernetis вот тут имеется, можете поддержать: https://www.ispmanager.ru/community/feature-request-7
AlexandreFrolov
Да, поддержал