Инфраструктура

Как настроить CI/CD пайплайн на VPS: Docker, автоматизация и безопасность

Поделиться:
How to build a CI/CD pipeline using a VPS

Если вы разработчик, работающий над живым приложением, вы постоянно вносите небольшие изменения. Исправления багов, обновления функций и настройка конфигурации всегда необходимы. Но развёртывание этих изменений вручную быстро надоедает, независимо от того, управляете ли вы одним приложением или шестью. Каждое изменение кода, которое вы пушите, должно попадать на ваш сервер без необходимости подключаться по SSH, пулить последний коммит и перезапускать приложение вручную. Это задача CI/CD пайплайна. И если у вас уже есть VPS, у вас есть всё необходимое для его создания.

Вот как настроить собственный пайплайн непрерывной интеграции и непрерывной доставки (CI/CD) – от выбора CI-инструмента, настройки Docker для чистых сборок, написания первого файла пайплайна и подключения автоматического развёртывания до добавления тестов и усиления безопасности. Это практические шаги, а не теория, и в примерах мы используем Ubuntu – самый распространённый дистрибутив Linux на виртуальных частных серверах, размещённых в Великобритании.

Зачем запускать CI/CD пайплайн на VPS?

Управляемые CI/CD сервисы, такие как CircleCI и Travis CI, взимают плату за минуту сборки. И если вы развёртываете несколько раз в день, эти затраты будут немалыми. Самостоятельно размещённый CI/CD на VPS-сервере, который вы уже оплачиваете, даёт вам фиксированные, предсказуемые ежемесячные расходы, независимо от того, как часто вы выкладываете обновления.

Но есть и другие реальные эксплуатационные преимущества. Вы получаете полный доступ root, поэтому можете установить любую зависимость, необходимую вашему проекту, не дожидаясь, пока вендор её поддержит – будь то определённые версии компиляторов, движки баз данных или системные библиотеки. Ваш сервер сборки теперь находится в той же сети, что и ваше производственное окружение (или его staging-копия). А это означает более быструю передачу артефактов и более строгий контроль над тем, где находится ваш код. И если вы находитесь в Великобритании и работаете с данными клиентов, размещение инфраструктуры непрерывной интеграции и непрерывной доставки в центре обработки данных в Великобритании также упрощает соблюдение нормативных требований.

Оборотная сторона этого подхода — ответственность. Вы сами управляете обновлениями, дисковым пространством, резервным копированием и временем безотказной работы. Но для многих разработчиков это справедливый обмен.

Что вам нужно перед началом

Оборудование и ОС

VPS с как минимум 2 vCPU, 4 ГБ ОЗУ и 80 ГБ SSD или NVMe накопителя легко справится с большинством задач CI/CD малого и среднего размера. Если вы запускаете GitLab CE (который объединяет в одном пакете Git-сервер, реестр контейнеров и CI-раннер), цельтесь минимум в 4 ГБ ОЗУ, а если ожидаются параллельные задачи — в 8 ГБ.

Установите свежую копию Ubuntu 22.04 или 24.04 LTS. Обе имеют длительные окна поддержки и обширную документацию сообщества, что упрощает устранение неполадок. Убедитесь, что SSH настроен с аутентификацией по ключу, а вход по паролю отключён.

Программные предварительные требования

Прежде чем устанавливать CI-инструмент, подготовьте основу:

  • Docker – Почти каждый современный CI/CD пайплайн выполняет сборки внутри контейнеров. Установите Docker Engine из официального apt-репозитория. Официальная версия более точно отслеживает вышестоящие релизы.
  • Git – Вашему VPS нужен установленный Git, чтобы клонировать репозитории. Если вы планируете размещать свой Git-сервер с помощью GitLab, он будет включён автоматически.
  • Обратный прокси – Nginx или Caddy будет располагаться перед вашей CI-панелью и обрабатывать HTTPS через Let's Encrypt. Запуск Jenkins или GitLab на голом порту без TLS — это риск безопасности, которого можно избежать за 10 минут настройки.
  • Брандмауэр – UFW (Uncomplicated Firewall) уже доступен в Ubuntu. Разрешите SSH (порт 22), HTTP (80) и HTTPS (443). Заблокируйте всё остальное.

Выберите ваш CI/CD инструмент

Три наиболее распространённых варианта для пайплайна на VPS — это Jenkins, GitLab CI и GitHub Actions с самостоятельным раннером. Каждый подходит для немного другого рабочего процесса.

