Как стать автором
Обновить

История создания идеального Docker для Laravel

Уровень сложностиПростой
Время на прочтение13 мин
Количество просмотров9.5K

Казалось бы, упаковать PHP в контейнер и настроить GitHub Actions - дело пяти минут. Но как часто бывает, реальность оказалась сложнее. Это история о том, как я вернулся к разработке на PHP и решал накопившиеся проблемы с деплоем Laravel-проекта. О том, как готовил Docker-образ, несколько раз переписывал процесс деплоя, находил компромиссы там, где это было возможно, и полностью перестраивал архитектуру там, где компромиссы были неприемлемы.

История одного проекта

Всё началось с пет-проекта - чат-бота для спикеров. Проект небольшой: бэкенд для Telegram и админка, ничего сложного. Стек технологий тоже достаточно простой:

  • PHP-FPM 8.4 с Nginx

  • Laravel в качестве основного фреймворка

  • Filament для админ-панели (изначально был Orchid)

  • Redis для сессий, очередей и кэширования

  • Docker и Docker Compose для развертывания

  • OrbStack для локальной разработки

  • GitHub Actions для CI/CD

За время существования проект прошёл через три большие итерации. В первой я набросал базовую логику, задеплоил, появились первые пользователи. Функционала хватало, но управление мероприятиями, основная часть проекта, было неудобным. Когда появилось свободное время, решил написать админку на Orchid (тут небольшое отступление - в последние пару лет я чаще работаю с Go, Ruby и Python). Набросал основные страницы, но времени закончить не хватило. Так проект и жил какое-то время, пока количество пользователей росло.

Когда я наконец вернулся к проекту, посмотрел на свой код свежим взглядом и решил переписать админку на Filament - платформе, о которой в последнее время много слышал. Было интересно и решить накопившиеся проблемы, и попробовать что-то новое. Админку написал быстро, локально всё работало отлично, но, как это часто бывает, на проде она не открылась. И тут началось самое интересное.

Изначально у меня был классический набор из четырёх GitHub Actions workflow - для линтинга, тестирования, сборки Docker-образа и деплоя. В workflow для линтинга использовался Laravel Pint:

lint.yml
name: Laravel Linting  
  
on:  
  push:  
    branches:  
      - main  
  pull_request:  
    branches:  
      - main  
  
jobs:  
  lint:  
    runs-on: ubuntu-24.04  
  
    steps:  
      - name: Checkout repository  
        uses: actions/checkout@v4  
  
      - name: Cache Composer dependencies  
        uses: actions/cache@v4  
        with:  
          path: vendor  
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}  
          restore-keys: ${{ runner.os }}-composer-  
  
      - name: Set up PHP  
        uses: shivammathur/setup-php@v2  
        with:  
          php-version: '8.3'  
          tools: composer  
  
      - name: Install Composer dependencies  
        run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader  
  
      - name: Run Laravel Pint  
        run: composer lint:pint  
  
      - name: Check security vulnerabilities  
        run: composer security-check

Тестирование проводилось с помощью PHPUnit:

test.yml
name: Laravel Testing  
  
on:  
  push:  
    branches:  
      - main  
  pull_request:  
    branches:  
      - main  
  
jobs:  
  test:  
    runs-on: ubuntu-24.04  
    services:  
      postgres:  
        image: postgres:17  
        env:  
          POSTGRES_USER: postgres  
          POSTGRES_PASSWORD: postgres  
          POSTGRES_DB: test  
        ports:  
          - 5432:5432  
        options: >-  
          --health-cmd="pg_isready -U postgres"  
          --health-interval=10s  
          --health-timeout=5s  
          --health-retries=5  
  
    steps:  
      - name: Checkout repository  
        uses: actions/checkout@v4  
  
      - name: Cache Composer dependencies  
        uses: actions/cache@v4  
        with:  
          path: vendor  
          key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}  
          restore-keys: ${{ runner.os }}-composer-  
  
      - name: Set up PHP  
        uses: shivammathur/setup-php@v2  
        with:  
          php-version: '8.3'  
          extensions: mbstring, pdo_pgsql  
          coverage: xdebug  
          tools: composer  
  
      - name: Install Composer dependencies  
        run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader  
  
      - name: Set up application environment  
        run: |  
          cp .env.example .env  
          php artisan key:generate  
          sed -i 's/DB_HOST=pgsql/DB_HOST=127.0.0.1/' .env  
          sed -i 's/DB_DATABASE=app/DB_DATABASE=test/' .env  
          sed -i 's/DB_USERNAME=user/DB_USERNAME=postgres/' .env  
          sed -i 's/DB_PASSWORD=/DB_PASSWORD=postgres/' .env  
  
      - name: Run migrations and seed database  
        run: php artisan migrate --seed  
  
      - name: Run tests  
        run: php artisan test

