Docker-escalado de nginx y php-fpm por separado

He estado jugando con docker y docker-compose y tengo una pregunta.

Actualmente mi docker-compose.yml se ve así:

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

La aplicación contiene php-fpm en el puerto 9000 y mi código de aplicación.Web es nginx con algunos bits de configuración.

Sin embargo, para conectar nginx a php-fpm, tengo esta línea:

fastcgi_pass    app:9000;

¿Cómo puedo escalar esto de manera efectiva? Si quisiera, por ejemplo, tener un contenedor nginx ejecutándose pero tres contenedores de aplicaciones ejecutándose, seguramente tendré tres instancias php-fpm, todas tratando de escuchar en el puerto 9000.

¿Cómo puedo tener cada instancia de php-fpm en un puerto diferente pero aún saber dónde están en mi configuración de nginx en un momento dado?

¿Estoy tomando el enfoque equivocado?

¡Gracias!

Una solución es agregar instancias php-fpm adicionales a su archivo docker-compose y luego usar un nginx ascendente como se menciona en las otras respuestas para equilibrar la carga entre ellas. Esto se hace en este ejemplo de repositorio 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;}

Esto no es realmente ideal porque requerirá cambiar la configuración de nginx y docker-compose.yml cuando desee escalar hacia arriba o hacia abajo.

Tenga en cuenta que el puerto 9000 es interno al contenedor y no a su host real, por lo que no importa que tenga varios contenedores php-fpm en el puerto 9000.

Docker adquirió Tutum este otoño. Tienen una solución que combina un contenedor HAProxy con su API para ajustar automáticamente la configuración del equilibrador de carga a los contenedores en ejecución que está equilibrando la carga. Es una buena solución. Luego, nginx apunta al nombre de host asignado al equilibrador de carga. Quizás Docker integre aún más este tipo de solución en sus herramientas después de la adquisición de Tutum. Hay un artículo al respecto aquí: https://web.archive.org/web/20160628133445/https://support.tutum.co/support/solutions/articles/5000050235-load-balancing-a-web-service

Tutum es actualmente un servicio de pago. Rancher es un proyecto de código abierto que proporciona una función de equilibrio de carga similar. También tienen un "ranchero".yml " que puede definir el equilibrio de carga y el escalado de la configuración de servicios en docker-compose.yml. http://rancher.com/the-magical-moment-when-container-load-balancing-meets-service-discovery/ http://docs.rancher.com/rancher/concepts/#load-balancer

ACTUALIZACIÓN 06/03/2017: He usado un proyecto llamado enclavamiento eso funciona con Docker para actualizar automáticamente la configuración de nginx y reiniciarla. Ver también @iwaseatenbyagrue's respuesta que tiene enfoques adicionales.

Puede usar un flujo ascendente para definir múltiples backends, como se describe aquí:

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

También querrá tener la configuración actualizada cada vez que nuevos backends mueran/entren en servicio con algo como:

https://github.com/kelseyhightower/confd

Aunque esta publicación es de 2015 y siento que soy necroico (lo siento, comunidad), siento que es valioso agregarla en este momento:

Hoy en día (y desde que se mencionó Kubernetes) cuando trabajas con Docker puedes usar Kubernetes o Docker Swarm muy fácilmente para resolver este problema. Ambos orquestadores aceptarán sus nodos de docker (un nodo = un servidor con Docker en él) y puede implementar servicios en ellos y administrarán los desafíos de puerto por usted utilizando redes superpuestas.

Como estoy más versado en Docker Swarm, así es como lo haría para abordar este problema (suponiendo que tenga un solo nodo Docker):

Inicializar el enjambre:

docker swarm init

cd en la raíz de su proyecto

cd some/project/root

crea una pila de enjambre desde tu docker-compose.yml (en lugar de usar docker-compose):

docker stack deploy -c docker-compose.yml myApp

Esto creará una pila de servicios de enjambre de docker llamada "myApp" y administrará los puertos por usted. Esto significa: Solo tiene que agregar una definición de "puerto: 9000:9000" a su servicio php-fpm en su archivo docker-compose y luego puede escalar el servicio php-fpm, por ejemplo, a 3 instancias, mientras que el enjambre equilibrará automáticamente la carga mágicamente las solicitudes entre las tres instancias sin necesidad de más trabajo.

En el caso de que sus contenedores Nginx y php-fpm estén en el mismo host, puede configurar un pequeño dnsmasq instancia en el host que utilizará el contenedor Nginx y ejecute un script para actualizar automáticamente el registro DNS cuando la dirección IP del contenedor haya cambiado.

He escrito un pequeño script para hacer esto (pegado a continuación), que actualiza automáticamente el registro DNS que tiene el mismo nombre que el nombre de los contenedores y los apunta a las direcciones IP de los contenedores:

#!/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

Luego, inicie su contenedor nginx con --dns host-ip-address, donde host-ip-address es la dirección IP del host en la interfaz docker0.

Su Configuración de Nginx debe resolver nombres dinámicamente:

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;  }}

Referencia:

Si su nginx y php-fpm están en hosts diferentes, puede probar la respuesta de @smaj.

Otro enfoque podría ser buscar algo como cónsul-plantilla.

Y, por supuesto, en algún momento, Kubernetes es posible que deba mencionarse.

Sin embargo, podría considerar un enfoque un poco más de "trozos de cuerda y cinta adhesiva" al observar lo que el consumo de eventos de docker podría hacer por usted (ejecutar docker events --since 0 para una muestra rápida).

Sería razonablemente trivial tener un script que analice estos eventos (teniendo en cuenta que hay varios paquetes de cliente disponibles, incluidos python, go, etc.), modifique un archivo de configuración y vuelva a cargar nginx (es decir, utilizando el enfoque consul-template, pero sin la necesidad de consul).

Sin embargo, para volver a su premisa original: siempre que sus contenedores php-fpm se inicien con su propia red (es decir, sin compartir la de otro contenedor, como el nginx), puede tener tantos contenedores escuchando en el puerto 9000 como desee, ya que tienen IP por contenedor, no hay problema con el "choque" de puertos.

La forma en que escale esto probablemente dependerá de cuál sea su objetivo final/caso de uso, pero una cosa que podría considerar es colocar HAProxy entre nginx y sus nodos php-fpm.Una cosa que esto podría permitirle hacer es simplemente nominar un rango (y posiblemente crear un docker network) para sus servidores php-fpm (es decir, 172.18.0.0/24), y tenga HAProxy configurado para intentar usar cualquier IP dentro de ese rango como backend. Dado que HAProxy tiene comprobaciones de estado, podría identificar rápidamente qué direcciones están activas y hacer uso de ellas.

Ver https://stackoverflow.com/questions/1358198/nginx-removing-upstream-servers-from-pool para una discusión sobre cómo nginx vs haproxy se ocupa de los flujos ascendentes.

A menos que esté utilizando una red docker dedicada para esto, puede podría necesita hacer una administración manual de IP para sus nodos php-fpm.