Jenkins

Jenkins — самый зрелый вариант. Он написан на Java, работает практически на всём и имеет экосистему плагинов, охватывающую почти каждый язык, фреймворк и целевое развёртывание. Установите его через apt, укажите ваш JDK и получите доступ к панели через Nginx.

Главное преимущество Jenkins — гибкость. Пайплайны Jenkins определяются в Jenkinsfile с использованием синтаксиса Groovy, и вы можете моделировать сложные графы сборки с параллельными этапами, условными шагами и общими библиотеками. Обратная сторона — накладные расходы. Jenkins требует больше памяти, чем более лёгкие альтернативы, а начальная настройка включает больше движущихся частей (агенты, плагины, хранилища учётных данных).

Хороший выбор… если ваш проект имеет сложную логику сборки, вам нужно интегрироваться с корпоративными инструментами или вы уже знакомы с Jenkins по предыдущей работе.

GitLab CI

Если вы хотите получить всё в одном: Git-хостинг, CI/CD, реестр контейнеров и отслеживание задач на одном VPS — GitLab Community Edition трудно превзойти. Пайплайны CI определяются в файле .gitlab-ci.yml в корне вашего репозитория, а GitLab Runner выполняет задания.

Установите GitLab CE, используя официальный пакет Omnibus, который включает PostgreSQL, Redis, Nginx и сервер приложений. Зарегистрируйте GitLab Runner на том же VPS (или отдельном) с помощью исполнителя Docker, чтобы каждое задание выполнялось в новом контейнере.

Хороший выбор… если вы хотите получить самодостаточную DevOps-платформу, предпочитаете определения пайплайнов на YAML или вам нужен частный реестр контейнеров без оплаты стороннего сервиса.

GitHub Actions с самостоятельным раннером

Если ваш код уже находится на GitHub, вам не нужно его перемещать. GitHub Actions позволяет зарегистрировать ваш VPS как самостоятельный раннер, так что файлы рабочих процессов по-прежнему хранятся в .github/workflows/, но задания выполняются на вашей собственной машине, а не на хостинговой инфраструктуре GitHub.

Установите раннер, используя скрипт, предоставленный на странице Settings > Actions > Runners вашего репозитория. Раннер подключается исходящим соединением к API GitHub. И вам не нужно открывать входящие порты, кроме SSH и HTTPS.

Хороший выбор… если вы привержены GitHub, хотите получить неограниченные бесплатные минуты сборки для публичных репозиториев или вам нужно развёртывать на VPS напрямую при пуше в main.

Настройте Docker для изоляции сборок

Независимо от того, какой CI-инструмент вы выберете, запуск сборок внутри контейнеров Docker — это самый безопасный способ поддерживать ваш VPS в чистоте. Каждое задание начинается с известного образа, устанавливает свои зависимости, выполняет шаги, а затем удаляет контейнер. Никакие данные не просачиваются между заданиями, и ничто от неудачной сборки не остаётся на хосте.

После установки Docker Engine добавьте пользователя службы вашего CI-инструмента (например, jenkins, gitlab-runner) в группу docker:

sudo usermod -aG docker gitlab-runner

Затем настройте ваш CI-инструмент для использования исполнителя Docker. Например, в GitLab CI вы регистрируете раннер следующим образом:

sudo gitlab-runner register

--url https://gitlab.yourdomain.com/

--registration-token YOUR_TOKEN

--executor docker

--docker-image node:20-alpine

Флаг --docker-image задаёт образ по умолчанию для заданий, которые не указывают свой. Выберите лёгкий образ, соответствующий вашему основному языку. node:20-alpine, python:3.12-slim или golang:1.22-alpine — хорошие отправные точки.

Важное замечание: избегайте запуска Docker-in-Docker (DinD) с флагом --privileged, если вы не понимаете последствий для безопасности. Если вам нужно создавать образы Docker внутри задания CI, используйте Kaniko или BuildKit. Оба могут создавать образы без необходимости повышенных привилегий на хосте.

Напишите ваш первый пайплайн

CI/CD пайплайн обычно проходит три этапа: сборка, тестирование и развёртывание. Вот минимальный .gitlab-ci.yml, который выполняет все три:

stages:

- build

- test

- deploy

build:

stage: build

image: node:20-alpine

script:

- npm ci

- npm run build

artifacts:

paths:

- dist/

test:

stage: test

image: node:20-alpine

script:

- npm ci

- npm run test

deploy:

stage: deploy

