Строгое разделение на сборку, релиз и запуск приложения
Строго разделяйте стадии сборки, релиза и запуска
Build, Release, Run — пятый фактор 12-Factor App. Принцип гласит:
Жизненный цикл приложения должен быть строго разделён на три стадии:
- Build — сборка артефакта из кода
- Release — объединение артефакта с конфигурацией
- Run — запуск процесса приложения
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ BUILD │ → │ RELEASE │ → │ RUN │
│ │ │ │ │ │
│ Компиляция │ │ Артефакт + │ │ Запуск │
│ Зависимости │ │ Конфигурация │ │ процесса │
│ Тесты │ │ │ │ │
└─────────────┘ └──────────────┘ └─────────────┘
↓ ↓ ↓
Артефакт Релиз Процесс
(код + deps) (артефакт + config) (исполнение)
Что происходит:
Вход: исходный код из репозитория
Выход: артефакт (бинарный файл, Docker-образ, JAR-файл)
Примеры:
# Python: установка зависимостей
pip install -r requirements.txt
# Артефакт: исходный код + установленные пакеты
# Node.js: установка зависимостей и сборка
npm install
npm run build
# Артефакт: файлы в dist/ + node_modules
# Go: компиляция
go build -o myapp
# Артефакт: бинарный файл myapp
# Docker: сборка образа
docker build -t myapp:latest .
# Артефакт: Docker-образЧто происходит:
Вход: артефакт + конфигурация (переменные окружения)
Выход: релиз (готовая к запуску версия)
Пример:
# Артефакт: Docker-образ myapp:latest
# Конфигурация:
# - DATABASE_URL=postgres://prod-db/app
# - SECRET_KEY=prod-secret
# - DEBUG=false
# Релиз: контейнер с применённой конфигурацией
docker run -d \
-e DATABASE_URL=postgres://prod-db/app \
-e SECRET_KEY=prod-secret \
-e DEBUG=false \
myapp:latestВажно: один и тот же артефакт может быть использован для разных релизов:
# Один артефакт (Docker-образ), разные релизы:
# Staging релиз
docker run -e DATABASE_URL=postgres://staging-db/app myapp:latest
# Production релиз
docker run -e DATABASE_URL=postgres://prod-db/app myapp:latestЧто происходит:
Вход: релиз (артефакт + конфигурация)
Выход: работающее приложение
Пример:
# Запуск процесса
python app.py
gunicorn app:app
docker start container_id
kubectl apply -f deployment.yamlВажно: на стадии Run код и конфигурация неизменны. Для изменений требуется новый Release.
# ❌ Неправильно: сборка при запуске
#!/bin/bash
# run.sh
# Установка зависимостей при каждом запуске
pip install -r requirements.txt
# Миграции БД при запуске
python manage.py migrate
# Сборка статики при запуске
npm run build
# Запуск приложения
python app.pyПроблемы:
# ✅ BUILD (сборка артефакта)
docker build -t myapp:abc123 .
# ✅ RELEASE (создание релиза)
# Релиз = образ myapp:abc123 + конфигурация
# ✅ RUN (запуск)
docker run -d myapp:abc123Преимущества:
Артефакт — это результат стадии Build:
| Язык | Артефакт |
|---|---|
| Python | Исходный код + установленные пакеты |
| Node.js | Файлы в dist/ + node_modules |
| Go | Бинарный файл |
| Java | JAR/WAR файл |
| Любой | Docker-образ |
# Семантическое версионирование
docker build -t myapp:1.0.0 .
docker build -t myapp:1.1.0 .
docker build -t myapp:2.0.0 .
# Git-коммиты как версии
docker build -t myapp:abc123 . # Короткий хэш коммита
docker build -t myapp:abc123def456 .
# Временные метки
docker build -t myapp:2024-01-15-10-30-00 .# Docker Registry
docker push registry.example.com/myapp:1.0.0
# AWS ECR
docker push 123456789.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0.0
# GitHub Packages
docker push ghcr.io/username/myapp:1.0.0
# Nexus, Artifactory
mvn deploy # Для Java# .github/workflows/deploy.yml
name: Build, Release, Deploy
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
- name: Run tests
run: docker run myapp:${{ github.sha }} pytest
- name: Push to registry
run: |
docker tag myapp:${{ github.sha }} registry.example.com/myapp:${{ github.sha }}
docker push registry.example.com/myapp:${{ github.sha }}
release:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to staging
run: |
kubectl set image deployment/myapp \
myapp=registry.example.com/myapp:${{ github.sha }} \
--namespace=staging
- name: Run integration tests
run: ./run-integration-tests.sh
- name: Deploy to production
run: |
kubectl set image deployment/myapp \
myapp=registry.example.com/myapp:${{ github.sha }} \
--namespace=production
run:
needs: release
runs-on: ubuntu-latest
steps:
- name: Health check
run: |
curl -f https://api.example.com/health || exit 1┌─────────────────────────────────────────────────────────┐
│ GitHub Push │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ BUILD: docker build, test, push to registry │
│ Артефакт: myapp:abc123 │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ RELEASE: kubectl set image (staging) │
│ Релиз: myapp:abc123 + staging-config │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ Integration Tests │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ RELEASE: kubectl set image (production) │
│ Релиз: myapp:abc123 + production-config │
└────────────────────┬────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ RUN: приложение обрабатывает запросы │
│ Health checks, monitoring │
└─────────────────────────────────────────────────────────┘
# ❌ Неправильно: откат через изменение кода
git revert HEAD
git push
# Ждать 30 минут, пока CI/CD соберёт новый артефакт# ✅ Правильно: запуск предыдущего артефакта
kubectl set image deployment/myapp myapp=registry.example.com/myapp:prev-commit
# Или через Docker
docker stop myapp
docker run -d registry.example.com/myapp:prev-commitПреимущества:
# ❌ Неправильно: Dockerfile
FROM python:3.12
COPY . /app
WORKDIR /app
# Сборка при каждом запуске контейнера!
CMD pip install -r requirements.txt && python app.pyПроблема: каждый запуск контейнера устанавливает зависимости заново.
Правильно:
# ✅ Правильно: сборка на стадии Build
FROM python:3.12
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt # Сборка
COPY . .
CMD ["python", "app.py"] # Только запуск# ❌ Неправильно: app.py
from flask import Flask
import subprocess
app = Flask(__name__)
# Миграции при каждом запуске!
subprocess.run(['python', 'manage.py', 'migrate'])
@app.route('/')
def index():
return 'Hello'Проблема: миграции должны выполняться на стадии Release, а не Run.
Правильно:
# CI/CD пайплайн:
# 1. Build
docker build -t myapp:latest .
# 2. Release (миграции перед запуском)
kubectl run migration --image=myapp:latest -- python manage.py migrate
# 3. Run
kubectl set image deployment/myapp myapp=myapp:latest# ❌ Неправильно: прямое редактирование на сервере
ssh user@server
vim /var/www/app.py
systemctl restart myappПроблема: изменения не отслеживаются, невозможно воспроизвести.
Правильно:
# Изменение кода → новый Build → новый Release
git commit -m "Fix bug"
git push
# CI/CD автоматически соберёт и развернёт новый артефакт# ❌ Неправильно: SCP на сервер
scp app.py user@server:/var/www/
scp -r node_modules/ user@server:/var/www/Проблема: нет версионирования, невозможно откатиться.
Правильно:
# Использование registry
docker build -t myapp:1.0.0 .
docker push registry.example.com/myapp:1.0.0
# Развёртывание из registry
kubectl set image deployment/myapp myapp=registry.example.com/myapp:1.0.0# BUILD
docker build -t myapp:dev .
# RELEASE (локальная конфигурация)
docker run -d \
-e DATABASE_URL=postgres://localhost/dev \
-e DEBUG=true \
myapp:dev
# RUN
# Приложение запущено, обработка запросов# BUILD (тот же артефакт)
docker build -t myapp:abc123 .
docker push registry.example.com/myapp:abc123
# RELEASE (staging конфигурация)
kubectl set image deployment/myapp \
myapp=registry.example.com/myapp:abc123 \
--namespace=staging
# RUN
# Staging окружение доступно для тестирования# BUILD (тот же артефакт)
# myapp:abc123 уже в registry
# RELEASE (production конфигурация)
kubectl set image deployment/myapp \
myapp=registry.example.com/myapp:abc123 \
--namespace=production
# RUN
# Production окружение обрабатывает запросы пользователей| Стадия | Инструменты |
|---|---|
| Build | Docker, Maven, Gradle, npm, pip, go build |
| Release | Kubernetes, Docker Compose, ECS, Heroku |
| Run | systemd, supervisord, Kubernetes, Docker |
| CI/CD | GitHub Actions, GitLab CI, Jenkins, ArgoCD |
| Registry | Docker Hub, AWS ECR, GitHub Packages, Nexus |
Задайте себе вопросы:
# ❌ Скрипт развёртывания
#!/bin/bash
cd /var/www/app
# Сборка при развёртывании
git pull
npm install
npm run build
python manage.py migrate
# Перезапуск
systemctl restart myappПроблемы:
# ✅ BUILD (CI/CD)
docker build -t myapp:abc123 .
docker push registry.example.com/myapp:abc123
# ✅ RELEASE
kubectl set image deployment/myapp myapp=registry.example.com/myapp:abc123
# ✅ RUN
# Приложение запущено за секундыРезультат:
Ключевой вывод: Строгое разделение на Build, Release, Run обеспечивает воспроизводимость, быстрый откат и предсказуемость развёртываний. Артефакт собирается один раз и используется для всех сред.
Вопросы ещё не добавлены
Вопросы для этой подтемы ещё не добавлены.