О насБлогКонтакты
DevOps25 марта 2016 г. 4 мин 23

Как настроить CI/CD для PHP-проекта в GitLab в 2016 году

AunimedaAunimeda
📋 Содержание

Как настроить CI/CD для PHP-проекта в GitLab в 2016 году

Коротко: Создайте .gitlab-ci.yml в корне репозитория, определите стадии test и deploy, укажите Docker-образ php:7.0, настройте SSH-ключ как переменную окружения в GitLab Settings → Variables. При push в master — автоматически запускаются тесты, при успехе — деплой на сервер.


.gitlab-ci.yml

# .gitlab-ci.yml — в корне репозитория

image: php:7.0-cli

stages:
  - prepare
  - test
  - deploy

variables:
  COMPOSER_HOME: "$CI_PROJECT_DIR/.composer"
  APP_ENV:       "testing"

cache:
  key: "$CI_COMMIT_REF_NAME"
  paths:
    - vendor/
    - .composer/
  # Кэшировать vendor/ между пайплайнами = не скачивать Composer каждый раз

# Стадия 1: установить зависимости
composer_install:
  stage: prepare
  script:
    - apt-get update -q && apt-get install -yq unzip git libzip-dev
    - docker-php-ext-install zip pdo_mysql mbstring
    - curl -sS https://getcomposer.org/installer | php
    - php composer.phar install --no-interaction --prefer-dist
  artifacts:
    paths:
      - vendor/
    expire_in: 1 hour

# Стадия 2: PHPUnit тесты
phpunit:
  stage: test
  services:
    - mysql:5.7
  variables:
    MYSQL_DATABASE:      test_db
    MYSQL_ROOT_PASSWORD: rootsecret
    DB_HOST:             mysql
    DB_DATABASE:         test_db
    DB_USERNAME:         root
    DB_PASSWORD:         rootsecret
  script:
    - docker-php-ext-install pdo_mysql
    - cp .env.testing .env
    - php artisan migrate --env=testing
    - vendor/bin/phpunit --coverage-text
  coverage: '/Lines:\s+(\d+\.\d+)%/'  # Парсинг покрытия из вывода

# Стадия 2: Статический анализ
phpcs:
  stage: test
  script:
    - vendor/bin/phpcs --standard=PSR2 app/ --ignore=app/Exceptions/
  allow_failure: true  # Не блокировать деплой из-за code style

# Стадия 3: Деплой на production (только master ветка)
deploy_production:
  stage: deploy
  script:
    - apt-get update -q && apt-get install -yq openssh-client rsync
    - mkdir -p ~/.ssh
    - echo "$DEPLOY_SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_rsa
    - chmod 600 ~/.ssh/id_rsa
    - echo "$DEPLOY_SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
    - chmod +x deploy.sh
    - ./deploy.sh
  only:
    - master
  environment:
    name:    production
    url:     https://myapp.ru
  when: on_success  # Только если все тесты прошли

deploy.sh

#!/bin/bash
# deploy.sh — деплой на production сервер

set -e  # Выйти при любой ошибке

DEPLOY_USER="deploy"
DEPLOY_HOST="10.0.0.1"
DEPLOY_PATH="/var/www/myapp"
RELEASE_PATH="$DEPLOY_PATH/releases/$(date +%Y%m%d%H%M%S)"
SHARED_PATH="$DEPLOY_PATH/shared"
CURRENT_LINK="$DEPLOY_PATH/current"

echo "=== Deploying to $DEPLOY_HOST ==="

# Создать директорию для нового релиза
ssh $DEPLOY_USER@$DEPLOY_HOST "mkdir -p $RELEASE_PATH"

# Скопировать код на сервер
rsync -az --delete \
    --exclude='.git' \
    --exclude='node_modules' \
    --exclude='tests' \
    ./ $DEPLOY_USER@$DEPLOY_HOST:$RELEASE_PATH/

