Локальная разработка в Docker

Хочу поделиться тем, как я в своей работе использую Docker для локальной разработки сайтов. Я не до конца понимаю, как оно там внутри устроено и частично тут будет описано то, как я работаю «по наитию». Если у кого-то будут комментарии и предложения, буду рад услышать, как этот воркфлоу можно оптимизировать.

Главное, что нам понадобится — это Docker.
Скачать его можно по этой ссылке: https://www.docker.com/get-started.



Создание каждого проекта начинается с файла docker-compose.yml в корне проекта:

version: "3"
services:
  nginx:
    image: nginx:latest
    stdin_open: true
    tty: true
    ports:
      - 80:80
    volumes:
      - 'nfsmount:/var/doodle'
      - ./.docker/vhost.conf:/etc/nginx/conf.d/default.conf:delegated
    depends_on:
      - php

  php:
    build: '.docker/'
    volumes:
      - 'nfsmount:/var/doodle'

  mariadb:
    image: mariadb
    ports:
      - 3306:3306
    command: [
        '--character-set-server=utf8mb4',
        '--collation-server=utf8mb4_unicode_ci'
    ]
    volumes:
      - ../mysql-data/database:/var/lib/mysql
    environment:
      MYSQL_DATABASE: doodle
      MYSQL_ROOT_USER: root
      MYSQL_ROOT_PASSWORD: trOgyy&gd34Kf

volumes:
  nfsmount:
    driver: local
    driver_opts:
      type: nfs
      o: addr=host.docker.internal,rw,nolock,hard,nointr,nfsvers=3
      device: ":${PWD}"

Здесь указываются реквизиты подключения к создаваемой базе данных (саму базу создавать не нужно) и путь к корню сайта внутри виртуального контейнера — /var/doodle.

Там же, в корне, создаём папку .docker, в ней будет лежать файл Dockerfile — здесь команды, которые будут выполнены при создании контейнера для PHP. Обычно из проекта в проект файл не меняется, там всё стандартно — .docker/Dockerfile

И в этой же папке будем хранить конфиг сервера, в файле vhost.conf.docker/vhost.conf, он тоже стандартный, единственное, указываем правильный путь к публичной директории. У меня это /var/doodle/www.

Вот так выглядит набор файлов на первом этапе:


Теперь запускаем создание контейнеров — docker-compose up -d. В первый раз будут скачиваться нужные файлы, будет происходить установка контейнеров и прочее. В дальнейшем эта команда просто запустит уже готовые контейнеры.

После того, как всё будет готово, загружаем MODX в папку www и можем запустить его установку по адресу http://localhost/setup/.

В качестве хоста для базы данных на MacOS нужно использовать host.docker.internal. Логин, пароль и имя базы данных указываем те, которые мы указали в docker-compose.yml



После установки MODX можно добавить в корень проекта .gitignore, чтобы лишние файлы в репозиторий не выгружались — .gitignore. И создать, собственно, git-репозиторий:
git init && git add -A && git commit -m "Initial commit"

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

Поэтому в командной работе приходится плотно знакомиться с миграциями. Миграции — это просто скрипты, которые производят операции в базе данных. То есть вместо того, чтобы создавать ресурс через админку MODX, вам нужно писать скрипт, который будет это делать — через newObject или runProcessor.

Для миграций удобно использовать phinx. Его просто установить через composer:
composer require robmorgan/phinx

Настройки миграций хранятся в файле phinx.php — здесь мы подключаем конфиг MODX, соответственно, реквизиты подключения к базе данных берём из этого конфига. Так же указываем шаблон для создания новых миграций db/modx_migration_template.php, где вы можете сохранить требуемую вам структуру файла миграций. Этот шаблон будет использоваться для всех новых команд.

Так как база данных у нас работает внутри контейнера, для выполнения команд, которые требуют подключения к базе данных, нужно запускать их изнутри. Команда, чтобы «войти» в контейнер:
docker exec -it doodle_php_1 bash

Мы окажемся в папке /var/www/html. Перейдём в папку проекта и создадим нашу первую миграцию:
cd /var/doodle/
vendor/bin/phinx create FirstMigration

Эта команда создаст файл в папке db/migrations. Вот так выглядит переименование главной страницы — db/migrations/20211010090307_first_migration.php, а вот так можно установить нужные пакеты — db/migrations/20211010092235_install_packages.php

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