Сборка образа выполнялась через docker/build-push-action@v3, используя GitHub Container Registry:

build.yml
name: Build and Push Docker Image  
  
on:  
  push:  
    tags:  
      - '*'  
  
jobs:  
  build:  
    runs-on: ubuntu-latest  
  
    steps:  
      - name: Checkout repository  
        uses: actions/checkout@v4  
  
      - name: Set up QEMU  
        uses: docker/setup-qemu-action@v2  
  
      - name: Set up Docker Buildx  
        uses: docker/setup-buildx-action@v2  
  
      - name: Log in to GitHub Container Registry  
        uses: docker/login-action@v2  
        with:  
          registry: ghcr.io  
          username: ${{ github.repository_owner }}  
          password: ${{ secrets.GITHUB_TOKEN }}  
  
      - name: Cache Docker layers  
        uses: actions/cache@v4  
        with:  
          path: /tmp/.buildx-cache  
          key: ${{ runner.os }}-docker-${{ hashFiles('**/deploy/php-fpm/Dockerfile') }}  
          restore-keys: ${{ runner.os }}-docker-  
  
      - name: Build and push Docker image  
        uses: docker/build-push-action@v3  
        with:  
          context: .  
          file: deploy/php-fpm/Dockerfile  
          target: production  
          push: true  
          tags: ghcr.io/${{ github.repository_owner }}/${{ github.repository }}:latest  
          cache-from: type=local,src=/tmp/.buildx-cache  
          cache-to: type=local,dest=/tmp/.buildx-cache-new  
  
      - name: Update Docker cache  
        if: always()  
        run: |  
          rm -rf /tmp/.buildx-cache  
          mv /tmp/.buildx-cache-new /tmp/.buildx-cache

А деплой осуществлялся по SSH с помощью appleboy/ssh-action@v1.0.3:

deploy.yml
name: Deploy to Production  
  
on:  
  push:  
    tags:  
      - '*'  
  
jobs:  
  deploy:  
    runs-on: ubuntu-latest  
    environment: Production  
  
    steps:  
      - name: Deploy to production server via SSH  
        uses: appleboy/ssh-action@v1.0.3  
        with:  
          host: ${{ vars.SERVER_HOST }}  
          username: ${{ vars.SERVER_USER }}  
          key: ${{ secrets.SERVER_SSH_KEY }}  
          script: |  
            echo "${{ secrets.DOCKER_PAT }}" | docker login ghcr.io -u "${{ github.repository_owner }}" --password-stdin  
            cd /var/www/domain.ru 
            docker compose -f docker-compose.prod.yml pull  
            docker compose -f docker-compose.prod.yml up -d --build  
            docker compose -f docker-compose.prod.yml exec app php artisan migrate --force

Dockerfile
FROM php:8.3-fpm-alpine AS base  
  
WORKDIR /app  
  
RUN apk update && apk add --no-cache \  
    git \  
    curl \  
    oniguruma-dev \  
    libxml2-dev \  
    libzip-dev \  
    libpng-dev \  
    libwebp-dev \  
    libjpeg-turbo-dev \  
    freetype-dev \  
    postgresql-dev \  
    libmemcached-dev \  
    zlib-dev \  
    zip \  
    unzip \  
    autoconf \  
    g++ \  
    make \  
    linux-headers \  
    $PHPIZE_DEPS  # PHP build dependencies  
  
