Приветствую всех не равнодушных!
Хочу поделиться с вами историей о том, как мы рефакторили код проекта на Битрикс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
Комментатор: А как же нам быть?
Я: Выход есть, но, возможно не идеальный. Делаю.
-
Подготавливаю виртуалку для работы множества пользователей
-
создаем каждому сотруднику учетную запись на сервере
-
заводим пользователя для управления контейнерами, к примеру web-master:web-master и его добавляем в группу docker.
-
всех пользователей добавляем в одну общую группу, к этому web-master
sudo usermod -g web-master anatoliy-pro,причем важно, чтобы первичная группа у всех пользователей была именно web-master, чтобы права на файлы не переписывались. -
разворачиваем директории с docker-compose проектами и делаем права на все файлы у докер-сборки 640 и владелец web-master:web-master, таким образом изменения для докера будут делаться под этим пользователем
-
в контейнерах директории www. делаем с правами пользователя, под которым будет идти работа
anatoliy-pro:web-master, на всякий случай после клонирования проекта в папку web, отключаем слежение за правами в git, чтобы они не улетели на продcd ./www && git config core.fileMode false -
Редактируем наш 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/
Добавить комментарий