Comment lier correctement les conteneurs PHP-FPM et Nginx Docker?

103

J'essaye de lier 2 conteneurs séparés:

Le problème est que les scripts php ne fonctionnent pas. Peut-être que la configuration de php-fpm est incorrecte. Voici le code source, qui se trouve dans mon référentiel . Voici le fichier docker-compose.yml:

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

et Dockerfileque j'ai utilisé pour créer une image personnalisée basée sur celle de nginx:

FROM nginx

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

Enfin, voici ma configuration d'hôte virtuel Nginx personnalisée:

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

Quelqu'un pourrait-il m'aider à configurer correctement ces conteneurs pour exécuter des scripts php?

PS Je lance des conteneurs via docker-composer comme ceci:

docker-compose up

à partir du répertoire racine du projet.

Victor Bocharsky
la source
1
Comment avez-vous essayé de les configurer jusqu'à présent ou quel code avez-vous utilisé. S'il vous plaît, ne me faites pas deviner que je ne suis pas du genre à deviner.
Matthew Brown aka Lord Matt
1
@MatthewBrown Huh, j'ai mis mon code dans un référentiel public sur GitHub et je pense que cela suffira, mais vous avez raison, mieux vaut montrer le code ici dans ma question aussi.
Victor Bocharsky
lorsque les images tournent, pouvez-vous docker execentrer dans le conteneur en cours d'exécution et envoyer un ping à fpm?
Vincent De Smet
1
@MatthewBrown oui, je l'ai gagné, merci
Victor Bocharsky
1
PS J'ai également réalisé une solution de travail pour lier Nginxet PHP-FPM avec Vagrant et Ansible. Vérifiez mon dépôt github.com/bocharsky-bw/vagrant-ansible-docker si vous le souhaitez.
Victor Bocharsky

Réponses:

32

Ne codez pas en dur l'IP des conteneurs dans la configuration nginx, le lien docker ajoute le nom d'hôte de la machine liée au fichier d'hôtes du conteneur et vous devriez pouvoir envoyer une requête ping par nom d'hôte.

EDIT: Docker 1.9 Networking ne vous oblige plus à lier des conteneurs, lorsque plusieurs conteneurs sont connectés au même réseau, leur fichier d'hôtes sera mis à jour afin qu'ils puissent se joindre par nom d'hôte.

Chaque fois qu'un conteneur docker tourne à partir d'une image (même arrêter / démarrer un conteneur existant), les conteneurs reçoivent de nouvelles adresses IP attribuées par l'hôte docker. Ces adresses IP ne sont pas dans le même sous-réseau que vos machines réelles.