RUN rm -rf /var/cache/apk/*  
  
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \  
    && docker-php-ext-install gd \  
    && docker-php-ext-install pdo pdo_pgsql mbstring zip exif pcntl bcmath opcache  
  
RUN pecl install redis memcached && docker-php-ext-enable redis memcached  
  
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer  
  
COPY . /app  
  
RUN cp .env.example .env  
  
RUN chown -R www-data:www-data /app \  
    && chmod -R 777 /app/storage \  
    && chmod -R 775 /app/bootstrap/cache  
  
FROM base AS production  
  
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress  
  
RUN php artisan key:generate  
  
RUN chown -R www-data:www-data /app/vendor \  
    && chmod -R 775 /app/vendor  
  
CMD ["php-fpm"]  
  
FROM base AS development  
  
RUN composer install --optimize-autoloader --no-interaction --no-progress  
  
RUN pecl install xdebug \  
    && docker-php-ext-enable xdebug \  
    && echo "xdebug.mode=debug" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \  
    && echo "xdebug.client_host=${HOST_IP}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \  
    && echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \  
    && echo "xdebug.idekey=PHPSTORM" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini  
  
ENV PHP_IDE_CONFIG="serverName=stage"  
  
RUN chown -R www-data:www-data /app/vendor \  
    && chmod -R 775 /app/vendor  
  
CMD ["php-fpm"]  
  
FROM development AS testing  
  
CMD ["vendor/bin/phpunit"]

Тут классическая multistage сборка, от которой я кайфанул - отдельно для прода, для запуска тестов и для дева с xdebug.

Вот docker-compose (их три под каждое окружение, но они похожи, поэтому для статьи думаю достаточно продовской):

Скрытый текст
services:  
  app:  
    image: ghcr.io/nemirlev/speakerhub-php-bot:latest  
    container_name: app  
    restart: unless-stopped  
    depends_on:  
      - pgsql  
    environment:  
        APP_ENV: production  
        APP_DEBUG: false  
        TELEGRAPH_WEBHOOK_DEBUG: false  
        DB_PASSWORD: ${{ secrets.DB_PASSWORD }}  
    networks:  
      - nginx-proxy  
      - backend  
  webserver:  
    image: nginx:alpine  
    container_name: webserver  
    restart: unless-stopped  
    volumes:  
      - ./deploy/nginx/nginx.stage.conf:/etc/nginx/conf.d/default.conf  
    environment:  
      VIRTUAL_HOST: domain.ru
      VIRTUAL_PORT: 80  
      VIRTUAL_PROTO: http  
      LETSENCRYPT_HOST: domain.ru
      LETSENCRYPT_EMAIL: email  
    depends_on:  
      - app  
    networks:  
      - nginx-proxy  
      - backend  
  pgsql:  
    image: postgres:15  
    container_name: pgsql  
    restart: unless-stopped  
    ports:  
      - "5432:5432"  
    environment:  
      POSTGRES_USER: user  
      POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}  
      POSTGRES_DB: app  
    volumes:  
      - pgdata:/var/lib/postgresql/data  
    networks:  
      - backend     
  redis:  
    image: redis:alpine  
    container_name: redis  
    restart: unless-stopped  
    ports:  
      - "6379:6379"  
    volumes:  
      - redisdata:/data  
    networks:  
      - backend   
  memcached:  
    image: memcached:alpine  
    container_name: memcached  
    restart: unless-stopped  
    ports:  
      - "11211:11211"  
    networks:  
      - backend  
    volumes:  
      - memcacheddata:/data  
volumes:  
  pgdata:  
    driver: local  
  redisdata:  
    driver: local  
  memcacheddata:  
    driver: local  
networks:  
  nginx-proxy:  
    external: true  
  backend:  
    internal: true

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

app:  
    image: ghcr.io/nemirlev/speakerhub-php-bot:latest  
    container_name: app  
    restart: unless-stopped  
    depends_on:  
      - pgsql
    volumes: 
	    - **laravel_app:/app**
    environment:  
        APP_ENV: production  
        APP_DEBUG: false  
        TELEGRAPH_WEBHOOK_DEBUG: false  
        DB_PASSWORD: ${{ secrets.DB_PASSWORD }}  
    networks:  
      - nginx-proxy  
      - backend  
webserver:  
	image: nginx:alpine  
	container_name: webserver  
	restart: unless-stopped  
	volumes:  
	  - ./deploy/nginx/nginx.stage.conf:/etc/nginx/conf.d/default.conf  
	  - **laravel_app:/app**
	environment:  
	  VIRTUAL_HOST: domain.ru
	  VIRTUAL_PORT: 80  
	  VIRTUAL_PROTO: http  
	  LETSENCRYPT_HOST: domain.ru
	  LETSENCRYPT_EMAIL: email  
	depends_on:  
	  - app  
	networks:  
	  - nginx-proxy  
	  - backend  

Но на этом приключения не закончились. При деплое новые изменения почему-то не применялись. Я почти сразу исключил проблемы с кэшем и правами, когда залез в контейнер и не увидел там физически новых файлов. Думал, что проблема в GitHub Actions, объединял все в один workflow, чтобы деплой гарантированно шёл после сборки, менял скрипты. Но потом, когда построчно стал проверять и моделировать влияние каждого действия, понял - проблема в volume. Он просто не обновлялся.

Временным решением стало удаление и пересоздание volume при деплое:

cd /var/www/domain.ru.v2  
%% docker compose down   %%
docker compose pull  
%% docker volume rm domainru_laravel_app   %%
docker compose up -d --build  
docker compose exec app php artisan migrate --force  
docker compose exec app php artisan filament:optimize

Но это было неудобно, да и преимущества volume в данном случае оказались под вопросом - особой разницы с монтированием локальной папки не было. Поэтому я заменил volume на прямое монтирование:

services:
  app:
    volumes: 
      - ./:/app
  webserver:
    volumes:
      - ./deploy/nginx/nginx.stage.conf:/etc/nginx/conf.d/default.conf
      - ./:/app

После этого я решил копнуть глубже и подумать, как это всё сделать более элегантно. Исследование пошло в двух направлениях. Первое - сборка nginx вместе с файлами, но это означало бы необходимость собирать два тяжёлых образа. Второе, более интересное - заменить php-fpm на что-то, что не требовало бы отдельного nginx для работы приложения. После изучения существующих решений я остановился на nginx unit. Рассматривал также Roadrunner, Swoole и FrankenPHP (последний особенно понравился возможностью собирать бинарник), но опасался сложностей с настройкой и необходимостью адаптировать код, о которых писали в интернете. Времени на полноценное тестирование всех вариантов просто не было.

Переход на Nginx Unit оказался удивительно простым - достаточно было заменить базовый образ в Dockerfile и добавить загрузку конфига. Изначально конфигурация unit выглядела так:

{  
    "listeners": {  
        "*:80": {  
            "pass": "routes"
        }  
    },  
    "routes": [  
        {  
            "match": {  
                "uri": "!/index.php"  
            },  
            "action": {  
                "share": "/app/public$uri",  
                "fallback": {  
                    "pass": "applications/laravel"  
                }  
            }  
        }  
    ],  
    "applications": {  
        "laravel": {  
            "type": "php",  
            "root": "/app/public/",  
            "script": "index.php"  
        }  
    }  
}

Приложение стало работать визуально быстрее, хотя специальных замеров я не проводил. Но когда выложил на прод, появились нюансы с HTTPS - браузер блокировал загрузку стилей из-за смешанного контента ([Warning] [blocked] The page at https://site.com/admin/login requested insecure content from http://site.com/css/filament/support/support.css?v=3.2.140.0. This content was blocked and must be served over HTTPS. (login, line 51)). Проблема решилась добавлением правильной обработки заголовков:

{
    "listeners": {
        "*:80": {
            "pass": "routes",
            "forwarded": {
                "protocol": "X-Forwarded-Proto",
                "client_ip": "X-Forwarded-For",
                "source": [
                    "172.19.0.0/16"
                ]
            }
        }
    },
    // остальная конфигурация без изменений
}

Что дальше

Текущий результат меня полностью устраивает, но планов ещё много. Хочу переделать pipeline CI/CD, попробовать уменьшить размер образа хотя бы до 300 мегабайт. В планах также внедрение мониторинга: Victoria Metrics для метрик, Loki для логов и Vector для их сбора. А ещё хочется упаковать все эти наработки в отдельный репозиторий, чтобы было под рукой. Если эта статья вызовет интерес - обязательно напишу продолжение про реализацию этих планов.

Итоговый Dockerfile:
FROM unit:php8.4 AS builder  
  
USER root  
  
RUN apt-get update && apt-get install -y \  
    gcc \  
    make \  
    autoconf \  
    pkg-config \  
    libicu-dev \  
    libpq-dev \  
    libzip-dev \  
    && rm -rf /var/lib/apt/lists/*  
  
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer  
  
WORKDIR /tmp/build  
  
RUN docker-php-ext-install pdo_pgsql opcache intl zip  
  
RUN pecl install redis  
  
RUN pecl install xdebug \  
    && mv /usr/local/lib/php/extensions/*/xdebug.so /tmp/build/xdebug.so  
  
