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

11

Я играл с 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 в любой момент времени?

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

Благодарность!

JimBlizz
источник

Ответы:

5

Одним из решений является добавление дополнительных экземпляров php-fpm в ваш файл docker-compose, а затем использование восходящего потока nginx, как упоминалось в других ответах, для балансировки нагрузки между ними. Это делается в этом примере docker-compose repo: 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.

Докер приобрел Тутум этой осенью. У них есть решение, которое комбинирует контейнер 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: Я использовал проект под названием interlock, который работает с Docker для автоматического обновления конфигурации nginx и перезапуска его. Также см. Ответ @ iwaseatenbyagrue, в котором есть дополнительные подходы.

rmarscher
источник
1

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

/programming/5467921/how-to-use-fastcgi-next-upstream-in-nginx

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

https://github.com/kelseyhightower/confd

smaj
источник
0

В случае, когда ваши контейнеры 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-addressIP-адрес хоста интерфейса 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.

xuhdev
источник
0

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

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

Тем не менее, вы могли бы рассмотреть немного более подход «кусочки строки и клейкой ленты», посмотрев на то, что могут потреблять события докера для потребителя (бегите 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 vs haproxy взаимодействует с апстримами.

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

iwaseatenbyagrue
источник
0

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

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

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

Инициализировать рой:

docker swarm init

перейдите в корень вашего проекта

cd some/project/root

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

docker stack deploy -c docker-compose.yml myApp

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

Worp
источник