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

Как запустить 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 отдаёт его на скачивание. В общем как-то они не находят друг друга, как их подружить в рамках одного контейнера?
  • Вопрос задан
  • 871 просмотр
Подписаться 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 скриптов.
Попробуйте сделать как здесь.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

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

Похожие вопросы