##################################################  
#
##################################################  
FROM builder AS prod-composer  
  
WORKDIR /app  
  
COPY . /app  
  
RUN composer install --no-dev --optimize-autoloader --no-interaction --no-progress  
  
##################################################  
#
##################################################  
FROM builder AS dev-composer  
  
WORKDIR /app  
  
COPY . /app  
  
RUN composer install --optimize-autoloader --no-interaction --no-progress  
  
##################################################  
#
##################################################  
FROM unit:php8.4 AS production  
  
RUN apt-get update && apt-get install -y --no-install-recommends \  
    libpq-dev \  
    libicu-dev \  
    libzip-dev \  
    libfcgi-bin \  
    procps \  
    && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*  
  
ENV PHP_INI_DIR=/usr/local/etc/php  
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" || true  
  
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/  
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/  
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/  
  
RUN docker-php-ext-enable redis  
  
COPY --from=prod-composer /app /app  
  
RUN rm -rf .env  
   
COPY .env.example /app/.env  
  
WORKDIR /app  
  
RUN php artisan config:clear && php artisan cache:clear && php artisan config:cache && php artisan config:cache && php artisan route:cache && php artisan view:cache  
  
RUN chown -R unit:unit /app/public \  
    && chmod -R 755 /app/public \  
    && chown -R unit:unit /app/storage \  
    && chmod -R 775 /app/storage \  
    && chmod -R 775 /app/bootstrap/cache  
  