image: alpine:latest

only:

- main

  скрипт:

    - apk add --no-cache openssh-client rsync

    - rsync -avz --delete dist/ deploy@yourserver:/var/www/app/

Если вы используете Jenkins, эквивалентный Jenkinsfile следует той же логике в другом синтаксисе:

pipeline {

  agent { docker { image 'node:20-alpine' } }

  stages {

    stage('Build') {

      steps { sh 'npm ci && npm run build' }

    }

    stage('Test') {

      steps { sh 'npm run test' }

    }

    stage('Deploy') {

      when { branch 'main' }

      steps {

        sh 'rsync -avz --delete dist/ deploy@yourserver:/var/www/app/'

      }

    }

  }

}

Оба примера делают одно и то же. Они устанавливают зависимости, собирают проект, запускают набор тестов и (если ветка main) синхронизируют результат с рабочим сервером.

Автоматическое развертывание на вашем VPS

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

Базовое развертывание с SSH и rsync

Для статических сайтов, простых Node.js приложений или PHP-проектов часто достаточно rsync через SSH. CI-раннер подключается к вашему рабочему серверу, копирует измененные файлы и перезапускает соответствующий сервис (Nginx, PM2, systemd unit или тот, который управляет вашим процессом).

Создайте на рабочем сервере выделенного пользователя deploy с ограниченной оболочкой и, если хотите более жесткого контроля, ограничьте его SSH-ключ определенными командами с помощью опций authorized_keys. Храните закрытый ключ в менеджере секретов вашего CI-инструмента (GitLab CI Variables, Jenkins Credentials или GitHub Actions Secrets) и внедряйте его во время выполнения. Никогда не фиксируйте ключи в репозитории.

Развертывание на основе контейнеров с Docker Compose

Если ваше приложение работает в контейнерах, этап развертывания превращается в: собрать образ → отправить его в реестр → подключиться по SSH к серверу и загрузить новый образ.

Типичный поток выглядит так:

  1. Задание CI собирает Docker-образ и помечает его хешем коммита.
  2. Отправляет образ в ваш приватный реестр (встроенный реестр GitLab или собственный Docker Registry на том же VPS).
  3. Подключается к рабочему серверу через SSH и выполняет docker compose pull && docker compose up -d.

Этот подход хорошо работает со стратегиями blue-green или rolling. Если что-то ломается, вы откатываетесь, указав Docker Compose на предыдущий тег образа.

Добавьте автоматическое тестирование в пайплайн

Ни один пайплайн никогда не должен пропускать тестирование. Непрерывная интеграция (часть CI/CD) существует именно для того, чтобы выявлять проблемы до того, как они попадут в production.

Как минимум, вы должны запускать набор модульных тестов вашего проекта на этапе тестирования. Если в вашем проекте есть интеграционные тесты, зависящие от базы данных, используйте служебные контейнеры Docker. GitLab CI и Jenkins позволяют развернуть контейнер PostgreSQL или MySQL рядом с задачей тестирования с помощью всего нескольких строк конфигурации.

Также стоит добавить линтинг и инструменты статического анализа. ESLint, Flake8, RuboCop и их аналоги выявляют нарушения стиля и потенциальные ошибки без полного прогона тестов. Они выполняются за секунды, поэтому почти не увеличивают время пайплайна.

Для веб-приложений рассмотрите добавление smoke-теста после развертывания — простой HTTP-запрос, проверяющий, что главная страница возвращает статус 200. Если smoke-тест не проходит, пайплайн инициирует оповещение, и вы узнаете о проблеме немедленно, а не из жалобы клиента.

Защитите свой пайплайн

CI/CD-пайплайн имеет доступ к вашему исходному коду, рабочим серверам и, вероятно, к нескольким ключам API и учетным данным базы данных. Относитесь к нему с той же серьезностью, что и к любому другому производственному компоненту инфраструктуры.

Не храните секреты в репозитории. Используйте встроенное управление секретами вашего CI-инструмента. Это могут быть переменные окружения в GitLab CI, плагин Credentials в Jenkins или зашифрованные секреты в GitHub Actions. Для более крупных развертываний HashiCorp Vault предоставляет централизованное управление секретами с аудитом логов и автоматической ротацией.

Ограничьте круг лиц, которые могут запускать развертывание. Защитите основную ветку так, чтобы только слитые ревьюируемые пул-реквесты могли инициировать этап развертывания. В GitLab вы можете комбинировать защищенные ветки с защищенными средами для добавления ручных шлюзов утверждения перед развертыванием в production.