# Выполнить шаги деплоя на сервере
ssh $DEPLOY_USER@$DEPLOY_HOST << EOF
    set -e

    # Символические ссылки на shared ресурсы
    ln -sf $SHARED_PATH/.env              $RELEASE_PATH/.env
    ln -sf $SHARED_PATH/storage           $RELEASE_PATH/storage

    cd $RELEASE_PATH

    # Установить зависимости
    composer install --no-dev --optimize-autoloader --no-interaction

    # Запустить миграции
    php artisan migrate --force

    # Кэшировать конфигурацию
    php artisan config:cache
    php artisan route:cache

    # Переключить current на новый релиз (атомарная операция)
    ln -sfn $RELEASE_PATH $CURRENT_LINK

    # Перезапустить PHP-FPM (graceful reload)
    sudo service php7.0-fpm reload

    # Очистить Queue Workers
    php artisan queue:restart

    # Удалить старые релизы (оставить последние 5)
    ls -dt $DEPLOY_PATH/releases/*/ | tail -n +6 | xargs rm -rf

    echo "Deploy complete: $RELEASE_PATH"
EOF

echo "=== Deploy successful ==="

Настройка переменных в GitLab

GitLab → Project → Settings → CI/CD → Variables

DEPLOY_SSH_PRIVATE_KEY = (приватный SSH-ключ, тип: File)
# Сгенерировать: ssh-keygen -t rsa -b 4096 -f deploy_key
# Публичный ключ добавить в ~/.ssh/authorized_keys на сервере

DEPLOY_SSH_KNOWN_HOSTS = (known_hosts запись)
# Получить: ssh-keyscan -H 10.0.0.1

.env.testing для CI

# .env.testing — используется только в CI
APP_ENV=testing
APP_DEBUG=true
APP_KEY=base64:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=

DB_CONNECTION=mysql
DB_HOST=mysql          # Имя сервиса из .gitlab-ci.yml services
DB_PORT=3306
DB_DATABASE=test_db
DB_USERNAME=root
DB_PASSWORD=rootsecret

CACHE_DRIVER=array     # Кэш в памяти, не Redis
SESSION_DRIVER=array
QUEUE_DRIVER=sync      # Очереди синхронно в тестах
MAIL_DRIVER=log        # Email в лог, не отправлять

Структура директорий на сервере (Zero-Downtime)

/var/www/myapp/
├── current/           ← Символическая ссылка на текущий релиз
├── shared/
│   ├── .env           ← Конфиг (не в git)
│   └── storage/       ← Загруженные файлы, логи, кэш
└── releases/
    ├── 20160325120000/ ← Релиз 1
    ├── 20160326093015/ ← Релиз 2
    └── 20160327140530/ ← Релиз 3 (current)

ln -sfn атомарная операция: nginx начинает отдавать файлы нового релиза мгновенно, без задержки.


Результаты после внедрения CI/CD

Метрика Ручной деплой GitLab CI/CD
Время деплоя 20–30 мин 4–6 мин
Ошибок в production 3–5/мес 0.5/мес
Обнаружение поломанных тестов На продакшне В CI
Откат при ошибке 15–20 мин 30 секунд (ln -sfn предыдущий релиз)

Главный результат: разработчики перестали бояться делать деплои. Частота релизов выросла с 2–3 в месяц до 8–12.

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

Как хранить сессии в Redis для нескольких серверов PHP (2016)aunimeda
DevOps

Как хранить сессии в Redis для нескольких серверов PHP (2016)

При горизонтальном масштабировании PHP на 2+ серверов сессии перестают работать: пользователь авторизован на сервере 1, запрос попадает на сервер 2 — сессия не найдена. Решение: хранить сессии в Redis, к которому имеют доступ все серверы. Настройка PHP, nginx upstream, Redis.

Как установить Let's Encrypt на nginx — первые дни после запуска (декабрь 2015)aunimeda
DevOps

Как установить Let's Encrypt на nginx — первые дни после запуска (декабрь 2015)

Let's Encrypt открылся для всех 3 декабря 2015 года. Бесплатные SSL-сертификаты с автопродлением — это изменило веб. В первые недели работы Certbot (тогда letsencrypt-auto) имел баги. Вот что работало и что нет в декабре 2015 на Ubuntu 14.04 + nginx.

Хабраэффект 2011: как пост на Хабре положил наш сервер и что мы с этим сделалиaunimeda
DevOps

Хабраэффект 2011: как пост на Хабре положил наш сервер и что мы с этим сделали

«Хабраэффект» — это когда публикация на Хабрахабр за 15 минут уничтожает сервер, который нормально работал месяцами. Мы через это прошли в 2011 году. Вот технический разбор того, что упало, почему упало и как мы перестроили стек чтобы это не повторилось.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в России. Бесплатная консультация.

Получить консультацию Все статьи