COPY ./deploy/unit/config.json /docker-entrypoint.d/config.json  
  
EXPOSE 8080  
CMD ["unitd", "--no-daemon"]  
  
  
##################################################  
#
##################################################  
FROM unit:php8.4 AS development  
  
RUN apt-get update && apt-get install -y --no-install-recommends \  
    libpq-dev \  
    libicu-dev \  
    libzip-dev \  
    libfcgi-bin \  
    procps \  
    && apt-get autoremove -y && apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*  
  
ENV PHP_INI_DIR=/usr/local/etc/php  
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" || true  
  
COPY --from=builder /usr/local/lib/php/extensions/ /usr/local/lib/php/extensions/  
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/  
COPY --from=builder /usr/local/bin/docker-php-ext-* /usr/local/bin/  
  
COPY --from=builder /tmp/build/xdebug.so /usr/local/lib/php/extensions/no-debug-non-zts-20240924/xdebug.so  
  
RUN docker-php-ext-enable redis  
   
ARG XDEBUG_ENABLED=true  
ARG XDEBUG_MODE=develop,coverage,debug,profile  
ARG XDEBUG_HOST=host.docker.internal  
ARG XDEBUG_IDE_KEY=PHPSTORM  
ARG XDEBUG_LOG=/dev/stdout  
ARG XDEBUG_LOG_LEVEL=0  
  
RUN if [ "${XDEBUG_ENABLED}" = "true" ]; then \  
    echo "xdebug.mode=${XDEBUG_MODE}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \  
    echo "xdebug.idekey=${XDEBUG_IDE_KEY}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \  
    echo "xdebug.log=${XDEBUG_LOG}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \  
    echo "xdebug.log_level=${XDEBUG_LOG_LEVEL}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini && \  
    echo "xdebug.client_host=${XDEBUG_HOST}" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ; \  
    echo "xdebug.start_with_request=yes" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini ; \  
fi  
  
COPY --from=dev-composer /app /app  
  
COPY ./deploy/unit/config.json /docker-entrypoint.d/config.json  
  
WORKDIR /app  
  
EXPOSE 8080  
CMD ["unitd", "--no-daemon"]

Github Action:
name: Build, Lint, Test, and Deploy  
  
on:  
  push:  
    tags:  
      - '*'  
  
