Docker-escalando nginx e php-fpm separadamente

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

Atualmente meu docker-compose.yml se parece com isso:

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

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

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

fastcgi_pass    app:9000;

Como posso efetivamente dimensionar 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 ouvir na porta 9000.

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

Estou tomando a abordagem errada?

Obrigado!

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

Isso não é realmente ideal porque exigirá a alteração da configuração do nginx e do Docker-compose.yml quando você quer escalar para cima ou para baixo.

Observe que a porta 9000 é interna ao contêiner e não ao seu host real, portanto, não importa se você tem 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 é o balanceamento de carga. Essa é uma boa solução. Em seguida, o nginx aponta para o nome do host atribuído ao balanceador de carga. Talvez o Docker integre ainda mais esse tipo de solução em suas ferramentas após a aquisição do 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. Rancher é um projeto de código aberto que fornece um recurso de balanceamento de carga semelhante. Eles também têm um " rancheiro-compor.yml " que pode definir o balanceamento de carga e o dimensionamento da configuração de 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 2017/03/06: eu usei um projeto chamado travar isso funciona com o Docker para atualizar automaticamente a configuração do nginx e reiniciá-la. Veja também @ iwaseatenbyagrue's resposta que tem abordagens adicionais.

Embora este post seja de 2015 e eu sinta que estou necroing (desculpe comunidade), sinto que é valioso adicionar neste momento:

Hoje em dia (e como o Kubernetes foi mencionado) quando você está trabalhando com o Docker, você pode usar o Kubernetes ou o Docker Swarm com muita facilidade para resolver esse problema. Ambos os orquestradores terão em seus nós docker (um nó = um servidor com Docker nele) e você pode implantar serviços para eles e eles vão gerenciar desafios de porta para você usando redes de sobreposição.

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

Inicializar o enxame:

docker swarm init

cd na raiz do seu projeto

cd some/project/root

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

docker stack deploy -c docker-compose.yml myApp

Isso criará uma pilha de serviços docker swarm 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 em seu arquivo docker-compose e, em seguida, você pode ampliar o serviço php-fpm, digamos, para 3 instâncias, enquanto o enxame carregará automaticamente as solicitações entre as três instâncias sem nenhum trabalho adicional necessário.

No caso em que seus contêineres Nginx e php-fpm estão no mesmo host, você pode configurar um pequeno dnsmasq instância no host a ser usado pelo contêiner Nginx e execute um script para atualizar automaticamente o registro DNS quando o endereço IP do contêiner for alterado.

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

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

Em seguida, inicie seu contêiner nginx com --dns host-ip-address, onde host-ip-address é 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ência:

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

Outra abordagem pode ser olhar para algo como cônsul-modelo.

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

No entanto, você pode considerar uma abordagem um pouco mais 'bits de string e fita adesiva' observando o que os Eventos do docker que consomem podem fazer por você (executar docker events --since 0 para uma 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 (ou seja, usando a abordagem consul-template, mas sem a necessidade de cônsul).

Para voltar à sua premissa original, no entanto: 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ê pode ter quantos contêineres escutando na porta 9000 quiser - como eles têm IPs por contêiner, não há problema com as portas 'conflitantes'.

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

Ver https://stackoverflow.com/questions/1358198/nginx-removing-upstream-servers-from-pool para uma discussão sobre como nginx vs HAProxy lida com upstreams.

A menos que você estivesse usando uma rede docker dedicada para isso, você poder precisa fazer algum gerenciamento de IP manual para seus nós php-fpm.