Первая сложность, с которой мы столкнёмся — это доступы к базе данных в конфиг-файле. Во-первых, хранить пароль к базе данных от продакшена в гитхабе — это плохая идея. А во-вторых, у каждого локальные доступы к базе данных будут свои, нужно это предусмотреть. Отредактируем www/core/config/config.inc.php — в начале файла проверим наличие локального конфига с доступами:
<?php
/**
 *  MODX Configuration file
 */
if (file_exists(__DIR__ . '/config.environment.inc.php')) {
    /* Include configuration for the current environment. */
    include __DIR__ . '/config.environment.inc.php';
} else {
    $database_type = 'mysql';
    $database_server = 'host.docker.internal';
    $database_user = 'root';
    $database_password = 'trOgyy&gd34Kf';
    $database_connection_charset = 'utf8mb4';
    $dbase = 'doodle';
    $table_prefix = 'modx_';
    $database_dsn = 'mysql:host=host.docker.internal;dbname=doodle;charset=utf8mb4';
}
// ...

Таким образом в файле config.environment.inc.php мы будем переопределять доступы к базе данных. Этот файл уже добавлен в .gitignore и в репозиторий выгружаться не будет. Так же нужно отредактировать файлы config.core.php — поменять там пути на относительные.



Разворачивать «продакшен» будем на modhost.pro. Создаём новый сайт с чистым MODX. Когда сайт будет установлен, просто удаляем папку www и клонируем репозиторий в корень аккаунта:
rm -rf www && git clone git@github.com:ilyautkin/doodle.git

Чтобы сайт был доступен извне, создадим символическую ссылку для папки www
ln -s doodle/www www

И укажем доступы к базе данных в файле doodle/www/core/config/config.environment.inc.php:
<?php
$database_type = 'mysql';
$database_server = '127.0.0.1';
$database_user = 's27685';
$database_password = 'dfHdhDFi67FDy5';
$database_connection_charset = 'utf8mb4';
$dbase = 's27685';
$table_prefix = 'fnO7CLkAJH1e_';
$database_dsn = 'mysql:host=127.0.0.1;dbname=s27685;charset=utf8mb4';

Теперь останется запустить миграции:
cd doodle
php7.3 /usr/bin/composer.phar install
php7.3 vendor/bin/phinx migrate

На этом всё — продакшен развёрнут. Можно продолжать разработку локально, в разных ветках. А деплой будет заключаться в стягивании изменений из гитхаба и запуске миграций:
git pull && php7.3 vendor/bin/phinx migrate
Илья Уткин
10 октября 2021, 13:06
modx.pro
4
6 099
+19

Комментарии: 35

