Docker - dimensionando nginx e php-fpm separadamente

11

Eu tenho brincado com docker e docker-compondo e tenho uma pergunta.

Atualmente, o meu docker-compose.yml está assim:

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

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

O aplicativo contém php-fpm na porta 9000 e o código do meu aplicativo. Web é nginx com alguns bits de configuração.

Isso funciona como eu esperaria, no entanto, para conectar o nginx ao php-fpm, tenho esta linha:

fastcgi_pass    app:9000;

Como posso efetivamente escalar isso? Se eu quisesse, por exemplo, ter um contêiner nginx em execução, mas três contêineres de aplicativos em execução, certamente terei três instâncias php-fpm tentando escutar na porta 9000.

Como posso ter cada instância do php-fpm em uma porta diferente, mas ainda sei onde elas estão na minha configuração do nginx a qualquer momento?

Estou adotando a abordagem errada?

Obrigado!

JimBlizz
fonte

Respostas:

5

Uma solução é adicionar instâncias adicionais de php-fpm ao seu arquivo docker-compose e, em seguida, usar um nginx upstream, conforme mencionado nas outras respostas, para equilibrar a carga entre elas. Isso é feito neste exemplo de repositório de encaixe: 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;
}

Isso não é realmente ideal, porque será necessário alterar a configuração do nginx e o docker-compose.yml quando você desejar aumentar ou diminuir a escala.

Observe que a porta 9000 é interna ao contêiner e não ao host real, portanto, não importa que você tenha vários contêineres php-fpm na porta 9000.

Docker adquiriu Tutum neste outono. Eles têm uma solução que combina um contêiner HAProxy com sua API para ajustar automaticamente a configuração do balanceador de carga aos contêineres em execução que ele está balanceando. Essa é uma boa solução. Em seguida, nginx aponta para o nome do host atribuído ao balanceador de carga. Talvez o Docker integrará ainda mais esse tipo de solução em suas ferramentas após a aquisição da Tutum. Há um artigo sobre isso aqui: https://web.archive.org/web/20160628133445/https://support.tutum.co/support/solutions/articles/5000050235-load-balancing-a-web-service

Tutum é atualmente um serviço pago. O Rancher é um projeto de código aberto que fornece um recurso de balanceamento de carga semelhante. Eles também têm um "rancher-compose.yml", que pode definir o balanceamento de carga e o dimensionamento da configuração dos serviços no docker-compose.yml. http://rancher.com/the-magical-moment-when-container-load-balancing-meets-service-discovery/ http://docs.rancher.com/rancher/concepts/#load-balancer

ATUALIZAÇÃO 06/06/2017: Eu usei um projeto chamado interlock que funciona com o Docker para atualizar automaticamente a configuração do nginx e reiniciá-la. Veja também a resposta de @ iwaseatenbyagrue, que tem abordagens adicionais.

rmarscher
fonte
0

No caso de seus contêineres Nginx e php-fpm estarem no mesmo host, você pode configurar uma pequena instância dnsmasq no host para ser usada pelo contêiner Nginx e executar um script para atualizar automaticamente o registro DNS quando o endereço IP do contêiner mudou.

Eu escrevi um pequeno script para fazer isso (colado abaixo), que atualiza automaticamente o registro DNS que tem o mesmo nome que o nome dos contêineres e os aponta para os endereços IP dos contêineres:

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

Em seguida, inicie o contêiner nginx com --dns host-ip-address, onde host-ip-addressestá o endereço IP do host na interface docker0.

Sua configuração do Nginx deve resolver nomes dinamicamente:

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

Referências:

Se o seu nginx e php-fpm estiverem em hosts diferentes, você pode tentar a resposta do @ smaj.

xuhdev
fonte
0

Outra abordagem pode ser procurar algo como um modelo de consul .

E, é claro, em algum momento, o Kubernetes pode precisar ser mencionado.

No entanto, você pode considerar uma abordagem um pouco mais de 'bits de cadeia de caracteres e fita adesiva' observando o que os eventos consumíveis do docker podem fazer por você (execute docker events --since 0uma amostra rápida).

Seria razoavelmente trivial ter um script olhando para esses eventos (tendo em mente que existem vários pacotes de clientes disponíveis, incluindo python, go, etc), alterando um arquivo de configuração e recarregando o nginx (por exemplo, usando a abordagem consul-template, mas sem a necessidade de cônsul).

Porém, para voltar à sua premissa original: desde que seus contêineres php-fpm sejam iniciados com sua própria rede (ou seja, não compartilhando o de outro contêiner, como o nginx), você poderá ter o mesmo número de contêineres ouvindo na porta 9000 como você deseja - como eles têm IPs por contêiner, não há problema com as portas 'colidindo'.

Como você dimensiona isso provavelmente dependerá do seu objetivo final / caso de uso, mas uma coisa que você pode considerar é colocar o HAproxy entre o nginx e os nós php-fpm. Uma coisa que isso pode permitir é simplesmente nomear um intervalo (e possivelmente criar um docker network) para seus servidores php-fpm (por exemplo, 172.18.0.0/24) e ter o HAproxy configurado para tentar usar qualquer IP desse intervalo como back-end . Como o HAproxy possui verificações de saúde, ele pode identificar rapidamente quais endereços estão ativos e utilizá-los.

Consulte /programming/1358198/nginx-removing-upstream-servers-from-pool para obter uma discussão sobre como o nginx vs haproxy lida com upstreams.

A menos que você esteja usando uma rede docker dedicada para isso, talvez seja necessário fazer um gerenciamento manual de IP para seus nós php-fpm.

iwaseatenbyagrue
fonte
0

Embora este post seja de 2015 e eu sinto que estou necroing (desculpe a comunidade), acho que vale a pena adicionar neste momento:

Atualmente (e desde que o Kubernetes foi mencionado), quando você trabalha com o Docker, pode usar o Kubernetes ou o Docker Swarm com muita facilidade para resolver esse problema. Os dois orquestradores receberão os nós do docker (um nó = um servidor com o Docker) e você poderá implantar serviços neles, e eles gerenciarão os desafios de porta para você, usando redes de sobreposição.

Como eu sou mais versado no Docker Swarm, é assim que você faria para abordar esse problema (supondo que você tenha um único nó do Docker):

Inicialize o enxame:

docker swarm init

cd na raiz do projeto

cd some/project/root

crie uma pilha swarm a partir do docker-compose.yml (em vez de usar o docker-compose):

docker stack deploy -c docker-compose.yml myApp

Isso criará uma pilha de serviços de encaixe chamada "myApp" e gerenciará as portas para você. Isso significa: Você só precisa adicionar uma definição "port: 9000: 9000" ao seu serviço php-fpm no seu arquivo docker-compose e, em seguida, pode ampliar o serviço php-fpm, digamos em 3 instâncias, enquanto o swarm irá balancear automaticamente as solicitações entre as três instâncias sem precisar de mais trabalho.

Worp
fonte