Production ենթակառուցվածք՝ լավագույն setup-ը յուրաքանչյուր փուլի համար
Reference ճարտարապետություններ՝ մեկ լավ սպասարկվող VPS-ից մինչև Kubernetes, և ինչպես անվտանգ անցնել դրանց միջև։
30 վայրկյանում
Չկա մեկ լավագույն ենթակառուցվածք, կա միայն ճիշտը քո ընթացիկ traffic-ի, թիմի և ռիսկի համար։ Պրոդուկտների մեծ մասը պետք է սկսի ճիշտ արված մեկ սերվերից, հետո տվյալների բազան և ֆայլերը տեղափոխի managed ծառայություններ, հետո ավելացնի load balancer և ավելի շատ app սերվերներ, և միայն այն ժամանակ ընդունի կոնտեյներների orchestration, երբ թիմին և մասշտաբին դա իսկապես պետք է։ Բոլոր փուլերում էականը նույնն է՝ առանձին միջավայրեր, կառավարվող secret-ներ, ստուգված backup-ներ, monitoring alert-ներով և պլանավորված migration-ներ, որպեսզի ոչ մի տվյալ չկորչի։
Ինչու եմ օգտագործում
- Production եմ վարում իմ և հաճախորդներիս Laravel և Next.js նախագծերի համար՝ ընդհանուր պանելային սերվերներից մինչև multi-tenant SaaS-ի առանձին սերվեր, ուստի ճարտարապետությունն ընտրում եմ ըստ փուլի, ոչ թե ըստ մոդայի։
- Տեսել եմ, թե որքան գումար և ուշադրություն է վատնում չափից ավելի կառուցված ենթակառուցվածքը փոքր նախագծերում, և որքան փխրուն են թերի կառուցված setup-ները առանց backup-ների և monitoring-ի։
- Ներկայումս ինքս եմ միավորում սերվերները, ուստի այս դասի migration playbook-ը հենց այն է, որին իրականում հետևում եմ։
Ինչպես է աշխատում
01Սկզբունքներ՝ մինչև դիագրամները
Լավ ենթակառուցվածքը ձանձրալի է՝ կանխատեսելի deploy-ներ, հստակ պատասխանատվություն և ձախողումներ, որոնք նկատվում են և վերականգնելի են։ Գործիքներ ընտրելուց առաջ պատասխանիր չորս հարցի։ Որքա՞ն downtime կարող է հանդուրժել բիզնեսը (availability target)։ Որքա՞ն տվյալ կարող է այն կորցնել (RPO), և որքա՞ն արագ պետք է վերականգնվի (RTO)։ Ո՞վ է այն շահագործելու գիշերվա ժամը 3-ին։ Ինչքա՞ն է ամսական բյուջեն։ Հետո աճիր քայլերով։ Յուրաքանչյուր քայլ պետք է վերացնի իրական bottleneck կամ ռիսկ, որը կարող ես անվանել՝ տվյալների բազան, որը կռվում է հավելվածի հետ հիշողության համար, մեկ սկավառակի վրա խրված upload-ներ, մեկ սերվեր, որը single point of failure է, կամ թիմ, որը deploy է անում տասնյակ ծառայություններ։ Հաջորդ փուլին չափազանց վաղ անցնելը ավելացնում է ծախս և բարդություն՝ առանց օգուտի, իսկ չափազանց ուշ անցնելը նշանակում է outage-ներ։ Ինչ փուլ էլ լինի, ենթակառուցվածքը նկարագրված պահիր կոդով կամ գոնե գրավոր runbook-ներով, որպեսզի այն կարելի լինի վերակառուցել։
02Փուլ 1՝ ճիշտ արված մեկ VPS
MVP-ի, փոքր բիզնեսի կայքի կամ ներքին գործիքի համար մեկ լավ կարգավորված սերվերը (2-ից 8 GB RAM) հաճախ լավագույն ընտրությունն է՝ էժան, արագ, հեշտ հասկանալի։ nginx-ը terminate է անում HTTPS-ը և route է անում հավելվածներին, PHP-FPM-ը և Node-ը աշխատում են իրենց օգտատերերով, տվյալների բազան և Redis-ը լսում են localhost-ում, worker-ները աշխատում են supervisor-ի կամ systemd-ի տակ։ Docker Compose-ը լավ տարբերակ է այս ամենը վերարտադրելի փաթեթավորելու համար։ Այն «ճիշտ արված» դարձնողը հավելվածների շուրջ ամեն ինչն է՝ hardening baseline-ը, Cloudflare-ը առջևում, տվյալների բազայի գիշերային dump-երը՝ պատճենված սերվերից դուրս, provider-ի snapshot-ները, uptime-ի և սկավառակի alert-ները և script արված deploy release թղթապանակներով։ Դրա թուլությունն այն է, որ այն single point of failure է, ուստի վերականգնման արագությունը կարևոր է․ պետք է կարողանաս այն վերակառուցել script-երից և backup-ներից ժամերի ընթացքում, և պետք է դա գոնե մեկ անգամ փորձած լինես։
- Cloudflare (DNS, WAF, CDN) → nginx → app պրոցեսներ → լոկալ DB/Redis։
- Backup-ները off-site, snapshot-ները միացված, restore-ները ստուգված։
- Տիպիկ արժեք՝ ամսական տասնյակ դոլարներ։
03Փուլ 2՝ app սերվեր + managed տվյալների բազա + object storage + CDN
Երբ տվյալները արժեքավոր են դառնում, կամ տվյալների բազային ավելի շատ հիշողություն է պետք, state-ը տեղափոխիր app սերվերից դուրս։ Managed տվյալների բազան բերում է ավտոմատ backup-ներ, point-in-time recovery և ոչ պարտադիր standby։ Օգտատերերի upload-ները գնում են S3-համատեղելի object storage լոկալ սկավառակի փոխարեն և սպասարկվում են CDN-ի միջոցով։ Session-ները և cache-ը գնում են Redis՝ managed կամ private սերվերի վրա։ App սերվերն այժմ չի պահում ոչ մի եզակի տվյալ, և դա է հիմնական փոփոխությունը․ կարող ես այն վերակառուցել կամ փոխարինել ցանկացած պահի, resize անել քիչ ռիսկով և հետագայում ավելացնել երկրորդը։ Կոնֆիգուրացիան և secret-ները գալիս են environment փոփոխականներից, և deploy-ները կառուցում են նույն artifact-ը staging-ի և production-ի համար։ Այս փուլը շատ շահութաբեր պրոդուկտների համար «ոսկե միջինն» է և լավ caching-ի ու index-ների դեպքում կարող է զարմանալի քանակությամբ traffic տանել։
- App սերվերը stateless է՝ միայն կոդ + config։
- Տվյալների բազա՝ managed, հասանելիություն միայն private ցանցով, PITR-ը միացված։
- Ֆայլեր՝ object storage + CDN․ upload-ներ սերվերի սկավառակի վրա չկան։
04Փուլ 3՝ load balancer, մի քանի app սերվեր, replica-ներ և queue-եր
Երբ մեկ app սերվերն այլևս բավարար չէ, կամ բիզնեսը պետք է գոյատևի սերվերի ձախողումից, երկու կամ ավելի նույնական app սերվերների առջևում դիր load balancer, իդեալում՝ տարբեր availability zone-ներում։ Load balancer-ը health-check է անում դրանք և դադարեցնում է traffic ուղարկել կոտրվածին։ Deploy-ները անցնում են սերվերներով մեկ առ մեկ։ Տվյալների բազան ստանում է read replica-ներ ծանր կարդալու traffic-ի և հաշվետվությունների համար, և standby՝ failover-ի համար։ Դանդաղ աշխատանքը (email-ներ, PDF-ներ, նկարների մշակում, webhook-ներ, import-ներ) տեղափոխվում է background queue-եր՝ առանձին worker սերվերներով, որպեսզի վեբ request-ները արագ մնան, և պոռթկումները կլանվեն։ Զգույշ եղիր թաքնված state-ի հետ․ ֆայլերի upload-ները, session-ները, պլանավորված job-երը (scheduler-ը գործարկիր միայն մեկ node-ի վրա կամ օգտագործիր lock) և in-memory cache-երը բոլորը պետք է լինեն ընդհանուր կամ համակարգված։ Replication lag-ը նաև նշանակում է, որ օգտատերը կարող է replica-ից անմիջապես չկարդալ այն, ինչ հենց նոր գրել է։
05Փուլ 4՝ կոնտեյներներ և Kubernetes
Կոնտեյներները յուրաքանչյուր ծառայություն փաթեթավորում են իր dependency-ների հետ, ուստի նույն image-ը աշխատում է laptop-ի վրա, CI-ում և production-ում։ Docker Compose-ը մեկ կամ մի քանի սերվերի վրա շատ բան է ծածկում։ Kubernetes-ը (կամ managed այլընտրանք, ինչպես ECS-ը, Cloud Run-ը կամ managed Kubernetes ծառայությունը) ավելացնում է scheduling շատ մեքենաների վրա, self-healing, rolling update-ներ, autoscaling և service discovery։ Այն իրեն արդարացնում է, երբ գործարկում ես շատ ծառայություններ, մի քանի թիմ deploy է անում անկախ, կամ traffic-ը խիստ տատանվում է։ Գինը իրական է՝ պլատֆորմ, որը պետք է սովորել և շահագործել, ավելի շատ YAML, ցանցը և անվտանգությունը սխալ կարգավորելու ավելի շատ ձևեր և սովորաբար ավելի բարձր հաշիվ։ Managed control plane-ը հեռացնում է ցավի մի մասը, բայց ոչ workload-երի բարդությունը։ Շատ հաջողված պրոդուկտների այն երբեք պետք չի գալիս․ ընդունիր այն, երբ դրա բացակայության համակարգման ծախսը հստակորեն ավելի բարձր է։
06Միջավայրեր, secret-ներ, DR, monitoring և ծախսեր
Յուրաքանչյուր փուլի պետք է առնվազն staging միջավայր, որը տարբերակներով և կոնֆիգուրացիայով կրկնում է production-ը, իր տվյալների բազայով և credential-ներով, որպեսզի փոփոխությունները ստուգվեն, մինչև հաճախորդները կտեսնեն դրանք։ Secret-ները ներարկվում են ըստ միջավայրի env ֆայլերից կամ secrets manager-ից, երբեք չեն կիսվում staging-ի և production-ի միջև, երբեք Git-ում չեն։ Disaster recovery-ն նշանակում է գրավոր, փորձարկված քայլեր՝ որտեղ են backup-ները, ինչպես վերակառուցել սերվերը, ինչպես failover անել տվյալների բազան, ով է որոշում։ Monitoring-ն ունի երեք մաս՝ մետրիկաներ (CPU, հիշողություն, սկավառակ, latency, սխալների մակարդակ, queue-ի երկարություն, օրինակ՝ Prometheus-ով և Grafana-ով), կենտրոնացված հավաքված log-եր (Loki կամ hosted ծառայություն) և alert-ներ, որոնք ինչ-որ մեկին արթնացնում են միայն օգտատերերի վրա իրական ազդեցության դեպքում, գումարած արտաքին uptime ստուգումներ։ Ծախսերը վերանայիր ամեն ամիս․ չօգտագործվող սերվերները, չափազանց մեծ instance-ները, հին snapshot-ները և egress-ի վճարները լուռ կուտակվում են։
- dev → staging → production, նույն build artifact-ը առաջ է տեղափոխվում։
- Alert՝ ըստ ախտանիշների (սխալներ, latency, downtime), հետաքննություն՝ մետրիկաներով և log-երով։
- Ռեսուրսները tag արա ըստ նախագծի և միջավայրի՝ տեսնելու, թե ուր է գնում գումարը։
07Սերվերների տեղափոխում կամ միավորում առանց տվյալների կորստի
Կայքերը նոր սերվեր տեղափոխելը կամ մի քանի սերվեր ավելի քչի մեջ միավորելը հիմնականում ուշադիր հաշվառում է։ Նախ inventory՝ հին սերվերի յուրաքանչյուր domain, հավելված, տվյալների բազա, cron job, worker, certificate, mailbox, environment ֆայլ և upload թղթապանակ։ Նոր սերվերը կառուցիր քո baseline-ից, հետո պատճենիր կոդը, ֆայլերը և տվյալների բազաները և ստուգիր․ գործարկիր հավելվածները hosts-file override-ով, համեմատիր տողերի և ֆայլերի քանակները, ստուգիր log-երը։ Մեկ օր առաջ իջեցրու DNS TTL-ը։ Cutover-ի ժամանակ հին հավելվածը կարճ ժամանակով դիր maintenance ռեժիմի, գործարկիր ֆայլերի վերջնական incremental sync և տվյալների բազայի թարմ dump, import արա այն, հետո փոխարկիր DNS-ը (Cloudflare proxy-ով՝ ակնթարթորեն)։ Հին սերվերը թող աշխատի, անձեռնմխելի, մինչև նորի վրա traffic-ը, տվյալները և backup-ները օրերով ստուգվեն․ միայն դրանից հետո շահագործումից հանիր այն՝ վերջնական backup-ից հետո։
# 1. Initial copy of code and uploads (preserves owners, permissions, links)
rsync -aHAX --info=progress2 [email protected]:/var/www/shop/ /var/www/shop/
# 2. At cutover: maintenance mode on the old server, then final delta sync
ssh [email protected] 'cd /var/www/shop && php artisan down'
rsync -aHAX --delete [email protected]:/var/www/shop/storage/ /var/www/shop/storage/
# 3. Fresh database copy streamed directly to the new server
ssh [email protected] 'mysqldump --single-transaction --routines --triggers shop_db' | mysql shop_db
# 4. Verify before switching DNS
mysql -e "SELECT COUNT(*) FROM shop_db.orders;"
ssh [email protected] "mysql -e 'SELECT COUNT(*) FROM shop_db.orders;'"
curl -sI --resolve example.com:443:127.0.0.1 https://example.com | head -1
Օգտագործեք, երբ
- Փուլ 1՝ MVP-ների, փոքր բիզնեսների և ներքին գործիքների համար՝ համեստ traffic-ով և բյուջեով։
- Փուլ 2՝ երբ տվյալները արժեքավոր են, կամ upload-ները և տվյալների բազայի հիշողությունը դուրս են գալիս մեկ մեքենայի սահմաններից։
- Փուլ 3՝ երբ պետք է գոյատևել սերվերի ձախողումից, կամ մեկ app սերվերն այլևս բավարար չէ։
- Փուլ 4՝ երբ շատ ծառայություններ և թիմեր deploy են անում անկախ, և պլատֆորմի ծախսն արդարացված է։
Մի՛ օգտագործեք, երբ
- Մի սկսիր Kubernetes-ով կամ microservice-ներով մի պրոդուկտի համար, որը դեռ չի գտել իր օգտատերերին։
- Դանդաղ հավելվածը շտկելու համար սերվերներ մի ավելացրու՝ նախքան query-ները, index-ները և caching-ը ստուգելը։
- Սերվերները մի տեղափոխիր առանց inventory-ի և rollback ճանապարհի (հին սերվերը կենդանի պահված)։
Այլընտրանքներ
- PaaS (Laravel Cloud, Vercel, Render, Fly.io և նմանատիպեր)
Ենթակառուցվածքի մեծ մասը հոգացվում է քո փոխարեն․ միավորի ավելի բարձր գին և ավելի քիչ վերահսկողություն։
- Serverless ֆունկցիաներ
Scale to zero և վճարում ըստ request-ի․ cold start-եր, սահմանափակումներ և vendor-ին հատուկ pattern-ներ։
- Managed կոնտեյներներ (ECS, Cloud Run)
Կոնտեյներների orchestration՝ առանց Kubernetes-ը ինքդ գործարկելու․ տարածված միջանկյալ քայլ։
- Hosting պանելներ
Լավ են շատ փոքր կայքերի և mailbox-ների համար, սահմանափակ՝ custom, scale արված հավելվածների համար։
Լավագույն պրակտիկաներ
- Ընտրիր ամենապարզ փուլը, որը բավարարում է քո availability-ին, RPO/RTO-ին և բյուջեին, և գրի առ այդ թիրախները։
- App սերվերները վաղ դարձրու stateless՝ upload-ները object storage-ում, session-ները և cache-ը Redis-ում։
- Staging-ը պահիր production-ին մոտ և առաջ տար նույն build artifact-ը։
- Secret-ները բաժանիր ըստ միջավայրի և պահիր Git-ից դուրս։
- Off-site backup-ներ պլանավորված restore փորձարկումներով և գրավոր DR runbook։
- Alert՝ օգտատիրոջը տեսանելի ախտանիշների վրա, և պահիր մետրիկաներ ու կենտրոնական log-եր ախտորոշման համար։
- Հաշիվը վերանայիր ամեն ամիս և հեռացրու այն, ինչ ոչ ոք չի օգտագործում։
Տարածված սխալներ
- Over-engineering՝ cluster, service mesh և հինգ միջավայր հարյուր օգտատիրոջով հավելվածի համար։
- Under-engineering՝ մեկ սերվեր առանց off-site backup-ների և առանց monitoring-ի, որը տանում է վճարող բիզնես։
- App սերվերները horizontal scale անել, մինչ upload-ները և session-ները դեռ գտնվում են յուրաքանչյուր սերվերի սկավառակի վրա։
- Migration-ի ժամանակ DNS-ը փոխարկել առանց TTL-ն իջեցնելու, վերջնական sync-ի կամ ստուգման և հին սերվերը չափազանց շուտ ջնջել։
- Մեկ տվյալների բազա կամ credential-ների մեկ հավաքածու կիսել staging-ի և production-ի միջև։
Խորը վերլուծություն
08Հասանելիության մաթեմատիկա. «ինը»-եր, dependency-ներ և error budget-ներ
Հասանելիության թիրախը վերածվում է downtime-ի կոնկրետ թույլատրելի ծավալի։ 30-օրյա ամսվա ընթացքում 99.5 տոկոսը թույլ է տալիս մոտ 3.6 ժամ, 99.9 տոկոսը՝ մոտ 43 րոպե, իսկ 99.99 տոկոսը՝ մոտ 4.3 րոպե, ինչին ոչ մի մարդկային on-call գործընթաց չի կարող հասնել առանց ավտոմատ failover-ի։ Հաջորդաբար միացված dependency-ները բազմապատկվում են. եթե հավելվածին պետք են սերվերը, database-ը և payment API-ն, յուրաքանչյուրը 99.9 տոկոս, ամբողջ ճանապարհը մոտավորապես 99.7 տոկոս է։ Զուգահեռ ավելորդությունը բարձրացնում է այն. երկու անկախ app սերվեր, որոնցից յուրաքանչյուրը ձախողվում է ժամանակի 0.1 տոկոսում, միասին անհասանելի են ընդամենը մոտ 0.0001 տոկոս, եթե դրանց ձախողումները իսկապես անկախ են, ինչը հաճախ խախտում են ընդհանուր database-ները, ռեգիոնները կամ deploy-ները։ Ձեր SLO-ի և 100 տոկոսի միջև տարբերությունը error budget-ն է։ Երբ այն առողջ է, ֆունկցիաներն ավելի արագ release արեք, երբ սպառված է, դանդաղեցրեք և ներդրում արեք հուսալիության մեջ։ Սա հուսալիության շուրջ վեճերը վերածում է թվի, որի շուրջ համաձայն են և՛ բիզնեսը, և՛ ինժեներները։
09Deploy ռազմավարություններ և expand-contract migration-ներ
Rolling deploy-ը load balancer-ի հետևում instance-ները փոխարինում է մեկ առ մեկ. էժան է, բայց որոշ ժամանակ հին և նոր տարբերակները միասին են տրաֆիկ սպասարկում։ Blue-green-ը աշխատեցնում է ամբողջական երկրորդ միջավայր և տրաֆիկը փոխում է միանգամից՝ ակնթարթային rollback-ով հետ փոխելու միջոցով, release-ի ընթացքում կրկնակի արժեքով։ Canary-ն տրաֆիկի փոքր բաժին, ասենք 5 տոկոս, ուղարկում է նոր տարբերակին և լայնացնում է այն միայն այն դեպքում, եթե error rate-երը և latency-ն առողջ են մնում։ Բոլոր երեքը պահանջում են, որ կոդի երկու տարբերակ կարողանա աշխատել նույն database-ի դեմ։ Expand-contract pattern-ը դա հնարավոր է դարձնում. նախ expand (ավելացրեք նոր սյունակը կամ աղյուսակը՝ nullable, առանց որևէ բան հեռացնելու), deploy արեք կոդ, որը գրում է երկուսում էլ և կարդում նորից, backfill արեք խմբաքանակներով, և միայն հետագա release-ում contract արեք՝ հեռացնելով հին սյունակը։ Երբեք մի վերանվանեք կամ մի հեռացրեք սյունակը նույն release-ում, որը դադարում է այն օգտագործել։ Հենց այս կարգապահությունն է, ոչ թե deploy գործիքը, որ zero-downtime release-ները դարձնում է իրական։
- Release 1. ավելացրեք nullable սյունակ, deploy արեք կոդ, որը գրում է հինում և նորում։
- Backfill արեք փոքր խմբաքանակներով, ոչ պիկային ժամերին, առաջընթացը log անելով։
- Release 2. կարդացեք միայն նոր սյունակը։ Release 3. հեռացրեք հինը։
10Alert-ներ այն բանի վրա, ինչ զգում են օգտատերերը. SLO-ներ և burn rate-եր
CPU-ի կամ հիշողության վրա alert-ները աղմուկ են ստեղծում. 90 տոկոս CPU-ով սերվերը, որն արագ է սպասարկում յուրաքանչյուր հարցում, նորմալ է։ Փոխարենը մարդկանց page արեք ախտանիշների վրա՝ ախտորոշելիս օգտագործելով RED մեթոդը ծառայությունների համար (rate, errors, duration) և USE-ը ռեսուրսների համար (utilisation, saturation, errors)։ Ամենահուսալի pattern-ը Google SRE workbook-ի burn-rate alert-ն է. alert արեք, երբ error budget-ը սպառվում է շատ ավելի արագ, քան SLO-ն թույլ է տալիս, ստուգված երկար և կարճ պատուհանով, որպեսզի այն արագ գործարկվի իրական incident-ների ժամանակ և արագ լուծվի դրանցից հետո։ Մեկ ժամ պահպանվող 14.4 burn rate-ը սպառում է 30-օրյա budget-ի 2 տոկոսը, ինչն արժե ինչ-որ մեկին արթնացնել։ Ավելի դանդաղ burn-երը դառնում են ticket-ներ, ոչ թե page-եր։ Վերահսկեք Prometheus label-ների cardinality-ն. user ID-ները կամ ամբողջական URL-ները որպես label ստեղծում են միլիոնավոր series և կարող են վայր գցել մոնիթորինգի համակարգը հենց այն պահին, երբ այն ձեզ պետք է։
groups:
- name: slo
rules:
- alert: ErrorBudgetFastBurn
expr: |
(sum(rate(http_requests_total{status=~"5.."}[1h]))
/ sum(rate(http_requests_total[1h]))) > (14.4 * 0.001)
and
(sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))) > (14.4 * 0.001)
for: 2m
labels:
severity: page
annotations:
summary: "99.9% SLO error budget burning 14x too fast"
11Capacity planning Little-ի օրենքով
Little-ի օրենքն ասում է, որ համակարգում հարցումների միջին քանակը հավասար է ժամանման արագությանը բազմապատկած դրանում անցկացրած ժամանակով։ Վայրկյանում 200 հարցման և 250 ms միջին պատասխանի ժամանակի դեպքում ցանկացած պահի մոտ 50 հարցում ընթացքի մեջ է, ուստի ձեզ պետք է առնվազն 50 PHP-FPM worker կամ համարժեք concurrency՝ գումարած պաշար։ Եթե latency-ն կրկնապատկվում է, որովհետև database-ը դանդաղում է, նույն տրաֆիկին պետք է 100 worker, ահա թե ինչու են դանդաղ dependency-ները այդքան հանկարծակի սպառում worker pool-երը։ Պլանավորեք պիկերի, ոչ թե միջինների համար. շատ պրոդուկտներ պիկային ժամերին կամ արշավների ժամանակ տեսնում են միջինից երեքից տասն անգամ ավելի տրաֆիկ։ Կայուն վիճակում utilisation-ը պահեք մոտ 50-ից 70 տոկոս, որպեսզի ձախողված node-ը կամ պիկը չտապալի մնացածը։ Launch-երից առաջ load test արեք k6-ի նման գործիքով՝ ավելացնելով բեռը, մինչև latency-ն թեքվի, և գրանցեք, թե որտեղ է այն թեքվում. այդ թիվը ձեր իրական թողունակությունն է։ Արժեքը հետևում է նույն տրամաբանությանը. right-size արեք ըստ չափված պիկերի և հետևեք egress-ին ու storage-ին, որոնք լուռ աճում են։
Իրական իրավիճակներ
Արշավը կբերի սովորականից տասն անգամ ավելի տրաֆիկ
Խնդիր
Մարքեթինգը հայտարարում է ուրբաթ երեկոյան հեռուստագովազդի մասին։ Պրոդուկտն աշխատում է մեկ app սերվերի վրա managed database-ով, և ոչ ոք չգիտի, թե որքան բեռ այն կարող է վերցնել։
Ինչպես լուծել
Նախ չափեք. load test արեք staging-ը production-ին նման տվյալներով k6-ով՝ աստիճանաբար բարձրացնելով, մինչև latency-ն աճի, և նշեք, թե որ ռեսուրսն է հագենում։ Հետո կրճատեք յուրաքանչյուր հարցման աշխատանքը. landing էջերը և հրապարակային API պատասխանները քեշավորեք CDN-ում, nginx-ում ավելացրեք կարճ microcache, email-ները և webhook-ները տեղափոխեք հերթեր։ Bottleneck-ը ժամանակավոր scale արեք, օրինակ՝ ավելի մեծ app սերվեր կամ database instance, որը պատվիրված է մեկ օր առաջ։ Արշավի օրը սառեցրեք deploy-ները, պատրաստեք թեթև static fallback էջ, և սխալների, latency-ի, հերթի երկարության և database կապերի dashboard-ները դրեք մեկ էկրանի վրա։ Դրանից հետո համեմատեք իրական պիկը թեստի հետ՝ ապագա պլանավորումը ճշգրտելու համար։
Միակ production սերվերը մեռնում է գիշերվա ժամը 3-ին
Խնդիր
Provider-ը հաղորդում է hardware-ի ձախողման մասին։ VPS-ը չի boot լինում, բիզնեսն աշխատում է դրա վրա, snapshot-ներ կան, բայց ամենանորը երեկ գիշերվանից է։
Ինչպես լուծել
Հետևեք runbook-ին՝ իմպրովիզացիայի փոխարեն։ Արագ որոշեք՝ սպասել provider-ին, թե վերակառուցել. վերականգնման հայտնի գնահատականը սովորաբար վերակառուցման օգտին է։ Ստեղծեք սերվեր վերջին snapshot-ից կամ baseline script-երից, վերականգնեք database-ի ամենանոր off-site backup-ը և վերարտադրեք log-երը, եթե point-in-time recovery-ն կարգավորված է, և upload-ները sync արեք object storage-ից։ Թեստավորեք hosts ֆայլի override-ով, հետո DNS-ը կամ floating IP-ն ուղղեք նոր մեքենային։ Status update-ը հրապարակեք շուտ։ Դրանից հետո գրեք blameless review. իրական RTO-ն և RPO-ն թիրախների համեմատ, և արդյոք հաջորդ քայլը managed database-ն է, թե standby սերվերը։
Սերվերների միավորում email-ը տեղափոխելու ընթացքում
Խնդիր
Մի քանի հին սերվեր միավորվում են ավելի քչերի մեջ, և դրանցից մեկում պահվող mailbox-ները պետք է տեղափոխվեն hosted email provider՝ առանց նամակներ կորցնելու։
Ինչպես լուծել
Mail-ը դիտարկեք որպես առանձին migration։ Ստեղծեք mailbox-ները նոր provider-ի մոտ, մեկ օր առաջ իջեցրեք MX-ի և հարակից record-ների TTL-ը, հետո պատճենեք գոյություն ունեցող նամակները IMAP sync գործիքով, ինչպես imapsync-ը։ Cutover-ի ժամանակ փոխեք MX record-ները, հրապարակեք provider-ի SPF include-ը և DKIM key-երը, և DMARC-ը պահեք մոնիթորինգի ռեժիմում (p=none), մինչև հաշվետվությունները ցույց տան, որ ամեն ինչ համընկնում է։ MX-ի տարածումից հետո աշխատեցրեք վերջնական incremental IMAP sync՝ ուշացած նամակները բռնելու համար։ Հին սերվերով mail ուղարկող հավելվածները պետք է անցնեն provider-ի SMTP-ին կամ email API-ին։ Հին mail ծառայությունը մի քանի շաբաթ պահեք read-only՝ ապամոնտաժելուց առաջ։
Արագ ուղեցույց
- Ստուգել DNS-ի ընթացիկ պատասխանը և TTL-ը
dig +noall +answer example.com A- Թեստավորել նոր սերվերը DNS-ը փոխելուց առաջ
curl -sI --resolve example.com:443:203.0.113.20 https://example.com- Փորձարկել sync-ը առանց պատճենելու
rsync -aHAXn --delete --itemize-changes src/ dest/- Ցուցադրել Compose-ի վերջնական config-ը
docker compose -f compose.yml -f compose.prod.yml config- Արագ load test
k6 run --vus 50 --duration 2m load-test.js- Թույլատրելի downtime 30 օրում
99.5% = 3.6 h, 99.9% = 43 min, 99.95% = 22 min, 99.99% = 4.3 min- Անհրաժեշտ worker-ներ (Little-ի օրենք)
concurrency = requests per second x average latency in seconds- Laravel maintenance mode bypass secret-ով
php artisan down --secret="your-random-token"- Ստուգել SPF և DMARC record-ները
dig +short TXT example.com && dig +short TXT _dmarc.example.com
Իմ իրական նախագծերում
- Գործարկում եմ չորս production Ubuntu սերվեր DigitalOcean-ում՝ երկու hosting պանելով սերվեր բազմաթիվ Laravel և Next.js կայքերով, մեկ առանձին սերվեր multi-tenant POS SaaS-ի համար և մեկը՝ e-commerce պլատֆորմի համար։
- SaaS սերվերը գործարկում է Docker Compose՝ dev-ի, prod-ի, TLS-ի և observability-ի առանձին override-ներով, nginx, PostgreSQL, Redis, BullMQ worker-ներ, Prometheus, Grafana և Loki, գումարած runbook-ներ և backup script-եր։
- Միավորման իմ մոտեցումն է չորս սերվերը երկուսի մեջ (SaaS-ը առանձին, մնացած բոլոր կայքերը միասին)՝ առանց պանելի, mail-ը տեղափոխված hosted provider-ի, DNS-ը Cloudflare-ում, սերվերից դուրս backup-ներ և upload-ները տեղափոխվում են S3-համատեղելի storage։
- Միավորելիս հետևում եմ inventory, պատճենում, ստուգում, DNS-ի փոխարկում հերթականությանը և հին սերվերը պահում եմ, մինչև ամեն ինչ ստուգված լինի։
Հարցեր և պատասխաններ
Q1Ի՞նչ ենթակառուցվածք խորհուրդ կտաս նոր startup պրոդուկտի համար։
Սովորաբար մեկ լավ կարգավորված սերվեր կամ պարզ PaaS, ոչ թե cluster։ Մեկ VPS nginx-ով, հավելվածով, լոկալ կամ փոքր managed տվյալների բազայով, Redis-ով և առջևում Cloudflare-ով մշակում է շատ ավելի շատ traffic, քան ստանում են նոր պրոդուկտների մեծ մասը։ Ինչի վրա պնդում եմ՝ գործառնական հիմունքներն են առաջին օրվանից՝ hardening, staging, script արված deploy-ներ, off-site backup-ներ ստուգված restore-ով, uptime-ի և սխալների monitoring։ Հավելվածը նախագծում եմ stateless՝ upload-ները object storage-ում, session-ները Redis-ում, կոնֆիգուրացիան environment փոփոխականներից, որպեսզի հետագայում managed տվյալների բազայի և մի քանի app սերվերի անցնելը լինի կոնֆիգուրացիայի փոփոխություն, ոչ թե վերագրում։ Գումարը ծախսիր պրոդուկտի վրա, մինչև իրական load-ը կամ availability-ի պահանջներն արդարացնեն ավելին։
Q2Ինչպե՞ս ես scale անում մեկ սերվերից մի քանիսի։
Նախ state-ը հեռացնում եմ app սերվերից․ տվյալների բազան տեղափոխվում է իր private կամ managed instance, upload-ները գնում են object storage CDN-ի հետևում, session-ները, cache-ը և lock-երը գնում են Redis, իսկ պլանավորված job-երն աշխատում են մեկ անգամ lock-ով։ Հետո երկու կամ ավելի նույնական app սերվերների առջևում դնում եմ health check-երով load balancer, որոնք կառուցված են նույն image-ից կամ script-ից, և deploy-ները անցկացնում եմ դրանցով մեկ առ մեկ։ Դանդաղ աշխատանքը տեղափոխվում է queue worker-ներ, որոնք scale են լինում առանձին։ Եթե կարդալը գերակշռում է, ավելացնում եմ read replica-ներ և հաշվետվությունները route անում այնտեղ։ Այս ամենից առաջ profile եմ անում, որովհետև բացակայող index-ը հաճախ ավելին է տալիս, քան երկրորդ սերվերը։
Q3Ե՞րբ է Kubernetes-ը իմաստ ունենում։
Երբ համակարգման խնդիրը, որը այն լուծում է, ավելի մեծ է, քան այն պլատֆորմը, որը պահանջում է։ Սա սովորաբար նշանակում է շատ ծառայություններ, մի քանի թիմ, որոնք deploy են անում անկախ, տատանվող traffic, որը շահում է autoscaling-ից, կամ շատ մեքենաների վրա հետևողական self-healing-ի և rolling update-ների կարիք։ Մեկ կամ մի քանի հավելվածի համար Docker Compose-ը մի երկու սերվերի վրա կամ managed container ծառայությունը, ինչպես ECS-ը կամ Cloud Run-ը, տալիս է օգուտի մեծ մասը շատ ավելի փոքր գործառնական բեռով։ Kubernetes-ին պետք են մարդիկ, ովքեր հասկանում են ցանցը, ռեսուրսների սահմանափակումները, անվտանգության policy-ները և upgrade-ները, նույնիսկ managed control plane-ի դեպքում։ Ես նախընտրում եմ պարզ setup-ը լավ վարել, քան բարդը՝ վատ։
Q4Ինչպե՞ս ես production կայքը տեղափոխում նոր սերվեր առանց տվյալների կորստի։
Inventory եմ անում հին սերվերի ամեն ինչը՝ domain-ներ, հավելվածներ, տվյալների բազաներ, cron job-եր, worker-ներ, certificate-ներ, environment ֆայլեր, upload-ներ և mail։ Նոր սերվերը կառուցում եմ baseline-ից, կոդը և ֆայլերը պատճենում եմ rsync-ով, restore եմ անում տվյալների բազայի պատճենը և կայքը ստուգում եմ hosts-file override-ով։ Մեկ օր առաջ իջեցնում եմ DNS TTL-ը։ Cutover-ի ժամանակ միացնում եմ maintenance ռեժիմը, գործարկում եմ վերջնական incremental rsync և տվյալների բազայի թարմ dump ու import, համեմատում եմ տողերի և ֆայլերի քանակները, հետո փոխարկում եմ DNS-ը։ Հետևում եմ նոր սերվերի log-երին և traffic-ին, հինը մի քանի օր անձեռնմխելի եմ պահում որպես rollback, անում եմ դրա վերջնական backup-ը և միայն դրանից հետո շահագործումից հանում։
Q5Ո՞րն է տարբերությունը staging-ի և production-ի միջև, և որքա՞ն մոտ պետք է դրանք լինեն։
Production-ը սպասարկում է իրական օգտատերերի և տվյալներ։ Staging-ը փորձարկման միջավայր է, որտեղ ստուգում ես release-ները, migration-ները և կոնֆիգուրացիան, մինչև դրանք կհասնեն production։ Այն պետք է համապատասխանի production-ին ծրագրային տարբերակներով, կոնֆիգուրացիայի կառուցվածքով, build գործընթացով և ենթակառուցվածքի ձևով, որպեսզի խնդիրները նախ այնտեղ երևան, բայց իր տվյալների բազայով, credential-ներով և sandbox ռեժիմով ինտեգրացիաներով, և սովորաբար ավելի փոքր սերվերներով։ Իրական անձնական տվյալները չպետք է պատճենվեն staging առանց անանունացման։ Նույն build artifact-ը պետք է staging-ից տեղափոխվի production, որպեսզի ստուգես ճիշտ այն, ինչ թողարկում ես։ Որքան մոտ են դրանք, այնքան քիչ անակնկալներ, թեև production-ի չափ արժեցող staging-ը փոքր թիմերի համար հազվադեպ է արդարացված։
Q6Ի՞նչ պետք է ներառի monitoring-ի և alerting-ի նվազագույն setup-ը։
Չորս բան։ Արտաքին uptime ստուգումներ հիմնական URL-ների և API-ների վրա՝ alert-ներով chat-ով կամ հեռախոսով։ Host-ի մետրիկաներ՝ սկավառակի տեղ, հիշողություն, CPU load և տվյալների բազայի միացումներ, alert-ներով մինչև սկավառակների լցվելը։ Հավելվածի ազդանշաններ՝ error tracking stack trace-երով, request-ների latency և սխալների մակարդակ, queue-ի երկարություն և ձախողված job-եր։ Եվ certificate-ների ժամկետը գումարած backup job-երի հաջողությունը՝ երկու ձախողում, որոնք լուռ են, մինչև ցավ պատճառեն։ Log-երը պետք է հավաքվեն կենտրոնացված՝ incident-ները հետաքննելու համար բավարար պահպանման ժամկետով։ Alert-ները պետք է լինեն գործողության ենթակա և քիչ․ եթե մարդիկ անտեսում են դրանք, դրանք աղմուկ են։ Prometheus, Grafana և Loki stack-ը ծածկում է սա self-hosted, մինչդեռ hosted գործիքները գումարը փոխանակում են ավելի քիչ սպասարկման հետ։
Սենիոր մակարդակի հարցեր
S1Ե՞րբ է multi-region կարգավորումն արդարացված։
Փոքր և միջին պրոդուկտների համար՝ հազվադեպ։ Մի քանի availability zone-ով մեկ ռեգիոնն արդեն դիմանում է սերվերի կամ data center-ի ձախողմանը. ամբողջական ռեգիոնալ outage-ը սովորական չէ։ Multi-region-ը բարդ խնդիրներ է ավելացնում. database-ի replication հարյուրավոր միլիվայրկյանների latency-ով, ընտրություն asynchronous replication-ի՝ տվյալների հնարավոր կորստով, և synchronous replication-ի՝ դանդաղ գրառումներով, միջև, օգտատերերի routing և failover-ի թեստավորում, որը հազվադեպ է կիրառվում։ Արժեքը մոտավորապես կրկնապատկվում է։ Այն արդարացված է, երբ պայմանագրերը պահանջում են ռեգիոնալ կայունություն, տվյալների residency-ի օրենքները պահանջում են լոկալ պահպանում, կամ տարբեր մայրցամաքների օգտատերերին պետք է ցածր latency գրառումների համար։ Ավելի էժան միջին տարբերակը warm standby-ն է. backup-ներ և infrastructure code պատրաստ երկրորդ ռեգիոնում՝ ժամերով, ոչ թե վայրկյաններով չափվող թեստավորված վերականգնման ժամանակով։
S2Նախագծեք disaster recovery SaaS-ի համար 5 րոպե RPO-ով և 1 ժամ RTO-ով։
Հինգ րոպե RPO-ն բացառում է գիշերային dump-երը. ինձ պետք է WAL-ի կամ binlog-ի շարունակական archiving դեպի storage այլ account-ում կամ ռեգիոնում, կամ managed database point-in-time recovery-ով, գումարած upload-ներ replicate արված object storage-ում։ Մեկ ժամ RTO-ն նշանակում է, որ վերակառուցումը պետք է ավտոմատացված լինի. infrastructure as code կամ script արված baseline-ներ, container image-ներ registry-ում, secret-ներ, որոնք կարելի է վերականգնել manager-ից, և DNS՝ ցածր TTL-ով, կամ load balancer, որը կարող եմ վերաուղղել։ Warm standby replica-ն database-ի մասը դարձնում է րոպեներ՝ restore-ի փոխարեն։ Հետո ապացուցեք. եռամսյակային drill, որը վերականգնում է մաքուր միջավայրում միայն off-site պատճեններից, ժամանակաչափված երկու թիրախների դեմ՝ գրավոր runbook-ով և անվանապես նշանակված որոշում կայացնողներով։
S3Self-hosted observability, թե՞ hosted ծառայություն։
Prometheus-ի, Grafana-ի և Loki-ի self-hosting-ը լիցենզիաների վրա քիչ է արժենում և տվյալները պահում է ձեր վերահսկողության տակ, ինչը հարմար է մեկ նվիրված միջավայրի կամ տվյալների խիստ կանոնների համար։ Բայց դա ևս մեկ production համակարգ է. storage-ը աճում է, upgrade-ները կոտրում են dashboard-ները, և եթե այն աշխատում է իր մոնիթորինգ արած սերվերում, մեռնում է նույն incident-ի ժամանակ։ Hosted գործիքները գանձում են ըստ host-երի, series-ի կամ log-երի ծավալի և կարող են թանկանալ բարձր cardinality-ով մետրիկաների կամ մանրամասն log-երի դեպքում, բայց հասանելի են, երբ ձեր ենթակառուցվածքը հասանելի չէ, և սպասարկում չեն պահանջում։ Իմ սովորական պատասխանը. արտաքին uptime check-երը և error tracking-ը՝ hosted, միշտ, մետրիկաներն ու log-երը՝ self-hosted առանձին փոքր սերվերում, երբ բյուջեն կարևոր է, և hosted, երբ թիմի ժամանակն ավելի թանկ է, քան հաշիվը։
S4Ինչպե՞ս եք խուսափում lock-in-ից managed ծառայություններ ընտրելիս։
Lock-in-ը կառավարելի ծախս է, ոչ թե մի բան, որից պետք է խուսափել ամեն գնով. managed ծառայությունները գոյություն ունեն, որովհետև իրական ժամանակ են խնայում։ Նախընտրում եմ բաց interface-ների վրա կառուցված ծառայություններ. PostgreSQL կամ MySQL՝ proprietary database-ների փոխարեն, S3-compatible object storage, Redis-compatible cache-եր, container-ներ և ստանդարտ պրոտոկոլներ, ինչպես SMTP-ն և OpenTelemetry-ն։ Application code-ը դրանց հետ խոսում է framework-ի աբստրակցիաներով, ինչպես Laravel-ի filesystem, queue և mail driver-ները, ուստի provider-ը փոխելը կոնֆիգուրացիայի հարց է։ Infrastructure as code-ը փաստաթղթավորում է այն ամենը, ինչը պետք կլինի վերստեղծել։ Ավելի խորը lock-in ընդունում եմ միայն այնտեղ, որտեղ ծառայությունը հստակ առավելություն է տալիս, և տվյալները պահում եմ export անելի՝ vendor-ից դուրս պարբերական backup-ներով, քանի որ տեղափոխելու ամենադժվար բանը միշտ տվյալներն են, ոչ թե compute-ը։
Բառարան
- SLO
- Service level objective. հուսալիության ներքին թիրախ, օրինակ՝ 30 օրվա ընթացքում հարցումների 99.9 տոկոսի հաջողություն։
- SLA
- Service level agreement. պայմանագրային խոստում հաճախորդներին, սովորաբար ավելի մեղմ, քան SLO-ն, տույժերով՝ չկատարելու դեպքում։
- Error budget
- Անհուսալիության այն ծավալը, որը թույլ է տալիս SLO-ն. դրա ծախսը ցույց է տալիս՝ ֆունկցիաներ release անել, թե կայունությունը շտկել։
- Single point of failure
- Մեկ կոմպոնենտ, որի ձախողումը վայր է գցում ամբողջ ծառայությունը, օրինակ՝ միակ սերվերը կամ database-ը։
- Stateless server
- App սերվեր, որը եզակի տվյալներ չի պահում, ուստի կարող է ցանկացած պահի փոխարինվել կամ կրկնօրինակվել։
- Blue-green deploy
- Երկու ամբողջական միջավայրի աշխատեցում և տրաֆիկի միանգամից փոխանցում հինից նորին։
- Canary release
- Տրաֆիկի փոքր բաժնի ուղարկում նախ նոր տարբերակին և ընդլայնում միայն այն դեպքում, եթե այն լավ է աշխատում։
- Expand-contract
- Database schema-ի փոփոխում backward-compatible քայլերով, որպեսզի հին և նոր կոդը կարողանան միաժամանակ աշխատել։
- Burn rate
- Որքան արագ է սպառվում error budget-ը՝ համեմատած այն արագության հետ, որը ճշգրիտ կսպառեր այն։
- Egress
- Cloud provider-ի ցանցից դուրս փոխանցված տվյալներ, որոնք հաճախ հաշվարկվում են ըստ գիգաբայթի և հեշտ է անտեսել։
Շարունակեք սովորել
Պրոդուկտի գաղափար ունե՞ք։Եկեք ստեղծենք այն։
Պատմեք ձեր գաղափարի, պրոդուկտի կամ թիմի մասին։ Սովորաբար պատասխանում եմ մեկ աշխատանքային օրվա ընթացքում։