voir docker liant des documents (c'est ce que compose utilise en arrière-plan)

mais plus clairement expliqué dans la docker-composedocumentation sur les liens et exposer

liens

links:
 - db
 - db:database
 - redis

Une entrée avec le nom de l'alias sera créée dans / etc / hosts à l'intérieur des conteneurs de ce service, par exemple:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

exposer

Exposez les ports sans les publier sur la machine hôte - ils ne seront accessibles qu'aux services liés . Seul le port interne peut être spécifié.

et si vous configurez votre projet pour obtenir les ports + d'autres informations d'identification via des variables d'environnement, les liens définissent automatiquement un ensemble de variables système :

Pour voir quelles variables d'environnement sont disponibles pour un service, exécutez docker-compose run SERVICE env.

name_PORT

URL complète, par exemple DB_PORT = tcp: //172.17.0.5: 5432

name_PORT_num_protocol

URL complète, par exemple DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

Adresse IP du conteneur, par exemple DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

Numéro de port exposé, par exemple DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

Protocole (tcp ou udp), par exemple DB_PORT_5432_TCP_PROTO=tcp

name_NAME

Nom du conteneur complet, par exemple DB_1_NAME=/myapp_web_1/myapp_db_1

Vincent De Smet
la source
2
vous n'avez pas non plus besoin de publier le port 9000 sur l'hôte, les ports sont ouverts entre les conteneurs docker liés, sauf si vous souhaitez dépanner le port directement à partir de votre hôte.
Vincent De Smet
Oui, vous avez raison, merci. Dans mon cas, je devrais utiliser fastcgi_pass fpm: 9000 au lieu de direct ip. Je ne sais pas que Docker l'ajoute automatiquement à l'hôte, mon mauvais.
Victor Bocharsky
Qu'en est-il du port, il vaut donc mieux utiliser expose au lieu de ports ? Ou je ne pourrais utiliser aucun de ces ports et exposer des directives parce que les conteneurs liés auront accès à ce port?
Victor Bocharsky
désolé pour la réponse tardive - je pense que vous devrez peut-être utiliser exposer, désolé je ne peux pas vérifier pour le moment
Vincent De Smet
2
--linkssont désormais obsolètes, selon la documentation du docker que vous référencez. Ils sont toujours actuellement pris en charge, mais le plan apparent est qu'ils soient obsolètes.
therobyouknow
86

Je sais que c'est un peu un vieux message, mais j'ai eu le même problème et je ne pouvais pas comprendre pourquoi votre code ne fonctionnait pas. Après BEAUCOUP de tests, j'ai découvert pourquoi.

Il semble que fpm reçoive le chemin complet de nginx et essaie de trouver les fichiers dans le conteneur fpm, il doit donc être exactement le même que server.rootdans la configuration nginx, même s'il n'existe pas dans le conteneur nginx.

Démontrer:

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
la source
4
BIEN JOUÉ!!! C'est exactement le point! J'ai défini la racine nginx sur un chemin alternatif autre qu'en cas /var/www/htmld'échec.
Alfred Huang
3
Aussi, juste une note qui :9000est le port qui est utilisé dans le conteneur et non celui qui est exposé à votre hôte. Il m'a fallu 2 heures pour comprendre cela. J'espère que vous n'êtes pas obligé.
shriek le
1
services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]
030
4
Vous n'avez pas du tout besoin d'inclure une portssection ici. Vous pouvez simplement en avoir besoin exposes'il n'est pas déjà dans l'image (ce qui est probablement le cas). Si vous faites une communication inter-conteneurs, vous ne devriez pas exposer le port PHP-FPM.
Seer
Je cherchais une solution pour AH01071: Got error 'Primary script unknown\n'et que le conteneur php-fpm doit partager le même répertoire avec les nœuds Web était la solution!
cptPH
23

Comme indiqué précédemment, le problème était que les fichiers n'étaient pas visibles par le conteneur fpm. Toutefois, pour partager des données entre des conteneurs, le modèle recommandé utilise des conteneurs de données uniquement (comme expliqué dans cet article ).

En bref: créez un conteneur qui ne contient que vos données, partagez-le avec un volume et liez ce volume dans vos applications avec volumes_from.

En utilisant compose (1.6.2 sur ma machine), le docker-compose.ymlfichier se lirait:

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

Notez que datapublie un volume lié aux services nginxet fpm. Ensuite, le Dockerfilepour le service de données , qui contient votre code source:

FROM busybox

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

Et le Dockerfilepour nginx, qui remplace simplement la configuration par défaut:

FROM nginx

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

Par souci de finalisation, voici le fichier de configuration requis pour que l'exemple fonctionne:

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

qui dit simplement à nginx d'utiliser le volume partagé comme racine du document, et définit la bonne configuration pour que nginx puisse communiquer avec le conteneur fpm (c'est-à-dire: le droit HOST:PORT, qui est fpm:9000grâce aux noms d'hôte définis par compose, et le SCRIPT_FILENAME).

