Реалізація 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 робочих днів.







