We detected you are likely not from a Russian-speaking region. Would you like to switch to the international version of the site?

К списку статей

Продвинутая CI/CD: настройка пайплайнов для Laravel и Django с Docker, GitHub Actions и динамическими окружениями

23 янв 2026 в 00:30 Усачёв Денис Евгеньевич

Современные web‑проекты требуют быстрых и надёжных процессов доставки кода. В статье рассматривается построение продвинутого CI/CD для двух популярных стеков: Laravel (PHP) и Django (Python). Мы используем Docker для изоляции окружений, GitHub Actions как оркестратор и динамические среды (preview‑environments) для тестирования изменений перед продакшеном.

Общая архитектура пайплайна

Схема выглядит так:

  • Исходный код хранится в репозитории GitHub.
  • Для каждого коммита запускается GitHub Actions workflow.
  • Workflow собирает Docker‑образ, прогоняет тесты и, при успешном результате, деплоит в staging‑окружение.
  • Для pull‑request создаётся preview‑environment – временный контейнер, доступный по уникальному URL.
  • После мержа в main происходит деплой в продакшен.

Docker‑конфигурация

Ниже приведён базовый docker-compose.yml, который подходит для обоих стеков, используя отдельные сервисы для веб‑приложения и базы данных.

version: "3.8"
services:
  # Laravel service
  laravel:
    build:
      context: ./laravel
      dockerfile: Dockerfile
    container_name: laravel_app
    env_file: ./laravel/.env
    ports:
      - "8000:80"
    depends_on:
      - db

  # Django service
  django:
    build:
      context: ./django
      dockerfile: Dockerfile
    container_name: django_app
    env_file: ./django/.env
    ports:
      - "8001:8000"
    depends_on:
      - db

  # Общая база данных
  db:
    image: postgres:15-alpine
    container_name: postgres_db
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: app_db
    volumes:
      - db_data:/var/lib/postgresql/data

volumes:
  db_data:

Dockerfile для Laravel

FROM php:8.1-fpm

# Установка зависимостей
RUN apt-get update && apt-get install -y \
    git \
    unzip \
    libpq-dev \
    && docker-php-ext-install pdo pdo_pgsql

# Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

WORKDIR /var/www/html
COPY . .

RUN composer install --no-interaction --prefer-dist --optimize-autoloader

EXPOSE 80
CMD ["php-fpm"]

Dockerfile для Django

FROM python:3.11-slim

# Установка зависимостей
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]

GitHub Actions: Laravel pipeline

name: Laravel CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build-test-deploy:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15-alpine
        env:
          POSTGRES_USER: user
          POSTGRES_PASSWORD: password
          POSTGRES_DB: app_db
        ports: [5432:5432]
        options: >-
          --health-cmd "pg_isready -U user"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to DockerHub (optional)
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Build Laravel image
        run: |
          docker build -t myorg/laravel:${{ github.sha }} ./laravel

      - name: Run tests
        run: |
          docker run --rm \
            -e DB_CONNECTION=pgsql \
            -e DB_HOST=host.docker.internal \
            -e DB_PORT=5432 \
            -e DB_DATABASE=app_db \
            -e DB_USERNAME=user \
            -e DB_PASSWORD=password \
            myorg/laravel:${{ github.sha }} php artisan test

      - name: Push image to registry
        if: github.ref == 'refs/heads/main'
        run: |
          docker push myorg/laravel:${{ github.sha }}

      - name: Deploy to Staging (Docker Swarm / Kubernetes)
        if: github.ref == 'refs/heads/main'
        env:
          IMAGE_TAG: ${{ github.sha }}
        run: |
          # Пример для Kubernetes
          kubectl set image deployment/laravel-deploy laravel=myorg/laravel:${IMAGE_TAG} --record

      - name: Create Preview Environment (for PR)
        if: github.event_name == 'pull_request'
        env:
          PREVIEW_TAG: preview-${{ github.event.pull_request.number }}
        run: |
          docker tag myorg/laravel:${{ github.sha }} myorg/laravel:${PREVIEW_TAG}
          docker push myorg/laravel:${PREVIEW_TAG}
          # Деплой в отдельный namespace
          kubectl create namespace preview-${{ github.event.pull_request.number }} || true
          kubectl set image deployment/laravel-deploy laravel=myorg/laravel:${PREVIEW_TAG} -n preview-${{ github.event.pull_request.number }} --record