iKanor
la source
On dirait que les données ne sont pas mises à jour de l'hôte vers les conteneurs, et lorsque je fais docker ps -a je vois que le conteneur de données est arrêté, est-ce un problème?
Aftab Naveed le
2
C'est le comportement attendu. Un conteneur contenant uniquement des données n'exécute aucune commande et sera simplement répertorié comme arrêté. En outre, le Dockerfileconteneur de données copie vos sources dans le conteneur au moment de la génération. C'est pourquoi ils ne seront pas mis à jour si vous modifiez les fichiers dans l'hôte. Si vous souhaitez partager les sources entre l'hôte et le conteneur, vous devez monter le répertoire. Changez le dataservice dans le fichier de composition à charger image: busybox, et dans la volumessection entrez ./sources:/var/www/html, où ./sourcesest le chemin de vos sources dans l'hôte.
iKanor
16

Nouvelle réponse

Docker Compose a été mis à jour. Ils ont maintenant un format de fichier version 2 .

Les fichiers de la version 2 sont pris en charge par Compose 1.6.0+ et nécessitent un moteur Docker de la version 1.10.0+.

Ils prennent désormais en charge la fonctionnalité de mise en réseau de Docker qui, lorsqu'elle est exécutée, configure un réseau par défaut appelé myapp_default

D'après leur documentation, votre fichier ressemblerait à ceci:

version: '2'

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

Comme ces conteneurs sont automatiquement ajoutés au réseau myapp_default par défaut , ils pourraient se parler. Vous auriez alors dans la configuration Nginx:

fastcgi_pass fpm:9000;

Aussi, comme mentionné par @treeface dans les commentaires, n'oubliez pas de vous assurer que PHP-FPM écoute sur le port 9000, cela peut être fait en éditant /etc/php5/fpm/pool.d/www.conflà où vous en aurez besoin listen = 9000.

Ancienne réponse

J'ai gardé ce qui suit ici pour ceux qui utilisent une ancienne version de Docker / Docker compose et aimeraient les informations.

Je n'arrêtais pas de tomber sur cette question sur Google en essayant de trouver une réponse à cette question, mais ce n'était pas tout à fait ce que je cherchais en raison de l'accent mis sur les questions / réponses sur docker-compose (qui au moment de la rédaction n'a qu'un support expérimental pour fonctionnalités de mise en réseau docker). Voici donc ma vision de ce que j'ai appris.

Docker a récemment abandonné sa fonction de lien au profit de sa fonction de réseaux

Par conséquent, en utilisant la fonctionnalité Docker Networks, vous pouvez lier des conteneurs en suivant ces étapes. Pour des explications complètes sur les options, lisez les documents liés précédemment.

Créez d'abord votre réseau

docker network create --driver bridge mynetwork

Exécutez ensuite votre conteneur PHP-FPM en vous assurant d'ouvrir le port 9000 et de l'assigner à votre nouveau réseau ( mynetwork).

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

Le bit important ici est le --name php-fpmà la fin de la commande qui est le nom, nous en aurons besoin plus tard.

Ensuite, exécutez à nouveau votre conteneur Nginx pour l'attribuer au réseau que vous avez créé.

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

Pour les conteneurs PHP et Nginx, vous pouvez également ajouter des --volumes-fromcommandes, etc. au besoin.

Vient maintenant la configuration Nginx. Ce qui devrait ressembler à ceci:

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

Notez le fastcgi_pass php-fpm:9000;dans le bloc d'emplacement. C'est dire conteneur de contact php-fpmsur le port 9000. Lorsque vous ajoutez des conteneurs à un réseau de pont Docker, ils reçoivent tous automatiquement une mise à jour du fichier d'hôtes qui place leur nom de conteneur par rapport à leur adresse IP. Ainsi, lorsque Nginx verra qu'il saura contacter le conteneur PHP-FPM que vous avez nommé php-fpmprécédemment et attribué à votre mynetworkréseau Docker.

Vous pouvez ajouter cette configuration Nginx soit pendant le processus de construction de votre conteneur Docker, soit après cela dépend de vous.

