Como vincular corretamente os containers php-fpm e Nginx Docker?

103

Estou tentando vincular 2 contêineres separados:

O problema é que os scripts php não funcionam. Talvez a configuração do php-fpm esteja incorreta. Aqui está o código-fonte, que está em meu repositório . Aqui está o arquivo docker-compose.yml:

nginx:
    build: .
    ports:
        - "80:80"
        - "443:443"
    volumes:
        - ./:/var/www/test/
    links:
        - fpm
fpm:
    image: php:fpm
    ports:
        - "9000:9000"

e Dockerfileque usei para criar uma imagem personalizada com base no nginx:

FROM nginx

# Change Nginx config here...
RUN rm /etc/nginx/conf.d/default.conf
ADD ./default.conf /etc/nginx/conf.d/

Por último, aqui está minha configuração de host virtual Nginx personalizada:

server {
    listen  80;

    server_name localhost;
    root /var/www/test;

    error_log /var/log/nginx/localhost.error.log;
    access_log /var/log/nginx/localhost.access.log;

    location / {
        # try to serve file directly, fallback to app.php
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/.+\.php(/|$) {
        fastcgi_pass 192.168.59.103:9000;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param HTTPS off;
    }
}

Alguém poderia me ajudar a configurar esses containers corretamente para executar scripts php?

PS: Eu executo contêineres por meio do docker-composer assim:

docker-compose up

do diretório raiz do projeto.

Victor Bocharsky
fonte
1
Como você tentou configurá-los até agora ou qual código você usou. Por favor, não me faça adivinhar que sou um lixo em adivinhar.
Matthew Brown, também conhecido como Lord Matt
1
@MatthewBrown Huh, coloquei meu código em um repositório público no GitHub e acho que isso será suficiente, mas você está certo, é melhor mostrar o código aqui na minha pergunta também.
Victor Bocharsky
quando as imagens aumentarem, você pode docker execentrar no contêiner em execução e executar ping no fpm?
Vincent De Smet
1
@MatthewBrown sim, ganhei, obrigado
Victor Bocharsky
1
PS Eu também consegui uma solução de trabalho para vincular Nginxe em PHP-FPM conjunto com o Vagrant e o Ansible. Verifique meu repositório github.com/bocharsky-bw/vagrant-ansible-docker se desejar.
Victor Bocharsky

Respostas:

32

Não codifique o ip dos contêineres na configuração do nginx, o docker link adiciona o nome do host da máquina vinculada ao arquivo hosts do contêiner e você deve ser capaz de fazer ping pelo nome do host.

EDIT: Docker 1.9 Networking não requer mais que você vincule containers, quando vários containers estão conectados à mesma rede, seu arquivo de hosts será atualizado para que eles possam se comunicar pelo nome do host.

Cada vez que um contêiner docker gira a partir de uma imagem (mesmo parando / iniciando um contêiner existente), os contêineres recebem novos IPs atribuídos pelo host docker. Esses ips não estão na mesma sub-rede que suas máquinas reais.

ver docs de vinculação do docker (é isso que o Compose usa em segundo plano)

mas explicado de forma mais clara nos docker-composedocumentos sobre links e exposição

links

links:
 - db
 - db:database
 - redis

Uma entrada com o nome do alias será criada em / etc / hosts dentro de contêineres para este serviço, por exemplo:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

expor

Exponha as portas sem publicá-las na máquina host - elas estarão acessíveis apenas para serviços vinculados . Apenas a porta interna pode ser especificada.

e se você configurar seu projeto para obter as portas + outras credenciais por meio de variáveis ​​de ambiente, os links definirão automaticamente um monte de variáveis ​​de sistema :

Para ver quais variáveis ​​de ambiente estão disponíveis para um serviço, execute docker-compose run SERVICE env.

name_PORT

URL completo, por exemplo, DB_PORT = tcp: //172.17.0.5: 5432

name_PORT_num_protocol

URL completo, por exemplo DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

Endereço IP do contêiner, por exemplo DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

Número da porta exposta, por exemplo DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

Protocolo (tcp ou udp), por exemplo DB_PORT_5432_TCP_PROTO=tcp

name_NAME

Nome do contêiner totalmente qualificado, por exemplo DB_1_NAME=/myapp_web_1/myapp_db_1

Vincent De Smet
fonte
2
você também não precisa publicar a porta 9000 no host, as portas estão abertas entre os contêineres docker vinculados, a menos que você queira solucionar o problema da porta diretamente de seu host.
Vincent De Smet
Sim, você está certo, obrigado. No meu caso, devo usar fastcgi_pass fpm: 9000 em vez de ip direto. Não sei se o Docker o adicionou ao host automaticamente, meu mal.
Victor Bocharsky
E a porta, então é melhor usar expor em vez de portas ? Ou não consegui usar nenhuma dessas portas e expor as diretivas porque os containers vinculados terão acesso a esta porta?
Victor Bocharsky
desculpe pela resposta tardia - acho que você pode precisar usar expor, desculpe, não posso verificar agora
Vincent De Smet
2
--linksagora estão obsoletos, de acordo com a documentação do docker que você faz referência. Eles ainda são suportados atualmente, mas o plano aparente é que se tornem obsoletos.
therobyouknow de
85

Eu sei que é um post antigo, mas eu tive o mesmo problema e não conseguia entender porque seu código não funcionava. Depois de MUITOS testes, descobri o porquê.

Parece que o fpm recebe o caminho completo do nginx e tenta encontrar os arquivos no contêiner fpm, então deve ser exatamente o mesmo que server.rootna configuração do nginx, mesmo que não exista no contêiner nginx.

Para demonstrar:

docker-compose.yml

nginx:
    build: .
    ports:
        - "80:80"
    links:
        - fpm
fpm:
    image: php:fpm
    ports:
        - ":9000"

    # seems like fpm receives the full path from nginx
    # and tries to find the files in this dock, so it must
    # be the same as nginx.root
    volumes:
        - ./:/complex/path/to/files/

/etc/nginx/conf.d/default.conf

server {
    listen  80;

    # this path MUST be exactly as docker-compose.fpm.volumes,
    # even if it doesn't exist in this dock.
    root /complex/path/to/files;

    location / {
        try_files $uri /index.php$is_args$args;
    }

    location ~ ^/.+\.php(/|$) {
        fastcgi_pass fpm:9000;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }
}

Dockerfile

FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/
Rafael Quintela
fonte
4
BEM FEITO!!! Esse é exatamente o ponto! Eu defini a raiz do nginx para um caminho alternativo diferente de /var/www/htmlcom falha.
Alfred Huang
3
Além disso, apenas uma observação de que :9000é a porta que está sendo usada no contêiner, não a que está exposta ao seu host. Levei 2 horas para descobrir isso. Felizmente, você não precisa.
grito de
1
services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]
030
4
Na verdade, você não precisa incluir uma portsseção aqui. Você só precisa fazer exposeisso se ainda não estiver na imagem (o que provavelmente está). Se você estiver fazendo comunicação entre contêineres, não deve expor a porta PHP-FPM.
Vidente
Estava procurando uma solução para AH01071: Got error 'Primary script unknown\n'e que o container php-fpm tivesse que compartilhar o mesmo diretório com os nós da web foi a solução!
cptPH
23

