Docker - mise à l'échelle séparée de nginx et php-fpm

11

J'ai joué avec docker et docker-compose et j'ai une question.

Actuellement, mon docker-compose.yml ressemble à ceci:

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

web:
    image: myname/nginx
    ports:
        - 80
    links:
        - app
    volumes_from:
        - app

L'application contient php-fpm sur le port 9000 et mon code d'application. Le Web est nginx avec quelques bits de configuration.

Cela fonctionne comme je m'y attendais, cependant, afin de connecter nginx à php-fpm, j'ai cette ligne:

fastcgi_pass    app:9000;

Comment puis-je mettre cela à l'échelle efficacement? Si je voulais, par exemple, avoir un conteneur nginx en cours d'exécution mais trois conteneurs d'application en cours d'exécution, je vais sûrement avoir trois instances php-fpm essayant toutes d'écouter sur le port 9000.

Comment puis-je avoir chaque instance de php-fpm sur un port différent tout en sachant où elles se trouvent dans ma configuration nginx à un moment donné?

Suis-je en train d'adopter la mauvaise approche?

Merci!

JimBlizz
la source

Réponses:

5

Une solution consiste à ajouter des instances php-fpm supplémentaires à votre fichier docker-compose, puis à utiliser un nginx en amont comme mentionné dans les autres réponses pour équilibrer la charge entre elles. Cela se fait dans cet exemple de dépôt 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;
}

Ce n'est pas vraiment idéal car cela nécessitera de changer la configuration nginx et docker-compose.yml lorsque vous souhaitez augmenter ou réduire.

Notez que le port 9000 est interne au conteneur et non à votre hôte réel, donc peu importe que vous ayez plusieurs conteneurs php-fpm sur le port 9000.

Docker a acquis Tutum cet automne. Ils ont une solution qui combine un conteneur HAProxy avec leur API pour ajuster automatiquement la configuration de l'équilibreur de charge aux conteneurs en cours d'exécution. C’est une bonne solution. Ensuite, nginx pointe vers le nom d'hôte attribué à l'équilibreur de charge. Docker intégrera peut-être davantage ce type de solution dans ses outils après l'acquisition de Tutum. Il y a un article à ce sujet ici: https://web.archive.org/web/20160628133445/https://support.tutum.co/support/solutions/articles/5000050235-load-balancing-a-web-service

Tutum est actuellement un service payant. Rancher est un projet open source qui fournit une fonction d'équilibrage de charge similaire. Ils ont également un "rancher-compose.yml" qui peut définir l'équilibrage de charge et la mise à l'échelle de la configuration des services dans le docker-compose.yml. http://rancher.com/the-magical-moment-when-container-load-balancing-meets-service-discovery/ http://docs.rancher.com/rancher/concepts/#load-balancer

MISE À JOUR 2017/03/06: J'ai utilisé un projet appelé interlock qui fonctionne avec Docker pour mettre à jour automatiquement la configuration nginx et la redémarrer. Voir également la réponse de @ iwaseatenbyagrue qui propose des approches supplémentaires.

rmarscher
la source
0

Dans le cas où vos conteneurs Nginx et php-fpm sont sur le même hôte, vous pouvez configurer une petite instance dnsmasq sur l'hôte à utiliser par le conteneur Nginx et exécuter un script pour mettre à jour automatiquement l'enregistrement DNS lorsque l'adresse IP du conteneur a modifié.

J'ai écrit un petit script pour cela (collé ci-dessous), qui met automatiquement à jour l'enregistrement DNS qui porte le même nom que le nom des conteneurs et les pointe vers les adresses IP des conteneurs:

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

Ensuite, démarrez votre conteneur nginx avec --dns host-ip-address, où host-ip-addressest l'adresse IP de l'hôte sur l'interface docker0.

Votre configuration Nginx devrait résoudre les noms de manière dynamique:

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

Les références:

Si vos nginx et php-fpm sont sur des hôtes différents, vous pouvez essayer la réponse de @ smaj.

xuhdev
la source
0

Une autre approche pourrait consister à examiner quelque chose comme le modèle de consul .

Et bien sûr, à un moment donné, il faudra peut-être mentionner Kubernetes .

Cependant, vous pouvez envisager une approche légèrement plus `` bits de chaîne et de ruban adhésif '' en examinant ce que les événements docker consommateurs pourraient faire pour vous (exécutez docker events --since 0pour un échantillon rapide).

Il serait raisonnablement trivial d'avoir un script examinant ces événements (en gardant à l'esprit que plusieurs packages clients sont disponibles, y compris pour python, go, etc.), modifiant un fichier de configuration et rechargeant nginx (c'est-à-dire en utilisant l'approche consul-template, mais sans besoin de consul).

Pour revenir à votre prémisse d'origine, cependant: tant que vos conteneurs php-fpm sont démarrés avec leur propre réseau (c'est-à-dire ne partageant pas celui d'un autre conteneur, tel que celui de nginx), alors vous pouvez avoir autant de conteneurs à l'écoute sur le port 9000 comme vous le souhaitez - car ils ont des adresses IP par conteneur, il n'y a pas de problème avec la «collision» des ports.

La façon dont vous adapterez cela dépendra probablement de votre objectif / cas d'utilisation ultime, mais une chose que vous pourriez envisager est de placer HAproxy entre nginx et vos nœuds php-fpm. Une chose que cela pourrait vous permettre de faire est simplement de nommer une plage (et éventuellement d'en créer une docker network) pour vos serveurs php-fpm (c'est-à-dire 172.18.0.0/24), et d'avoir HAproxy configuré pour essayer d'utiliser n'importe quelle IP de cette plage comme backend . Étant donné que HAproxy dispose de contrôles d'intégrité, il peut rapidement identifier les adresses actives et les utiliser.

Voir /programming/1358198/nginx-removing-upstream-servers-from-pool pour une discussion sur la façon dont nginx vs haproxy traite les amonts.

À moins que vous n'utilisiez un réseau de docker dédié pour cela, vous devrez peut- être effectuer une gestion IP manuelle pour vos nœuds php-fpm.

iwaseatenbyagrue
la source
0

Bien que ce post date de 2015 et que je sens que je suis nécroissant (communauté désolée), je pense qu'il est utile d'ajouter à ce stade:

De nos jours (et puisque Kubernetes a été mentionné) lorsque vous travaillez avec Docker, vous pouvez utiliser Kubernetes ou Docker Swarm très facilement pour résoudre ce problème. Les deux orchestrateurs prendront en charge vos nœuds Docker (un nœud = un serveur avec Docker dessus) et vous pouvez leur déployer des services et ils géreront les défis de port pour vous en utilisant des réseaux de superposition.

Comme je suis plus versé dans Docker Swarm, voici comment vous le feriez pour aborder ce problème (en supposant que vous avez un seul nœud Docker):

Initialisez l'essaim:

docker swarm init

cd dans la racine de votre projet

cd some/project/root

créez une pile d'essaim à partir de votre docker-compose.yml (au lieu d'utiliser docker-compose):

docker stack deploy -c docker-compose.yml myApp

Cela créera une pile de services d'essaimage de docker appelée "myApp" et gérera les ports pour vous. Cela signifie: vous n'avez qu'à ajouter une définition "port: 9000: 9000" à votre service php-fpm dans votre fichier docker-compose, puis vous pouvez étendre le service php-fpm, disons à 3 instances, tandis que l'essaim équilibrer automatiquement les demandes entre les trois instances sans aucun travail supplémentaire.

Worp
la source