CI/CD на GitHub Actions
CI/CD на GitHub Actions
Цель: настроить полный цикл CI/CD для приложения, лежащего на GitHub.
Сначала — деплой на один сервер по SSH, потом — масштабирование на несколько.
Всё расписано максимально подробно, с подсказками и примерами.
🧭 0. Общая картина (что мы строим)
- CI (Continuous Integration)
Проверка кода на каждом push/PR: сборка, тесты, линтеры. - CD (Continuous Delivery / Deployment)
Автоматический деплой на сервер при изменениях вmain. - Безопасность и воспроизводимость
Секреты в GitHub Secrets, доступ по SSH-ключу, откаты и логи.
⚙️ 1. Предварительные требования
1.1 GitHub репозиторий
- Репозиторий с вашим приложением (Node.js, Python, Go и т.д.)
- В корне создайте директорию
.github/workflows/для YAML-файлов.
1.2 Сервер
- Публичный IP или домен (например, Ubuntu 22.04)
- Пользователь
deployбез пароля sudo - Порт SSH открыт (обычно 22)
1.3 Способ деплоя
Вариант A: rsync + systemd (просто, но ручнее)
Вариант B: Docker + docker-compose (современно, легко масштабируется)
➡️ Начни с варианта B — он лучше подходит для CI/CD.
🧑💻 2. Подготовка сервера
2.1 Создать пользователя и добавить SSH-ключ
adduser deploy
usermod -aG sudo deploy
su - deploy
mkdir -p ~/.ssh && chmod 700 ~/.ssh
nano ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
Создай ключ для GitHub Actions:
ssh-keygen -t ed25519 -C "github-actions" -f ./github_actions_ed25519
github_actions_ed25519.pub→ добавь на сервер в~/.ssh/authorized_keysgithub_actions_ed25519→ сохрани, позже добавим в Secrets
2.2 Установить Docker и docker-compose
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | \
sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker deploy
sudo systemctl enable docker --now
Проверка:
docker version
docker compose version
2.3 Подготовить директорию приложения
su - deploy
mkdir -p ~/apps/myapp
cd ~/apps/myapp
Сюда положим docker-compose.yml.
🔐 3. Добавление секретов в GitHub
Открой Repository → Settings → Secrets and variables → Actions
Добавь:
| Имя | Описание |
|---|---|
SSH_HOST |
IP сервера |
SSH_USER |
deploy |
SSH_PORT |
22 |
SSH_PRIVATE_KEY |
содержимое приватного ключа |
REGISTRY |
например ghcr.io/<user> |
REGISTRY_USERNAME |
логин от реестра |
REGISTRY_PASSWORD |
пароль или токен |
💡 Все пароли и ключи хранятся только в Secrets, не в коде!
📂 4. Структура проекта
myapp/
├─ .github/
│ └─ workflows/
│ └─ ci-cd.yml
├─ docker-compose.yml
├─ Dockerfile
└─ (исходники приложения)
Пример Dockerfile (Node.js)
FROM node:20-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY --from=build /app/dist ./dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
Пример docker-compose.yml
version: "3.9"
services:
web:
image: ghcr.io/yourorg/myapp:latest
restart: always
env_file:
- .env
ports:
- "80:3000"
✅ 5. Настройка CI (Continuous Integration)
Файл: .github/workflows/ci-cd.yml
name: CI-CD
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- name: Install dependencies
run: npm ci
- name: Lint
run: npm run lint --if-present
- name: Test
run: npm test --if-present
- name: Build
run: npm run build --if-present
🚀 6. CD (Continuous Deployment)
Добавь в тот же ci-cd.yml:
cd:
needs: [ci]
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Log in to Docker registry
uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- name: Build & push Docker image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ secrets.REGISTRY }}/myapp:latest
${{ secrets.REGISTRY }}/myapp:${{ github.sha }}
- name: Prepare SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Deploy to server
run: |
ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
"cd ~/apps/myapp && \
docker login ${{ secrets.REGISTRY }} -u ${{ secrets.REGISTRY_USERNAME }} -p ${{ secrets.REGISTRY_PASSWORD }} && \
docker compose pull && \
docker compose up -d && \
docker image prune -f"
🧠 7. Что происходит при деплое
- GitHub Actions подключается по SSH к серверу.
- Логинится в Docker Registry.
- Тянет новый образ.
- Перезапускает контейнеры.
- Удаляет старые, неиспользуемые образы.
✅ В итоге, каждое изменение в main автоматически обновляет сервер.
📈 8. Масштабирование на несколько серверов
Добавь matrix в job:
strategy:
fail-fast: false
matrix:
server:
- { host: "203.0.113.10", port: "22" }
- { host: "203.0.113.11", port: "22" }
и замени команду деплоя:
ssh -p ${{ matrix.server.port }} ${{ secrets.SSH_USER }}@${{ matrix.server.host }} ...
Так один job задеплоит приложение на все сервера.
🔍 9. Частые ошибки
| Проблема | Решение |
|---|---|
Permission denied |
Проверь права ключа (chmod 600) и пользователя deploy |
docker compose not found |
Используй docker-compose или установи плагин |
| Образ не обновляется | Используй тег ${{ github.sha }} |
| Secrets не видны | Деплой разрешён только из main |
🧾 10. Контрольный чек-лист
- ☑️ Создан пользователь
deploy - ☑️ Настроен SSH-доступ
- ☑️ Docker установлен
- ☑️
docker-compose.ymlлежит в~/apps/myapp - ☑️ Secrets заполнены
- ☑️
.github/workflows/ci-cd.ymlсоздан - ☑️ Деплой из
mainработает
🚧 11. Что улучшить потом
- Добавить staging окружение
- Реализовать ручное подтверждение деплоя
- Добавить healthcheck после деплоя
- Добавить миграции БД в пайплайн
- Подключить мониторинг (Grafana, Loki)
📘 12. Минимальный рабочий пример (целиком)
name: CI-CD
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm test --if-present
- run: npm run build --if-present
cd:
needs: [ci]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ secrets.REGISTRY }}
username: ${{ secrets.REGISTRY_USERNAME }}
password: ${{ secrets.REGISTRY_PASSWORD }}
- uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ secrets.REGISTRY }}/myapp:latest
${{ secrets.REGISTRY }}/myapp:${{ github.sha }}
- name: Prepare SSH
run: |
mkdir -p ~/.ssh
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_HOST }} >> ~/.ssh/known_hosts
- name: Deploy
run: |
ssh -p ${{ secrets.SSH_PORT }} ${{ secrets.SSH_USER }}@${{ secrets.SSH_HOST }} \
"cd ~/apps/myapp && \
docker login ${{ secrets.REGISTRY }} -u ${{ secrets.REGISTRY_USERNAME }} -p ${{ secrets.REGISTRY_PASSWORD }} && \
docker compose pull && \
docker compose up -d && \
docker image prune -f"
✅ Итог
Теперь ты можешь:
- Автоматизировать сборку и тестирование проекта
- Автоматически обновлять приложение на сервере
- Расширить систему на несколько серверов
- Постепенно добавить staging, мониторинг и миграции
💬 Если нужно — можно добавить шаблон для Python, Go или Ruby-приложений в этом же формате.
📚 Дополнительные ресурсы
Версия документа: 1.0
Дата последнего обновления: 26 октября 2025
Автор: DevOps Community