Битрикс: от модулей к сервисам

от автора

Приветствую всех не равнодушных!

Хочу поделиться с вами историей о том, как мы рефакторили код проекта на Битрикс24 под DDD архитектуру. Возможно кому-то это будет полезно, а возможно, и сам подчерпну что-то новое для себя.

Hidden text

Для тех, кто с ходу заявит — только не Битрикс, советую ознакомиться со статьей:

Согласен, что это не лучшая платформа, однако, от ее популярности в СНГ никуда не денешься.

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

Итак, имеется портал написанный давно на Битрикс24 коробочной версии, задача — упростить поддержку, избавиться от кучи портянок в кастомных модулях, привести код к единообразию, чтобы разработчики делали его в едином стиле, добавить возможность быстрого переключения между внешними сервисами одного типа.
Задача два — позволить разработчикам битрикса, развиваться, не оставаясь заложниками одной CRM

Шаг 1: контейнеризация

За основу была взята сборка

Клонируем ее в собственный репозиторий:

mkdir my_project  cd my_project  git clone https://github.com/bitrixdock/bitrixdock ./  git remote set-url origin https://my_gitlab/repo_docker git push

Далее встает вопрос о том, что продуктовая ветка должна быть обезопасена, как минимум — нестандартными портами и на ней не должно быть adminer

Hidden text

На деле, админер вообще лучше не использовать, но это полемика.

Для того, чтобы конфигурировать можно было не через docker-compose.yml и всем его раскидывать, удобнее будет сделать ветки master, stage, developer, на которых уже можно делать конфигурацию для конкретной среды и она будет обновляться путем git pull

Далее будет приводиться пример, будто все в одной ветке на одной сборке, вы же у себя сами решайте, что вам на каких окружениях требуется:

Добавляем сервисы для сбора логов и хранения сессий.

Дабы обойти санкции на поставку ELK, воспользуемся готовой сборкой не от официалов:

    rabbitmq:         image: rabbitmq:3-management         # container_name: rabbitmq         hostname: rabbitmq         volumes_from:             - source         ports:             - '${INTERFACE}:${RABBIT_PORT1:-15672}:15672'             - '${INTERFACE}:${RABBIT_PORT2:-5672}:5672'         restart: always         environment:             RABBITMQ_DEFAULT_USER: ${RABBIT_USER}             RABBITMQ_DEFAULT_PASS: ${RABBIT_PASS}             RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: -rabbit log_levels [{connection,error},{default,error}] disk_free_limit 2147483648             TZ: Europe/Moscow         stdin_open: true         tty: true         networks:             - bitrixdock      elk:         image: sebp/elk         environment:             node.name: elk             ES_JAVA_OPTS: -Xms512m -Xmx512m             # Bootstrap password.             # Used to initialize the keystore during the initial startup of             # Elasticsearch. Ignored on subsequent runs.             ELASTIC_PASSWORD: ${ELASTIC_PASSWORD:-}             # Use single node discovery in order to disable production mode and avoid bootstrap checks.             # see: https://www.elastic.co/guide/en/elasticsearch/reference/current/bootstrap-checks.html             discovery.type: single-node             LS_JAVA_OPTS: -Xms256m -Xmx256m             LOGSTASH_INTERNAL_PASSWORD: ${LOGSTASH_INTERNAL_PASSWORD:-}         #        mem_limit: 4g         volumes:             - ./elk/logstash/20-input.conf:/etc/logstash/conf.d/20-input.conf             - ./elk/logstash/30-output.conf:/etc/logstash/conf.d/30-output.conf         ports:             - "${KIBANA_PORT:-5601}:5601"             - "${ELASTIC_PORT:-9200}:9200"             - "${LOGSTASH_PORT:-5044}:5044"         networks:             - bitrixdock

Как видите container_name: rabbitmq закомментировано, для того, чтобы docker-compose сам назначал имя ${COMPOSE_PROJECT_NAME}_serviceName, таким образом, если у вас будет несколько проектов с таким docker-compose.yml, то сервисы не будут стучаться в чужие сборки по имени контейнера.

