Docker - масштабирование nginx и php-fpm отдельно

Я поиграл с docker и docker-compose, и у меня возник вопрос.

В настоящее время мой docker-compose.yml выглядит следующим образом:

app:    image: myname/php-app    volumes:        - /var/www    environment:        <SYMFONY_ENVIRONMENT>: devweb:    image: myname/nginx    ports:        - 80    links:        - app    volumes_from:        - app

Приложение содержит php-fpm на порту 9000 и мой код приложения.Web - это nginx с несколькими битами конфигурации.

Это работает так, как я ожидал бы, однако для подключения nginx к php-fpm у меня есть эта строка:

fastcgi_pass    app:9000;

Как я могу эффективно масштабировать это? Если бы я хотел, например, чтобы был запущен один контейнер nginx, но запущены три контейнера приложений, тогда у меня наверняка будет три экземпляра php-fpm, которые будут пытаться прослушивать порт 9000.

Как я могу иметь каждый экземпляр php-fpm на другом порту, но при этом знать, где они находятся в моей конфигурации nginx в любой момент времени?

Я использую неправильный подход?

Спасибо!

Одним из решений является добавление дополнительных экземпляров php-fpm в ваш файл docker-compose, а затем использование восходящего потока nginx, как упоминалось в других ответах, для балансировки нагрузки между ними. Это делается в этом примере репозитория docker-compose: https://github.com/iamyojimbo/docker-nginx-php-fpm/blob/master/nginx/nginx.conf#L137