Como apontado antes, o problema era que os arquivos não eram visíveis pelo contêiner fpm. No entanto, para compartilhar dados entre contêineres, o padrão recomendado é usar contêineres somente de dados (conforme explicado neste artigo ).

Resumindo a história: crie um contêiner que apenas armazene seus dados, compartilhe-os com um volume e vincule esse volume em seus aplicativos com volumes_from.

Usando compose (1.6.2 em minha máquina), o docker-compose.ymlarquivo seria:

version: "2"
services:
  nginx:
    build:
      context: .
      dockerfile: nginx/Dockerfile
    ports:
      - "80:80"
    links:
      - fpm
    volumes_from:
      - data
  fpm:
    image: php:fpm
    volumes_from:
      - data
  data:
    build:
      context: .
      dockerfile: data/Dockerfile
    volumes:
      - /var/www/html

Observe que datapublica um volume que está vinculado aos serviços nginxe fpm. Em seguida, Dockerfilepara o serviço de dados , que contém seu código-fonte:

FROM busybox

# content
ADD path/to/source /var/www/html

E o Dockerfilepara nginx, que apenas substitui a configuração padrão:

FROM nginx

# config
ADD config/default.conf /etc/nginx/conf.d

Para fins de conclusão, aqui está o arquivo de configuração necessário para o exemplo funcionar:

server {
    listen 0.0.0.0:80;

    root /var/www/html;

    location / {
        index index.php index.html;
    }

    location ~ \.php$ {
        include fastcgi_params;
        fastcgi_pass fpm:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name;
    }
}

que apenas diz ao nginx para usar o volume compartilhado como raiz do documento e define a configuração correta para que o nginx seja capaz de se comunicar com o contêiner fpm (isto é: o correto HOST:PORT, que é fpm:9000graças aos nomes de host definidos por compose, e o SCRIPT_FILENAME).