Николай Савин
10 октября 2021, 13:27
+2
О! Огонь — мне как раз надоело на каждый чих сайтики на сервере разворачивать
    iWatchYouFromAfar
    10 октября 2021, 14:17
    +1
    Вот эта крутая тема, давно хочу создать окружения работы с MODx на докере! Спасибо за статью, Илья!

    Единственное, на докере разворачиваю стек с nodejs и mongo, там вместо host.docker.internal обычно указываю название контейнера базы данных.
      Руслан Сафин
      10 октября 2021, 16:49
      +2
      давно уже пользуюсь докером, очень удобно. почитайте еще про docker swarm, там можно скейлить инстансы сервисов
        Александр Мельник
        10 октября 2021, 19:28
        0
        расскажите пожалуйста, зачем это? Что это вам дает? Локальная разработка в докере. Как по мне это даже звучит странно. Почему просто не установить на локальном компьютере php, mysql и nginx? Зачем запускать все это в контейнерах, терять в производительности, усложнять конфигурацию. Какую выгоду вы получаете от разработки локально в докере?
          Илья Уткин
          11 октября 2021, 09:34
          0
          Ну проектов в команде много. Часто приходится подключать к проекту нового разработчика. В нашей ситуации он делает просто git clone, docker-compose up -d, запускает миграции и готов уже работать над своей задачей.
          Александр Мельник
          10 октября 2021, 19:35
          +1
          Я еще могу понять использование докера там, где крайне сложный проект, который задействует mysql, postgress, redis, php, python и go, все это завязано в общую систему. Но для modx? зачем? И как вы потом это переносите на хостинг? Как вы запускаете ваши докер контейнере и заставляете хостинг «подцепить домен» к вашей конфигурации? Или вы для любого сайта на modx теперь используете выделенный сервер? Как выэто поясняете заказчику или «будущим» программистам, которые возьмут на обслуживание простенький сайт на modx, а потом обнаружат что она работет в докер композере…
            Aleksandr Huz
            10 октября 2021, 19:46
            0
            Александр, я рекомендую еще раз прочитать внимательно статью.

            А деплой будет заключаться в стягивании изменений из гитхаба и запуске миграций:
            git pull && php7.3 vendor/bin/phinx migrate
              Александр Мельник
              10 октября 2021, 20:02
              +2
              Вы так говорите тезка, как будто все уже 3 раза проверили.
              git pull это еще не ответы на все вопросы. В чем смысл разрабатывать в докере если контейнер не выгружается на сервер? Я вот еще раз прочел пошагово инструкцию и вижу, что Илья создает инфраструктуру из докер контейнеров у себя локально, а вот на сервер то это не попадает? Туда идут только исходные коды из git? Разве смысл контейнеризации не в том, чтобы один и тот же контейнер запустить как у себя локально так и на сервере? Где в инструкции установка докера на сервере? Где копирование контейнеров на сервер, где их запуск?
                Руслан Сафин
                10 октября 2021, 20:08
                0
                я думаю это вводная статья для новичков, которые хотят познакомится с технологией… понятно что удобство докера в одинаковой среде при деплое, но если выкатить в одну статью сборку контейнера в гите, пуш в регистри, деплой и т.д. получится каша для новичков
                  Алексей Шумаев
                  11 октября 2021, 09:11
                  0
                  Это отличная вводная статья для новичков. Кто-то, кто что-то слышал про Doker, но боялся спросить использовать, увидит, что всё не так страшно и даже modx можно вписать в современный стек разработки.
                  Само-собой предполагается, что нужно почитать ещё материалы по теме, но чтобы попробовать — вполне достаточно.

                  Надо оно для modx или нет — решает каждый сам для себя, конечно. Я однозначно рекомендую попробовать, хотя бы для своего роста как разработчика. Чтобы потом легко перейти на современную разработку в любой команде.

                  Самое крутое, что я пока вижу в Doker'е — возможность разбить один большой сервис на кучу микросервисов, которые можно легко сопровождать/обновлять по отдельности + масштабирование + отказоустойчивость. Да, есть накладные расходы, но оно того однозначно стоит.
                  Собираюсь некоторые старые разработки перевести на Doker + k8s.
                    Александр Мельник
                    11 октября 2021, 09:19
                    +1
                    Никто не говорит, что докер это плохая технология. Но это инструмент и его нужно применять там, где он нужен и приносит выгоду. Насколько я понимаю, докер создан чтобы решить проблему разных сред разработки. Обеспечить беспроблемный перенос программ с сервера на сервер, на заботясь о постоянной переконфигурации. Поэтому и показалось, что использовать докер просто на своем компьютере, но при этом не выгружать контейнеры на сервер — это странное использование инструмента.
                      Алексей Шумаев
                      11 октября 2021, 09:24
                      0
                      Согласен. Я вот к этой части вопроса отписался:
                      Я еще могу понять использование докера там, где крайне сложный проект, который задействует mysql, postgress, redis, php, python и go, все это завязано в общую систему. Но для modx? зачем?
                      :-)
                Илья Уткин
                12 октября 2021, 13:01
                0
                Да, на продакшене докер-контейнеры не используются. Но в командной разработке докер позволяет сильно упростить работу. Меня не волнует, кто на чём работает, какая система стоит у коллег. Я знаю, что любой разработчик просто склонирует себе репозиторий проекта, запустит контейнеры и приступит к работе.
                Александр Мельник
                11 октября 2021, 09:09
                0
                А еще очень интересно, ведь получается что решена самая главная беда modx, это версионный контроль чанков, сниппетов и прочего, которые хранятся в базе данных. Было написано много «костылей» чтобы обойти это и гитифай и гитмодекс, а получается что это можно решить простыми миграциями? Хотя я лично не понимаю как, например в ларавеле миграции это php код который ты пишешь сам и который описывает изменения в базе данных и потом единажды запускается, так же устроены миграции в doctrine. Как созданный в админке сниппет вдруг попадет в миграцию. В общем нужно найти время и выполнить то что написал Илья, спасибо ему что делится с нами.
                  Николай Савин
                  11 октября 2021, 09:16
                  +1
                  Вот вам еще один вариант работы с элементами базы данных, где можно применить контроль версий — это App, которые Василий Наумкин предложил еще в 2017 году.
                  На базе этого решения функционирует текущая версия modx.pro.
                  Здесь конечно не миграции, но тоже php код который позволяет создать, удалить, обновить любые элементы внутри базы данных.
                    Илья Уткин
                    11 октября 2021, 13:06
                    0
                    Да, у нас в проектах в основном используются нативные статические элементы. Вот для того, чтобы создать, например, чанк, нужно написать миграцию. А в дальнейшем изменения хранятся в git-репозитории.

                    Так же с помощью миграции создаётся, например, раздел «Новости». Его ID сохраняется в настройках контекста. В итоге на продакшене раздел наполняется ресурсами но разработчику не требуется учитывать эти ресурсы — он всегда использует актуальный ID. У каждого разработчика, на тестовом сервере, на продакшене ID-шники разные, но это никого не парит.
                    Дмитрий
                    11 октября 2021, 20:42
                    0
                    Привет! Спасибо за статью! Мы пытались внедрить у себя Docker для локальной разработки на MODX, сделали примерно такой же конфиг как у Ильи. Но столкнулись с тем, что если на linux все хорошо, то на MacOS и Windows MODX в докере работает в несколько раз медленнее, чем если поставить его на ту же машину и систему, но на чистый LEMP в VirtualBox.
                    Если на несложных проектах это не очень критично, то на большом проекте где и так есть проблемы с производительностью, страницы через докер грузятся у меня на маке по 10-20 секунд, работать невозможно. На винде чуть получше, но все равно хуже virtualBox'a в 2-4 раза.
                    Проверили очевидные гипотезы, почему это может происходить, убрали прямой mount папок из OS в контейнер (оставили заливку по ssh), вынесли файлы в отдельный volume, проверили что WSL v==2 на винде, и еще кучу всяких советов из инета. Но увы, по прежнему тормозит.

                    Кто-нибудь сталкивался с такими проблемами?
                    Поделитесь опытом, пожалуйста.
                      Семён Кудрявцев
                      12 октября 2021, 08:19
                      +3
                      С медленной работой сталкиваются все, кто пытается на windows и macos пробрасывать исходники из хостовой системы в wsl а потом в docker, причина элементарная — виртуализация, когда активируется wsl, основная операционка становится виртуальной машиной №1, дистрибутив в wsl становится виртуальной машиной №2, обе они работают параллельно на гипервизоре первого уровня от микрософт если на винде (под капотом убогий и медленный hyper-v) В итоге когда ты запрашиваешь страничку, запущенную в докере, она сначала из одной виртуалки идет в другую виртуалку, а потом из второй через моунт тома попадает в докер — отсюда и тормоза. Если исходники разместить сразу в wsl, откуда они будут сразу попадать в докер, то скорость будет близка к нативной, с учетом накладных расходов докера, но на глаз это очень трудно увидеть. В итоге всё летает как положено) Сам долгое время с этим мучился, пока не закопался в эту виртуализацию и не разобрался что и как там работает.
                        Илья Уткин
                        12 октября 2021, 09:55
                        +2
                        Да, была проблема, но после того, как добавили участки с nfsmount всё стало хорошо
                          Дмитрий
                          12 октября 2021, 15:27
                          0
                          Спасибо, попробую!
                        Дмитрий
                        12 октября 2021, 18:29
                        0
                        Илья, нет ли у тебя какой-нибудь базы примеров миграций для основных типов объектов MODX?
                        ресурс, чанк, сниппет, MIGX-конфиг и т.д. Особенно MIGX-конфиг сейчас нужен ?
                          Илья Уткин
                          13 октября 2021, 07:21
                          +1
                          Да, конечно давно сформировался набор стандартных миграций. Но, к сожалению это внутренняя разработка, она лежит в приватном репозитории и код писал не я. Так что поделиться с общественностью не могу.

                          По конфигам MIGX я делал так — создавал конфиг через интерфейс, потом экспортировал его и в миграции было что-то типа такого:

                          $name = 'configName';
                          $file_path = dirname(__DIR__) . '/migx/configname.json';
                          
                          $this->modx->getService('migx', 'Migx', MODX_CORE_PATH . 'components/migx/model/migx/', []);
                          $config = $this->modx->getObject('migxConfig', ['name' => $name]);
                          if (!$config) {
                              $config = $this->modx->newObject('migxConfig');
                              $config->set('name', $name);
                          }
                          
                          $json = file_get_contents($file_path);
                          $config->fromArray($this->modx->migx->importconfig($this->modx->fromJson($json)));
                          $config->save();
                            Дмитрий
                            13 октября 2021, 14:30
                            +1
                            Круто! То что надо! Спасибо огромное!
                            Если когда-нибудь решите выложить остальное открыто на гитхаб, обещаю вам сходу 5 звезд от нашей команды ??
                          Артём
                          15 октября 2021, 09:21
                          0
                          @Илья Уткин приветствую! Как профи, подскажите, пожалуйста, в чем может быть проблема с пакетами Modstore при локальной разработке?
                            Илья Уткин
                            15 октября 2021, 10:53
                            0
                            У меня проблемы нет — локально пакеты нормально устанавливаются. Вероятно, какая-то проблема в настройках локального сервера, но что это за проблема — не знаю. Если кто-то найдёт, в чём дело — напишите, добавим информацию в FAQ
                            Alexander V
                            19 октября 2021, 14:13
                            +1
                            А не проще было поднять обычный LAMP/LEMP? Например с помощью Ansible.
                              Илья Уткин
                              19 октября 2021, 15:15
                              +1
                              А как работать в команде в таком случае? Как быстро подключить нового разработчика? Ему нужно будет выгружать дамп базы данных и весь сайт?

                              В случае с Docker этого делать не нужно. Новому разработчику требуется только
                              git clone https://github.com/user/project
                              composer install
                              docker-compose up -d
                              docker exec -it doodle_php_1 bash
                              php vendor/bin/phinx migrate
                              Чуть-чуть попить чай (установка может занимать несколько минут) — и всё, можешь приступать к работе.
                                Илья Уткин
                                19 октября 2021, 15:16
                                0
                                И мне тут не важно, на чём работает этот новый разработчик — Windows, Ubuntu, Mac — я знаю, что у него всё запустится именно в том окружении, каком надо.
                                  Александр Мельник
                                  31 октября 2021, 22:31
                                  0
                                  решил попробовать, но что то не складывается у меня с Docker
                                  Все выполняю как вы описали, но вылезла куча ошибок еще на этапе построения образов.
                                  На компьютере — Kubuntu.
                                    Александр Мельник
                                    31 октября 2021, 22:52
                                    0
                                    и вот глядя на ошибки, осознаю что я совершенно не понимаю, как работает проброска портов. Вот к примеру для образа базы данных указано 3306:3306 из контейнера будет торчать в локальную машину порт 3306. Но что если у меня там уже моя локальная база данных работает? Или же nginx из контейнера выпячивает 80 порт, а у меня локально стоит apache на 80 порту, то что откроется на localhost
                                    А если порты (или что-то еще наверное) нужно подбирать под каждую машину отдельно (а кого то занят локальный порт 3306 а у кого-то нет) то теряется вся универсальность докера.
                                  Alexander V
                                  19 октября 2021, 15:18
                                  0
                                  Так же и работать, с помощью Git.
                                Александр Мельник
                                27 февраля 2022, 17:09
                                0
                                @Илья Уткин Илья, прошло уже некоторое время с момента публикации.
                                Расскажите, продолжаете ли вы с командой пользоваться для разработки на modx докером?
                                Обнаружились какие-то минусы?
                                  Илья Уткин
                                  28 февраля 2022, 11:04
                                  +1
                                  Да, продолжаем пользоваться, всё хорошо работает
                                  Никита Кирясов
                                  02 марта 2023, 13:26
                                  0
                                  @Илья Уткин
                                  Здравствуйте, подскажите пожалуйста в связи с чем может быть вызвана данная ошибка при запуске команды — docker-compose up -d?
                                  • Error response from daemon: error while mounting volume '/var/lib/docker/volumes/docker-test_nfsmount/_data': error resolving passed in network volume address: lookup host.docker.internal: no such host
                                  Нужно ли предоставить какие-либо дополнительные данные от меня, чтобы понять в чем может быть проблема?
                                    Павел Голубев
                                    22 августа 2023, 15:05
                                    0
                                    @Илья Уткин При попытке поставить на MacOS
                                    Error response from daemon: error while mounting volume '/var/lib/docker/volumes/doodle_nfsmount/_data': failed to mount local volume: mount :/Users/catsmeatman/Projects/doodle:/var/lib/docker/volumes/doodle_nfsmount/_data, data: addr=192.168.65.254,nolock,hard,nointr,nfsvers=3: connection refused
                                    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
                                    35