Як правильно зв’язати контейнери php-fpm та Nginx Docker?


103

Я намагаюся зв'язати 2 окремі контейнери:

Проблема в тому, що php-скрипти не працюють. Можливо, конфігурація php-fpm неправильна. Ось вихідний код, який міститься у моєму сховищі . Ось файл docker-compose.yml:

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

і Dockerfileякий я використовував для створення власного зображення на основі nginx:

FROM nginx

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

Нарешті, ось моя власна конфігурація віртуального хосту 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;
    }
}

Хтось може допомогти мені правильно налаштувати ці контейнери для виконання php-скриптів?

PS Я запускаю контейнери через docker-composer так:

docker-compose up

з кореневого каталогу проекту.


1
Як ви намагалися їх налаштувати дотепер або який код ви використовували? Будь ласка, не змушуйте мене здогадуватися, що я сміття вгадувати.
Matthew Brown aka Lord Matt

1
@MatthewBrown Так, я розмістив свій код у загальнодоступному сховищі на GitHub і думаю, що цього буде достатньо, але ти маєш рацію, краще показати код тут у своєму питанні.
Віктор Бочарський

коли зображення обертаються, чи можете ви docker execперейти в запущений контейнер і пінгувати fpm?
Вінсент Де Смет

1
@MatthewBrown так, я виграв, дякую
Віктор Бочарський

1
PS Я також досягнув робочого рішення для зв'язку Nginxта PHP-FPM разом з Vagrant та Ansible. Перевірте мій репо github.com/bocharsky-bw/vagrant-ansible-docker, якщо хочете.
Віктор Бочарський

Відповіді:


32

Не жорстко кодуйте ip контейнерів у конфігурації nginx, посилання docker додає ім'я хосту пов'язаної машини до файлу хостів контейнера, і ви повинні мати можливість пінгувати за іменем хосту.

EDIT: Docker 1.9 Мережа більше не вимагає зв’язування контейнерів, коли кілька контейнерів під’єднано до однієї мережі, файл їхніх хостів буде оновлений, щоб вони могли зв’язати один одного за іменем хосту.

Щоразу, коли контейнер докера обертається із зображення (навіть зупиняючи / запускаючи існуючий контейнер), контейнери отримують нові ip-адреси, призначені хостом докера. Ці IP-адреси знаходяться не в тій самій підмережі, що і ваші фактичні машини.

перегляньте документ, що зв’язує документи (це те, що компонування використовується у фоновому режимі)

але більш чітко пояснюється в docker-composeдокументації на посилання та викриття

посилання

links:
 - db
 - db:database
 - redis

Запис з псевдонімом буде створений в / etc / hosts усередині контейнерів для цієї послуги, наприклад:

172.17.2.186  db
172.17.2.186  database
172.17.2.187  redis

викривати

Виставляйте порти, не публікуючи їх на хост-машині - вони будуть доступні лише для пов’язаних служб . Можна вказати лише внутрішній порт.

і якщо ви налаштуєте свій проект на отримання портів + ​​інших облікових даних за допомогою змінних середовища, посилання автоматично встановлюють купу системних змінних :

Щоб побачити, які змінні середовища доступні для служби, запустіть docker-compose run SERVICE env.

name_PORT

Повна URL-адреса, наприклад DB_PORT = tcp: //172.17.0.5: 5432

name_PORT_num_protocol

Повна URL-адреса, напр DB_PORT_5432_TCP=tcp://172.17.0.5:5432

name_PORT_num_protocol_ADDR

IP-адреса контейнера, напр DB_PORT_5432_TCP_ADDR=172.17.0.5

name_PORT_num_protocol_PORT

Викритий номер порту, напр DB_PORT_5432_TCP_PORT=5432

name_PORT_num_protocol_PROTO

Протокол (tcp або udp), напр DB_PORT_5432_TCP_PROTO=tcp

name_NAME

Повна назва контейнера, напр DB_1_NAME=/myapp_web_1/myapp_db_1


2
вам також не потрібно публікувати порт 9000 на хості, порти відкриті між зв’язаними контейнерами докера, якщо ви не хочете вирішити проблему з портом безпосередньо з вашого хосту.
Вінсент Де Смет

Так, ти маєш рацію, дякую. У моєму випадку я повинен використовувати fastcgi_pass fpm: 9000 замість прямого ip. Я не знаю, що Docker автоматично додає його до хостингу, на жаль.
Віктор Бочарський,

А як щодо порту, тож краще використовувати expose замість портів ? Або я не міг використовувати жоден із цих портів і виставляти директиви, оскільки пов'язані контейнери матимуть доступ до цього порту?
Віктор Бочарський,

вибачте за пізню відповідь - я думаю, вам може знадобитися використати викриття, вибачте, я не можу зараз перевірити
Вінсент Де Смет

2
--linksтепер застаріли, згідно з документацією докера, на яку ви посилаєтесь. Наразі вони все ще підтримуються, але очевидний план полягає в тому, щоб вони застаріли.
therobyouknow

86