GitHub Actions: Django pipeline

name: Django CI/CD

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  build-test-deploy:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:15-alpine
        env:
          POSTGRES_USER: user
          POSTGRES_PASSWORD: password
          POSTGRES_DB: app_db
        ports: [5432:5432]
        options: >-
          --health-cmd "pg_isready -U user"
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r ./django/requirements.txt

      - name: Build Docker image
        run: |
          docker build -t myorg/django:${{ github.sha }} ./django

      - name: Run tests
        run: |
          docker run --rm \
            -e DATABASE_URL=postgres://user:password@host.docker.internal:5432/app_db \
            myorg/django:${{ github.sha }} python manage.py test

      - name: Push image to registry
        if: github.ref == 'refs/heads/main'
        run: |
          docker push myorg/django:${{ github.sha }}

      - name: Deploy to Staging
        if: github.ref == 'refs/heads/main'
        env:
          IMAGE_TAG: ${{ github.sha }}
        run: |
          kubectl set image deployment/django-deploy django=myorg/django:${IMAGE_TAG} --record

      - name: Create Preview Environment (for PR)
        if: github.event_name == 'pull_request'
        env:
          PREVIEW_TAG: preview-${{ github.event.pull_request.number }}
        run: |
          docker tag myorg/django:${{ github.sha }} myorg/django:${PREVIEW_TAG}
          docker push myorg/django:${PREVIEW_TAG}
          kubectl create namespace preview-${{ github.event.pull_request.number }} || true
          kubectl set image deployment/django-deploy django=myorg/django:${PREVIEW_TAG} -n preview-${{ github.event.pull_request.number }} --record

Динамические preview‑окружения

Для каждого pull‑request создаётся отдельный namespace (Kubernetes) или отдельный стек в Docker‑Swarm. Это позволяет QA‑инженерам и дизайнерам проверять изменения в изолированном окружении без влияния на основную ветку.

  • URL формируется автоматически, например https://pr-12.myproject.dev.
  • Окружения автоматически уничтожаются через 24 ч или после закрытия PR.
  • Для управления сроком жизни можно использовать kubectl delete namespace в отдельном workflow, триггерящем по событию pull_request.closed.

Лучшие практики

  • Секреты в GitHub Secrets – храните токены Docker Hub, креденшалы БД и SSH‑ключи.
  • Кеширование зависимостей – используйте Docker layer caching и actions/cache для pip/composer.
  • Тесты на уровне контейнеров – запускать их в том же образе, что и продакшен, чтобы избежать «works on my machine».
  • Версионирование образов – тегировать как commit‑sha, branch‑name и latest при необходимости.
  • Мониторинг и алерты – интегрировать с Sentry, Prometheus или GitHub Checks для мгновенного фидбэка.

Заключение

Сочетание Docker, GitHub Actions и динамических preview‑окружений позволяет построить полностью автоматизированный, масштабируемый и безопасный процесс доставки кода как для Laravel, так и для Django. Такой пайплайн ускоряет выпуск новых функций, повышает качество продукта и снижает риск ошибок в продакшене.

Компания RybinskLab предлагает профессиональные услуги по разработке, настройке CI/CD, контейнеризации и поддержке инфраструктуры под ключ. Свяжитесь с нами, чтобы вывести ваш проект на новый уровень автоматизации.

* Материал подготовлен с использованием ИИ-ассистента, проверен и отредактирован экспертом RybinskLab.

Поделиться материалом:

Нужна сложная разработка?

Усачёв Денис Евгеньевич — проектирование архитектуры, бэкенд на PHP/Python, интеграции API и базы данных.

Обсудить проект