Реализация параллельной работы старого и нового сайта (A/B-миграция)
A/B-миграция — подход, при котором старый и новый сайт работают одновременно. Трафик постепенно переключается с одного на другой. Позволяет обнаружить проблемы на малой аудитории до полного переключения.
Архитектура A/B-миграции
DNS/CDN → Load Balancer (nginx/Cloudflare)
↓
┌────────────┴────────────┐
│ │
Old Site (90%) New Site (10%)
old-site:8080 new-site:8081
Критически важно: обе системы должны работать с одной базой данных (или синхронизировать данные в реальном времени).
Вариант 1: Nginx weighted upstream
upstream site_upstream {
server old-site:8080 weight=9;
server new-site:8081 weight=1; # 10% трафика
}
server {
listen 80;
server_name company.com;
proxy_pass http://site_upstream;
}
Постепенное переключение: 10% → 25% → 50% → 90% → 100% с интервалами по несколько дней.
Вариант 2: Cookie-based routing (стабильный опыт)
Пользователь, попавший на новый сайт, остаётся на нём при последующих визитах:
split_clients "${remote_addr}${http_user_agent}" $new_site_user {
10% "yes"; # 10% пользователей → новый сайт
* "";
}
server {
set $upstream_server "old-site:8080";
# Если уже помечен — направить на новый
if ($cookie_site_version = "new") {
set $upstream_server "new-site:8081";
}
# Если попал в 10% — направить на новый и поставить cookie
if ($new_site_user = "yes") {
set $upstream_server "new-site:8081";
add_header Set-Cookie "site_version=new; Path=/; Max-Age=86400; SameSite=Lax";
}
proxy_pass http://$upstream_server;
}
Вариант 3: Cloudflare Workers для гибкого роутинга
// cloudflare-worker.js
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
async function handleRequest(request) {
const url = new URL(request.url)
const cookie = request.headers.get('Cookie') || ''
// Уже имеет версию
if (cookie.includes('site_version=new')) {
return fetch(NEW_SITE_URL + url.pathname + url.search, request)
}
if (cookie.includes('site_version=old')) {
return fetch(OLD_SITE_URL + url.pathname + url.search, request)
}
// Распределить новых пользователей (хэш по IP)
const clientIP = request.headers.get('CF-Connecting-IP') || ''
const hash = await hashString(clientIP)
const percentage = 10 // 10% на новый сайт
const useNew = (hash % 100) < percentage
const targetUrl = useNew ? NEW_SITE_URL : OLD_SITE_URL
const response = await fetch(targetUrl + url.pathname + url.search, {
...request,
headers: request.headers
})
const newHeaders = new Headers(response.headers)
const cookieValue = useNew ? 'new' : 'old'
newHeaders.append('Set-Cookie', `site_version=${cookieValue}; Path=/; Max-Age=86400; SameSite=Lax`)
return new Response(response.body, {
status: response.status,
headers: newHeaders
})
}
Синхронизация данных между сайтами
Если старый и новый сайт имеют разные БД, данные должны синхронизироваться:
# Webhook на старом сайте при создании/обновлении контента
def on_content_updated(post_id, event_type):
payload = serialize_post(post_id)
# Отправить на новый сайт
requests.post(
'http://new-site-internal/api/sync/content',
json={
'event': event_type, # 'created', 'updated', 'deleted'
'data': payload,
'timestamp': time.time()
},
headers={'X-Sync-Key': SYNC_SECRET}
)
Или через общую очередь:
# RabbitMQ/Redis Streams как шина между сайтами
publisher.publish('content.updated', {
'legacy_id': post_id,
'action': 'update',
'data': post_data
})
Мониторинг во время A/B-миграции
Сравнивать метрики обеих версий:
# Grafana dashboard: Old vs New side by side
panels:
- title: "Response Time (p95)"
queries:
- {expr: "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{app='old-site'}[5m]))", legend: "Old Site"}
- {expr: "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{app='new-site'}[5m]))", legend: "New Site"}
- title: "Error Rate"
queries:
- {expr: "rate(http_requests_total{app='old-site',status=~'5..'}[5m])", legend: "Old Errors"}
- {expr: "rate(http_requests_total{app='new-site',status=~'5..'}[5m])", legend: "New Errors"}
Алерт при аномалии на новом сайте:
- alert: NewSiteErrorSpike
expr: |
rate(http_requests_total{app="new-site",status=~"5.."}[2m]) >
2 * rate(http_requests_total{app="old-site",status=~"5.."}[2m])
for: 3m
annotations:
summary: "New site error rate 2x higher than old site"
Критерии полного переключения
Перед переводом 100% трафика на новый сайт:
- Error rate ≤ error rate старого сайта
- p95 response time ≤ p95 старого сайта
- Нет регрессии в конверсии (по GA4/Metrika)
- Все критичные функции протестированы на реальных пользователях
- Команда готова к быстрому rollback
Rollback
# Мгновенный rollback через nginx
# Изменить weight: old-site weight=10, new-site weight=0
nginx -s reload
# Или через Cloudflare: изменить Worker environment variable NEW_SITE_PERCENTAGE=0
Срок выполнения
Настройка A/B-миграции с постепенным переключением трафика, синхронизацией данных и мониторингом — 4–7 рабочих дней.