Регулярно обновляйте свои CI-инструменты. Jenkins, GitLab и GitHub Actions ранеры получают исправления безопасности. Подпишитесь на соответствующие списки рассылки и применяйте обновления сразу после их выпуска. Скомпрометированный CI-сервер — одно из худших инцидентов безопасности, с которым может столкнуться команда разработчиков, потому что у него есть доступ на запись в production.

Ограничьте права ранера. Если CI-раннеру не нужно собирать Docker-образы, не добавляйте его в группу docker. Если ему не нужен root-доступ, не давайте его. Применяйте принцип наименьших привилегий так же, как вы делаете это для любой другой служебной учетной записи.

Мониторинг сборок и раннее обнаружение сбоев

Как только пайплайн запущен, вы захотите знать, когда что-то идет не так, и желательно до того, как это заметит пользователь.

Большинство CI-инструментов включают встроенные опции уведомлений. GitLab и Jenkins могут отправлять электронные письма или сообщения в Slack при сбое пайплайна. GitHub Actions поддерживает вебхук-уведомления и имеет маркетплейс, полный интеграций для Slack и Discord.

Помимо уведомлений, следите за продолжительностью сборки. Набор тестов, который постепенно увеличивается с 2 минут до 15 минут, — признак того, что проблема требует внимания. Причиной могут быть медленные тесты, отсутствующие кеши или раннер с нехваткой ресурсов. Панель аналитики пайплайнов GitLab CI отслеживает это с течением времени. Пользователи Jenkins могут использовать плагин Pipeline Stage View для аналогичной визуализации.

На стороне инфраструктуры мониторьте сам VPS. Использование CPU, RAM и диска влияет на производительность пайплайна. Если ваш CI-раннер конкурирует с производственным приложением за ресурсы, сборки будут замедляться и в конечном итоге падать. Легкая система мониторинга (Prometheus и Grafana или даже простая cron-задача, проверяющая дисковое пространство) избавит вас от сюрпризов вроде «место на диске закончилось».


Ваше хостинговое окружение влияет на все: от скорости сборки и надежности развертывания до того, как быстро пайплайн восстанавливается после сбоя задания. Если вы впервые настраиваете CI/CD-пайплайн на VPS или переносите существующий пайплайн с управляемого сервиса, тарифные планы VPS от Fasthosts предоставляют выделенные ресурсы, NVMe-накопители и центры обработки данных в Великобритании. Кроме того, вас поддерживает команда техподдержки, доступная круглосуточно. Сравните тарифы VPS-серверов, чтобы найти подходящий для вашего проекта, или свяжитесь с нашей командой продаж.

Часто задаваемые вопросы о CI/CD-пайплайне

Сколько оперативной памяти нужно CI/CD-пайплайну на VPS-сервере?

Для легковесного развертывания с использованием GitHub Actions self-hosted ранеров или Drone для небольших проектов может хватить 2 ГБ ОЗУ. Если вы используете GitLab CE со всеми сопутствующими сервисами (PostgreSQL, Redis, Puma), 4 ГБ — это практический минимум, а 8 ГБ дают запас для параллельных заданий. Jenkins находится где-то посередине, и 2–4 ГБ обычно достаточно, если вы не выполняете много параллельных сборок.

Можно ли запускать CI/CD-пайплайн и производственное приложение на одном VPS?

Можно, но с оговоркой. CI-сборки, особенно компиляция и тестовые наборы, интенсивно используют CPU и память. Если сборка вызовет скачок использования ресурсов в то время, когда производственное приложение обрабатывает трафик, пострадают оба. Для небольших проектов и личных сайтов совместное использование VPS подходит. Для всего, что критично для бизнеса, разделите CI-раннер и производственную среду, даже если оба находятся на разных VPS-экземплярах одного провайдера.

Какой CI/CD-инструмент лучше всего подходит для проекта одного разработчика?

GitHub Actions с self-hosted раннером — это самая простая отправная точка, если ваш код находится на GitHub. Не нужно устанавливать дополнительное серверное ПО, только легковесный агент на вашем VPS. GitLab CI стоит дополнительных усилий по настройке, если вы также хотите частный Git-хостинг и встроенный реестр контейнеров. Jenkins обычно избыточен для одного разработчика, если у вас нет особых требований к плагинам или рабочему процессу.

Нужен ли Docker для запуска CI/CD-пайплайна на VPS?