Docker: escala de nginx y php-fpm por separado

11

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

Actualmente mi docker-compose.yml tiene este aspecto:

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

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

Esto funciona como lo esperaría, sin embargo, para conectar nginx a php-fpm tengo esta línea:

fastcgi_pass    app:9000;

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

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

¿Estoy tomando el enfoque equivocado?

¡Gracias!

JimBlizz
fuente

Respuestas:

5

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 ellos. 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 su host real, por lo que no importa que tenga múltiples 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 equilibra la carga. Esa es una buena solución. Entonces 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 luego 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 pago. Rancher es un proyecto de código abierto que proporciona una característica similar de equilibrio de carga. También tienen un "rancher-compose.yml" que puede definir el equilibrio de carga y la escala de la configuración de los servicios en el 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 2017/03/06: He usado un proyecto llamado interlock que funciona con Docker para actualizar automáticamente la configuración de nginx y reiniciarla. También vea la respuesta de @ iwaseatenbyagrue que tiene enfoques adicionales.

rmarscher
fuente
0

En el caso de que sus contenedores Nginx y php-fpm estén en el mismo host, puede configurar una pequeña instancia de dnsmasq en el host para que la use el contenedor Nginx y ejecutar un script para actualizar automáticamente el registro DNS cuando la dirección IP del contenedor tenga 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 señala a las direcciones IP de los contenedores:

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

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

Su configuración de Nginx debería 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;
  }
}

Referencias

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

xuhdev
fuente
0

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

Y, por supuesto, en algún momento, Kubernetes puede necesitar ser mencionado.

Sin embargo, podría considerar un enfoque un poco más de 'bits de cadena y cinta adhesiva' al observar lo que los eventos de Docker que consumen podrían hacer por usted (ejecute docker events --since 0una muestra rápida).

Sería razonablemente trivial tener una secuencia de comandos que observe estos eventos (teniendo en cuenta que hay varios paquetes de clientes disponibles, incluidos python, go, etc.), enmendar un archivo de configuración y volver a cargar nginx (es decir, utilizando el enfoque de consul-template, pero sin la necesidad de cónsul).

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

La forma en que escala 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 es simplemente nominar un rango (y posiblemente crear un docker network) para sus servidores php-fpm (es decir, 172.18.0.0/24), y tener HAproxy configurado para intentar usar cualquier IP dentro de ese rango como back-end. . Dado que HAproxy tiene comprobaciones de estado, podría identificar rápidamente qué direcciones están activas y utilizarlas.

Vea /programming/1358198/nginx-removing-upstream-servers-from-pool para una discusión sobre cómo nginx vs haproxy trata con los upstreams.

A menos que esté utilizando una red de acopladores dedicada para esto, es posible que deba realizar una gestión de IP manual para sus nodos php-fpm.

iwaseatenbyagrue
fuente
0

Aunque esta publicación es de 2015 y siento que estoy necroing (lo siento comunidad), siento que es valioso agregar en este momento:

Hoy en día (y desde que se mencionó a Kubernetes) cuando trabajas con Docker puedes usar Kubernetes o Docker Swarm muy fácilmente para resolver este problema. Ambos orquestadores tomarán sus nodos docker (un nodo = un servidor con Docker en él) y puede implementar servicios en ellos y gestionarán los desafíos de puertos para 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):

Inicializa el enjambre:

docker swarm init

cd en la raíz de tu proyecto

cd some/project/root

cree una pila de enjambre desde su 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 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, digamos 3 instancias, mientras que el enjambre lo hará equilibra automáticamente la carga de las solicitudes entre las tres instancias sin más trabajo necesario.

Worp
fuente