¿Cómo vincular correctamente los contenedores php-fpm y Nginx Docker?

103

Estoy tratando de vincular 2 contenedores separados:

El problema es que los scripts php no funcionan. Quizás la configuración de php-fpm sea incorrecta. Aquí está el código fuente, que está en mi repositorio . Aquí está el archivo docker-compose.yml:

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

y Dockerfileque usé para construir una imagen personalizada basada en la de nginx:

FROM nginx

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

Por último, aquí está mi configuración personalizada de host virtual Nginx:

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

¿Alguien podría ayudarme a configurar estos contenedores correctamente para ejecutar scripts php?

PD : ejecuto contenedores a través de docker-composer como este:

docker-compose up

desde el directorio raíz del proyecto.

Victor Bocharsky
fuente
1
¿Cómo ha intentado configurarlos hasta ahora o qué código ha utilizado? Por favor, no me hagas adivinar que soy una tontería adivinar.
Matthew Brown alias Lord Matt
1
@MatthewBrown Huh, puse mi código en un repositorio público en GitHub y creo que esto será suficiente, pero tienes razón, es mejor mostrar el código aquí también en mi pregunta.
Victor Bocharsky
cuando las imágenes giran, ¿puede docker execingresar al contenedor en ejecución y hacer ping a fpm?
Vincent De Smet
1
@MatthewBrown sí, lo gané, gracias
Victor Bocharsky
1
PD También logré una solución de trabajo para vincular Nginxy PHP-FPM junto con Vagrant y Ansible. Consulte mi repositorio github.com/bocharsky-bw/vagrant-ansible-docker si lo desea.
Victor Bocharsky

Respuestas:

32

No codifique la IP de los contenedores en la configuración de nginx, el enlace docker agrega el nombre de host de la máquina vinculada al archivo de hosts del contenedor y debería poder hacer ping por nombre de host.

EDITAR: Docker 1.9 Networking ya no requiere que vincule contenedores, cuando varios contenedores están conectados a la misma red, su archivo de hosts se actualizará para que puedan comunicarse entre sí por nombre de host.

Cada vez que un contenedor de la ventana acoplable gira a partir de una imagen (incluso cuando detiene / inicia un contenedor existente), el host de la ventana acoplable asigna una nueva dirección IP a los contenedores. Estas ip no están en la misma subred que sus máquinas reales.

ver docker vinculando documentos (esto es lo que compose usa en segundo plano)

pero explicado con más claridad en los docker-composedocumentos sobre enlaces y exposición

Enlaces

links:
 - db
 - db:database
 - redis

Se creará una entrada con el nombre del alias en / etc / hosts dentro de los contenedores para este servicio, por ejemplo:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

exponer

Exponga los puertos sin publicarlos en la máquina host ; solo serán accesibles para los servicios vinculados . Solo se puede especificar el puerto interno.

y si configura su proyecto para obtener los puertos + otras credenciales a través de variables de entorno, los enlaces configuran automáticamente un montón de variables del sistema :

Para ver qué variables de entorno están disponibles para un servicio, ejecute docker-compose run SERVICE env.

name_PORT

URL completa, por ejemplo, DB_PORT = tcp: //172.17.0.5: 5432

name_PORT_num_protocol

URL completa, p. Ej. DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

Dirección IP del contenedor, p. Ej. DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

Número de puerto expuesto, p. Ej. DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

Protocolo (tcp o udp), p. Ej. DB_PORT_5432_TCP_PROTO=tcp

name_NAME

Nombre del contenedor completamente calificado, p. Ej. DB_1_NAME=/myapp_web_1/myapp_db_1

Vincent De Smet
fuente
2
tampoco es necesario que publique el puerto 9000 en el host, los puertos están abiertos entre los contenedores de la ventana acoplable vinculados, a menos que desee solucionar el problema del puerto directamente desde su host.
Vincent De Smet
Sí, tienes razón, gracias. En mi caso, debería usar fastcgi_pass fpm: 9000 en lugar de ip directa. No sé si Docker lo agrega al host automáticamente, mi mal.
Victor Bocharsky
¿Qué pasa con el puerto, entonces es mejor usar exponer en lugar de puertos ? ¿O no podría usar ninguno de estos puertos y exponer directivas porque los contenedores vinculados tendrán acceso a este puerto?
Victor Bocharsky
perdón por la respuesta tardía, creo que es posible que deba usar exponer, lo siento, no puedo verificar ahora mismo
Vincent De Smet
2
--linksahora están obsoletos, de acuerdo con la documentación de la ventana acoplable a la que hace referencia. Todavía se admiten actualmente, pero el plan aparente es que queden obsoletos.
therobyouknow
86

Sé que es una publicación antigua, pero tuve el mismo problema y no pude entender por qué tu código no funcionó. Después de MUCHAS pruebas, descubrí por qué.

