Как правильно связать контейнеры 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
Как вы до сих пор пытались их настроить или какой код вы использовали. Пожалуйста, не заставляйте меня угадывать, что я - чушь.
Мэтью Браун, он же лорд Мэтт,
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 link добавляет имя хоста связанного компьютера в файл hosts контейнера, и вы должны иметь возможность пинговать по имени хоста.

РЕДАКТИРОВАТЬ: Docker 1.9 Networking больше не требует связывать контейнеры, когда несколько контейнеров подключены к одной сети, их файл хостов будет обновлен, чтобы они могли связываться друг с другом по имени хоста.

Каждый раз, когда док-контейнер запускается из образа (даже при остановке / запуске существующего контейнера), контейнеры получают новый IP-адрес, назначенный хост-докером. Эти IP-адреса не находятся в той же подсети, что и ваши фактические машины.

см. докеры, связывающие документы (это то, что compose использует в фоновом режиме)

но более четко объяснено в 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 добавит его в host автоматически, плохо.
Виктор Бочарский
А как насчет порта, так что лучше использовать expose вместо портов ? Или я не мог использовать какой-либо из этих портов и выставлять директивы, потому что связанные контейнеры будут иметь доступ к этому порту?
Виктор Бочарский
извините за поздний ответ - я думаю, вам может понадобиться использовать expose, извините, я не могу проверить прямо сейчас
Винсент Де Смет
2
--linksтеперь устарели, согласно документу докеров, на который вы ссылаетесь. В настоящее время они все еще поддерживаются, но очевидный план состоит в том, чтобы они устарели.
therobyouknow
85

Я знаю, что это старый добрый пост, но у меня была такая же проблема, и я не мог понять, почему ваш код не работает. После МНОГО испытаний я понял, почему.

Похоже, что 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;
    }
}

Dockerfile

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.
Seer
Искал решение, 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).

iKanor
источник
Похоже, данные не обновляются с хоста до контейнеров, и когда я делаю docker ps -a, я вижу, что контейнер данных остановлен, это проблема?
Афтаб Навид
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, пытаясь найти ответ на этот вопрос, но это было не совсем то, что я искал из-за акцента в вопросах и ответах на 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, все они автоматически получают обновление файла hosts, которое помещает их имя контейнера в соответствие с их IP-адресом. Поэтому, когда Nginx увидит, что он будет знать, что нужно связаться с контейнером PHP-FPM, который вы назвали php-fpmранее и назначили вашей mynetworkсети Docker.

Вы можете добавить эту конфигурацию Nginx либо во время процесса сборки вашего контейнера Docker, либо после этого решать вам.

DavidT
источник
Также не забудьте убедиться, что php-fpmпрослушивает порт 9000. Это будет listen = 9000в /etc/php5/fpm/pool.d/www.conf.
treeface
Спасибо, @treeface, хорошее замечание. Я обновил ваш комментарий.
DavidT
8

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

Обновлены мои файлы примеров ниже этим последним откровением (спасибо @alkaline)

Кажется, это минимальная настройка для docker 2.0 forward (потому что в 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();

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

127.0.0.1 site.local.[YOUR URL].com

и запустите свой docker-compose up

$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 "

Leberknecht
источник
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 👍, спасибо
Саймон Дэвис