Zero-downtime deployment-ს ჩვეულებრივ load balancer-ებისა და rolling update-ების ენით აღწერენ, რაც უსარგებლოა, თუ ერთი სერვერი გაქვს. ერთ მანქანაზე შეფერხების მიზეზი უფრო მარტივია: ძველი პროცესი ჩერდება მანამდე, სანამ ახალი პასუხის გაცემისთვის მზად იქნება. დახურე ეს და მორჩი. სამი რამ ხურავს: ახალი პროცესი გაუშვი მეორე პორტზე და ტრაფიკი მხოლოდ მაშინ გადართე, როცა ის პასუხობს; ასწავლე ძველ პროცესს, რომ უკვე მიღებული მოთხოვნები დაასრულოს და არ დაკარგოს; და დაწერე მიგრაციები, რომლებთანაც კოდის ორივე ვერსიას შეუძლია იცხოვროს. არცერთს მეორე მანქანა არ სჭირდება, პირველ ორს კი დაახლოებით ოცი ხაზის სამუშაო თითო.
მესამეშია ნამდვილი სირთულე და სწორედ ამის გამოა, რომ "zero-downtime" deploy-ების უმეტესობას მაინც ოცდაათწამიანი ხვრელი აქვს.
საიდან მოდის შეფერხება სინამდვილეში#
გამოსწორებამდე გაზომე. ერთ სერვერზე deploy თანმიმდევრობაა, რომლის შუაშიც გაზომვადი ხვრელია:
- deploy ინსტრუმენტი ძველ პროცესს გაჩერებას უბრძანებს.
- პროცესი გადის. თუ მყისიერად კვდება, ყველა მიმდინარე მოთხოვნა იკარგება.
- პორტი თავისუფლდება.
- ახალი პროცესი ირთვება, კონფიგურაციას კითხულობს, მონაცემთა ბაზის connection pool-ს ხსნის და პორტს იკავებს.
- ის პასუხის გაცემას იწყებს.
შეფერხება არის მანძილი მე-2 და მე-5 ნაბიჯს შორის, და მას თითქმის მთლიანად მე-4 ნაბიჯი იკავებს. ეს რიცხვი ძლიერ იცვლება და შენი უნდა იცოდე:
| სტეკი | პირველ პასუხამდე ჩვეულებრივი დრო |
|---|---|
| პატარა Node ან Python HTTP სერვისი | 0.2-1 წამი |
| Next.js ან დიდი Node აპლიკაცია | 2-6 წამი |
| Django ან Rails თბილი ქეშით | 3-10 წამი |
| JVM სერვისი | 10-60 წამი |
| გეიმ სერვერი, რომელიც სამყაროს ტვირთავს | 20 წამიდან რამდენიმე წუთამდე |
გაზომე ერთხელ ხელით: გააჩერე სერვისი, ჩართე და უყურე, როდის დასრულდება მოთხოვნა წარმატებით. თუ პასუხი ერთ წამზე ნაკლებია, პატიოსნად, ჩვეულებრივი restart 04:00-ზე გონივრული საინჟინრო გადაწყვეტილებაა და ამ პოსტის დანარჩენი ნაწილი არჩევითია. თუ პასუხი რვა წამია და დღეში სამჯერ აკეთებ deploy-ს, არჩევითი არ არის.
არსებობს მეორე, ნაკლებად თვალსაჩინო წყარო: მოთხოვნები, რომლებიც მიმდინარეობდა, როცა ძველი პროცესი გავიდა. ისინი შენს შეფერხების გაზომვაში საერთოდ არ ჩანს, რადგან endpoint სწრაფად დაბრუნდა, მაგრამ ვიღაცის ატვირთვა ჩავარდა და ვიღაცის გადახდის webhook-ს connection reset მოუვიდა. დაკარგული მიმდინარე მოთხოვნები ჩვეულებრივ ორიდან უფრო დამაზიანებელი ნახევარია.
გაუშვი ახალი პროცესი ძველის გაჩერებამდე#
მთავარი ხრიკი ისაა, რომ საჯარო პორტზე მსმენელი შენი აპლიკაცია არ უნდა იყოს. წინ reverse proxy დააყენე, აპლიკაცია შიდა პორტზე გაუშვი და deploy ასე გამოიყურება: გაუშვი ახალი ასლი სხვა შიდა პორტზე, დაელოდე სანამ ჯანმრთელი გახდება, proxy მისკენ მიმართე, შემდეგ გააჩერე ძველი. საჯარო პორტი არასოდეს იხურება. რას აკეთებს reverse proxy ზოგად შემთხვევას აღწერს; ეს კონკრეტულია, რომელიც მის დაყენებას ამართლებს.
nginx-ით გადართვა ორხაზიანი ცვლილება და reload-ია:
upstream app { server 127.0.0.1:3001;}server { listen 80; location / { proxy_pass http://app; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_read_timeout 60s; }}$ nginx -t && nginx -s reloadnginx -s reload დიზაინით graceful-ია: master პროცესი ახალ worker-ებს ახალი კონფიგურაციით უშვებს და ძველ worker-ებს აძლევს საშუალებას, დაასრულონ მოთხოვნები, რომლებსაც ამუშავებენ, სანამ გავლენ. ყოველთვის ჯერ nginx -t გაუშვი, რადგან გატეხილი კონფიგურაციით reload უარყოფილია, გატეხილი კონფიგურაციით restart კი - გათიშვაა.
ორი პროცესის მენეჯერი ამას შენ მაგივრად აკეთებს და ღირს მათი ცოდნა.
gunicorn-ს შეუძლია საკუთარი თავის შეცვლა socket-ის დაკარგვის გარეშე. გაუგზავნე master-ს USR2 და ის ახალი კოდით თავიდან ირთვება, სანამ ძველი worker-ები აგრძელებენ მუშაობას; როცა ახალი master ამოვა, გაუგზავნე ძველს WINCH, რომ თავისი worker-ები გრაციოზულად გაუშვას, შემდეგ QUIT. სუფთა კოდის ცვლილებისთვის, კონფიგურაციის ცვლილების გარეშე, უბრალო HUP ჩვეულებრივ საკმარისია - ის worker-ებს სათითაოდ თავიდან უშვებს, რაც რამდენიმე worker-იან კონფიგურაციაში ნიშნავს, რომ ყოველთვის ვიღაც პასუხობს. გაითვალისწინე, რომ --preload ამას ფუჭს ხდის, რადგან კოდი worker-ების ნაცვლად master-ში იტვირთება.
PM2 cluster რეჟიმში Node-ისთვის იგივეს აკეთებს: pm2 reload app worker-ებს ერთბაშად კი არა, სათითაოდ უშვებს თავიდან, და თუ დაამატებ wait_ready: true-ს და სერვერის რეალურად მოსმენის დაწყების შემდეგ გამოიძახებ process.send("ready")-ს, ის ელოდება ყოველი worker-ის ნიშანს, სანამ შემდეგს მოკლავს. ეს ნამდვილი rolling deploy-ია ერთ მანქანაზე. PM2 თუ hosting panel მაინც ღირს წასაკითხად მის დამატებამდე, რადგან თუ პანელი პროცესს უკვე ზედამხედველობს, შესაძლოა ისეთ პრობლემას წყვეტდე, რომელიც არ გაქვს.
Graceful shutdown დაახლოებით ათი ხაზია#
ხრიკის მეორე ნახევარი ისაა, რომ ძველმა პროცესმა გასვლისას წესიერად იმოქმედოს. ჩვეულებრივი გაჩერება SIGTERM-ს აგზავნის; kill არაფერს არ აგზავნის. დაამუშავე SIGTERM, შეწყვიტე ახალი კავშირების მიღება, დაასრულე რაც გაქვს და შემდეგ გადი - მკაცრი ვადით, რომ გაჭედილმა მოთხოვნამ deploy სამუდამოდ ღია არ დატოვოს.
const server = app.listen(process.env.PORT || 3000);let shuttingDown = false;process.on("SIGTERM", () => { if (shuttingDown) return; shuttingDown = true; server.close(() => { pool.end().then(() => process.exit(0)); }); // Keep-alive sockets will otherwise hold server.close open. if (server.closeIdleConnections) server.closeIdleConnections(); setTimeout(() => process.exit(1), 15000).unref();});closeIdleConnections ხაზს ერთი შეხედვით ჩანაზე მეტი მნიშვნელობა აქვს. server.close() მსმენელს აჩერებს, მაგრამ არსებული კავშირების დასრულებას ელოდება, HTTP keep-alive კავშირები კი თავისით არ სრულდება - უმოქმედო კავშირების დახურვის გარეშე ყოველ deploy-ზე 15-წამიან timeout-ს დაეჯახები. მეთოდი Node-ის ახალ ვერსიებზე არსებობს; შეამოწმე შენი.
WSGI ან ASGI აპლიკაციისთვის ეკვივალენტი კოდი კი არა, კონფიგურაციაა: gunicorn --graceful-timeout 30 worker-ებს ნახევარ წუთს აძლევს დასასრულებლად, ხოლო uvicorn SIGTERM-ს ახალი კავშირების უარყოფით და არსებულების დაცლით ამუშავებს. systemd-ში ვადა აშკარა გახადე, რომ unit-მა დაცლის შუაში SIGKILL არ მიიღოს:
[Service]ExecStart=/home/app/.venv/bin/gunicorn -c gunicorn.conf.py app:appExecReload=/bin/kill -HUP $MAINPIDKillSignal=SIGTERMTimeoutStopSec=45Restart=on-failureRestartSec=2სტეკის მიუხედავად, შაბლონი იდენტურია და ჩავარდნაც იდენტურია: პროცესი, რომელიც SIGTERM-ს იგნორირებს, timeout-ის შემდეგ კვდება ხელში დარჩენილი სამუშაოთი. Graceful shutdown და health check-ები თითოეული framework-ის დეტალებს გადის.
Health check-ები, რომლებსაც აზრი აქვს#
"დაელოდე, სანამ პასუხობს" საჭიროებს განმარტებას, რას ნიშნავს პასუხობს, და ჩვეულებრივი შეცდომა არასწორი რამის შემოწმებაა.
გამოიყენე ორი endpoint სხვადასხვა დანიშნულებით:
- Liveness პასუხობს კითხვას "ეს პროცესი ხომ არ გაიჭედა?" მან საკუთარ თავს გარდა არაფერი უნდა შეამოწმოს და 200 დააბრუნოს, სანამ event loop ტრიალებს. თუ liveness მონაცემთა ბაზას ამოწმებს, ბაზის ოცდაათწამიანი შეფერხება სრულიად ჯანმრთელ აპლიკაციას თავიდან ჩართავს, რაც პატარა პრობლემას გათიშვად აქცევს.
- Readiness პასუხობს კითხვას "შეუძლია ამ პროცესს ტრაფიკის მომსახურება?" ის ამოწმებს იმას, რაც მოთხოვნას სჭირდება: ბაზის ping-ს, ქეშს, გაეშვა თუ არა მიგრაციები, დასრულდა თუ არა warm-up. სწორედ ამას ელოდება deploy სკრიპტი.
app.get("/livez", (req, res) => res.status(200).send("ok"));app.get("/readyz", async (req, res) => { if (shuttingDown) return res.status(503).send("draining"); try { await pool.query("SELECT 1"); res.status(200).send("ready"); } catch { res.status(503).send("not ready"); }});readiness-ის მიერ დაცლის დროს 503-ის დაბრუნება პატარა დეტალია, რომელიც ყველაფერ დანარჩენს მუშაობს: proxy ახალი მოთხოვნების გაგზავნას იმ ასლზე წყვეტს გამორთვის დაწყების წამში და არა დასრულების წამში. deploy სკრიპტი შემდეგ ციკლი ხდება:
$ for i in $(seq 1 60); do> curl -fsS http://127.0.0.1:3001/readyz && break> sleep 1> doneსამოცი მცდელობა, ერთი წამის ინტერვალით, შემდეგ დანებდი და არ გადართო. ტაიმერით არასოდეს გადართო.
მიგრაციები ყველაზე რთული ნაწილია#
სვეტის დამატება უსაფრთხოა. მისი გადარქმევა - არა, რადგან რამდენიმე წამით ორივე ვერსია მუშაობს: ძველი კოდი full_name-ში წერს, ახალი კოდი display_name-ს კითხულობს, და რომელი მოთხოვნაც არასწორ მხარეს მოხვდება, null-ს იღებს. გამოსწორება ისაა, რომ გადარქმევა არასოდეს გააკეთო ერთ deploy-ში. დაამატე, გაუშვი, backfill გააკეთე, გადართე წაკითხვა და მხოლოდ შემდეგ, მომდევნო release-ში, წაშალე - სამი ან ოთხი მოსაწყენი deploy ერთი ამაღელვებლის ნაცვლად. ამას ჩვეულებრივ expand and contract ეძახიან და ეს არის მთელი დისციპლინა.
| ოპერაცია | უსაფრთხოა deploy-ის დროს? | რატომ |
|---|---|---|
ADD COLUMN nullable | დიახ | ძველი კოდი მას უგულებელყოფს |
ADD COLUMN default-ით | დიახ PostgreSQL 11+-ზე | ძველი ვერსიები ცხრილს თავიდან წერდნენ |
CREATE INDEX CONCURRENTLY | დიახ | ექსკლუზიური lock არ არის. ტრანზაქციაში ვერ გაეშვება |
CREATE INDEX | არა | წერას მთელი აგების განმავლობაში ბლოკავს |
DROP COLUMN | მხოლოდ მას შემდეგ, რაც კოდი მას აღარ კითხულობს | ძველი კოდი, რომელიც მას select-ს უკეთებს, შეცდომას იღებს |
RENAME COLUMN | არასოდეს ერთ ნაბიჯში | ორივე ვერსია ვერ იქნება მართალი |
ADD CONSTRAINT ... NOT VALID | დიახ | შემდეგ VALIDATE CONSTRAINT ცალკე |
| სვეტის ტიპის შეცვლა | ჩვეულებრივ არა | ხშირად ცხრილს თავიდან წერს და ბლოკავს |
| დიდი ცხრილის backfill | მხოლოდ ნაწილ-ნაწილ | ერთი დიდი UPDATE lock-ებს იჭერს და ბლოატს იწვევს |
კიდევ ერთი დაცვა, რომელიც არაფერი ღირს და ამ პრობლემის უარეს ვერსიას აგვარებს. DDL ბრძანება, რომელსაც lock ვერ მიუღია, ელოდება, და მის უკან მოსული ყველა მოთხოვნაც ელოდება, ამიტომ ერთმა დაბლოკილმა ALTER TABLE-მა შეიძლება მთელი აპლიკაცია გააჩეროს, თან თითქოს არაფერს აკეთებდეს. შეზღუდე:
SET lock_timeout = '3s';SET statement_timeout = '60s';ALTER TABLE orders ADD COLUMN display_name text;თუ სამ წამში lock ვერ მიიღო, ჩავარდება, უფრო მშვიდ წუთს ხელახლა სცდი და ვერავინ ამჩნევს. Backfill-ები ნაწილ-ნაწილ მიდის, მათ შორის commit-ით:
UPDATE orders SET display_name = full_nameWHERE display_name IS NULL AND id IN ( SELECT id FROM orders WHERE display_name IS NULL LIMIT 5000);მიგრაციები შეფერხების გარეშე ხშირი შემთხვევებისთვის expand-and-contract-ის სრულ თანმიმდევრობას გადის.
რისი zero-downtime გახდომა არ შეიძლება#
პატიოსანი თავი, რადგან ამის მკითხველთა ნახევარი ვებ აპლიკაციებს კი არა, გეიმ სერვერებს უშვებს.
გეიმ სერვერის restart შეფერხებაა. არ არსებობს proxy-ს ხრიკი, რომელიც Minecraft-ის ან Valheim-ის სამყაროს ჩატვირთულს დატოვებს, სანამ პროცესი, რომელშიც ის ცხოვრობს, იცვლება, რადგან სამყაროს მდგომარეობა ამ პროცესის მეხსიერებაშია და მას მხოლოდ ერთი პროცესი შეიძლება ფლობდეს. რისი გაკეთებაც შეგიძლია: შეფერხება იყოს მოსალოდნელი და მოკლე - დაგეგმე, როცა არავინ არის, ჩატში გააფრთხილე უკუთვლით და დარწმუნდი, რომ გაჩერება სუფთაა და შენახვა სრულდება. Restart-ის გრაფიკები, რომლებიც გვეხმარება საათის არჩევას აღწერს.
proxy ქსელი პრობლემის ფორმას ცვლის და არ აქრობს. თუ Velocity რამდენიმე Minecraft backend-ის წინ დგას, minigames სერვერის restart survival-ზე მყოფებს არ გათიშავს და მოთამაშეები ჯერ lobby-ში შეგიძლია გადაიყვანო, რომ ქსელში დარჩნენ, სანამ მათი სერვერი ბრუნდება. ეს მართლა სასარგებლოა და არ არის zero downtime სერვერისთვის, რომელსაც თავიდან ურთავ. Minecraft Velocity proxy ქსელები დაყენებას შეიცავს.
ხანგრძლივი კავშირები ყოველთვის წყდება. WebSocket-ები, გეიმ socket-ები და server-sent event-ები ძველი პროცესიდან ახალზე ვერ გადაეცემა. პასუხი კლიენტის მხარესაა: ხელახლა დაკავშირება exponential backoff-ით და ხელახალი გამოწერა reconnect-ზე, რომ შეფერხება მოკლე ჩავარდნად იქცეს და არა გატეხილ გვერდად. თუ კლიენტს სუფთად ვერ შეუძლია ხელახლა დაკავშირება, სერვერის მხარის ვერანაირი მუშაობა ვერ დაგეხმარება. WebSocket-ები reverse proxy-ს უკან proxy-ს კონფიგურაციას აღწერს, რომელიც მათ დანარჩენ დროს ცოცხლად ინახავს.
ყველაფერი, რასაც ერთი writer აქვს. მიგრაცია, რომელიც ცხრილს თავიდან წერს, მონაცემთა ბაზის ძრავის განახლება, ერთი ასლის queue worker, რომელიც lock-ს ინახავს. ზოგი ამათგან მოკლე შეიძლება გახდეს. არცერთი ერთ მანქანაზე უხილავი ვერ გახდება.
თავად მონაცემთა ბაზის restart. თუ აპლიკაცია და მონაცემთა ბაზა ერთ პატარა სერვერზეა და ბაზას თავიდან რთავ, აპლიკაცია გათიშულია, როგორიც არ უნდა იყოს მისი deploy პროცესი.
ამის გაკეთება panel-ზე, ერთი container-ით#
panel-ზე დაფუძნებულ ჰოსტზე თითოეული სერვერი ერთი container-ია ერთი პროცესის ხით, და ეს განსაზღვრავს, რა არის შესაძლებელი.
ორპორტიანი შაბლონი ამის შიგნითაც მუშაობს, რადგან არაფერი გიშლის ახალი ასლი მეორე შიდა პორტზე გაუშვა - RE:NODE-ზე Network ტაბში პორტების დამატება და წაშლა შეგიძლია, ამიტომ მეორე გამოყოფა ხელმისაწვდომია, თუ გადართვა გარედან უნდა ჩანდეს. სადაც proxy წინ დგას, აპლიკაციისა და ვებ გეგმები reverse-proxy სლოტს შეიცავს: მიუთითე A ჩანაწერი ნაჩვენებ მისამართზე და სერტიფიკატი ავტომატურად გაიცემა და განახლდება, ხოლო კლიენტის რეალური მისამართი X-Forwarded-For-ში მოდის, რომელსაც შენი აპლიკაციისთვის უნდა უთხრა, რომ ენდოს.
შეზღუდვა, რომელიც უნდა გაითვალისწინო, მეხსიერებაა. გადაფარვისას ერთ მეხსიერების ლიმიტში აპლიკაციის ორ ასლს უშვებ, ამიტომ პიკური მოხმარება დაახლოებით ორმაგია. ეს აქ უფრო მნიშვნელოვანია, ვიდრე უმეტეს ჰოსტზე, რადგან მეხსიერების ლიმიტზე kernel container-ს აჩერებს და ის swap-ის ნაცვლად სუფთად თავიდან ირთვება - ზუსტად ის მკვეთრი გაჩერება, რომლის თავიდან აცილებასაც ცდილობდი. თუ აპლიკაცია 700 MB-ს იყენებს და გეგმა 1 GB-ია, შენ deploy კი არა, გადაფარვა გკლავს. ან გეგმა გადაფარვაზე მოარგე, ან გამოიყენე worker-by-worker reload, რომელსაც მეხსიერებაში ყოველთვის მხოლოდ ერთი დამატებითი worker აქვს. Node-ის მეხსიერების ლიმიტები ახსნილი გვიჩვენებს, როგორ დააყენო heap ისე, რომ runtime container-მდე ჩავარდეს.
თავად deploy მექანიზმი Git-ია: RE:NODE-ის აპლიკაციის ხაზები GitHub-იდან GitHub App-ით, მოკლევადიანი token-ებით ჩამოტვირთავენ, pull-on-start და deploy-on-push გადამრთველებით, და deploy-on-push მხოლოდ იმ სერვერს რთავს თავიდან, რომელიც უკვე მუშაობდა. სწორედ ეს restart არის ხვრელი, რომლის შესახებაცაა ეს პოსტი, ამიტომ graceful shutdown handler პირველია, რაც ღირს დაწერა - ეს არის ნაწილი, რომელიც restart-ს დაკარგული კავშირებიდან პაუზად აქცევს. Node აპლიკაციის deploy GitHub-იდან დაყენებას ბოლომდე გადის.
Rollback მეორე გათიშვის გარეშე#
deploy პროცესი, რომლის სწრაფად უკან დაბრუნებაც შეუძლებელია, დასრულებული არ არის. სამი წესი rollback-ს იაფად ინარჩუნებს:
- წინა release დისკზე შეინახე. საქაღალდე თითო release-ზე და symlink
current-ზე კლასიკური განლაგებაა, rollback კი symlink-ის გადამისამართება და reload. ძველი release-ების წაშლა მეხუთის შემდეგ ერთხაზიანი cron ამოცანაა. - მონაცემთა ბაზას არასოდეს დააბრუნებ. მხოლოდ წინ მიმავალი მიგრაციები, დაწერილი ისე, რომ კოდის წინა ვერსია მაინც მუშაობდეს. თუ expand-and-contract წესს დაემორჩილე, ძველი კოდი ახალ სქემასთან კარგად მუშაობს და rollback მხოლოდ აპლიკაციაა.
- Backup გააკეთე წინასწარ და არა შემდეგ. ყოველთვის, იმ deploy-ებზეც, რომლებშიც დარწმუნებული ხარ. panel-ზე ეს ერთი ღილაკი ან ერთი დაგეგმილი ამოცანაა.
და მთელი პროცესი გაიმეორე სადმე, რაც production არ არის. deploy სკრიპტს იგივე თვისება აქვს, რაც backup-ს: სანამ იმ პირობებში არ გაუშვიათ, რისთვისაც დაიწერა, ის ჰიპოთეზაა. Staging და production ერთ ანგარიშზე არის ადგილი, სადაც გაუშვა.
FAQ#
მჭირდება load balancer zero-downtime deploy-ებისთვის?
არა. ერთ სერვერზე საქმეს აკეთებს reverse proxy ორი შიდა პორტის წინ, ან პროცესის მენეჯერი, რომელიც worker-ებს სათითაოდ აახლებს. ჩვეულებრივ დიაგრამებში ნაჩვენები load balancer სხვა პრობლემას წყვეტს - მთელი მანქანის დაკარგვისგან გადარჩენას - რასაც ერთი სერვერი მაინც ვერ შეძლებს.
რამდენ ხანს უნდა ელოდოს graceful shutdown?
საკმარისად დიდხანს შენი ყველაზე ნელი ნორმალური მოთხოვნისთვის, ზღვრით: 15-დან 45 წამამდე უმეტეს ვებ აპლიკაციას ფარავს. ვადის შემდეგ მკაცრი გასვლა დააყენე, რომ ერთმა გაჭედილმა მოთხოვნამ deploy ღია არ დატოვოს, და დარწმუნდი, რომ supervisor-ის საკუთარი kill timeout შენსაზე გრძელია, თორემ ის პროცესს დაცლის შუაში მოკლავს.
შეიძლება zero downtime Minecraft-ის ან გეიმ სერვერისთვის?
არა იმ სერვერისთვის, რომელსაც თავიდან რთავ. სამყარო ამ პროცესის მეხსიერებაში ცხოვრობს. შეგიძლია შეფერხება შეამოკლო და დაგეგმო, მოთამაშეები გააფრთხილო, შეინარჩუნო proxy ქსელი, რომ ერთდროულად მხოლოდ ერთ backend-ს შეეხოს, და დარწმუნდე, რომ გაჩერება სუფთაა და არაფერი იკარგება. ამის zero downtime-ად წოდება არაპატიოსნება იქნებოდა.
რა არის deploy-ის დროს დაკარგული მოთხოვნების ყველაზე დიდი მიზეზი?
პროცესი, რომელიც SIGTERM-ზე დაცლის გარეშე მაშინვე გადის. ის uptime მონიტორინგში უხილავია, რადგან endpoint სწრაფად ბრუნდება, და ჩანს როგორც შემთხვევითი ჩავარდნილი ატვირთვები, webhook-ები და 502-ები ლოგში ყოველი release-ის შემდეგ რამდენიმე წამის განმავლობაში.
უნდა შევცვალო ჩემი მონაცემთა ბაზის მიგრაციები?
თუ კოდის ორ ვერსიას გადაფარავ, დიახ - სწორედ ესაა expand and contract-ის მთელი აზრი. თუ ამის ნაცვლად მოკლე გაჩერებას იღებ, დესტრუქციული მიგრაციები ჩვეულებრივ შეგიძლია გაუშვა, რაც პატარა სერვისისთვის მშვიდი საათით სრულიად გონივრული კომპრომისია. გადაწყვიტე გააზრებულად და ნუ აღმოაჩენ ამას release-ის დროს.
ღირს health check endpoint პატარა აპლიკაციაზე?
დიახ, და ძირითადად deploy-ისთვის და არა მონიტორინგისთვის. ეს არის განსხვავება იმას შორის, რომ ტრაფიკი მაშინ გადართო, როცა ახალი პროცესი მზადაა, და იმას შორის, რომ თვითნებური sleep-ის შემდეგ გადართო, რაც ყველაზე გავრცელებული მიზეზია, რის გამოც "zero-downtime" deploy მაინც ოთხი წამით გასცემს შეცდომებს.




კომენტარები
სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.