Я знаю, що це старовинна публікація, але у мене була та сама проблема, і я не міг зрозуміти, чому ваш код не працював. Після БАГАТО тестів я з’ясував, чому.

Здається, fpm отримує повний шлях від nginx і намагається знайти файли в контейнері fpm, тому він повинен бути точно таким же, як server.rootу конфігурації nginx, навіть якщо він не існує в контейнері nginx.

Демонструвати:

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

Докерфайл

FROM nginx:latest
COPY ./default.conf /etc/nginx/conf.d/

4
МОЛОДО !!! Саме в цьому річ! Я встановив корінь nginx на альтернативний шлях, відмінний від /var/www/htmlпомилки.
Альфред Хуанг

3
Крім того, просто зауважте, що :9000порт, який використовується в контейнері, а не той, який піддається вашому хосту. Мені знадобилося 2 години, щоб це зрозуміти. Сподіваємось, вам не потрібно.
вереск

1
services.fpm.ports is invalid: Invalid port ":9000", should be [[remote_ip:]remote_port[-remote_port]:]port[/protocol]
030

4
Вам насправді не потрібно взагалі включати тут portsрозділ. Можливо, вам це просто знадобиться, exposeякщо його ще немає на зображенні (що воно, ймовірно, є). Якщо ви здійснюєте міжконтейнерний зв’язок, вам не слід виставляти порт PHP-FPM.
Провидця

AH01071: Got error 'Primary script unknown\n'Шукав рішення і те, що контейнер php-fpm повинен спільно використовувати той самий каталог з веб-вузлами, це рішення!
cptPH

23

Як зазначалося раніше, проблема полягала в тому, що файли не були видимі контейнером fpm. Однак для обміну даними між контейнерами рекомендованим шаблоном є використання контейнерів, що містять лише дані (як пояснено в цій статті ).

Коротше кажучи: створіть контейнер, який просто вміщує ваші дані, поділіться ними з томом і зв’яжіть цей том у своїх програмах volumes_from.

За допомогою compose (1.6.2 на моїй машині) docker-compose.ymlфайл буде читати:

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

Зверніть увагу, що dataпублікується том, пов’язаний із послугами nginxта fpm. Тоді Dockerfileдля служби даних , що містить ваш вихідний код:

FROM busybox

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

І Dockerfileдля nginx, який просто замінює конфігурацію за замовчуванням:

FROM nginx

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

Для завершення ось конфігураційний файл, необхідний для роботи прикладу:

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

який просто повідомляє nginx використовувати спільний том як корінь документа та встановлює правильну конфігурацію для nginx, щоб мати можливість спілкуватися з контейнером fpm (тобто: правою HOST:PORT, що fpm:9000завдяки іменам хостів, визначеним compose, і SCRIPT_FILENAME).


Схоже, дані не отримують оновлення від хосту до контейнерів, і коли я роблю docker ps -a, я бачу, що контейнер даних зупинений, це проблема?
Aftab Naveed

2
Це очікувана поведінка. Контейнер, який містить лише дані, не запускає жодної команди, і він просто буде перерахований як зупинений. Крім того, Dockerfileконтейнер даних копіює ваші джерела до контейнера під час побудови. Ось чому вони не будуть оновлені, якщо ви зміните файли на хості. Якщо ви хочете поділитися джерелами між хостом і контейнером, вам потрібно змонтувати каталог. Змініть dataслужбу у файлі створення для завантаження image: busyboxта в volumesрозділі введіть ./sources:/var/www/html, де ./sourcesє шлях до ваших джерел на хості.
iKanor

16

Нова відповідь

Docker Compose оновлено. Тепер вони мають формат файлу версії 2 .

Файли версії 2 підтримуються Compose 1.6.0+ і вимагають Docker Engine версії 1.10.0+.

Тепер вони підтримують мережеву функцію Docker, яка під час запуску встановлює мережу за замовчуванням під назвою myapp_default

З їхньої документації ваш файл буде виглядати приблизно так:

version: '2'

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

Оскільки ці контейнери автоматично додаються до мережі myapp_default за замовчуванням, вони зможуть спілкуватися між собою. Тоді ви мали б у конфігурації Nginx:

fastcgi_pass fpm:9000;

Також, як згадував @treeface у коментарях, пам’ятайте, щоб PHP-FPM прослуховував порт 9000, це можна зробити, відредагувавши /etc/php5/fpm/pool.d/www.confтам, де вам потрібно listen = 9000.

Стара відповідь

Я згадав нижче для тих, хто використовує стару версію Docker / Docker compose, і хотів би отримати інформацію.

Я постійно натрапляв на це запитання в google, намагаючись знайти відповідь на це запитання, але це було не зовсім те, що я шукав через акценти на Q / A на docker-compose (який на момент написання статті має лише експериментальну підтримку для функції мережевих докерів). Тож ось мій погляд на те, що я дізнався.

Docker нещодавно припинив свою функцію посилання на користь функції мережі

Тому за допомогою функції Docker Networks ви можете зв’язати контейнери, виконавши ці кроки. Повні пояснення щодо варіантів читайте в документах, зв’язаних раніше.