Parece que fpm recibe la ruta completa de nginx e intenta encontrar los archivos en el contenedor fpm, por lo que debe ser exactamente igual que server.rooten la configuración de nginx, incluso si no existe en el contenedor nginx.

Demostrar:

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
fuente
4
¡¡¡BIEN HECHO!!! ¡Ese es exactamente el punto! Configuré la raíz de nginx en una ruta alternativa que no sea /var/www/htmlcon falla.
Alfred Huang
3
Además, solo tenga en cuenta que :9000es el puerto que se está utilizando en el contenedor, no el que está expuesto a su host. Me tomó 2 horas resolver esto. Con suerte, no es necesario.
chillido
1
services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]
030
4
En realidad, no es necesario incluir una portssección aquí. Es posible que solo lo necesite exposesi aún no está en la imagen (que probablemente lo esté). Si está realizando una comunicación entre contenedores, no debería exponer el puerto PHP-FPM.
Vidente
¡Estaba buscando una solución AH01071: Got error 'Primary script unknown\n'y que el contenedor php-fpm tiene que compartir el mismo directorio con los nodos web fue la solución!
cptPH
23

Como se señaló anteriormente, el problema era que el contenedor fpm no podía ver los archivos. Sin embargo, para compartir datos entre contenedores, el patrón recomendado es utilizar contenedores de solo datos (como se explica en este artículo ).

En pocas palabras: cree un contenedor que solo contenga sus datos, compártalos con un volumen y vincule este volumen en sus aplicaciones con volumes_from.

Usando componer (1.6.2 en mi máquina), el docker-compose.ymlarchivo leería:

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

Tenga en cuenta que datapublica un volumen que está vinculado a los servicios nginxy fpm. Luego, Dockerfilepara el servicio de datos , que contiene su código fuente:

FROM busybox

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

Y Dockerfilepara nginx, que simplemente reemplaza la configuración predeterminada:

FROM nginx

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

En aras de la finalización, aquí está el archivo de configuración necesario para que funcione el ejemplo:

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 simplemente le dice a nginx que use el volumen compartido como raíz del documento, y establece la configuración correcta para que nginx pueda comunicarse con el contenedor fpm (es decir, el derecho HOST:PORT, que es fpm:9000gracias a los nombres de host definidos por compose, y el SCRIPT_FILENAME).

iKanor
fuente
Parece que los datos no se actualizan desde el host a los contenedores, y cuando lo hago, docker ps -a veo que el contenedor de datos se detuvo, ¿es eso un problema?
Aftab Naveed
2
Ese es el comportamiento esperado. Un contenedor de solo datos no ejecuta ningún comando, y simplemente aparecerá como detenido. Además, el Dockerfiledel contenedor de datos está copiando sus fuentes en el contenedor en el momento de la compilación. Es por eso que no se actualizarán si cambia los archivos en el host. Si desea compartir las fuentes entre el host y el contenedor, debe montar el directorio. Cambie el dataservicio en el archivo de redacción para cargar image: busybox, y en la volumessección ingrese ./sources:/var/www/html, donde ./sourcesestá la ruta a sus fuentes en el host.
iKanor
16

Nueva respuesta

Se ha actualizado Docker Compose. Ahora tienen un formato de archivo de la versión 2 .

Los archivos de la versión 2 son compatibles con Compose 1.6.0+ y requieren un motor Docker de la versión 1.10.0+.

Ahora admiten la función de red de Docker, que cuando se ejecuta configura una red predeterminada llamada myapp_default

De su documentación, su archivo se vería como el siguiente:

version: '2'

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

Como estos contenedores se agregan automáticamente a la red predeterminada myapp_default , podrían comunicarse entre sí. Luego tendrías en la configuración de Nginx:

fastcgi_pass fpm:9000;

Además, como lo menciona @treeface en los comentarios, recuerde asegurarse de que PHP-FPM esté escuchando en el puerto 9000, esto se puede hacer editando /etc/php5/fpm/pool.d/www.confdonde lo necesite listen = 9000.

Respuesta antigua

He guardado lo siguiente aquí para aquellos que usan una versión anterior de Docker / Docker compose y quisieran la información.

Seguí tropezando con esta pregunta en Google cuando intentaba encontrar una respuesta a esta pregunta, pero no era exactamente lo que estaba buscando debido al énfasis de Q / A en docker-compose (que en el momento de escribir este artículo solo tiene soporte experimental para funciones de red de Docker). Así que aquí está mi opinión sobre lo que he aprendido.

Docker ha desaprobado recientemente su función de enlace a favor de su función de redes

Por lo tanto, con la función Docker Networks puede vincular contenedores siguiendo estos pasos. Para obtener explicaciones completas sobre las opciones, lea los documentos vinculados anteriormente.

Primero crea tu red

docker network create --driver bridge mynetwork

A continuación, ejecute su contenedor PHP-FPM asegurándose de abrir el puerto 9000 y asignarlo a su nueva red ( mynetwork).

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

