Назад | Перейти на главную страницу

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

Я играл с docker и docker-compose и у меня есть вопрос.

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

app:
    image: myname/php-app
    volumes:
        - /var/www
    environment:
        <SYMFONY_ENVIRONMENT>: dev

web:
    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

Тутум в настоящее время является платной услугой. 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's ответ который имеет дополнительные подходы.

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

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

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

https://github.com/kelseyhightower/confd

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

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

Тем не менее, вы могли бы рассмотреть вариант с чуть большим количеством «кусочков струны и клейкой ленты», посмотрев на то, что потребляющие события докера могут сделать для вас (запустите docker events --since 0 для быстрого образца).

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

Однако, чтобы вернуться к исходной предпосылке: пока ваши контейнеры 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 и haproxy работают с апстримами.

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

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

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

#!/bin/bash

# 10 seconds interval time by default
INTERVAL=${INTERVAL:-10}

# dnsmasq config directory
DNSMASQ_CONFIG=${DNSMASQ_CONFIG:-.}

# commands used in this script
DOCKER=${DOCKER:-docker}
SLEEP=${SLEEP:-sleep}
TAIL=${TAIL:-tail}

declare -A service_map

while true
do
    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} $INTERVAL
done

Затем запустите контейнер 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.

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

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

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

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

docker swarm init

cd в корень вашего проекта

cd some/project/root

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

docker stack deploy -c docker-compose.yml myApp

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