jobs:  
  lint:  
    runs-on: ubuntu-latest  
    continue-on-error: true 
  
    steps:  
      - name: Checkout repository  
        uses: actions/checkout@v4  
  
      - name: Set up PHP  
        uses: shivammathur/setup-php@v2  
        with:  
          php-version: '8.4'  
  
      - name: Install dependencies  
        run: composer install --no-interaction --prefer-dist --no-progress  
  
      - name: Run Linter (PHPStan)  
        run: vendor/bin/phpstan analyse --memory-limit=1G  
  
  test:  
    runs-on: ubuntu-latest  
    continue-on-error: true  
  
    steps:  
      - name: Checkout repository  
        uses: actions/checkout@v4  
  
      - name: Set up PHP  
        uses: shivammathur/setup-php@v2  
        with:  
          php-version: '8.4'  
  
      - name: Install dependencies  
        run: composer install --no-interaction --prefer-dist --no-progress  
  
      - name: Generate key  
        run: php artisan key:generate  
  
      - name: Run Tests  
        run: vendor/bin/phpunit --testdox  
  
  deploy:  
    runs-on: ubuntu-latest  
    needs: [ lint, test ] 
  
    environment: Production  
  
    steps:  
      - name: Deploy to Production Server  
        uses: appleboy/ssh-action@v1.0.3  
        with:  
          host: ${{ vars.SERVER_HOST }}  
          username: ${{ vars.SERVER_USER }}  
          key: ${{ secrets.SERVER_SSH_KEY }}  
          script: |  
            cd /var/www/domain.ru.v2  
            docker compose pull  
            docker compose up -d --build  
            docker compose exec app php artisan migrate --force  
            docker compose exec app php artisan filament:optimize

Docker compose:
services:  
    app:  
        build:  
            context: .  
            dockerfile: deploy/unit/Dockerfile  
            target: production  
        container_name: app  
        restart: unless-stopped  
        depends_on:  
            - pgsql  
        environment:  
            DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
            VIRTUAL_HOST: domain.ru 
            VIRTUAL_PORT: 80  
            VIRTUAL_PROTO: http  
            LETSENCRYPT_HOST: domain.ru 
            LETSENCRYPT_EMAIL: email@me.ru 
        networks:  
            - nginx-proxy  
            - backend  
    pgsql:  
        image: postgres:17  
        container_name: pgsql  
        restart: unless-stopped  
        environment:  
            POSTGRES_USER: speakerhub  
            POSTGRES_DB: speakerhub  
            POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
        volumes:  
            - pgdata:/var/lib/postgresql/data  
        networks:  
            - backend  
            - nginx-proxy  
    redis:  
        image: redis:alpine  
        container_name: redis  
        restart: unless-stopped  
        volumes:  
            - redisdata:/data  
        networks:  
            - backend  
            - nginx-proxy  
    memcached:  
        image: memcached:alpine  
        container_name: memcached  
        restart: unless-stopped  
        networks:  
            - backend  
        volumes:  
            - memcacheddata:/data  
volumes:  
    pgdata:  
        driver: local  
    redisdata:  
        driver: local  
    memcacheddata:  
        driver: local  
networks:  
    nginx-proxy:  
        external: true  
    backend:  
        internal: true

Nginx unit conf
{  
    "listeners": {  
        "*:80": {  
            "pass": "routes",  
            "forwarded": {  
                "protocol": "X-Forwarded-Proto",  
                "client_ip": "X-Forwarded-For",  
                "source": [  
                    "172.19.0.0/16"  
                ]  
            }  
        }  
    },  
    "routes": [  
        {  
            "match": {  
                "uri": "!/index.php"  
            },  
            "action": {  
                "share": "/app/public$uri",  
                "fallback": {  
                    "pass": "applications/laravel"  
                }  
            }  
        }  
    ],  
    "applications": {  
        "laravel": {  
            "type": "php",  
            "root": "/app/public/",  
            "script": "index.php"  
        }  
    }  
}

Если остались вопросы или хотите обсудить тему подробнее — пишите в комментариях и подписывайтесь на мой канал в телеграмме.

Теги:
Хабы:
Всего голосов 13: ↑12 и ↓1+13
Комментарии13

Публикации

Работа

DevOps инженер
29 вакансий
PHP программист
83 вакансии

Ближайшие события