Рецепты Docker: Monkey patch, часть третья

    Пожалуйста, начинайте читать серию заметок с начала, здесь: habrahabr.ru/post/267441

    Настройка локально



    В этой статье я предполагаю, что служба docker запущена на той же машине, на которой выполняются команды, и у процесса есть доступ на чтение к текущей папке. Еще я подразумеваю, что вы умеете настраивать связку PHP-FPM и Nginx.

    Беру образы Nginx и PHP 7.

    ~$ docker pull nginx
    ...
    ~$ docker pull php:7-fpm
    Status: Downloaded newer image for php:7-fpm
    

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

    ~$ docker create --name=php7 php:7-fpm
    3d1b737edfcc3f1102fa54c91f9120da4b86d8cbba3092b6f80156c0e31b4d8f
    ~$ docker create --name=nginx nginx
    80be81b27e012fd061ff4b682f0b7b8803500bc38a4b9f787f91661603b2d4b7
    


    PHP


    Начну с PHP — его настроить сложнее. Где лежат конфиги для PHP — можно увидеть в его Dockerfile:
        ENV PHP_INI_DIR /usr/local/etc/php
           --with-config-file-scan-dir="$PHP_INI_DIR/conf.d" \
        WORKDIR /var/www/html
        COPY php-fpm.conf /usr/local/etc/
    

    Копирую себе из контенера содержимое каталога с файлами конфигурации php
    ~$ mkdir monkeypatch
    ~$ cd monkeypatch/
    $ docker cp php7:/usr/local/etc localetc
    $ ls localetc/
    pear.conf        php            php-fpm.conf        php-fpm.conf.default    php-fpm.d
    $ ls localetc/php
    conf.d
    

    Мейнтейнеры положили в образ php-fpm.conf, но не положили дефолтный php.ini. Придется взять его из исходников php.
    $ docker cp "$PHP7:/usr/src/php/php.ini-development" localetc/php/php.ini
    

    Правлю конфиги, как обычно. В какой папке PHP ищет расширения? Узнать можно, запустив php, например, во временном контейнере.

    $ docker run --rm php:7-fpm php -i |grep extension_dir
    extension_dir => /usr/local/lib/php/extensions/no-debug-non-zts-20141001 => /usr/local/lib/php/extensions/no-debug-non-zts-20141001
    $ docker run --rm php:7-fpm ls /usr/local/lib/php/extensions/no-debug-non-zts-20141001
    opcache.a
    opcache.so
    

    В расширениях только opcache, можно подключить его.
    $ echo extension_dir = "/usr/local/lib/php/extensions/no-debug-non-zts-20141001" >>  localetc/php/php.ini
    $ echo zend_extension = opcache.so >> localetc/php/php.ini
    

    Пересоздаю контейнер php и монтирую в него папку с конифгами. Путь к монтируемой папке должен быть от корня — служба не знает, из какой папки вызывается клиент docker.
    $ docker rm php7
    php7
    $ docker run -v "$(pwd)/localetc:/usr/local/etc" --name=php7 php:7-fpm php -i |grep Configuration
    Configuration File (php.ini) Path => /usr/local/etc/php
    Loaded Configuration File => /usr/local/etc/php/php.ini
    

    Теперь можно пересоздать контейнер php7 с тестовым приложением на php. Создатели образа не позаботились о том, чтобы php-fpm работал как демон, так что надо самим запускать его фоном, не освобождая стандартные каналы ввода-вывода.
    $ docker rm php7
    $ mkdir scripts
    $ echo " scripts/test.php
    $ docker run -v "$(pwd)/localetc:/usr/local/etc" \
        -v "$(pwd)/scripts:/scripts" \
        --name=php7 php:7-fpm &
    [29-Aug-2015 15:19:25] NOTICE: fpm is running, pid 1
    [29-Aug-2015 15:19:25] NOTICE: ready to handle connections
    

    Пока что для удобства отладки я оставляю вывод из контейнера php-fpm в свою консоль.

    NGINX


    С Nginx всё просто и стандартно. Копирую на диск папку конфигов:
    $ docker cp nginx:/etc/nginx .
    

    В папке nginx/ надо отредактировать nginx.conf, fastcgi_params по вкусу, и создать конфигурационный файл для своего сайта в nginx/conf.d/. Основное для связи nginx с php — это указать в имени хоста имя контейнера с php, а директивы root и SCRIPT_FILENAME должны указывать на путь, который php поймёт в своём контейнере php7.
        location ~ \.php$ {
            fastcgi_pass   php7:9000;
            fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
    

    Монтирую конфиги в контейнер nginx и запускаю с маппингом 80-го порта контейнера на локальный 8080.
    $ docker rm nginx
    $ docker run -v "$(pwd)/nginx:/etc/nginx" -p 8080:80 --name=nginx nginx &
    $ curl 127.0.0.1:8080/test.php
    172.17.0.65 -  29/Aug/2015:15:50:29 +0000 "GET /test.php" 200
    Hello world! 7.0.0RC1
    

    Rock'n'Roll!

    В версии 1.7 в команде docker run надо указывать параметр --link чтобы в контейнере резолвилось имя другого контейнера. В версии 1.8.1 все работает и без этого параметра.

    Логи


    Мейнтейнеры образа php решили писать все логи fpm в /proc/self/fd/2, он же STDERR — как error_log, так и access.log. Однако, лог запросов у меня будет писать nginx, а в работе php меня интересуют только ошибки, поэтому предлагаю отредактировать localetc/php-fpm.conf и написать что-то привычное:
    error_log = /var/log/php/php-fpm.error.log
    ;access.log = /proc/self/fd/2 
    

    В Nginx обошлись без самодеятельности, так что включаю access log в конфиге сайта nginx/conf.d/site.ru.conf
    access_log  /var/log/nginx/host.access.log  main;
    

    Теперь можно создать папку для логов c правом записи для демона docker и подмонтировать ее в контейнеры. В эту же папку можно писать и вывод контейнеров, при этом контейнеры можно детачить:
    $ mkdir log
    $ sudo chgrp docker log/
    $ sudo chmod g+rwx log/
    $ docker stop nginx php7
    $ docker rm nginx php7
    $ docker run -d --name=php7 \
        -v "$(pwd)/localetc:/usr/local/etc" \
        -v "$(pwd)/scripts:/scripts" \
        -v "$(pwd)/log:/var/log/php" \
        php:7-fpm >>log/docker.php.log 2>&1
    $ docker run -d --name=nginx \
        -v "$(pwd)/nginx:/etc/nginx" \
        -v "$(pwd)/log:/var/log/nginx" \
        -p 8080:80 \
        nginx >>log/docker.nginx.log 2>&1
    $ curl 127.0.0.1:8080/test.php
    Hello world! 7.0.0RC1
    

    Когда надо поменять конфигурацию — можно дать команду перезагрузки php и nginx.
    $ docker exec php7 pkill -o -USR2 php-fpm
    $ docker exec nginx service nginx reload
    Reloading nginx: nginx.
    

    Когда php 7 будет включен в дистрибутив Debian в образе php:7 появится init-скрипт. При желании, можно добавить его самостоятельно из дистрибутива по выбору.

    Продолжение можно найти здесь.

    Дополнение от 23 сент.
    Учитывая уровень баттхерта, который вызвали мои заметки, уточню: у нас разные цели. В моем случае полный стек из приложения, базы, библиотек и всех конфигов поднимается из одного архива размером несколько мегабайт. Я могу его передать по почте, залить на google drive, предоставить заказчику как результат работы, приаттачить к тикету и деплоить на серверах.
    Админы хотят поставить registry, зациклить на нем и разработку, и деплоймент, делать его бекапы, мониторить, поднимать когда когда он упадет, быть очень нужными. Нормально, но можно лучше.
    Когда у меня приложение с базой и конфигами в одном небольшом файле, весь стек поднимается одной командой, и при этом все службы работают независимо, у меня нет SPOF, бекап может быть хоть прямо в git.
    Для транспорта при выкладке я могу использовать и registry, и git, и rsync, и ssh, и vagrant — я не ограничен, и это отдельная тема.
    Если мы собрали все в один большой образ — у нас нет выбора, только молиться на registy.

    Мне не нужна зависимость приложения от версии php. При каждом изменении в любой части стека надо пересобрать весь образ и заливать в registry? Мне это не подходит. Если я обновил версию php — приложение я не пересобираю. Если я обновил MySQL — php я не трогаю.

    Да, это рвет шаблон, такого раньше не было. Эта статья для тех, кому нужен модульный дизайн системы. Если вы признаете один единственный правильный путь — эта статья не для вас.
    Share post

    Similar posts

    Comments 44

      +6
      Мне кажется, что для работы с такими связками сервисов типа nginx+php-fpm или подобного имеет смысл использовать docker-compose. Пишется простенький yaml-файл, затем docker-compose up, и всё, сервисы запущены и соединены.
        0
        Я не хочу загромождать тему. Будет и про mysql, и про compose.
          +5
          Не получится ли с такой мелкой нарезкой на серии повторение проблем документации на докер — попали на страницу из гугла, увидели простейший пример, скопипастили, а до docker-compose и data volumes так и не добрались никогда?
            –1
            Что делать, habrahabr не позволяет структурировать материал. Здесь много чего не хватает.
            Читайте на gitbooks
      +4
      Вот если взялись объяснять технологию, то хотя бы best practices прочитайте. Не нужно ничего копировать в контейнер, нужно создавать Dockerfile. Не нужно монтировать директорию $(pwd), нужно создавать data volume container. И про docker-compose вам правильно написали.
        –2
        «Не читал, но осуждаю» :) Прошлая статья серии — об этом.
          +1
          У вас ссылка «начало» ведет на другую статью, а на вот эту habrahabr.ru/post/267451 вообще невозможно попасть. К сожалению, у меня не так много времени, что отлеживать все ваши публикации. И если вы знаете, что так делать нельзя, то зачем пишите? Или вот там же пишите про композицию. И как вы её в докере будете делать, эту композицию?
            –4
            Да, к сожалению, я не могу удобно структурировать материал, простите. У меня тоже не настолько много времени чтобы придумывать обход проблем формата хабра.
            Вариант решения — в ответе выше. Уточните, пожалуйста что нельзя делать и как?
        +7
        Самый странный способ неправильного использования docker, что я видел. Я бы не называл это «рецептом Docker» но анти-рецептом. Вместо вменяемого создания Dockerfile автор всю кастомизацию стандартных контейнеров делает через volumes. То, что после этого контейнер будет прибит гвоздями к именно этому хосту видимо его не волнует. Причина этих странных телодвижений видимо должна быть понятна из habrahabr.ru/post/267451, но я не в состоянии ее понять. Единственное, что я смог там увидеть, это ошибочная уверенность автора в том, что при обычном, вменяемом использовании контейнеров и расширения иx функциональности для себя конвенциональными способами, надо «заплатить за хранение на Docker Hub».

          –4
          Способ полностью противоречит документации. Нет никакой ложки.
          1. Официальные образы docker — аналог rpm/dev, собирать приложение в одном образе с runtime — аналогично сборке php, nginx, mysql и приложения в одном rpm. Сделать можно, но никто так не делает.
          PHP, Nginx, MySQL удобно ставить из официальных репозиториев, и подключать свои конфиги, чтобы этот официальный пакет без проблем обновить. Аналогично, я хочу ничего трогать в официальных образах.
          2. Когда мы собрали свой образ, мы можем сделать или save, или push. Если сделать save образу, созданному на базе nginx — дамп будет весить пару сотен мегабайт. Раздавать это в команде некомфортно. Если мы делаем push — мы выкладываем наш код всему миру, что для серьезных проектов недопустимо.
          Третьего способа передать наш образ на другой компьютер не существует. Ошибочность, к сожалению, не у меня.
            +5
            А теперь как оно на самом деле:

            1. Официальные образы это всего лишь образы, никакой особой мистики в них нет. Они могут кому-то подойти «как-есть», кому-то послужить базовыми а кому-то как источник информации о том, как сделать нечто свое из них.
            2. Когда мы собрали свой образ то ему можно сделать push. Но его вовсе не обязательно делать в публичный или даже приватный docker hub. Поднимаем docker-registry и пушим туда, как все нормальные люди, которым надо приватный репозиторий для докера.
            3. Кроме того, когда сборка включает в себя все, что надо и Dockerfile является тем, чем он обычно является, ничего не мешает раздавать не образ, но именно этот самый Dockerfile с тем, что надо для его сборки. Это прекрасно сохраняется в любой системе контроля версий.
              –3
              1. аналогично rpm и deb. Кто-то собирает свои пакеты, кто-то ставит официальные. Собирайте, поддерживайте свои, я не против.

              2. приватный репозиторий сделать можно, но надо заплатить :) О том и речь. У нас же не магазин на битриксе, а несколько микросервисов, и каждому нужен репозиторий. Мы же облачное масштабируемое приложение обсуждаем?

              3. Да, можно поднять registry. Можно настроить Vagrant. Можно работать образами виртуальных машин и забыть про docker.
              Я не проповедь читаю ;)
                +5
                Я дал ссылку на docker-registry, за него не надо ничего платить. То, что вы показали это их сервис для корпораций. По моей ссылке есть простая и ясная инструкция, как поднять registry у себя. Ну вот еще одна, которая прямо так и называется «Deploying a registry server».

                И при чем тут «когда приложение в hub»? Что мешает сделать build без хаба из своих исходных Dockerfile?
                  +2
                  хмм, это как минимум «неспортивно» редактировать свой ответ после того, как на него пришел комментарий. Я даже удивлен что хабр такое позволяет. И нет, «и каждому нужен репозиторий» это ерунда. Ну на самом деле, стоит почитать про docker registry а не придумывать себе что оно такое и станет понятно, что он нужен один, а не на приложение.
                    –5
                    Я не состязаюсь, простите, но мне все равно прав я или нет. Если у вас есть вопрос — я могу пояснить. Могу забить :)
                    Эта заметка не про registry, не про compose, не про swarm. Могу написать о registry отдельную заметку.
                      +3
                      у меня нет вопросов и я знаю ответы. Единственная цель моих комментариев это оградить незрелые в докер умы, от вредных советов.
                        –5
                        лучше всего — напишите свою статью как правильно и запостите здесь ссылку на нее
                          +1
                          лучше всего вы не пишите, а идите читайте доки, думайте мозгом, набивайте шишки на реальных задачах, пробуйте и иксперементируйте, и избавляйтесь от своего чсв (я слышал что выкидывание макбука в окно неплохо помогает). Потому как уже и сказал umputun для неопытного ваши статьи сделают только плохо, для тех кто разобрался неплохое развлечение их почитать но не более того.
                      –2
                      Сделать build из своих исходных Dockerfile на другом компьютере мешает необходимость дополнительных телодвижений: или передавать отдельно свое приложение, или поднимать registry, или платить.
                        0
                        Каких таких телодвижений?
                          –2
                          Вы предложили настраивать и поддерживать внутренний registry. Это значит, что надо решать проблему SPOF этого registry, а именно: организовывать бекап его данных, мониторить, делать репликацию, чтобы не останавливать выкладки. Нужны все обычные телодвижения для дополнительного сервиса в полноценной инфраструктуре.
                            +3
                            Достаточно навести порядок с версиями (читай — системами контроля версий) и даже если вы «потеряли» свой registry, то восстановить его не проблема.
                            Да и вообще, вся соль докера в том, что даже бэкап реестра билдов (ежели таков необходим) вам провернуть очень просто.
                            Можно вопрос, а вы программист?
                              –3
                              Вы оперируете субъективными критериями: «не проблема», «очень просто». Конечно, запустить ракету в космос — тоже не проблема. Я описываю альтернативное решение без проблем вообще.
                              +1
                              докер регистри (или distrubution нынче) поднимается все в том же контейнере, дата волумы бэкапятся на раз, а в силу распределенного характера можно и вовсе бэкапы не делать, так как даже если что-то слетит, просто можно с серверов где эти образы задеплоены попушить обратно в докер дистрибьюшен. Собственно так же как и с git.

                              Расходы на поддержание инфраструктуры для докера в принципе не особо велики, по сравнению с профитом который он дает.
                                –2
                                Конечно, но определение «не велики» относительное. У моего друга из Ebay дневной лимит на добавление серверов 50 тысяч долларов, ему все проблемы в рунете — мышинная возня, он просто добавляет десяток серверов в любой момент. Все относительно.
                                0
                                И все же. Что конкретно вам мешает?

                                Сделать build из своих исходных Dockerfile на другом компьютере мешает необходимость дополнительных телодвижений
                                  0
                                  Необходимость передавать на другой компьютер отдельно Dockerfile и само приложение. Я предпочитаю передавать один маленький .tar.xz, и из него одной командой разворачивать работающую систему с базой, веб-сервером, runtime, библиотеками и конфигами.
                                    0
                                    Зачем отдельно? Распаковали архив, выполнили docker-machine create и docker-compose up -d. В чём проблема? Да и архив зачем? Можно взять из вашего центрального репозитория.
                              0
                              Почему, если так не хочется поднимать registry, не использовать docker save/load и при этом все-таки делать кастомизацию через Dockerfile (который можно версионировать)? Проблема в размере?
                                –1
                                Конечно. Работать с архивами полного стека своего приложения размером 2 мб без требований доступности стороннего репозитория намного удобнее, чем с архивами размером 70 мегабайт или обязательной хорошей быстрой связью с интернет. Я работаю с ноута в поездках.
                    0
                    Я правильно понимаю что вы используете Docker исключительно для dev окружения, то есть вы не билдите контейнеры и не деплоите их на сервер… то есть… это такая вот странная замена Vagrant? Зачем вам докер тогда? С тем же успехом можно было бы взять vagrant + ansible и вышло бы удобнее в плане поддержки. Весь профит докера в управлении инфрастуктурой, версионизации, возможность уже проверенную на стэйджинге инфрастуктуру без изменений и быстро выкатить на прод например. А с таким подходом этот профит теряется. А что до разработчиков, те из них что сидят на маках будут слегка грустить, ибо что бы добиться производительности файловой системы нормальной приходится много поплясать с бубном для того что бы настроить хотя бы NFS.
                      0
                      Наоборот, мой подход позволяет на лету подменять любую часть стека приложения без нужны пересобирать образ с runtime, web-сервером и базой данных. В разработке преимущества большие, конечно — можно запустить 5 версий php и быстро между ними переключаться.
                      +2
                      Знаете, docker начал использовать совсем недавно, собрал на его основе среду для сборки деб-пакетов. Главное, но не единственное, конечно же, что понял из штудирования информации за десяток часов: «Use DOCKERFILE, Luke»

                      Всё то, что Вы делаете (docker cp, docker foobar |, docker foobar >) как то вызывает вопрос: мы одну литературу читали?

                      Судя по комментариям, не просто так у меня возникают такие вопросы. Рейтинга статей тоже символизирует. Про обещанную оркестрацию в гитбуке ничего не нашёл.
                        –3
                        Господа, это оригинальное исследование с фатальным недостатком. Похоже, оно рвет шаблоны — вы привыкли делать иначе :)
                        Мой алгоритм работает, и он удобен.
                        Буду рад если вы опишите недостатки не новизны восприятия информации, а технические.
                          +2
                          оно рвет шаблоны

                          Да нет, я так тоже делал, когда я только только разбирался с докером. Это особенно удобно когда докер используется частично.

                          недостатки не новизны восприятия информации, а технические

                          Даже с применением docker-compose сильно усложняется деплоймент. что бы было проще подменять на сервере контейнеры можно конечно замутить data-волумы для кода и конфигов, но это сильно усложнит docker-compose. С Dockerfile и регистри/дистрибьюшен что бы задеплоиться мне надо только сделать pull на сервере и подменить контейнеры на новые, благо docker-compose это уже умеет относительно удобно делать.

                          Из недостатков… был у меня проектик с вендорной утилиткой для обработки видео, которая собиралась через раз. С вашим подходом докер мне никак не поможет, с Dockerfile я могу сборку запихнуть туда, и как только у меня контейнер собрался и работает, повесить на него стабильный тег и больше не беспокоиться о том что оно может отвалиться из-за другой версии библиотеки и т.д.
                            0
                            Эта дискуссия повторяет холивары десятилетней давности относительно официальных репозиториев пакетов, собственных репозиториев, сборки софта из исходников, и смешанных вариантов по типу gentoo :)
                            Каждому свое.

                            Ни в коем случае я не против распространения своих утилит вместе с библиотеками в образах. Я это делаю.
                            Безусловно, registry — это удобно, и, возможно, я в будущем напишу об этом. Вряд ли опубликую здесь при подобной доброжелательности аудитории :)
                            При желании можно обойтись и без него, и без composer. Единого православного способа не существует.
                              –1
                              К слову, для деплоймента приложения, структуры СУБД и конфигов нужен только один образ в виде одного небольшого .tar.xz-файла. Все разворачивается из него и compose-конфига одной единственной командой.
                              При этом, можно подменять модули на лету — php, python, nginx, перепрыгивать с mariadb на percona, развернуть локально репликацию, и откатиться при сбое. Удобно для stage и разработки.

                              Да, админам это не нужно, это ломает привычный development-deployment cycle. Мне так удобно.
                              –1
                              банальное распространение изменений. Что мне делать на кластере из 20 машин с Вашими знаниями о том, как менять контейнеры?
                              Именно в этом месте DOCKERFILE является логичным применением. А то, что предлагаете Вы — весьма, весьма оригинальным
                              решением.
                              image

                              А рвёт оно не шаблоны, а здравый смысл, превращая докер в… во что, кстати? =) Чего Вы вообще добивались в своём оригинальном исследовании? Поиск нестнадартных подходов? Удалось. Но «нестандартный» != «жизнеспособный»
                              Вот ещё один нестандартный подход
                              image
                                –2
                                Тема сисиек не раскрыта :) Поменьше аргументов, побольше картинок! И котиков, котики хороши на любую тему.
                                  0
                                  Если серьезно, ответ такой.
                                  Разложить сервисы на группу серверов так, чтобы с фронта автоматом резолвилась база, и при failover переключался мастер, как это умеет compose локально, не выйдет. Ip надо прописывать ручками — как минимум, в конфиге swarm. Серебрянная пуля отменяется, Dokcerfile и registry ничем не помогут. Когда мы выкладываем на кластер, нам в обоих случаях нужны дополнительные инструменты и ручная настройка.
                              +1
                              habrahabr.ru/info/help/rules, правило 2:
                              Хабрахабр — не ЖЖ и не центр мирового кросспостинга. Не нужно копировать публикации из других блогов и сайтов, указывая, что ранее они были опубликованы в другом месте.
                                0
                                надо было узнать чего не хватает перед публикацией на серьезных ресурсах, спасибо, теперь доработаю

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