upstream php {    #If there's no directive here, then use round_robin.    #least_conn;    server dockernginxphpfpm_php1_1:9000;    server dockernginxphpfpm_php2_1:9000;    server dockernginxphpfpm_php3_1:9000;}

На самом деле это не идеально, потому что для этого потребуется изменить конфигурацию nginx и docker-compose.yml, когда вы захотите увеличить или уменьшить масштаб.

Обратите внимание, что порт 9000 является внутренним для контейнера, а не для вашего фактического хоста, поэтому не имеет значения, что у вас есть несколько контейнеров php-fpm на порту 9000.

Docker приобрел Tutum этой осенью. У них есть решение, которое объединяет контейнер HAProxy с их api для автоматической настройки конфигурации балансировщика нагрузки в соответствии с запущенными контейнерами, которые он балансирует. Это хорошее решение. Затем nginx указывает на имя хоста, назначенное балансировщику нагрузки. Возможно, Docker продолжит интегрировать этот тип решения в свои инструменты после приобретения Tutum. Здесь есть статья об этом: https://web.archive.org/web/20160628133445/https://support.tutum.co/support/solutions/articles/5000050235-load-balancing-a-web-service

Tutum в настоящее время является платной услугой. Rancher - это проект с открытым исходным кодом, который предоставляет аналогичную функцию балансировки нагрузки. У них также есть "rancher-compose.yml", который может определять балансировку нагрузки и масштабирование настроек служб в docker-compose.yml. http://rancher.com/the-magical-moment-when-container-load-balancing-meets-service-discovery/ http://docs.rancher.com/rancher/concepts/#load-balancer

ОБНОВЛЕНИЕ 2017/03/06: я использовал проект под названием блокировка это работает с Docker для автоматического обновления конфигурации nginx и ее перезапуска. Также смотрите статью @iwaseatenbyagrue ответ который имеет дополнительные подходы.

Вы можете использовать восходящий поток для определения нескольких бэкэндов, как описано здесь:

https://stackoverflow.com/questions/5467921/how-to-use-fastcgi-next-upstream-in-nginx

Вы также хотели бы, чтобы конфигурация обновлялась всякий раз, когда новые бэкенды умирают / вводятся в эксплуатацию с помощью чего-то вроде:

https://github.com/kelseyhightower/confd

Хотя этот пост датирован 2015 годом, и я чувствую, что я умираю (извините, сообщество), я чувствую, что на данный момент это ценно добавить:

В настоящее время (и с тех пор, как был упомянут Kubernetes), когда вы работаете с Docker, вы можете очень легко использовать Kubernetes или Docker Swarm для решения этой проблемы. Оба оркестратора будут принимать ваши узлы docker (один узел = один сервер с Docker на нем), и вы можете развертывать на них службы, и они будут управлять проблемами портов для вас, используя оверлейные сети.

Поскольку я больше разбираюсь в Docker Swarm, вот как вы могли бы это сделать, чтобы подойти к этой проблеме (предполагая, что у вас есть один узел Docker):

Инициализируйте рой:

docker swarm init

компакт-диск в корень вашего проекта

cd some/project/root

создайте стек swarm из вашего docker-compose.yml (вместо использования docker-compose):

docker stack deploy -c docker-compose.yml myApp

Это создаст стек служб docker swarm под названием "MyApp" и будет управлять портами для вас. Это означает: вам нужно только добавить одно определение "порт: 9000: 9000" к вашей службе php-fpm в вашем файле docker-compose, а затем вы можете увеличить масштаб службы php-fpm, скажем, до 3 экземпляров, в то время как swarm автоматически волшебным образом балансирует нагрузку запросов между три экземпляра без какой-либо дополнительной работы.

В случае, когда ваши контейнеры Nginx и php-fpm находятся на одном хосте, вы можете настроить небольшой dnsmasq экземпляр на хосте, который будет использоваться контейнером Nginx, и запустите скрипт для автоматического обновления записи DNS при изменении IP-адреса контейнера.

Я написал небольшой сценарий для этого (вставлено ниже), который автоматически обновляет DNS-запись, имеющую то же имя, что и имя контейнеров, и указывает им на IP-адреса контейнеров:

#!/bin/bash# 10 seconds interval time by defaultINTERVAL=${INTERVAL:-10}# dnsmasq config directoryDNSMASQ_CONFIG=${DNSMASQ_CONFIG:-.}# commands used in this scriptDOCKER=${DOCKER:-docker}SLEEP=${SLEEP:-sleep}TAIL=${TAIL:-tail}declare -A service_mapwhile truedo    changed=false    while read line    do        name=${line##* }        ip=$(${DOCKER} inspect --format '{{.NetworkSettings.IPAddress}}' $name)        if [ -z ${service_map[$name]} ] || [ ${service_map[$name]} != $ip ] # IP addr changed        then            service_map[$name]=$ip            # write to file            echo $name has a new IP Address $ip >&2            echo "host-record=$name,$ip"  > "${DNSMASQ_CONFIG}/docker-$name"            changed=true        fi    done < <(${DOCKER} ps | ${TAIL} -n +2)    # a change of IP address occured, restart dnsmasq    if [ $changed = true ]    then        systemctl restart dnsmasq    fi    ${SLEEP} $INTERVALdone

Затем запустите свой контейнер nginx с помощью --dns host-ip-address, где host-ip-address является IP-адресом хоста на интерфейсе docker0.

Ваш Конфигурация Nginx должны разрешать имена динамически:

server {  resolver host-ip-address;  listen 80;  server_name @server_name@;  root /var/www/@root@;  index index.html index.htm index.php;  location ~ ^(.+?\.php)(/.*)?$ {    try_files $uri =404;    include fastcgi_params;    fastcgi_param SCRIPT_FILENAME $document_root$1;    set $backend "@fastcgi_server@";    fastcgi_pass $backend;  }}

Рекомендации:

Если ваш nginx и php-fpm находятся на разных хостах, вы можете попробовать ответ @smaj.

Другим подходом может быть изучение чего-то вроде консул-шаблон.

И, конечно, в какой-то момент, Kubernetes возможно, потребуется упомянуть об этом.

Тем не менее, вы могли бы рассмотреть немного более "кусочки веревки и клейкой ленты" подход, посмотрев на то, что может сделать для вас использование событий docker (выполнить docker events --since 0 для быстрого примера).

Было бы достаточно тривиально иметь скрипт, просматривающий эти события (учитывая, что доступно несколько клиентских пакетов, в том числе для python, go и т.д.), Вносящий изменения в конфигурационный файл и перезагружающий nginx (т.Е. используя подход consul-template, но без необходимости consul).

Однако вернемся к вашей первоначальной предпосылке: до тех пор, пока ваши контейнеры php-fpm запускаются со своей собственной сетью (т.Е. не совместно используют сеть другого контейнера, такого как nginx), у вас может быть столько контейнеров, прослушивающих порт 9000, сколько вы хотите - сколько у них есть на-контейнерные IP-адреса, нет проблем с "конфликтом" портов.

То, как вы это масштабируете, вероятно, будет зависеть от вашей конечной цели / варианта использования, но одна вещь, которую вы могли бы рассмотреть, - это размещение HAProxy между nginx и вашими узлами php-fpm.Одна вещь, которую это могло бы позволить вам сделать, - это просто назначить диапазон (и, возможно, создать docker network) для ваших серверов php-fpm (т.е. 172.18.0.0/24) и настройте HAProxy на попытку использовать любой IP-адрес в этом диапазоне в качестве серверной части. Поскольку у HAProxy есть проверки работоспособности, он может быстро определить, какие адреса находятся в режиме реального времени, и использовать их.

Видеть https://stackoverflow.com/questions/1358198/nginx-removing-upstream-servers-from-pool для обсуждения того, как nginx vs haproxy справляется с восходящими потоками.

Если вы не использовали для этого выделенную сеть docker, вы вероятно нужно выполнить некоторое ручное управление IP-адресами для ваших узлов php-fpm.