Шаг 2: настройка

Для настройки логирования в ЕЛК, выберем схему php monolog -> rabbitMQ -> logStash -> elasticSearch -> kibana

Про то, как настроить монолог и отправить сообщения в очередь, мы поговорим в разделе настройки проекта, а сейчас покажу — как забирать эти сообщения из очереди:
./elk/logstash/20-input.conf

input {     rabbitmq {         host => "rabbitmq" # имя контейнера         port => 5672          queue => "elk" # имя очереди         durable => true         passive => true         exchange => "logs" # имя обмена         user => "rabbit-user"         password => "rabbit-password"     } } 

Форматируем сообщения:

./elk/logstash/30-output.conf

output {   elasticsearch {     hosts => ["localhost"]     manage_template => false     index => "%{[@type]}-%{[@source]}-%{+YYYY.MM.dd}"   } }

Logstash забирает сообщения из очереди logs и передает их в elasticsearch, через kibana мы можем их просматривать и создавать дашборды. Все будет доступно по localhost:SERVICE_PORT из списка

- "${KIBANA_PORT:-5601}:5601" - "${ELASTIC_PORT:-9200}:9200" - "${LOGSTASH_PORT:-5044}:5044"

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

      source:         image: alpine:latest         # container_name: source         volumes:             - ./logs/${WEB_SERVER_TYPE}:/var/log/${WEB_SERVER_TYPE}             - ./logs/php:/var/log/php             - ./logs/db:/var/log/mysql             - ./logs/memcached:/var/log/memcached             - db:/var/lib/mysql             - cache:/var/lib/memcached             - ${SITE_PATH}:/var/www/bitrix             - /etc/localtime:/etc/localtime/:ro             - ./rabbitmq/data:/var/lib/rabbitmq          networks:             - bitrixdock

Запускаем сборку и любуемся:

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

.idea .vscode .fleet logs .env www/* # Проект rabbitmq/data

Шаг 3: решение проблем

Проблема 1: в контейнеры, директория с проектом ./www пробрасывается по пути: /var/www/bitrix/ и все наши внешние изменения, сразу изменяются и в контейнерах. Но возникает следующее: Пользователь, изменяя файлы снаружи, внутри перезаписывает права на файл с id внешнего пользователя. Таким образом, если мы зайдем внутрь контейнера docker-exec -u www-data php bash, перейдем в директорию проекта cd /var/www/bitrix/ и посмотрим права на файлы, то увидим, что все файлы, которые менялись изнутри, с владельцем www-data:www-data а измененный снаружи — id_user:id_group и это приводит к тому, что на linux системах контейнеры теряют доступ на чтение этих файлов (чего не происходит на windows и macOS), но мы же приверженцы прав на файлы 640 и не хотим все делать 777?

Решение: добавляем в наш .env переменные с id пользователя, который работает снаружи

UDP: На примере выше, настройки mysql оставлены по-умолчанию, тк считаю плохой практикой держать БД с проектом на одной машине, я этот сервис отключаю, но оставил как пример или для локальной разработки.

Далее меняем для сервисов php и web_server запись в docker-compose.yml на подобную, для пробрасывания этих переменных окружения.

      php:         build:             context: ./php/${PHP_VERSION}             args:                 UID: ${UID:-1000}                 GID: ${GID:-1000}         # container_name: php

Меняем пользователей, под которым работают сервисы в их Dockerfile RUN usermod -u ${UID} www-data

Hidden text
FROM phpdockerio/php:8.2-fpm  LABEL org.opencontainers.image.source="https://github.com/bitrixdock/bitrixdock"  RUN apt-get update \     && apt-get -y --no-install-recommends install  \     libc-client-dev libkrb5-dev libssl-dev \     php8.2-gd \     php8.2-imagick \     php8.2-intl \     php8.2-interbase \     php8.2-mbstring \     php8.2-mcrypt \     php8.2-memcache \     php8.2-memcached \     php8.2-mysql \     php8.2-opcache \     php8.2-soap \     php8.2-zip \     php8.2-imap \     php8.2-curl \     && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* /usr/share/doc/*  # Получение переменных из docker-compose.yml ARG UID ARG GID  COPY ./php.ini /etc/php/8.2/fpm/conf.d/90-php.ini COPY ./php.ini /etc/php/8.2/cli/conf.d/90-php.ini  # Настройка imagick COPY ./imagick/policy.xml /etc/ImageMagick-6/policy.xml  # Смена пользователя www-data на пользователя извне RUN usermod -u ${UID} www-data RUN echo 'User: ' ${UID};  WORKDIR "/var/www/bitrix"  EXPOSE 9000

Вуаля! В контейнерах изменения пишутся под тем же пользователем, что и снаружи, таким образом 640 нас начинает устраивать.

Проблема 2: при работе с проектом, желательно вынести кеширование и сессии в memcache, чтобы не нагружать файловую систему, внесем соответствующие настройки.

Hidden text
<?php return [     /*****      * Cache в Memcache.      *****/     'cache' => [         'value' => [             'type' => 'memcache',             'memcache' => [                 'host' => 'memcached',                 'port' => '11211',             ],             'sid' => $_SERVER["DOCUMENT_ROOT"] . "#01"         ],     ], ]

<?php const BX_SECURITY_SESSION_MEMCACHE_HOST = 'memcached'; const BX_SECURITY_SESSION_MEMCACHE_PORT = 11211;

Контейнер memcache работает, и работает очень быстро, по сравнению с дисковым кешем, что видно по запросам

Однако! Видим, что на macOS и Linux все прекрасно, но на windows машинах появляются тормоза, вплоть до 6 секунд при загрузке. Это крайне не приятно и сильно мешает разработке.

Путем поиска и умозаключений, было выявлено, что проблема заключается в работе wsl2, и когда очень много файлов, то начинаются заметные тормоза при медленных дисках родительской системы.

Решение 1: Закупить SSD и перенести проект на него.

Решение 2: Организовать облако и сделать площадки для разработчиков через kubernetes

Решение 3:

Комментатор: У нас бюджет только на одну виртуалку, как нам быть?

Я: Если мы запустим 2 контейнера на 1 виртуалке, то они поругаются за права владения портами 80 и 443

Комментатор: А как же нам быть?

Я: Выход есть, но, возможно не идеальный. Делаю.

  1. Подготавливаю виртуалку для работы множества пользователей

    1. создаем каждому сотруднику учетную запись на сервере

    2. заводим пользователя для управления контейнерами, к примеру web-master:web-master и его добавляем в группу docker.

    3. всех пользователей добавляем в одну общую группу, к этому web-master sudo usermod -g web-master anatoliy-pro, причем важно, чтобы первичная группа у всех пользователей была именно web-master, чтобы права на файлы не переписывались.

    4. разворачиваем директории с docker-compose проектами и делаем права на все файлы у докер-сборки 640 и владелец web-master:web-master, таким образом изменения для докера будут делаться под этим пользователем

    5. в контейнерах директории www. делаем с правами пользователя, под которым будет идти работа anatoliy-pro:web-master, на всякий случай после клонирования проекта в папку web, отключаем слежение за правами в git, чтобы они не улетели на прод cd ./www && git config core.fileMode false

    6. Редактируем наш docker-compose.yml на ветке developer, чтобы каждой площадке дать свои собственные порты

Hidden text

.env

#Порты для площадки PORT_80=your_port PORT_443=your_port

В сервисах меняем порты на переменные

docker-compose.yml

          ports:             - '${INTERFACE}:${PORT_80:-80}:80'             - '${INTERFACE}:${PORT_443:-443}:443'

Таким образом, проблема будет решена и по адресу http://server:your_port будет открывать конкретная площадка и никто не будет драться за одинаковые порты.

Часть 4: заключение

Знаю, что тема статьи Битрикс, а мы до него даже не дошли.

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

На эти настройки будет завязана вся работа, которую я опишу во второй части, если будет интерес к теме.


ссылка на оригинал статьи https://habr.com/ru/articles/824946/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *