Jak poprawnie połączyć kontenery php-fpm i Nginx Docker?

103

Próbuję połączyć 2 oddzielne kontenery:

Problem w tym, że skrypty php nie działają. Być może konfiguracja php-fpm jest nieprawidłowa. Oto kod źródłowy, który jest w moim repozytorium . Oto plik docker-compose.yml:

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

i Dockerfilektórego użyłem do zbudowania niestandardowego obrazu na podstawie nginx:

FROM nginx

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

Na koniec, oto moja niestandardowa konfiguracja wirtualnego hosta 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;
    }
}

Czy ktoś mógłby mi pomóc poprawnie skonfigurować te kontenery do wykonywania skryptów php?

PS Kontenery uruchamiam przez docker-composer w ten sposób:

docker-compose up

z katalogu głównego projektu.

Victor Bocharsky
źródło
1
Jak do tej pory próbowałeś je skonfigurować lub jakiego kodu użyłeś. Proszę, nie każ mi zgadywać, że zgadywanie jest śmieciem.
Matthew Brown aka Lord Matt
1
@MatthewBrown Huh, umieściłem swój kod w publicznym repozytorium na GitHubie i myślę, że to wystarczy, ale masz rację, lepiej też pokazać kod tutaj w moim pytaniu.
Victor Bocharsky
kiedy obrazy się obracają, czy możesz docker execprzejść do uruchomionego kontenera i pingować fpm?
Vincent De Smet
1
@MatthewBrown tak, wygrałem, dzięki
Victor Bocharsky
1
PS Osiągnąłem również rozwiązanie robocze, które połączyłem Nginxi PHP-FPM razem z Vagrant i Ansible. Sprawdź moje repozytorium github.com/bocharsky-bw/vagrant-ansible-docker, jeśli chcesz.
Victor Bocharsky

Odpowiedzi:

32

Nie koduj na stałe adresu IP kontenerów w konfiguracji nginx, link docker dodaje nazwę hosta połączonej maszyny do pliku hosts kontenera i powinieneś być w stanie pingować według nazwy hosta.

EDYCJA: Docker 1.9 Networking nie wymaga już łączenia kontenerów, gdy wiele kontenerów jest podłączonych do tej samej sieci, ich plik hosts zostanie zaktualizowany, aby mogły się ze sobą łączyć za pomocą nazwy hosta.

Za każdym razem, gdy kontener docker obraca się z obrazu (nawet zatrzymuje / uruchamia istniejący kontener), kontenery otrzymują nowe adresy IP przypisane przez hosta dockera. Te adresy IP nie znajdują się w tej samej podsieci, co rzeczywiste komputery.

zobacz dokumenty z linkami do dockera (tego używa redagowanie w tle)

ale dokładniej wyjaśniono w docker-composedokumentach na temat linków i ekspozycji

spinki do mankietów

links:
 - db
 - db:database
 - redis

Wpis z nazwą aliasu zostanie utworzony w / etc / hosts w kontenerach dla tej usługi, np .:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

expose

Udostępniaj porty bez publikowania ich na komputerze-hoście - będą dostępne tylko dla połączonych usług . Można określić tylko port wewnętrzny.

a jeśli skonfigurujesz swój projekt, aby uzyskać porty i inne dane uwierzytelniające za pośrednictwem zmiennych środowiskowych, łącza automatycznie ustawią kilka zmiennych systemowych :

Aby zobaczyć, jakie zmienne środowiskowe są dostępne dla usługi, uruchom docker-compose run SERVICE env.

name_PORT

Pełny adres URL, np. DB_PORT = tcp: //172.17.0.5: 5432

name_PORT_num_protocol

Pełny adres URL, np DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

Adres IP kontenera, np DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

Widoczny numer portu, np DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

Protokół (tcp lub udp), np DB_PORT_5432_TCP_PROTO=tcp

name_NAME

W pełni kwalifikowana nazwa kontenera, np DB_1_NAME=/myapp_web_1/myapp_db_1

Vincent De Smet
źródło
2
nie musisz także publikować portu 9000 na hoście, porty są otwarte między połączonymi kontenerami docker, chyba że chcesz rozwiązać problem z portem bezpośrednio z hosta.
Vincent De Smet
Tak, masz rację, dzięki. W moim przypadku powinienem użyć fastcgi_pass fpm: 9000 zamiast bezpośredniego adresu IP. Nie wiem, że Docker automatycznie dodaje go do hosta, moja wina.
Victor Bocharsky
A co z portem, więc lepiej używać expose zamiast portów ? Lub nie mogłem użyć żadnego z tych portów i udostępnić dyrektywy, ponieważ połączone kontenery będą miały dostęp do tego portu?
Victor Bocharsky
przepraszam za spóźnioną odpowiedź - myślę, że może być konieczne użycie expose, przepraszam, nie mogę teraz sprawdzić
Vincent De Smet
2
--linkssą teraz przestarzałe, zgodnie z dokumentacją dockera, do której się odnosisz. Są one nadal aktualnie obsługiwane, ale pozorny plan jest dla nich być zastąpiony.
therobyouknow
86

Wiem, że to dość stary post, ale miałem ten sam problem i nie mogłem zrozumieć, dlaczego twój kod nie działa. Po wielu testach dowiedziałem się, dlaczego.

Wygląda na to, że fpm otrzymuje pełną ścieżkę z nginx i próbuje znaleźć pliki w kontenerze fpm, więc musi być dokładnie taka sama jak server.rootw konfiguracji nginx, nawet jeśli nie istnieje w kontenerze nginx.

Aby zademonstrować:

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
źródło
4
DOBRA ROBOTA!!! Dokładnie o to chodzi! Ustawiłem katalog główny nginx na ścieżkę alternatywną inną niż /var/www/htmlz niepowodzeniem.
Alfred Huang
3
Ponadto wystarczy uwaga, że :9000jest to port używany w kontenerze, a nie ten, który jest widoczny dla twojego hosta. Zajęło mi to 2 godziny, zanim to rozgryzłem. Miejmy nadzieję, że nie musisz.
krzyk
1
services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]
030
4
W rzeczywistości nie musisz tutaj wcale uwzględniać portssekcji. Możesz go po prostu potrzebować expose, jeśli nie ma go już na obrazie (a prawdopodobnie tak jest). Jeśli wykonujesz komunikację między kontenerami, nie powinieneś ujawniać portu PHP-FPM.
Seer
Szukałem rozwiązania dla AH01071: Got error 'Primary script unknown\n'i że kontener php-fpm musi dzielić ten sam katalog z węzłami sieciowymi, było rozwiązaniem!
cptPH
23

Jak wspomniano wcześniej, problem polegał na tym, że pliki nie były widoczne przez kontener fpm. Jednak do udostępniania danych między kontenerami zalecany wzorzec używa kontenerów tylko do danych (jak wyjaśniono w tym artykule ).

Krótko mówiąc: utwórz kontener, który po prostu przechowuje Twoje dane, udostępnij go woluminowi i połącz ten wolumin w swoich aplikacjach z volumes_from.

Używając redagowania (1.6.2 na moim komputerze), docker-compose.ymlplik miałby postać:

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

Zwróć uwagę, że datapublikuje wolumin połączony z usługami nginxi fpm. Następnie Dockerfileza dane usługi, która zawiera kod źródłowy:

FROM busybox

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

A Dockerfiledla nginx, który po prostu zastępuje domyślną konfigurację:

FROM nginx

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

Ze względu na zakończenie, oto plik konfiguracyjny wymagany do działania przykładu:

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

który po prostu mówi nginx, aby używał współdzielonego woluminu jako katalogu głównego dokumentu i ustawia odpowiednią konfigurację dla nginx, aby móc komunikować się z kontenerem fpm (tj: prawo HOST:PORT, które jest fpm:9000dzięki nazwom hostów zdefiniowanym przez compose i SCRIPT_FILENAME).

iKanor
źródło
Wygląda na to, że dane nie są aktualizowane z hosta do kontenerów, a kiedy robię docker ps -a, widzę, że kontener danych został zatrzymany, czy to problem?
Aftab Naveed
2
To oczekiwane zachowanie. Kontener zawierający tylko dane nie uruchamia żadnego polecenia i będzie po prostu wyświetlany jako zatrzymany. Ponadto Dockerfilekontener danych kopiuje źródła do kontenera w czasie kompilacji. Dlatego nie zostaną zaktualizowane, jeśli zmienisz pliki na hoście. Jeśli chcesz udostępniać źródła między hostem a kontenerem, musisz zamontować katalog. Zmień datausługę w pliku redagowania do załadowania image: busybox, aw volumessekcji wprowadź ./sources:/var/www/html, gdzie ./sourcesjest ścieżka do źródeł na hoście.
iKanor,
16

Nowa odpowiedź

Docker Compose został zaktualizowany. Mają teraz format pliku w wersji 2 .

Pliki w wersji 2 są obsługiwane przez Compose 1.6.0+ i wymagają Docker Engine w wersji 1.10.0+.

Obsługują teraz funkcję sieciową Dockera, która po uruchomieniu konfiguruje domyślną sieć o nazwie myapp_default

Z ich dokumentacji Twój plik wyglądałby jak poniżej:

version: '2'

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

Ponieważ te kontenery są automatycznie dodawane do domyślnej sieci myapp_default , będą mogły ze sobą rozmawiać. Miałbyś wtedy w konfiguracji Nginx:

fastcgi_pass fpm:9000;

Jak wspomniał @treeface w komentarzach, pamiętaj, aby upewnić się, że PHP-FPM nasłuchuje na porcie 9000, można to zrobić edytując /etc/php5/fpm/pool.d/www.conftam, gdzie będzie to potrzebne listen = 9000.

Stara odpowiedź

Zachowałem poniżej tutaj dla tych, którzy używają starszej wersji tworzenia Docker / Docker i chcą uzyskać informacje.

Ciągle natknąłem się na to pytanie w Google, próbując znaleźć odpowiedź na to pytanie, ale nie było to do końca to, czego szukałem, ze względu na nacisk Q / A na docker-compose (który w czasie pisania ma tylko eksperymentalne wsparcie dla funkcje sieciowe Dockera). Oto moje spojrzenie na to, czego się nauczyłem.

Docker niedawno wycofał funkcję łącza na rzecz funkcji sieci

Dlatego za pomocą funkcji Docker Networks można łączyć kontenery, wykonując następujące kroki. Aby uzyskać pełne wyjaśnienia dotyczące opcji, przeczytaj wcześniej powiązane dokumenty.

Najpierw utwórz swoją sieć

docker network create --driver bridge mynetwork

Następnie uruchom kontener PHP-FPM, otwierając port 9000 i przypisując go do nowej sieci ( mynetwork).

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

Ważny jest tutaj --name php-fpmkoniec polecenia, którym jest nazwa, będziemy potrzebować później.

Następnie ponownie uruchom kontener Nginx i przypisz go do utworzonej sieci.

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

W przypadku kontenerów PHP i Nginx możesz również dodać --volumes-frompolecenia itp. Zgodnie z wymaganiami.

Teraz nadchodzi konfiguracja Nginx. Który powinien wyglądać podobnie do tego:

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

Zwróć uwagę na fastcgi_pass php-fpm:9000;w bloku lokalizacji. To mówi kontener kontaktowy php-fpmw porcie 9000. Po dodaniu kontenerów do sieci mostu Docker wszystkie one automatycznie otrzymują aktualizację pliku hostów, która umieszcza nazwę kontenera w stosunku do adresu IP. Więc kiedy Nginx zobaczy, że będzie wiedział, jak skontaktować się z kontenerem PHP-FPM, który nazwałeś php-fpmwcześniej i przypisałeś do swojej mynetworksieci Docker.

Możesz dodać tę konfigurację Nginx podczas procesu budowania kontenera Docker lub później, zależy to od Ciebie.

DavidT
źródło
Pamiętaj również, aby upewnić się, że php-fpmnasłuchuje na porcie 9000. To będzie listen = 9000w /etc/php5/fpm/pool.d/www.conf.
treeface
Dzięki @treeface dobra uwaga. Zaktualizowałem Twój komentarz.
DavidT
8

Tak jak poprzednie odpowiedzi rozwiązały, ale należy to wyraźnie określić: kod php musi znajdować się w kontenerze php-fpm, podczas gdy pliki statyczne muszą znajdować się w kontenerze nginx. Dla uproszczenia większość ludzi właśnie dołączyła cały kod do obu, tak jak to zrobiłem poniżej. Jeśli w przyszłości prawdopodobnie wyodrębnię te różne części kodu we własnych projektach, aby zminimalizować, które kontenery mają dostęp do których części.

Zaktualizowałem moje przykładowe pliki poniżej o najnowsze objawienie (dziękuję @alkaline)

Wydaje się, że jest to minimalna konfiguracja dla Dockera 2.0 do przodu (ponieważ w Docker 2.0 jest dużo łatwiej)

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

( ZAKTUALIZOWANO powyższy docker-compose.yml : w przypadku witryn, które mają css, javascript, pliki statyczne itp., Będziesz potrzebować tych plików dostępnych dla kontenera nginx. Cały kod php będzie nadal dostępny dla kontenera fpm. Ponownie, ponieważ mój kod podstawowy to niechlujna mieszanka css, js i php, ten przykład po prostu dołącza cały kod do obu kontenerów)

W tym samym folderze:

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

W kodzie folderu:

./code/index.php:

<?php
phpinfo();

i nie zapomnij zaktualizować pliku hosts:

127.0.0.1 site.local.[YOUR URL].com

i uruchom docker-compose

$docker-compose up -d

i wypróbuj adres URL ze swojej ulubionej przeglądarki

site.local.[YOUR URL].com/index.php
Phillip
źródło
1
Twój plik konfiguracyjny nginx zakłada, że ​​Twoja witryna ma tylko pliki php. Najlepszą praktyką jest utworzenie reguły lokalizacji nginx dla plików statycznych (jpg, txt, svg, ...) i unikanie interpretera php. W takim przypadku zarówno kontenery nginx, jak i php wymagają dostępu do plików witryny. Powyższa odpowiedź @iKanor rozwiązuje ten problem.
Bernard
Dzięki @Alkaline, pliki statyczne to problem z moją pierwotną odpowiedzią. W rzeczywistości nginx naprawdę potrzebuje co najmniej plików css i js, aby były lokalne dla tej maszyny, aby działać poprawnie.
Phillip,
7

Myślę, że musimy również nadać pojemnikowi fpm objętość, prawda? Więc =>

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

Jeśli tego nie zrobię, napotkam ten wyjątek podczas uruchamiania żądania, ponieważ fpm nie może znaleźć żądanego pliku:

[błąd] 6 # 6: * 4 FastCGI wysłane w stderr: „Nieznany skrypt podstawowy” podczas odczytu nagłówka odpowiedzi z nadawcy, klient: 172.17.42.1, serwer: localhost, żądanie: „GET / HTTP / 1.1”, nadrzędne: „fastcgi : //172.17.0.81: 9000 ", host:" localhost "

leberknecht
źródło
1
Tak masz rację! Musimy udostępniać pliki fpm i nginx
Victor Bocharsky
Mam przykład z pracy Nginxi PHP-FPMna GitHub
Victor Bocharsky
1

Dla kogokolwiek innego

Błąd Nginx 403: indeks katalogu [folder] jest zabroniony

podczas używania index.phpwhile index.htmldziała doskonale i po uwzględnieniu index.phpw indeksie w bloku serwera konfiguracji witryny w formaciesites-enabled

server {
    listen 80;

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

    index index.php

    ...
}

Upewnij się, że plik nginx.conf w /etc/nginx/nginx.confrzeczywistości ładuje konfigurację witryny w httpbloku ...

http {

    ...

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

    # Load our websites config 
    include /etc/nginx/sites-enabled/*;
}
myol
źródło
dzięki za to, stare, ale wciąż pouczające, musiałem użyć / usr / share / nginx / html 👍, dzięki
Simon Davies