Несколько версий PHP на хосте с помощью Docker

    Часто перед разработчиками PHP встаёт задача проверить работу веб-приложения под несколько версий интерпретатора. Решить её можно разными способами. Можно банально установить разные версии PHP на один хост, но это чревато конфликтами библиотек и другими сложностями. Вторая крайность — сделать несколько изолированных виртуальных машин с разным окружением, но здесь не обойтись без чрезмерного использования аппаратных ресурсов и излишней траты времени на разворачивание рабочего окружения. На настоящий момент наиболее просто решить данную задачу можно с помощью Docker.

    image

    Ниже я опишу рабочее решение под Ubuntu 18, где в качестве стека используется связка Nginx + PHP-FPM. Данное решение легко масштабируется: контейнер с PHP-FPM занимает всего 300 Мб в памяти, а добавить контейнеры с другими версиями интерпретатора можно тремя командами (или в зависимости от предпочтений даже одной — run). Второй плюс этого решения в том, что разработчику не нужно переключать веб-сервер между интерпретаторами, так как они уже разнесены в разные контейнеры (код приложения при этом используется один и тот же).

    Дополнение: Судя по комментариям, некоторые читатели не поняли, для каких случаев подходит описанное решение. Поэтому хочу обратить внимание, что всё нижеизложенное предназначено для использования ИСКЛЮЧИТЕЛЬНО на рабочей станции разработчика, а также может с некоторыми оговорками подойти для стейдж-сервера.

    Итак, начнём…

    1. Устанавливаем Docker


    sudo apt update
    
    sudo apt install ca-certificates curl software-properties-common
    
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
    
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
    
    sudo apt update
    
    sudo apt install docker-ce

    2. Устанавливаем контейнеры с нужными версиями PHP


    В качестве примера создания рабочего окружения использованы PHP версий 7.1 и 7.2 из официального Docker-репозитория PHP. По аналогии при наличии образа можно установить какие угодно версии PHP:

    sudo docker pull php:7.1.25-fpm-stretch
    
    sudo docker create --name=fpm71 -p 127.0.0.1:9071:9000 -v /var/www:/var/www php:7.1.25-fpm-stretch
    
    sudo docker start fpm71
    
    sudo docker pull php:7.2.13-fpm-stretch
    
    sudo docker create --name=fpm72 -p 127.0.0.1:9072:9000 -v /var/www:/var/www php:7.2.13-fpm-stretch
    
    sudo docker start fpm72

    PHP-FPM по умолчанию работает на 9000 порту. При создании образов мы опубликовали 9000-е порты контейнеров на свободные 9071 и 9072 порты хост-машины (номера взяты произвольно из непривилегированного диапазона). Далее на эти порты мы будем проксировать запросы на обработку PHP (параметр fastcgi_pass в конфигурации виртуальных хостов Nginx).

    Также понадобилось пробросить внутрь контейнеров каталог с проектами (/var/www), иначе PHP-FPM ругается, что не видит файлов (если знаете, как сделать этот момент лучше/правильней, то пишите в комментарии).

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

    sudo docker ps -a
    
    sudo netstat -lpn


    3. Настраиваем окружение для виртуальных хостов


    Добавляем в /etc/hosts строки:
    127.0.0.1    project.local.php71  ### php 7.1
    127.0.0.1    project.local.php72  ### php 7.2
    

    Создаём каталог для проекта:

    sudo mkdir -p /var/www/project.local
    
    echo '<?php phpinfo(); ?>' | sudo tee /var/www/project.local/index.php
    

    Название для проекта (project.local) и виртуальных хостов (project.local.php71/72) я взял произвольно, но вы можете использовать удобные для вас названия (только не забудьте при этом поменять настройки виртуальных хостов).

    Изначально в индексный файл была положена только одна команда phpinfo, после настройки и проверки работоспособности системы, index.php нужно будет заменить на тот, что используется в проекте.

    4. Устанавливаем nginx и настраиваем виртуальные хосты


    sudo apt install nginx

    Создаём файл /etc/nginx/sites-available/project.local.php71 с описанием первого виртуального хоста (он будет использоваться для проверки работы проекта под PHP v.7.1):

    server {
      listen 80;
      server_name project.local.php71;
      index index.php;
      root /var/www/project.local;
    
      location / {
        try_files $uri $uri/ =404;
      }
    
      location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9071;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }
    }
    

    Точно так же файл /etc/nginx/sites-available/project.local.php72 для второго виртуального хоста:

    server {
      listen 80;
      server_name project.local.php72;
      index index.php;
      root /var/www/project.local;
    
      location / {
        try_files $uri $uri/ =404;
      }
    
      location ~ \.php$ {
        fastcgi_pass 127.0.0.1:9072;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
      }
    }

    Теперь делаем симлинки на вышесозданные конфигурации виртуальных хостов и перегружаем Nginx:

    cd /etc/nginx/sites-enabled
    
    sudo ln -s ../sites-available/project.local.php71
    
    sudo ln -s ../sites-available/project.local.php72
    
    sudo systemctl reload nginx

    5. Проверяем


    curl --silent http://project.local.php71/index.php | grep -o "PHP Version [0-9\.]\{1,\}"
    
    curl --silent http://project.local.php72/index.php | grep -o "PHP Version [0-9\.]\{1,\}"
    

    В качестве результата мы должны получить версию PHP (как результат обработки команды phpinfo интерпретаторами разных версий).

    Теперь осталось лишь залить свой проект в папку /var/www/project.local и можно проверять его работу в интерпретаторе PHP 7.1 по адресу http://project.local.php71 и PHP 7.2 по http://project.local.php71.

    Дополнительные материалы
    1. Полное практическое руководство по Docker

    Similar posts

    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 28

      +3
      Подобным образом можно установить несколько версий fpm и без докера, коль уж вы всеравно локальный nginx используете
        +4
        особенно с учётом того что условно-стандартный ondrej репозиторий как раз так и сделан чтобы несколько версий php друг другу не мешали; плюс будет чуть быстрее из за того что для fastcgi_pass будет использоваться не сетевой а файловый socket
          +2
          Например: у TimeWeb в /opt установлены версии от 5.3 до 7.2 и вполне нормально живут себе.
          0
          > без чрезмерного использования аппаратных ресурсов и излишней траты времени на разворачивание рабочего окружения.
          Познакомьтесь уже с lxc и чем-то вроде ansible.

          Но вообще справедливо говорят, что ondrej-евские пакеты (а он, если что, мейнтейнер php-пакетов в debian) ставятся скопом всех версий, а потом (даже если говорить про самый сложный вариант — апачевый модуль) переключаются в одну команду. Ну а fpm просто вешается на разные порты.

          Единственный вариант, когда есть смысл морочиться с описанной вами схемой — когда нужно тестить код под, например, php5.2 и php7+. Зачем — другой вопрос, но банально понадобятся разные версии дистрибутивов, если не хочется морочиться со сборкой руками.
            –5
            Познакомьтесь уже с lxc и чем-то вроде ansible.


            Разве конфигурирование этой связки сравнится с элегантностью решения, которое предоставляет Docker: одна консольная команда в Docker — run (ну или 3 что у меня) против N команд связки lxc+ansible?
              +2
              Это только при условии, что есть готовые image в registry (и при условии, что задача позволяет доверять им). Такое случается крайне редко, а писать докерфайлы — не шибко быстрее, чем дернуть пару команд (создать контейнер, запустить на нём ansible — вот уж для него-то плейбуков есть на любой случай жизни).
              А ещё и остаются вопросы с различным statefull-содержимым (а у PHP его исторически много — от сессий, до привычки на любой чих писать в локальные каталоги).

              Более того, конкретно php-ные image в публичном registry — дерьмо. Люди там больше выпендривались «смотрите как я могу!», чем создавали production-ready образ.
                0
                Очень красиво заминусовали ВОПРОС!!!
                А вам не кажется, что минусовать вопрос глупо, при этом даже не приведя ссылки на какой-нибудь мануал в пользу других решений хотя бы с минимальным анализом затраченных ресурсов (как железных, так и временных на разворачивание)?
                  0
                  > А вам не кажется, что минусовать вопрос глупо
                  Мне? Мне вообще лениво в эти стрелочки целиться.

                  > ри этом даже не приведя ссылки на какой-нибудь мануал
                  мануал к чему? Поставить 2 мета-пакета и написать 4 конфига?

                  И вы вообще смотрели в dockerfile, который вы советуете всем использовать?
                0
                Более того, оно превосходит по элегатности решение с докером :)

                Не говоря уже о том, что можно и даже нужно использовать разные апстримы (/etc/nginx/conf.d), хотя вот разные пулы, на мой взгляд, подключаются неудобно (или я не умею их правильно и удобно подключать?)
                0
                Под 5.2 наверняка еще особо древние вебсерверы потребуются для тестов, и там скорее всего будет полноценная виртуалка с ядром типа 2.6.27
                  0
                  debian 6 отлично работает на ядре 3.x, php 5.2 туда ставится. Да и в докере эта связка в целом терпимо (ну… для тестов) работает.
                  На 4.х не тестировал, уже не нужно было, но суть, думаю, не поменяется.
                0
                Nginx тогда уж тоже в Докер положить. А то какая-то мешанина.

                И важный минус, такая конфигурация при перезапуске сервера перестанет работать. Докер не поднимаем простые контейнеры автоматически.
                  0

                  docker run --restart=always ...
                  После перезапуска всё продолжит работать. Не то чтобы это best practices, но docker daemon может стартовать контейнеры автоматически.

                  0
                  В такой конфигурации все вируальные хосты будут работать под одним пользователем, что небезопасно.
                    0
                    От www-data? o.O
                    Что в этом небезопасного?
                      0
                      Один юзер хостинга сможет лазить по файлам другого юзера хостинга. Действительно, что там небезопасного то.
                        0
                        Откуда юзеры хостинга на рабочей станции разработчика? Поясните, из какой фразы можно сделать вывод, что речь в статье про продакшн?
                          0
                          Не вижу в статье ничего про личный комп разработчика.
                          А если мы говорим про некий тестовый/отладочный/staging сервер, то там почти наверняка больше 1 проекта, в этих проектах наверняка есть баги и этот сервер почти наверняка смотрит в инет. Найдя уязвимость каком-нибудь в одном всеми забытом проекте, кулхацкер получит доступ ко всем проектам через файловую систему. Так что такую конфигурацию однозначно фтопку.
                            0

                            Описанное в статье как раз и годиться только для машины разработчика или тестировщика. Тащить это на сервер (любой) плохая идея и не только из-за возможных проблем с безопасностью. Описаное в принципе не является хорошим примером работы с контейнерами.

                              0
                              Описанное в статье вообще ни для чего не годится. dev-окружение в принципе сильно отличается от продакшена. Как минимум будет отладчик с логгером, какой-нибудь упаковщик с хотрелоадом, будут другие настройки (memory limit и т.п.), будет куча dev-пакетов типа того же бабеля. Тащить туда ещё и докер ради докера, а потом скакать с ним по граблям как минимум глупо.
                                0

                                Так Docker как раз и решает проблему разницы между продом и дев окружением. И не только эту. Но взамен требует изменить подход к разработке достаточно сильно, что судя по всему часто вызывает непонимание того, зачем вообще он нужен разработчикам, если и без него всё работает.

                                  0
                                  Нет такой проблемы как «разница между продом и девом», это не проблема, а возможность. Возможность юзать отладчики, сборщики и прочие тулы для разработки, а не для прода.
                                  Да, перед выкатыванием на прод код нужно проверить в «обстановке, максимально приближенной к боевой». Для этого есть staging сервера, самые разные тесты от юнит до интеграционных и прочее прочее. И это должен быть именно отдельный сервер, а не так что разработчик сообщает что «мамой клянусь, я в докере протестил».
                                  Я понимаю, зачем нужен докер девопсам и тестировщикам. Я понимаю, как получить от него пользу разработчику — ну, хочешь там попробовать какой-нибудь эластик, установил, попробовал, снёс. Но для описанной в статье задачи докеру места нет.
                          0
                          Ну, если в один из проектов получится залить эксплоит. Но это уже разговор о другом будет.
                          А так там всё разграничено локациями в nginx и сокетами в php-fpm.
                      0

                      отмечу, что еще не рассмотрен вопрос изоляции разных проектов друг от друга. а часто это имеет значение. :-)

                        –1
                        lryzhik, поясните пожалуйста, какое значение имеет изоляция проектов друг от друга на рабочей станции разработчика?

                        Надеюсь, все правильно поняли, что фраза «перед разработчиками PHP встаёт задача» имеет отношение именно к фазе разработки, а не к продакшн?
                          +1

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

                        0
                        Благодаря статьям подобного рода, среди новичков распространяется мнение, что решение всех задач — это docker.
                        Потом, прочитавшего эту статью новичка другой новичок спросит «А как поставить php7.1?», а тот скажет «А разверни docker-контейнер!» и кинет ссылку на эту статью.
                        По сути, докер нужен для весьма специфичных задач, не надо его пихать куда попало. Пожалуйста.
                        Лучше изучите внимательней документацию.
                          0
                          почему нельзя через nix установить несколько версий PHP, без всяких виртуальных машин и конфликтов библиотек?

                          Only users with full accounts can post comments. Log in, please.