Pull to refresh

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

Website development *PHP *System Analysis and Design *
Пожалуйста, начинайте читать серию заметок с начала, здесь: 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 я не трогаю.

Да, это рвет шаблон, такого раньше не было. Эта статья для тех, кому нужен модульный дизайн системы. Если вы признаете один единственный правильный путь — эта статья не для вас.
Tags:
Hubs:
Total votes 21: ↑8 and ↓13 -5
Views 20K
Comments Comments 44