Настройка окружения разработчика: Docker, Git, CI/CD

За 10+ лет работы я настраивал рабочее окружение сотни раз — от простых WordPress-проектов до сложных корпоративных систем на Laravel. И честно говоря, правильная настройка окружения экономит месяцы времени в будущем.

Почему Docker изменил мою жизнь разработчика

Я помню времена, когда настройка нового проекта занимала целый день. XAMPP, локальные базы данных, конфликты версий PHP — это был настоящий ад. У меня был клиент с проектом на PHP 7.4, а другой требовал PHP 8.1. Постоянные переключения версий через Homebrew на macOS превращались в рутину.

Docker решил все эти проблемы одним махом. Сейчас я могу запустить любой проект с нужным окружением за 2 минуты. На моей практике Docker показал себя как абсолютно необходимый инструмент для современной разработки.

Основные преимущества Docker в веб-разработке:

Грубо говоря, если вы до сих пор не используете Docker — вы теряете кучу времени впустую. Я настраиваю Docker Compose для каждого проекта, даже для простых лендингов на WordPress.

💡
Совет из практики: Создайте базовые Docker Compose файлы для WordPress, Laravel и Bitrix. Это сэкономит часы при старте новых проектов.

Настройка Docker Compose для веб-проектов

Я использую стандартную связку: nginx, PHP-FPM, MySQL, Redis. Вот мой базовый docker-compose.yml для Laravel-проектов, который работает безотказно:

version: '3.8'

services:
  nginx:
    image: nginx:1.25-alpine
    container_name: ${APP_NAME}-nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./:/var/www/html
      - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./docker/nginx/sites/:/etc/nginx/sites-available/
      - ./docker/nginx/ssl/:/etc/ssl/
    depends_on:
      - php
    networks:
      - laravel

  php:
    build:
      context: ./docker/php
      dockerfile: Dockerfile
    container_name: ${APP_NAME}-php
    volumes:
      - ./:/var/www/html
      - ./docker/php/local.ini:/usr/local/etc/php/conf.d/local.ini
    networks:
      - laravel
    depends_on:
      - mysql
      - redis

  mysql:
    image: mysql:8.0
    container_name: ${APP_NAME}-mysql
    restart: unless-stopped
    tty: true
    ports:
      - "3306:3306"
    environment:
      MYSQL_DATABASE: ${DB_DATABASE}
      MYSQL_USER: ${DB_USERNAME}
      MYSQL_PASSWORD: ${DB_PASSWORD}
      MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
    volumes:
      - ./docker/mysql/data:/var/lib/mysql
      - ./docker/mysql/my.cnf:/etc/mysql/my.cnf
    networks:
      - laravel

  redis:
    image: redis:7-alpine
    container_name: ${APP_NAME}-redis
    restart: unless-stopped
    ports:
      - "6379:6379"
    networks:
      - laravel

networks:
  laravel:
    driver: bridge

volumes:
  mysql_data:
    driver: local

Для PHP-FPM я собираю свой образ с нужными расширениями. Вот Dockerfile, который использую для большинства проектов:

FROM php:8.2-fpm

# Устанавливаем системные зависимости
RUN apt-get update && apt-get install -y \
    git \
    curl \
    libpng-dev \
    libonig-dev \
    libxml2-dev \
    libzip-dev \
    zip \
    unzip \
    nodejs \
    npm

# Устанавливаем расширения PHP
RUN docker-php-ext-install \
    pdo_mysql \
    mbstring \
    exif \
    pcntl \
    bcmath \
    gd \
    zip

# Устанавливаем Redis расширение
RUN pecl install redis && docker-php-ext-enable redis

# Устанавливаем Composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer

# Создаём пользователя
RUN groupadd -g 1000 www
RUN useradd -u 1000 -ms /bin/bash -g www www

# Копируем существующие права на приложение
COPY --chown=www:www . /var/www/html

# Меняем текущего пользователя на www
USER www