DavidT
la source
N'oubliez pas également de vous assurer que vous php-fpmécoutez sur le port 9000. Ce serait listen = 9000en /etc/php5/fpm/pool.d/www.conf.
treeface
Merci @treeface bon point. J'ai mis à jour avec votre commentaire.
DavidT
8

Comme les réponses précédentes l'ont résolu, mais cela devrait être déclaré très explicitement: le code php doit vivre dans le conteneur php-fpm, tandis que les fichiers statiques doivent vivre dans le conteneur nginx. Pour plus de simplicité, la plupart des gens viennent de joindre tout le code aux deux, comme je l'ai également fait ci-dessous. Dans le futur, je séparerai probablement ces différentes parties du code dans mes propres projets afin de minimiser quels conteneurs ont accès à quelles parties.

J'ai mis à jour mes fichiers d'exemple ci-dessous avec cette dernière révélation (merci @alkaline)

Cela semble être la configuration minimale pour docker 2.0 avant (car les choses sont devenues beaucoup plus faciles dans 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

( MISE À JOUR du docker-compose.yml ci-dessus : pour les sites qui ont des fichiers css, javascript, statiques, etc., vous aurez besoin de ces fichiers accessibles au conteneur nginx. Tout en ayant toujours tout le code php accessible au conteneur fpm. Encore une fois, car mon code de base est un mélange désordonné de css, js et php, cet exemple attache simplement tout le code aux deux conteneurs)

Dans le même dossier:

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

Dans le code du dossier:

./code/index.php:

<?php
phpinfo();

et n'oubliez pas de mettre à jour votre fichier hosts:

127.0.0.1 site.local.[YOUR URL].com

et lancez votre docker-compose up

$docker-compose up -d

et essayez l'URL de votre navigateur préféré

site.local.[YOUR URL].com/index.php
Phillip
la source
1
Votre fichier de configuration nginx suppose que votre site Web ne contient que des fichiers php. Il est recommandé de créer une règle de localisation nginx pour les fichiers statiques (jpg, txt, svg, ...) et d'éviter l'interpréteur php. Dans ce cas, les conteneurs nginx et php doivent accéder aux fichiers du site Web. La réponse de @iKanor ci-dessus s'occupe de cela.
Bernard
Merci @Alkaline, les fichiers statiques posent problème avec ma réponse originale. En fait, nginx a vraiment besoin, au minimum, que les fichiers css et js soient locaux sur cette machine pour fonctionner correctement.
Phillip
7

Je pense que nous devons également donner le volume au conteneur fpm, n'est-ce pas? Donc =>

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

Si je ne fais pas cela, je rencontre cette exception lors du lancement d'une demande, car fpm ne peut pas trouver le fichier demandé:

[erreur] 6 # 6: * 4 FastCGI envoyé dans stderr: "Script principal inconnu" lors de la lecture de l'en-tête de réponse en amont, client: 172.17.42.1, serveur: localhost, requête: "GET / HTTP / 1.1", en amont: "fastcgi : //172.17.0.81: 9000 ", hôte:" localhost "

Leberknecht
la source
1
Oui, tu as raison! Nous devons partager des fichiers avec fpm et nginx
Victor Bocharsky
J'ai un exemple de travail avec Nginxet PHP-FPMsur GitHub
Victor Bocharsky
1

Pour quiconque obtient

Erreur Nginx 403: l'index de répertoire de [dossier] est interdit

lors de l'utilisation de index.phpwhile index.htmlfonctionne parfaitement et après avoir inclus index.phpdans l'index dans le bloc serveur de leur configuration de site danssites-enabled

server {
    listen 80;

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

    index index.php

    ...
}

Assurez-vous que votre fichier nginx.conf /etc/nginx/nginx.confcharge réellement la configuration de votre site dans le httpbloc ...

http {

    ...

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

    # Load our websites config 
    include /etc/nginx/sites-enabled/*;
}
myol
la source
merci pour cela, vieux mais toujours informatif, j'ai dû utiliser / usr / share / nginx / html 👍, merci
Simon Davies