Введение
При развертывании приложений в 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. Подготовка пользователя и директории
Все действия по деплою выполняются от имени непривилегированного пользователя deploy. Для управления контейнерами этот пользователь должен состоять в системной группе docker.
1.1. Проверка прав пользователя deploy
Если вы настраивали сервер по руководству Настройка 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. Настройка Deploy Key для репозитория
Чтобы сервер мог безопасно клонировать и стягивать приватный репозиторий с GitHub без использования личных паролей или токенов с избыточными правами, используется отдельный сервисный SSH-ключ.
2.1. Генерация ключа и настройка SSH config
Если вы уже создавали сервисный ключ ~/.ssh/github_deploy при первичной настройке VPS, этот шаг можно пропустить. Если ключа нет, сгенерируйте его под пользователем deploy:
ssh-keygen -t ed25519 -f ~/.ssh/github_deploy -C "deploy@vps"
Нажмите Enter на всех запросах парольной фразы. Настройте файл ~/.ssh/config:
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_deploy
IdentitiesOnly yes
Задайте строгие права доступа:
chmod 600 ~/.ssh/config ~/.ssh/github_deploy
chmod 644 ~/.ssh/github_deploy.pub
2.2. Привязка 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.
Проверьте соединение с сервера:
ssh -T git@github.com
При первом подключении введите yes. Ответ Hi ...! You've successfully authenticated подтвердит готовность.
3. Первичное клонирование и первый запуск
Перед автоматизацией проект необходимо один раз клонировать и запустить вручную, чтобы создать файл переменных окружения .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.
4. Секреты в GitHub Actions
Для удаленного подключения runner’а GitHub Actions к вашему серверу добавьте секреты в репозитории: Settings → Secrets and variables → Actions → New repository secret.
Добавьте 4 обязательных секрета:
HOST— публичный IP-адрес вашего VPS (<ip_сервера>);USERNAME— имя пользователя деплоя (deploy);KEY— приватный SSH-ключ для авторизации на сервере (содержимое локального файла~/.ssh/id_ed25519);PORT— порт SSH-сервера (по умолчанию22или кастомный).
5. Создание workflow деплоя
В корне локального репозитория создайте файл .github/workflows/deploy.yml:
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 адаптируются под ваш фреймворк или удаляются, если контейнеру не требуются миграции.
Обязательный флаг -T
Советы и практики
Инфраструктура автодеплоя полностью готова. Каждый коммит в ветку main запускает runner, который обновляет код, собирает новые слои образов и бесшовно перезапускает сервисы.
Чек-лист первого деплоя
- Внесите изменение в коде проекта локально и выполните коммит:
git commit -am "test: deploy verification" git push origin main - Откройте вкладку Actions в репозитории на GitHub и проследите за выполнением пайплайна.
- Убедитесь, что все шаги завершились с зеленым статусом.
- Откройте сайт в браузере и проверьте, что изменения применились. Перезапускать Caddy не требуется — он автоматически маршрутизирует трафик на поднятый контейнер.
Команды для других фреймворков
В зависимости от архитектуры приложения блок служебных команд в workflow адаптируется:
- Чистый PHP / FrankenPHP:
Миграций нет, шаг 3 не требуется — контейнер пересобирается и перезапускается автоматически. - 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
Шпаргалка команд
- Просмотр логов запущенного приложения:
cd /var/www/apps/example-app && docker compose logs -f --tail=100 - Мониторинг ресурсов контейнеров:
docker stats - Проверка состояния контейнеров:
docker compose ps - Ручной перезапуск сервисов:
docker compose restart - Проверка подключения контейнера к сети прокси:
docker network inspect web_gateway