iKanor
fonte
Parece que os dados não são atualizados do host para os contêineres e, quando faço docker ps -a, vejo o contêiner de dados parado, isso é um problema?
Aftab Naveed de
2
Esse é o comportamento esperado. Um contêiner somente de dados não executa nenhum comando e apenas será listado como interrompido. Além disso, o Dockerfiledo contêiner de dados está copiando suas fontes para o contêiner em tempo de construção. É por isso que eles não serão atualizados se você alterar os arquivos no host. Se você deseja compartilhar as fontes entre o host e o contêiner, você precisa montar o diretório. Altere o dataserviço no arquivo de composição a ser carregado image: busyboxe, na volumesseção entre ./sources:/var/www/html, onde ./sourcesestá o caminho para suas fontes no host.
iKanor
16

Nova Resposta

O Docker Compose foi atualizado. Eles agora têm um formato de arquivo da versão 2 .

Os arquivos da versão 2 são compatíveis com Compose 1.6.0+ e requerem um Docker Engine da versão 1.10.0+.

Eles agora suportam o recurso de rede do Docker que, quando executado, configura uma rede padrão chamada myapp_default

Pela documentação, seu arquivo seria parecido com o seguinte:

version: '2'

services:
  web:
    build: .
    ports:
      - "8000:8000"
  fpm:
    image: phpfpm
  nginx
    image: nginx

Como esses contêineres são adicionados automaticamente à rede padrão myapp_default , eles podem se comunicar entre si. Você teria então na configuração do Nginx:

fastcgi_pass fpm:9000;

Também como mencionado por @treeface nos comentários, lembre-se de garantir que o PHP-FPM esteja ouvindo na porta 9000, isso pode ser feito editando /etc/php5/fpm/pool.d/www.confonde for necessário listen = 9000.

Resposta Antiga

Eu mantive o abaixo aqui para aqueles que usam uma versão mais antiga do Docker / Docker compose e gostariam das informações.

Continuei tropeçando nessa questão no Google ao tentar encontrar uma resposta para ela, mas não era bem o que eu estava procurando devido à ênfase de Q / A em docker-compose (que no momento em que este artigo foi escrito apenas tem suporte experimental para recursos de rede do docker). Portanto, aqui está minha opinião sobre o que aprendi.

Docker suspendeu recentemente seu recurso de link em favor de seu recurso de rede

Portanto, usando o recurso Docker Networks, você pode vincular containers seguindo estas etapas. Para obter explicações completas sobre as opções, leia os documentos vinculados anteriormente.

Primeiro crie sua rede

docker network create --driver bridge mynetwork

Em seguida, execute seu contêiner PHP-FPM garantindo que você abra a porta 9000 e atribua à sua nova rede ( mynetwork).

docker run -d -p 9000 --net mynetwork --name php-fpm php:fpm

A parte importante aqui é o --name php-fpmno final do comando que é o nome, vamos precisar disso mais tarde.

Em seguida, execute seu contêiner Nginx novamente para atribuir à rede que você criou.

docker run --net mynetwork --name nginx -d -p 80:80 nginx:latest

Para os contêineres PHP e Nginx, você também pode adicionar --volumes-fromcomandos, etc., conforme necessário.

Agora vem a configuração do Nginx. Que deve ser semelhante a isto:

server {
    listen 80;
    server_name localhost;

    root /path/to/my/webroot;

    index index.html index.htm index.php;

    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php-fpm:9000; 
        fastcgi_index index.php;
        include fastcgi_params;
    }
}

Observe o fastcgi_pass php-fpm:9000;no bloco de localização. Isso está dizendo contêiner de contato php-fpmno porto 9000. Quando você adiciona contêineres a uma rede de ponte Docker, todos eles obtêm automaticamente uma atualização do arquivo de hosts que coloca o nome do contêiner em relação ao endereço IP. Portanto, quando o Nginx vir que saberá entrar em contato com o contêiner PHP-FPM que você nomeou php-fpmanteriormente e atribuiu à sua mynetworkrede Docker.

Você pode adicionar essa configuração do Nginx durante o processo de criação do seu contêiner do Docker ou depois disso, depende de você.

DavidT
fonte
Lembre-se também de ter certeza de que php-fpmestá escutando na porta 9000. Isso seria listen = 9000em /etc/php5/fpm/pool.d/www.conf.
treeface
Obrigado @treeface, bom ponto. Eu atualizei com o seu comentário.
DavidT
8