Lo importante aquí es el --name php-fpmal final del comando que es el nombre, lo necesitaremos más adelante.

A continuación, ejecute su contenedor Nginx nuevamente y asigne a la red que creó.

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

Para los contenedores PHP y Nginx, también puede agregar --volumes-fromcomandos, etc., según sea necesario.

Ahora viene la configuración de Nginx. Que debería verse algo similar a esto:

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 fastcgi_pass php-fpm:9000;en el bloque de ubicación. Eso es, contacte con el contenedor php-fpmen el puerto 9000. Cuando agrega contenedores a una red de puente de Docker, todos obtienen automáticamente una actualización del archivo de hosts que coloca el nombre del contenedor en su dirección IP. Entonces, cuando Nginx lo vea, sabrá comunicarse con el contenedor PHP-FPM que nombró php-fpmanteriormente y asignó a su mynetworkred Docker.

Puede agregar esa configuración de Nginx durante el proceso de compilación de su contenedor Docker o después, depende de usted.

DavidT
fuente
También recuerde asegurarse de que php-fpmesté escuchando en el puerto 9000. Esto estaría listen = 9000en formato /etc/php5/fpm/pool.d/www.conf.
treeface
Gracias @treeface buen punto. Me he actualizado con tu comentario.
DavidT
8

Como se resolvió en las respuestas anteriores, pero debe indicarse de manera muy explícita: el código php debe vivir en el contenedor php-fpm, mientras que los archivos estáticos deben vivir en el contenedor nginx. Para simplificar, la mayoría de la gente acaba de adjuntar todo el código a ambos, como también he hecho a continuación. En el futuro, probablemente separe estas diferentes partes del código en mis propios proyectos para minimizar qué contenedores tienen acceso a qué partes.

Actualicé mis archivos de ejemplo a continuación con esta última revelación (gracias @alkaline)

Esta parece ser la configuración mínima para docker 2.0 hacia adelante (porque las cosas se volvieron mucho más fáciles en 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

( ACTUALIZADO el docker-compose.yml arriba : para los sitios que tienen css, javascript, archivos estáticos, etc., necesitará que esos archivos sean accesibles para el contenedor nginx. Sin dejar de tener todo el código php accesible para el contenedor fpm. De nuevo, porque mi código base es una mezcla desordenada de css, js y php, este ejemplo solo adjunta todo el código a ambos contenedores)

En la misma carpeta:

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

En el código de carpeta:

./code/index.php:

<?php
phpinfo();

y no olvide actualizar su archivo de hosts:

127.0.0.1 site.local.[YOUR URL].com

y ejecuta tu docker-compose

$docker-compose up -d

y prueba la URL de tu navegador favorito

site.local.[YOUR URL].com/index.php
Phillip
fuente
1
Su archivo de configuración nginx asume que su sitio web solo tiene archivos php. Es una buena práctica crear una regla de ubicación nginx para archivos estáticos (jpg, txt, svg, ...) y evitar el intérprete php. En ese caso, tanto los contenedores nginx como php necesitan acceder a los archivos del sitio web. La respuesta anterior de @iKanor se encarga de eso.
Bernard
Gracias @Alkaline, los archivos estáticos son un problema con mi respuesta original. De hecho, nginx realmente necesita, como mínimo, que los archivos css y js sean locales en esa máquina para que funcione correctamente.
Phillip
7

Creo que también debemos darle el volumen al contenedor fpm, ¿no es así? Entonces =>

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

Si no hago esto, me encuentro con esta excepción al disparar una solicitud, ya que fpm no puede encontrar el archivo solicitado:

[error] 6 # 6: * 4 FastCGI enviado en stderr: "Secuencia de comandos primaria desconocida" mientras se lee el encabezado de respuesta del flujo ascendente, cliente: 172.17.42.1, servidor: localhost, solicitud: "GET / HTTP / 1.1", flujo ascendente: "fastcgi : //172.17.0.81: 9000 ", host:" localhost "

leberknecht
fuente
1
¡Sí, tiene usted razón! Debemos compartir archivos con fpm y nginx
Victor Bocharsky
Tengo un ejemplo de trabajo con Nginxy PHP-FPMen GitHub
Victor Bocharsky
1

Para cualquier otra persona

Error Nginx 403: el índice de directorio de [carpeta] está prohibido

al usar index.phpwhile index.htmlfunciona perfectamente y habiendo incluido index.phpen el índice en el bloque del servidor de la configuración de su sitio ensites-enabled

server {
    listen 80;

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

    index index.php

    ...
}

Asegúrese de que su archivo nginx.conf en /etc/nginx/nginx.confrealmente cargue la configuración de su sitio en el httpbloque ...

http {

    ...

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

    # Load our websites config 
    include /etc/nginx/sites-enabled/*;
}
myol
fuente
gracias por esto, viejo pero aún informativo, tuve que usar / usr / share / nginx / html 👍, gracias
Simon Davies