Задать вопрос

Как запустить php и nginx в одном контейнере?

Есть у меня 2 докерфайла, которые я назвал Dockerfile1 и Dockerfile2 (да, с креативностью у меня так себе).

Вы первый разместил php и собственно приложение, и назвал его qwerty1 (про свою креативность я уже писал выше), собственно, вот он:
FROM php:8.2-fpm

RUN apt-get update \
 && apt-get install -y libpq-dev \
 && apt-get install -y libzip-dev zip

COPY ./app /var/www/app

WORKDIR /var/www/app


Приложение же состоит из одного файлика index.php и выводит текст "Hello World!".

Ну и собираю его командой
docker build --file Dockerfile1 --tag 'qwerty1' .

Во втором докерфайле у меня nginx, собственно вот его содержание
FROM qwerty1

RUN apt-get update \
 && apt-get install -y nginx

COPY ./nginx/vhost.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

STOPSIGNAL SIGTERM

CMD ["nginx", "-g", "daemon off;"]


А конфиг nginx выглядит так
server {
    listen 80;
    root /var/www/app;
    index index.php;

    location / {
        try_files $uri /$uri /index.php?$query_string;
    }

    location ~ [^/]\.php(/|$) {
        fastcgi_pass 127.0.0.1:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_split_path_info ^(.+\.php)(/.*)$;
        fastcgi_param HTTPS off;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param APPLICATION_ENV local;
    }

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;
}


Собирается аналогичным образом и запускается как run --name qwerty -p 8080:80 qwerty2

Контейнер запускается, но показывает дефолтную страницу nginx, а при явном запросе файла index.php отдаёт его на скачивание. В общем как-то они не находят друг друга, как их подружить в рамках одного контейнера?
  • Вопрос задан
  • 986 просмотров
Подписаться 4 Простой 2 комментария
Решения вопроса 3
@humoured
Вы всё на свете найдёте в коробке с карандашами
PHP и Nginx незачем запускать в одном контейнере. Что бы контейнеры были связаны друг с другом, нужно их объединять в docker-compose.yml.
Ответ написан
saboteur_kiev
@saboteur_kiev
software engineer
Приложение же состоит из одного файлика index.php и выводит текст "Hello World!".


php не умеет выводить текст в браузер. Это значит что в первом докерфайле уже устанавливается какой-то веб сервер, возможно httpd

можно конечно просто установку nginx-а в первый докерфайл перенести, но надо разобраьтся как его состыковать с php-fpm.

А еще проще найти готовый. связка nginx с php достаточно стандартная сборка, уверен есть официальный образ, еще и не один от разных вендоров.
Ответ написан
@MadridianFox
Web-программист, многостаночник
1) Запуск нескольких процессов в одном контейнере противоречит концепции докера. Докер сделан так, чтобы запускать один процесс (не считая дочерних процессов)

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

Проще всего с загружаемыми файлами. Они в любом случае не должны храниться в контейнере и скорее всего это будет volume, который монтируется в оба контейнера.
Взаимодействие между контейнерами по сети тоже штука не сложная, но зависит от способа запуска контейнера.
Если контейнеры запущены через docker run в одной сети (поведение по умолчанию), то они видят друг друга по сети и могут использовать имя контейнера как dns имя.
Если два контейнера запущены в рамках одного docker-compose.yml файла (это ещё называется docker compose project), то они тоже друг друга видят, но уже не по имени контейнера, а по имени сервиса (ключ под которым контейнер указан в секции services).
Если приложение запускается в kubernetes, то там по умолчанию все контейнера pod'a имеют общий локалхост и могут обращаться к друг другу через 127.0.0.1.

Всё это - поведение по умолчанию, которое можно изменить явно указывая какую сеть использовать, какой hostname должен быть у контейнера и т.д.

Важно: nginx работает с dns в обход стандартных инструментов операционной системы. Если вы используете в директивах proxy_pass, fastcgi_pass доменное имя, а не ip адрес, вам нужно дополнительно указать в конфиге директиву resolver <dns-ip>;, где в качестве нужно указать ip адрес dns сервера.
Для докера это обычно 127.0.0.11, для кубера это адрес внутреннего dns сервера.

Раздавать статику можно по разному. Два концептуально разных способа:
- собрать образ nginx в котором есть статика, тут можно либо наследовать образ как сделали это вы, либо копировать статику в чистый образ nginx, что несколько чище и красивее
- убрать статику из образа вообще, выделив её в volume или cdn, но тут придётся при отгрузке сервиса актуализировать статику во внешнем хранилище

2) если очень хочется запустить несколько процессов в контейнере, то нужно запустить один процесс, который запустит остальные. Есть такие программы - менеджеры процессов. Это может быть supervosord, pm2, runit и т.п.
Это такая программа, основной задачей которой является запуск других программ. Обычно у неё есть свой файл конфигурации, в котором вы описываете какие программы запустить и как это сделать.
Ещё раз повторю - докер придуман не для этого. Не стоит делать этот приём основным при работе с докером.
Это костыль. Он иногда нужен, но очень редко.

3) То что nginx отдаёт код index.php, это проблема не настройки контейнеров, а конфигурации nginx. Если бы проблема была только в контейнерах, то вы бы получили не код скрипта, а 502.
Первое что бросается в глаза в вашем конфиге - это регулярка в локейшене для обработки php скриптов.
Попробуйте сделать как здесь.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы
YCLIENTS Москва
от 350 000 до 400 000 ₽
Viletech Москва
от 70 000 до 120 000 ₽
Data World Москва
от 180 000 до 210 000 ₽