Введение
При развертывании приложений в 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
Важно
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
- Откройте репозиторий вашего проекта на GitHub.
- Перейдите в Settings → Deploy Keys → Add deploy key.
- В поле Title введите
VPS Deploy Key. - В поле Key вставьте скопированную строку (флаг Allow write access включать не нужно).
- Нажмите 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
Вывод
Инфраструктура автодеплоя полностью готова. Каждый коммит в ветку main запускает runner, который обновляет код, собирает новые слои образов и перезапускает сервисы.
1. Чек-лист проверки первого деплоя
- Внесите изменение в коде проекта локально и выполните коммит:
git commit -am "test: deploy verification" git push origin main - Откройте вкладку Actions в репозитории на GitHub и проследите за выполнением пайплайна.
- Убедитесь, что все шаги завершились с зеленым статусом.
- Откройте сайт в браузере и проверьте, что изменения применились. Перезапускать 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