Спочатку створіть свою мережу

docker network create --driver bridge mynetwork

Потім запустіть контейнер PHP-FPM, переконавшись, що ви відкрили порт 9000 і призначили новій мережі ( mynetwork).

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

Важливим бітом тут є --name php-fpmкінець команди, що є ім'ям, це нам знадобиться пізніше.

Потім запустіть свій контейнер Nginx знову, призначивши створену мережу.

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

Для контейнерів PHP та Nginx ви також можете додавати --volumes-fromкоманди тощо, якщо потрібно.

Тепер настає конфігурація Nginx. Що повинно виглядати приблизно так:

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

Зверніть увагу на fastcgi_pass php-fpm:9000;в блоці розташування. Це говорить про контактний контейнер php-fpmу порту 9000. Коли ви додаєте контейнери до мережі мостів Docker, всі вони автоматично отримують оновлення файлу хостів, яке вказує ім'я контейнера проти їх IP-адреси. Отже, коли Nginx побачить, що він знатиме, що повинен зв’язатись із контейнером PHP-FPM, який ви назвали php-fpmраніше та призначили вашій mynetworkмережі Docker.

Ви можете додати цю конфігурацію Nginx або під час процесу збірки вашого контейнера Docker, або пізніше - до вас.


Також не забудьте переконатися, що php-fpmпрослуховуєте порт 9000. Це було б listen = 9000у /etc/php5/fpm/pool.d/www.conf.
treeface

Дякую @treeface хороший момент. Я оновив ваш коментар.
DavidT

8

Як і попередні відповіді вирішили, але їх слід чітко вказати: php-код повинен знаходитись у контейнері php-fpm, тоді як статичні файли повинні знаходитись у контейнері nginx. Для простоти більшість людей щойно прикріпили весь код до обох, як я це зробив нижче. Якщо в майбутньому, я, ймовірно, виділю ці різні частини коду у своїх власних проектах, щоб мінімізувати, які контейнери мають доступ до яких частин.

Оновив мої прикладні файли нижче цим останнім відкриттям (дякую @alkaline)

Це, здається, мінімальне налаштування для docker 2.0 вперед (оскільки в 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

( ОНОВЛЕНО docker-compose.yml вище : для сайтів, які мають css, javascript, статичні файли тощо, вам знадобляться ці файли, доступні для контейнера nginx. Хоча при цьому весь php-код доступний для контейнера fpm. Знову тому, тому що мій базовий код - це брудна суміш css, js та php, цей приклад просто додає весь код до обох контейнерів)

У цій же папці:

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

У коді папки:

./code/index.php:

<?php
phpinfo();

і не забудьте оновити файл хостів:

127.0.0.1 site.local.[YOUR URL].com

і запустіть ваш Docker-Compose

$docker-compose up -d

і спробуйте URL із улюбленого браузера

site.local.[YOUR URL].com/index.php

1
Ваш конфігураційний файл nginx передбачає, що на вашому веб-сайті є лише php-файли. Це найкраща практика - створити правило розташування nginx для статичних файлів (jpg, txt, svg, ...) та уникати інтерпретатора php. У цьому випадку як контейнери nginx, так і php потребують доступу до файлів веб-сайту. Відповідь @iKanor вище подбає про це.
Бернард

Дякую @Alkaline, статичні файли - це проблема моєї оригінальної відповіді. Насправді, для належної роботи nginx дійсно потрібно, щоб файли css та js були локальними для цієї машини.
Філіп

7

Я думаю, нам також потрібно надати обсягу контейнеру fpm, чи не так? Отже =>

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

Якщо я цього не роблю, я стикаюся з цим винятком під час запуску запиту, оскільки fpm не може знайти потрібний файл:

[помилка] 6 # 6: * 4 FastCGI надіслано в stderr: "Первинний сценарій невідомий" під час читання заголовка відповіді з потоку, клієнт: 172.17.42.1, сервер: localhost, запит: "GET / HTTP / 1.1", вище: "fastcgi : //172.17.0.81: 9000 ", хост:" localhost "


1
Так, ти маєш рацію! Ми повинні ділитися файлами з fpm та nginx
Віктор Бочарський,

У мене є приклад роботи з GitHubNginx і PHP-FPMна ньому
Віктор Бочарський

1

Для тих, хто ще отримує

Помилка Nginx 403: індекс каталогу [папка] заборонений

при використанні index.phpwhile index.htmlпрацює ідеально і включивши index.phpв індекс в серверному блоці конфігурацію свого сайту вsites-enabled

server {
    listen 80;

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

    index index.php

    ...
}

Переконайтеся, що файл nginx.conf /etc/nginx/nginx.confфактично завантажує конфігурацію вашого сайту в httpблок ...

http {

    ...

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

    # Load our websites config 
    include /etc/nginx/sites-enabled/*;
}

дякую за це, старий, але все ж інформативний, мені довелося використовувати / usr / share / nginx / html 👍, дякую
Саймон Девіс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.