# Экспонируем порт 9000 и запускаем php-fpm сервер
EXPOSE 9000
CMD ["php-fpm"]

На практике эта связка позволяет разворачивать проект на любой машине командой docker-compose up -d. У меня был случай, когда новый разработчик в команде запустил проект за 5 минут, хотя раньше на настройку локалки уходило пол дня.

Git workflow для командной разработки

Честно говоря, я видел проекты, где Git использовался как простое хранилище файлов. Это плохая идея. Правильный Git workflow — это основа продуктивной работы команды.

Я использую модифицированный Git Flow для большинства проектов. Основные ветки:

Вот мой стандартный .gitignore для Laravel-проектов, который экономит время на исключение ненужных файлов:

# Laravel
/vendor
/node_modules
/public/hot
/public/storage
/storage/*.key
.env
.env.backup
.env.production
.phpunit.result.cache
Homestead.json
Homestead.yaml
npm-debug.log
yarn-error.log

# Docker
docker/mysql/data/*
!docker/mysql/data/.gitkeep

# IDE
.idea/
.vscode/
*.swp
*.swo

# OS
.DS_Store
Thumbs.db

# Logs
*.log
storage/logs/*
!storage/logs/.gitkeep

Для автоматизации я настраиваю Git hooks. Вот pre-commit hook, который запускает PHP CS Fixer перед каждым коммитом:

#!/bin/sh

# Pre-commit hook для проверки кода
PROJECT=$(php artisan --version | head -n1 | awk '{print $1}' | tr '[:upper:]' '[:lower:]')
STAGED_FILES_CMD=$(git diff --cached --name-only --diff-filter=ACMR HEAD | grep \\.php)

# Проверяем, есть ли файлы для коммита
if [ "$STAGED_FILES_CMD" = "" ]; then
  exit 0
fi

STAGED_FILES=$(echo "$STAGED_FILES_CMD")

echo "Checking PHP Lint..."
for FILE in $STAGED_FILES
do
  php -l -d display_errors=0 $PROJECT/$FILE
  if [ $? != 0 ]
  then
    echo "Fix the error before commit."
    exit 1
  fi
  FILES="$FILES $PROJECT/$FILE"
done

# Запускаем PHP CS Fixer
if [ "$FILES" != "" ]
then
  echo "Running Code Sniffer..."
  ./vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run --diff $FILES
  if [ $? != 0 ]
  then
    echo "Fix the error before commit!"
    echo "Run php-cs-fixer fix to fix automatically or fix it manually."
    exit 1
  fi
fi

exit $?
⚠️
Внимание: Никогда не коммитьте напрямую в main ветку. Всегда используйте Pull Requests для code review.

Настройка CI/CD pipeline

Автоматизация деплоя — это не роскошь, а необходимость. У меня был клиент, который деплоил сайт через FTP каждый раз вручную. Один раз он случайно удалил продакшен базу. После этого случая мы настроили полноценный CI/CD.

Я предпочитаю GitLab CI для большинства проектов. Вот базовый .gitlab-ci.yml, который использую для Laravel:

stages:
  - test
  - build
  - deploy

variables:
  MYSQL_ROOT_PASSWORD: secret
  MYSQL_DATABASE: laravel_test
  MYSQL_USER: laravel
  MYSQL_PASSWORD: secret
  DB_HOST: mysql

cache:
  paths:
    - vendor/
    - node_modules/

# Тестирование
test:php:
  stage: test
  image: php:8.2
  services:
    - mysql:8.0
  before_script:
    - apt-get update -qq && apt-get install -y -qq git curl libmcrypt-dev libjpeg-dev libpng-dev libfreetype6-dev libbz2-dev libzip-dev
    - docker-php-ext-install zip pdo_mysql
    - curl -sS https://getcomposer.org/installer | php
    - php composer.phar install --no-dev --no-scripts
    - cp .env.testing .env
    - php artisan key:generate
    - php artisan migrate
  script:
    - php vendor/bin/phpunit --configuration phpunit.xml --coverage-text --colors=never

# Проверка кода
test:code-style:
  stage: test
  image: php:8.2
  before_script:
    - curl -sS https://getcomposer.org/installer | php
    - php composer.phar install --no-scripts
  script:
    - php vendor/bin/php-cs-fixer fix --config=.php-cs-fixer.php --dry-run --diff

# Сборка для продакшена
build:production:
  stage: build
  image: node:18
  before_script:
    - npm install
  script:
    - npm run production
  artifacts:
    paths:
      - public/css/
      - public/js/
      - public/mix-manifest.json
  only:
    - main

# Деплой на staging
deploy:staging:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache rsync openssh
    - eval $(ssh-agent -s)
    - echo "$STAGING_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo "$STAGING_SERVER_HOSTKEYS" >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - rsync -rav --delete --exclude='.git/' --exclude='storage/app/' --exclude='storage/logs/' . $STAGING_USER@$STAGING_HOST:$STAGING_PATH
    - ssh $STAGING_USER@$STAGING_HOST "cd $STAGING_PATH && php artisan migrate --force && php artisan config:cache && php artisan route:cache"
  only:
    - develop

# Деплой на продакшен
deploy:production:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache rsync openssh
    - eval $(ssh-agent -s)
    - echo "$PRODUCTION_PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - echo "$PRODUCTION_SERVER_HOSTKEYS" >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  script:
    - rsync -rav --delete --exclude='.git/' --exclude='storage/app/' --exclude='storage/logs/' . $PRODUCTION_USER@$PRODUCTION_HOST:$PRODUCTION_PATH
    - ssh $PRODUCTION_USER@$PRODUCTION_HOST "cd $PRODUCTION_PATH && php artisan migrate --force && php artisan config:cache && php artisan route:cache && php artisan queue:restart"
  when: manual
  only:
    - main

Для GitHub Actions использую похожую логику, но с другим синтаксисом. GitHub Actions удобнее для open-source проектов, но GitLab CI мне нравится больше для коммерческой разработки.

Обязательно настраиваю уведомления в Slack или Telegram при успешном/неуспешном деплое. Вот простой скрипт для уведомлений в Telegram:

#!/bin/bash

TELEGRAM_BOT_TOKEN="your_bot_token"
TELEGRAM_CHAT_ID="your_chat_id"
PROJECT_NAME="$1"
STATUS="$2"
BRANCH="$3"

if [ "$STATUS" = "success" ]; then
    MESSAGE="✅ $PROJECT_NAME: деплой ветки $BRANCH успешно завершён"
else
    MESSAGE="❌ $PROJECT_NAME: ошибка деплоя ветки $BRANCH"
fi

curl -s -X POST https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/sendMessage \
    -d chat_id=$TELEGRAM_CHAT_ID \
    -d text="$MESSAGE"

Мониторинг и логирование в CI/CD

После настройки автоматического деплоя обязательно нужен мониторинг. Я интегрирую каждый CI/CD pipeline с системой мониторинга. Использую связку Prometheus + Grafana для метрик и ELK Stack для логов.

В GitLab CI добавляю этап проверки здоровья после деплоя:

health_check:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache curl
  script:
    - sleep 30 # Ждём запуска приложения
    - curl -f http://your-domain.com/health || exit 1
    - curl -f http://your-domain.com/api/status || exit 1
  only:
    - main
    - develop

На практике эта проверка не раз спасала от деплоя сломанного кода. У меня был случай, когда после деплоя сайт возвращал 500 ошибку из-за неправильной конфигурации Redis. Health check поймал это сразу.

Для детального мониторинга добавляю метрики в код приложения. Вот пример middleware для Laravel, который отправляет метрики времени ответа:

sendMetrics([
            'request_duration_ms' => $duration,
            'request_method' => $request->method(),
            'request_uri' => $request->path(),
            'response_status' => $response->status(),
            'timestamp' => time()
        ]);
        
        return $response;
    }
    
    private function sendMetrics(array $metrics)
    {
        // Отправка в InfluxDB или Prometheus
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, config('monitoring.metrics_endpoint'));
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($metrics));
        curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Authorization: Bearer ' . config('monitoring.api_token')
        ]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, 5);
        
        curl_exec($ch);
        curl_close($ch);
    }
}
ℹ️
Полезно знать: Настройка полноценного мониторинга может показаться сложной, но это окупается уже после первого предотвращённого инцидента. Я рекомендую начинать с простых health checks и постепенно усложнять.

Безопасность в CI/CD pipeline

Безопасность CI/CD — это отдельная большая тема. За годы практики я видел множество проектов, где секреты хранились прямо в коде репозитория. Это однозначно плохая идея.

Основные принципы безопасности, которые я применяю:

В GitLab CI добавляю этап сканирования безопасности:

security:dependency-scan:
  stage: test
  image: php:8.2
  before_script:
    - curl -sS https://getcomposer.org/installer | php
    - php composer.phar install --no-scripts
  script:
    - php composer.phar audit
    - npm audit
  allow_failure: false

security:secret-scan:
  stage: test
  image: alpine:latest
  before_script:
    - apk add --no-cache git
    - git clone https://github.com/trufflesecurity/trufflehog.git
  script:
    - ./trufflehog/trufflehog --regex --entropy=False .
  allow_failure: false

Для хранения секретов использую HashiCorp Vault или встроенные возможности GitLab/GitHub. Никогда не храню пароли и API ключи в .env файлах в репозитории.

Оптимизация скорости CI/CD

Медленный CI/CD убивает продуктивность команды. У меня был проект, где полный цикл тестирования и деплоя занимал 45 минут. Разработчики начали пропускать тесты, чтобы не ждать. Это плохая практика.

Основные способы ускорения CI/CD:

Вот оптимизированная версия .gitlab-ci.yml с параллельным запуском тестов:

stages:
  - test
  - build
  - deploy

# Глобальный кеш
cache: &global_cache
  key: ${CI_COMMIT_REF_SLUG}
  paths:
    - vendor/
    - node_modules/
    - .composer-cache/
  policy: pull-push

# Параллельные тесты
test:unit:
  stage: test
  image: php:8.2
  cache:
    <<: *global_cache
    policy: pull
  script:
    - php vendor/bin/phpunit --testsuite=Unit

test:feature:
  stage: test
  image: php:8.2
  services:
    - mysql:8.0
  cache:
    <<: *global_cache
    policy: pull
  script:
    - php vendor/bin/phpunit --testsuite=Feature

test:integration:
  stage: test
  image: php:8.2
  services:
    - mysql:8.0
    - redis:7
  cache:
    <<: *global_cache
    policy: pull
  script:
    - php vendor/bin/phpunit --testsuite=Integration

Также использую многоэтапную сборку Docker для ускорения. Вот пример оптимизированного Dockerfile:

# Многоэтапная сборка для оптимизации
FROM php:8.2-fpm as base

# Устанавливаем системные зависимости
RUN apt-get update && apt-get install -y \
    git curl libpng-dev libzip-dev zip unzip \
    && docker-php-ext-install pdo_mysql zip \
    && apt-get clean \
    && rm -rf /var/lib/apt/lists/*

# Этап для Composer
FROM base as composer
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
WORKDIR /app
COPY composer.* ./
RUN composer install --no-dev --optimize-autoloader --no-scripts

# Этап для фронтенда
FROM node:18-alpine as frontend
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run production

# Финальный этап
FROM base as production
WORKDIR /var/www/html
COPY . .
COPY --from=composer /app/vendor ./vendor
COPY --from=frontend /app/public/css ./public/css
COPY --from=frontend /app/public/js ./public/js
COPY --from=frontend /app/mix-manifest.json ./mix-manifest.json

RUN chown -R www-data:www-data /var/www/html

На практике эти оптимизации сокращают время сборки с 15-20 минут до 3-5 минут. Это критично важно для продуктивности команды.

Интеграция с системами мониторинга

После настройки CI/CD обязательно интегрирую его с системами мониторинга. Использую Sentry для отслеживания ошибок, New Relic для APM и собственный стек на базе Prometheus + Grafana.

В Laravel добавляю автоматическую отправку уведомлений о деплое в Sentry:

 env('SENTRY_LARAVEL_DSN'),
    'release' => env('APP_VERSION', 'unknown'),
    'environment' => env('APP_ENV', 'production'),
    
    // Уведомления о релизах
    'send_default_pii' => false,
    'traces_sample_rate' => env('SENTRY_TRACES_SAMPLE_RATE', 0.1),
    
    'before_send' => function (\Sentry\Event $event): ?\Sentry\Event {
        // Фильтруем чувствительные данные
        return $event;
    },
];

А в CI/CD добавляю команду для создания релиза в Sentry:

# В .gitlab-ci.yml после успешного деплоя
- curl -sL https://sentry.io/get-cli/ | bash
- sentry-cli releases new $CI_COMMIT_SHA
- sentry-cli releases set-commits $CI_COMMIT_SHA --auto
- sentry-cli releases finalize $CI_COMMIT_SHA
- sentry-cli releases deploys $CI_COMMIT_SHA new -e production

Это позволяет связывать ошибки в продакшене с конкретными релизами и быстро откатываться при необходимости.

💡
Из практики: Настройте автоматические уведомления о критических ошибках в Slack или Telegram. Я получаю уведомление в течение 30 секунд после появления 500 ошибки на любом из проектов.

Типовые ошибки и их решение

За годы работы я собрал список самых частых проблем с CI/CD и их решений. Это сэкономит вам кучу времени на отладке.

Проблема 1: Docker контейнеры падают с ошибкой памяти
Решение: Увеличиваем лимиты памяти в docker-compose.yml и оптимизируем PHP конфигурацию:

services:
  php:
    image: php:8.2-fpm
    deploy:
      resources:
        limits:
          memory: 512M
        reservations:
          memory: 256M
    environment:
      - PHP_MEMORY_LIMIT=256M
      - PHP_MAX_EXECUTION_TIME=300

Проблема 2: Медленная установка зависимостей в CI
Решение: Используем кеширование и зеркала репозиториев:

# .gitlab-ci.yml
variables:
  COMPOSER_CACHE_DIR: ".composer-cache"
  NPM_CONFIG_CACHE: ".npm-cache"

cache:
  paths:
    - .composer-cache/
    - .npm-cache/
    - vendor/
    - node_modules/

before_script:
  - composer config cache-dir .composer-cache
  - composer config repos.packagist composer https://packagist.org

Проблема 3: Падение тестов из-за состояния базы данных
Решение: Используем транзакции в тестах и отдельную тестовую БД:

У меня был проект, где тесты падали из-за того, что использовали продакшн Redis. После настройки отдельного окружения для тестов проблема исчезла.

Настройка правильного окружения разработчика — это инвестиция в будущее проекта. Грубо говоря, час потраченный на настройку Docker и CI/CD экономит недели времени в процессе разработки. И это не преувеличение — я это проверил на десятках проектов.

Если нужна помощь с настройкой окружения для вашего проекта, обращайтесь за поддержкой Битрикс или поддержкой WordPress. А для более сложных задач можем обсудить доработку сайта под ваши потребности.

Правильно настроенное окружение — это основа успешного проекта. И чем раньше вы это сделаете, тем больше времени и нервов сэкономите в будущем.

Нужна помощь с настройкой DevOps-процессов?

Настроим современное окружение разработки и автоматизируем ваши процессы развертывания.

П
Павел
Веб-разработчик · 10+ лет опыта · Bitrix, WordPress, Laravel

Читайте также

Настройка почты для сайта: SPF, DKIM, DMARC Сколько стоит поддержка сайта в 2026 году Настройка мультидоменного сайта: полное руководство 2026