Ссылка скопирована

Автодеплой Docker-проектов на VPS через GitHub Actions и SSH

DonVardix DonVardix

Введение

При развертывании приложений в Docker-контейнерах классический деплой с выполнением composer или npm прямо на хосте не применим — все зависимости и исполняемая среда изолированы внутри образов. Ручное подключение к серверу для каждого docker compose build отнимает время и создает риск прерывания работы соседних сервисов.

Что будет сделано:

  • Настроены права доступа пользователя deploy для управления демоном Docker без sudo;
  • Сгенерирован и подключен сервисный SSH-ключ для загрузки приватного кода из GitHub;
  • Настроен пайплайн GitHub Actions (appleboy/ssh-action) для атомарного обновления исходного кода, пересборки и бесшовного перезапуска контейнеров;
  • Разобраны тонкости выполнения миграций базы данных без TTY-терминала и автоматическая очистка неиспользуемых слоев образов.

Результат: надежный CI/CD пайплайн, где любой git push в ветку main автоматически собирает и обновляет Docker-контейнеры на VPS без необходимости ручного входа на сервер и без перезагрузки реверс-прокси Caddy.


1. Подготовка пользователя и прав Docker

Все действия по деплою выполняются от имени пользователя deploy. Для управления контейнерами этот пользователь должен состоять в системной группе docker.

1.1. Проверка и добавление прав группы

Если вы настраивали сервер по руководству Настройка VPS под мультипроектный Docker-стек с Caddy, пользователь уже настроен. Если нет, выполните под root:

usermod -aG docker deploy

Переключитесь на пользователя deploy:

su - deploy

Проверьте, что Docker отвечает без sudo:

docker ps

1.2. Подготовка директории проекта

Создайте каталог для приложения внутри директории /var/www/apps:

mkdir -p /var/www/apps/example-app

Важно

Все файлы, папки проектов и команды деплоя должны принадлежать строго пользователю deploy:deploy. Не запускайте docker compose под root.

2. Выпуск SSH-ключа на сервере

Чтобы сервер мог безопасно стягивать приватный репозиторий с GitHub, создайте отдельную пару ключей Ed25519 без кодовой фразы.

Под пользователем deploy:

ssh-keygen -t ed25519 -f ~/.ssh/github_deploy -C "deploy@vps"

Нажмите Enter на всех запросах ввода пароля. Задайте строгие права доступа:

chmod 600 ~/.ssh/github_deploy
chmod 644 ~/.ssh/github_deploy.pub

3. Привязка Deploy Key в GitHub

Выведите публичный ключ на экран:

cat ~/.ssh/github_deploy.pub
  1. Откройте репозиторий вашего проекта на GitHub.
  2. Перейдите в Settings → Deploy Keys → Add deploy key.
  3. В поле Title введите VPS Deploy Key.
  4. В поле Key вставьте скопированную строку (флаг Allow write access включать не нужно).
  5. Нажмите Add key.

4. Настройка SSH config на сервере

Для автоматического использования созданного ключа настройте клиент SSH.

Создайте или отредактируйте файл ~/.ssh/config:

nano ~/.ssh/config

Добавьте параметры для хоста github.com:

Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/github_deploy
    IdentitiesOnly yes

Выставите права и протестируйте соединение:

chmod 600 ~/.ssh/config
ssh -T git@github.com

При первом подключении подтвердите отпечаток хоста (yes). В ответ GitHub выдаст успешное приветствие.


5. Первичное клонирование и первый запуск

Перед автоматизацией проект необходимо первично клонировать и запустить вручную один раз, чтобы создать файл переменных окружения .env.

Перейдите в папку проекта:

cd /var/www/apps/example-app
git clone git@github.com:username/repository.git .

Создайте и заполните рабочий .env:

nano .env

Запустите контейнеры в фоновом режиме:

docker compose up -d --build

Убедитесь, что веб-сервис проекта успешно подключен к внешней сети web_gateway и отдает ответ через Caddy.


6. Настройка секретов в GitHub Actions

Для подключения GitHub Actions к серверу требуются учетные данные. Откройте репозиторий на GitHub: Settings → Secrets and variables → Actions → New repository secret.

Добавьте 4 обязательных секрета:

  • HOST — публичный IP-адрес сервера (203.0.113.10);
  • USERNAME — имя пользователя деплоя (deploy);
  • KEY — приватный SSH-ключ администратора для входа на сервер под пользователем deploy;
  • PORT — SSH-порт сервера (по умолчанию 22).

7. Создание workflow деплоя

В корне локального репозитория создайте файл конфигурации .github/workflows/deploy.yml. Набор команд в блоке script настраивается под требования вашего приложения — ниже приведен готовый пример для проекта на базе PHP/Laravel:

name: Deploy Docker App

on:
  push:
    branches:
      - main

concurrency:
  group: deploy-docker-app
  cancel-in-progress: true

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Deploy via SSH
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.HOST }}
          username: ${{ secrets.USERNAME }}
          key: ${{ secrets.KEY }}
          port: ${{ secrets.PORT }}
          script: |
            set -e

            cd /var/www/apps/example-app

            # 1. Синхронизация репозитория
            git fetch origin main
            git reset --hard origin/main

            # 2. Пересборка и запуск контейнеров
            docker compose build
            docker compose up -d

            # 3. Выполнение миграций и сброс кеша
            docker compose exec -T app php artisan migrate --force
            docker compose exec -T app php artisan optimize:clear

            # 4. Очистка устаревших Docker-образов
            docker image prune -f

Шаги 1, 2 и 4 (синхронизация репозитория, запуск контейнеров и очистка старых образов) одинаковы для любых Docker-приложений. Команды шага 3 заменяются на характерные для вашего фреймворка (примеры для Node.js, Python и WordPress приведены в конце статьи) или удаляются, если контейнеру не требуются миграции.

Обязательный флаг -T

В CI/CD сессиях SSH нет TTY-терминала. Если выполнить docker compose exec без флага -T, команда упадет с ошибкой «the input device is not a TTY». Флаг -T отключает псевдо-терминал и обязателен для запуска миграций и консольных утилит в автоматическом режиме.

Вывод

Инфраструктура автодеплоя полностью готова. Каждый коммит в ветку main запускает runner, который обновляет код, собирает новые слои образов и перезапускает сервисы.

1. Чек-лист проверки первого деплоя

  1. Внесите изменение в коде проекта локально и выполните коммит:
    git commit -am "test: deploy verification"
    git push origin main
  2. Откройте вкладку Actions в репозитории на GitHub и проследите за выполнением пайплайна.
  3. Убедитесь, что все шаги завершились с зеленым статусом.
  4. Откройте сайт в браузере и проверьте, что изменения применились. Перезапускать Caddy не требуется — он автоматически маршрутизирует трафик на поднятый контейнер.

2. Добавление команд для других фреймворков

В зависимости от типа приложения блок служебных команд в workflow адаптируется:

  • Node.js / Next.js:
    Сборка происходит на этапе docker compose build внутри multi-stage Dockerfile, дополнительных команд exec не требуется.
  • Django / Python:
    docker compose exec -T app python manage.py migrate --noinput
    docker compose exec -T app python manage.py collectstatic --noinput
  • WordPress (Bedrock):
    docker compose exec -T app wp core update-db

3. Шпаргалка команд

  • Просмотр логов запущенного приложения:
    cd /var/www/apps/example-app && docker compose logs -f --tail=100
  • Проверка состояния контейнеров:
    docker compose ps
  • Ручной перезапуск сервисов:
    docker compose restart
  • Проверка подключения контейнера к сети прокси:
    docker network inspect web_gateway