Como as respostas anteriores resolveram, mas devem ser declaradas muito explicitamente: o código php precisa viver no contêiner php-fpm, enquanto os arquivos estáticos precisam viver no contêiner nginx. Para simplificar, a maioria das pessoas apenas anexou todo o código a ambos, como também fiz abaixo. No futuro, provavelmente irei separar essas diferentes partes do código em meus próprios projetos para minimizar quais contêineres têm acesso a quais partes.

Atualizei meus arquivos de exemplo abaixo com esta última revelação (obrigado @alkaline)

Esta parece ser a configuração mínima para docker 2.0 forward (porque as coisas ficaram muito mais fáceis no docker 2.0)

docker-compose.yml:

version: '2'
services:
  php:
    container_name: test-php
    image: php:fpm
    volumes:
      - ./code:/var/www/html/site
  nginx:
    container_name: test-nginx
    image: nginx:latest
    volumes:
      - ./code:/var/www/html/site
      - ./site.conf:/etc/nginx/conf.d/site.conf:ro
    ports:
      - 80:80

( ATUALIZADO o docker-compose.yml acima : Para sites que têm css, javascript, arquivos estáticos, etc, você precisará desses arquivos acessíveis ao contêiner nginx. Embora ainda tenha todo o código php acessível ao contêiner fpm. Novamente, porque meu código base é uma mistura confusa de css, js e php, este exemplo apenas anexa todo o código a ambos os contêineres)

Na mesma pasta:

site.conf:

server
{
    listen   80;
    server_name site.local.[YOUR URL].com;

    root /var/www/html/site;
    index index.php;

    location /
    {
        try_files $uri =404;
    }

    location ~ \.php$ {
        fastcgi_pass   test-php:9000;
        fastcgi_index  index.php;
        fastcgi_param  SCRIPT_FILENAME  $document_root$fastcgi_script_name;
        include        fastcgi_params;
    }
}

No código da pasta:

./code/index.php:

<?php
phpinfo();

e não se esqueça de atualizar seu arquivo hosts:

127.0.0.1 site.local.[YOUR URL].com

e execute seu docker-compose

$docker-compose up -d

e tente o URL do seu navegador favorito

site.local.[YOUR URL].com/index.php
Phillip
fonte
1
Seu arquivo de configuração nginx assume que seu site possui apenas arquivos php. É uma prática recomendada criar uma regra de localização nginx para arquivos estáticos (jpg, txt, svg, ...) e evitar o interpretador de php. Nesse caso, os contêineres nginx e php precisam acessar os arquivos do site. A resposta de @iKanor acima cuida disso.
Bernard
Obrigado @Alkaline, os arquivos estáticos são um problema com a minha resposta original. Na verdade, o nginx realmente precisa, no mínimo, que os arquivos css e js sejam locais naquela máquina para funcionar corretamente.
Phillip
7

Acho que também precisamos dar o volume ao container fpm, não é? Então =>

fpm:
    image: php:fpm
    volumes:
        - ./:/var/www/test/

Se eu não fizer isso, encontro essa exceção ao disparar uma solicitação, pois o fpm não consegue encontrar o arquivo solicitado:

[erro] 6 # 6: * 4 FastCGI enviado em stderr: "Script primário desconhecido" ao ler o cabeçalho de resposta do upstream, cliente: 172.17.42.1, servidor: localhost, solicitação: "GET / HTTP / 1.1", upstream: "fastcgi : //172.17.0.81: 9000 ", host:" localhost "

Leberknecht
fonte
1
Sim você está certo! Devemos compartilhar arquivos com fpm e nginx
Victor Bocharsky
Tenho um exemplo de trabalho com Nginxe PHP-FPMno GitHub
Victor Bocharsky
1

Para qualquer outra pessoa recebendo

Erro Nginx 403: índice de diretório de [pasta] é proibido

ao usar index.phpenquanto index.htmlfunciona perfeitamente e tendo incluído index.phpno índice no bloco de servidor de sua configuração de site emsites-enabled

server {
    listen 80;

    # this path MUST be exactly as docker-compose php volumes
    root /usr/share/nginx/html;

    index index.php

    ...
}

Certifique-se de que o arquivo nginx.conf /etc/nginx/nginx.confcarregue realmente a configuração do seu site no httpbloco ...

http {

    ...

    include /etc/nginx/conf.d/*.conf;

    # Load our websites config 
    include /etc/nginx/sites-enabled/*;
}
myol
fonte
obrigado por isso, antigo, mas ainda informativo, eu tive que usar / usr / share / nginx / html 👍, obrigado
Simon Davies