পর্ব ২: দুটো load balancer, দুটো pool, আর কেন আরও সার্ভার যোগ করাই যথেষ্ট ছিল না
একটা পরীক্ষা-প্ল্যাটফর্মের application layer আমরা কীভাবে নতুন করে গড়লাম: দুটো load balancer, দুটো autoscale pool, প্রতি node-এ দুটো container-সহ Next.js frontend, tag-ভিত্তিক backend, ৫ থেকে ৮ মিনিট পিছিয়ে থাকা CPU metric, আর ২১৯টা probe ও ০ error-এর rollout।
নতুন frontend-এ DNS বদলানোর এক ঘণ্টা পরেও পুরোনো সার্ভারটা প্রায় ৩৬% traffic পাচ্ছিল। TTL আমরা আগেই ৩০০ সেকেন্ডে নামিয়েছিলাম। নতুন সেটআপ hosts-file entry দিয়ে আগেই test করা হয়েছিল। তবু এক-তৃতীয়াংশেরও বেশি দর্শনার্থী নতুন সদর দরজা পাশ কাটিয়ে সোজা পুরোনো বাড়িতে ঢুকে যাচ্ছিল, কারণ তাদের resolver পুরোনো ঠিকানাটাই মনে রেখেছিল।
এই সংখ্যাটাই কারণ, কেন পুরোনো সার্ভার আমাদের ফেরার পথ হিসেবে বেঁচে ছিল। পুরো এই ধাপটার সারকথাও এটাই: কাগজে প্রতিটি ধাপ সহজ দেখাত, আর প্রতিটি ধাপে একটা করে মাপা চমক লুকিয়ে ছিল।
পর্ব ১-এ আমরা দেখেছিলাম, প্রথম bottleneck ছিল একটা rate limiter আর Valkey-র কানেকশনের বন্যা, সার্ভারের ঘাটতি নয়। এরপরই কেবল সার্ভার বাড়ানো সঠিক হাতিয়ার হয়ে ওঠে, আর তখনো দরকার ছিল একটা architecture। এই লেখায় সেটাই আছে: দুটো load balancer, দুটো autoscale pool, নতুন করে গড়া frontend, এমন একটা backend যেখানে যেকোনো node বদলে ফেলা যায়, আর এমন একটা deploy পদ্ধতি যা request ফেলে দেয় না। সেই পদ্ধতির প্রথম সংস্করণটা অবশ্য ফেলেছিল।
এটি "From One Server to Exam-Day Ready" নামের পাঁচ পর্বের কেস স্টাডির দ্বিতীয় পর্ব। NovaCommerce একটি কাল্পনিক নাম; architecture, সংখ্যা আর ভুলগুলো সবই বাস্তব।
কেন আগে আরও সার্ভার নয়
পর্ব ১-এ bottleneck তিনবার জায়গা বদলেছে। প্রথমে একটা rate limiter, যা শিক্ষার্থীদের গুনত IP address ধরে, ফলে একটা পুরো স্কুল একটাই বালতি ভাগ করে নিত আর পেত HTTP 429। তারপর Valkey: একটা load test-এ ২,৮৫৪টা application error এসেছিল, কারণ প্রতিটি request Valkey-র সঙ্গে নতুন TLS কানেকশন খুলত, আর Valkey তত দ্রুত সেগুলো নিতে পারছিল না। শুধু তৃতীয়টা, অর্থাৎ নিছক backend CPU, এমন সমস্যা যা আরও সার্ভারে মেটে: ৪ vCPU / ৮ GB-এর একটা node আসল API মিক্সের প্রায় ৬৫ থেকে ৭০ request/s সামলাত।
প্রথম দুটো সমস্যার ওপর node যোগ করলে সেগুলো বরং বাড়ত। প্রতিটি নতুন node একই limiter কোড চালাত, আর একই Valkey-র সঙ্গে নিজের কানেকশন খুলত। আমরা বেশি সার্ভারের টাকা দিতাম, অথচ একই error দেখতাম, শুধু আরও দ্রুত। তাই এই লেখা তৃতীয় bottleneck নিয়ে, আর সেটা ঠিকমতো সারার গল্প।
একটা নয়, দুটো load balancer
আমাদের প্রথম নকশায় সবকিছুর সামনে একটাই load balancer ছিল। একটা দুটোর চেয়ে সস্তা, দেখভালও কম। অবস্থা: Considered, তারপর Rejected।
DigitalOcean-এর load balancer host name বা URL path দেখে route করতে পারে না, আর প্রতিটি load balancer ঠিক একটা tag-কে, অর্থাৎ একটা সার্ভার-গোষ্ঠীকে target করে। frontend আর backend node দুটো আলাদা গোষ্ঠী: আকার, health check আর scaling-এর নিয়ম, সবই আলাদা। ব্যাপারটা যেন এমন এক রিসেপশন ডেস্ক, যে সব দর্শনার্থীর হাতে একই ঘরের তালিকা ধরিয়ে দেয়।
ওয়েবসাইট আর API-র domain DNS-এ আগেই আলাদা ছিল, তাই বিভাজনটা বিনা খরচে এল: প্রতি স্তরের জন্য একটা করে load balancer, প্রত্যেকটা নিজের pool-এর সামনে। অবস্থা: Implemented। দুটো মিলিয়ে মাসে ৪৮ ডলার।
Warm pool নেই, তাই রাঁধুনিদের রান্নাঘরেই রাখলাম
শুরুর দিকে আমরা একটা লোভনীয় চাহিদা লিখে রেখেছিলাম: ৩ থেকে ৪ সেকেন্ডের মধ্যে traffic নিতে পারে এমন একটা warm standby। এই platform-এ সেটা নেই। DigitalOcean-এর autoscale pool-এ warm pool বলে কিছু নেই, আর একটা নতুন droplet তৈরি হতে, বুট করতে, পরীক্ষা পাস করতে আর load balancer-এ ঢুকতে মিনিট লাগে। দরজার সামনে ইঞ্জিন চালু রেখে দাঁড়িয়ে থাকা ট্যাক্সি আর ডেকে আনতে হয় এমন ট্যাক্সি এক জিনিস নয়।
তাই বদলে আমরা দুটো একঘেয়ে কাজ করলাম।
যে বাড়তি ক্ষমতা আগে থেকেই কাজ করছে। দুটো pool-এরই minimum ২টা node, আর দুটোই সব সময় load balancer-এর ভেতরে থাকে। একটা গেলে অন্যটা আগে থেকেই traffic নিচ্ছে।
Pre-scaling। বড় পরীক্ষার প্রায় ৪৫ মিনিট আগে আমরা pool-এর minimum বাড়াই, যাতে প্রথম শিক্ষার্থী Start চাপার আগেই বাড়তি node-গুলো বুট হয়ে, পরীক্ষা পাস করে, কাজ শুরু করে দেয়। একটা বাড়তি backend node-এর খরচ ঘণ্টায় প্রায় ০.০৮ ডলার।
সেকেন্ড-স্তরের scaling-এর জন্য Kubernetes (DOKS)-ও ভেবে দেখেছিলাম। চালাতে হতো অনেক বেশি কিছু, তাই পরের জন্য তুলে রেখেছি। অবস্থা: Considered, implement করা হয়নি।
Frontend: আটটা CPU আর একজন ক্যাশিয়ার
পুরোনো frontend ছিল ৮ vCPU / ১৬ GB-এর একটা droplet, তাতে একটা Next.js container, droplet-এই certbot দিয়ে TLS, আর কোনো load balancer নেই। সেটা মরলে সাইটও বন্ধ। সঙ্গে নীরবে টাকাও নষ্ট হতো: একটা Node.js প্রসেস প্রায় একটা কোরে রেন্ডার করে, তাই ওই আটটা CPU-র বেশিরভাগই বসে থাকত। যেন আটটা কাউন্টারওয়ালা সুপারমার্কেটে একজনমাত্র ক্যাশিয়ার।
নতুন নকশায় একটা বড় সার্ভারের বদলে চলে অনেকগুলো ছোট, একরকম node। Figure ১-এ পুরো application layer দেখানো হয়েছে; আমরা বাঁ দিক থেকে এগোব।
দুই স্তর, প্রতিটির নিজস্ব load balancer ও pool। worker-ই একমাত্র fixed সার্ভার, যা দুই pool-এর বাইরে।
একটা frontend node
প্রতিটি node-এ nginx বসে আছে দুটো একরকম Next.js container-এর সামনে, প্রতি vCPU-তে একটা করে। যে সেটিংগুলো গুরুত্বপূর্ণ:
Container-এর port শুধু localhost-এ বাঁধা, তাই nginx ছাড়া আর কেউ container-এ পৌঁছাতে পারে না।
nginx দুই container-এর মধ্যে ভাগ করে least_conn দিয়ে: প্রতিটি request যায় সবচেয়ে কম সক্রিয় কানেকশনওয়ালা container-এ।
প্রতিটি container-এর memory সীমা ১.৫ GB, সঙ্গে restart: always আর log rotation।
nginx load balancer থেকে আসল client IP নেয়, তাই সবাইকে একই ঠিকানা মনে না করে per-student limit ঠিকঠাক কাজ করে।
একটা micro-cache static ফাইল, ছবি আর হোম পেজ সামলায়, Next.js-কে জাগাতেই হয় না।
Next.js সাড়া দিলে তবেই /lb-health পাস করে, তাই app মরে গেলে nginx বেঁচে থাকলেও node load balancer-এর তালিকা থেকে বাদ পড়ে।
HTTPS শেষ হয় frontend load balancer-এ, আর cloud firewall frontend node-কে শুধু ওই load balancer থেকেই port 80 নিতে দেয়।
DNS বদল, ফেরার পথ রেখে
নতুন frontend আমরা বানিয়েছি পুরোনো সার্ভারের পাশেই, আর নিজেদের মেশিনে hosts-file entry দিয়ে test করেছি, ফলে আসল domain শুধু আমাদের জন্য নতুন load balancer-এ যাচ্ছিল, আর কারও জন্য নয়। পুরোনো সার্ভারের সঙ্গে ১২টা আসল পেজ মিলিয়ে দেখলাম, DNS-এর TTL ৩০০ সেকেন্ডে নামালাম, তারপর বদল করলাম।
ফেরার পথ হিসেবে পুরোনো সার্ভার রেখে দিয়েছিলাম, কারণ DNS ফিরিয়ে দিলেই পুরো undo হয়ে যেত। আমরা যা ভেবেছিলাম, তার চেয়ে বেশিক্ষণ লেগেছে: এক ঘণ্টা পরেও cache-হওয়া DNS-এর কারণে সেটা প্রায় ৩৬% traffic পাচ্ছিল। TTL একটা অনুরোধ, আদেশ নয়। সার্ভারটা আগেভাগে ধ্বংস করলে প্রায় এক-তৃতীয়াংশ দর্শনার্থী এমন এক ঠিকানায় গিয়ে পড়ত, যেখানে আর কেউ সাড়া দেয় না।
Load test, আর একটা সস্তা node
দুটো frontend node সেকেন্ডে প্রায় ৩৫৬ request সামলেছে, p95 ০.০৫ সেকেন্ড, error ০, CPU ৫৫ থেকে ৬০%। pool প্রথমে চলত dedicated-CPU droplet-এ। test-এ দেখা গেল অনেক জায়গা বাকি, তাই আমরা সরে এলাম ২ vCPU / ৪ GB-এর shared AMD droplet-এ, যার প্রতিটির দাম মাসে প্রায় ২৮ ডলার। সস্তা node-টা নিরাপদ হয়েছে অনুমানে নয়, মাপজোখে।
২ থেকে ১০ node-এর pool, 2 vCPU / 4 GB shared AMD, প্রতিটিতে ২টা container
একটা সার্ভার মরলে
সাইট বন্ধ
অন্য node সেবা চালিয়ে যায়
২টা node নিয়ে মাপা
রেন্ডারিং প্রায় একটা কোরে আটকে
প্রায় 356 request/s, p95 0.05 s, error 0, CPU 55 থেকে 60%
Backend: droplet ID থেকে tag-এ
আগে backend load balancer দুটো droplet-কে ID ধরে target করত। তৃতীয় সার্ভার নিজে থেকে কখনো যোগ দিতে পারত না, কারণ load balancer মাত্র দুটো নাম চিনত। আমরা target বদলে ID-র জায়গায় tag বসালাম। এ যেন অতিথি-তালিকার বদলে কর্মীর ব্যাজ: ব্যাজ যার গলায় আছে, সে-ই ঢুকতে পারে।
বদলটা ছিল একটা API update, যা load balancer-এর বাকি সব সেটিং অক্ষত রেখেছে, আর বদলের সময় error হয়েছে ০। তখন থেকে pool-এর তৈরি প্রতিটি node-এ tag থাকে, নিজে থেকেই load balancer-এ যোগ দেয়, আর একই tag-ভিত্তিক firewall ও database access নিয়মের আওতায় পড়ে। HTTPS চলে শুরু থেকে শেষ পর্যন্ত: load balancer private network দিয়ে প্রতিটি backend-এর কাছে আবার encrypt করে পাঠায়। প্রতিটি node বুট হয় একই golden snapshot থেকে।
Frontend pool
Backend pool
Node
2 vCPU / 4 GB shared AMD, মাসে প্রায় $28
4 vCPU / 8 GB, মাসে প্রায় $56
Minimum / maximum
2 / 10
2 / 10
Scale-out নিয়ম
গড় CPU 70%, cooldown ৫ মিনিট
গড় CPU 70% (পরে 55%), cooldown ৫ মিনিট
Health check
Next.js সাড়া দিলে /lb-health পাস করে
/lb-health-এর উত্তর দেয় শুধু nginx
Stateless node, আর যে সার্ভারটা তা নয়
web node-গুলো আগে থেকেই stateless ছিল, তবু pool-এর হাতে তুলে দেওয়ার আগে আমরা আবার যাচাই করেছি। session, cache আর queue থাকে managed Valkey-তে, log যায় stderr-এ, আর upload, শিক্ষার্থীদের লিখিত উত্তরের PDF-সহ, যায় Spaces object storage-এ। ৭ অক্টোবর আমরা প্রমাণ পেয়েছি: ২৪ ঘণ্টায় backend node-গুলো ডিস্কে লিখেছে ০টা ফাইল। এই কারণেই যেকোনো node যেকোনো সময় মুছে ফেলা যায়।
প্রতিটি backend node চালায় শুধু web (nginx) আর app (php-fpm)। queue, scheduler, WebSocket সার্ভার আর OMR web node-এ বন্ধ রাখা, যাতে বাড়তি সার্ভার কোনো job দুবার না চালায়। এগুলো সবই চলে দুই pool-এর বাইরের একমাত্র fixed worker-এ, যা একটা পরিচিত single point of failure; পর্ব ৪-এ সেখানে আবার ফিরব।
Health check আর draining
backend-এ /lb-health-এর উত্তর দেয় একা nginx, framework কখনো বুটই করে না, তাই PHP বা database কোনোটাই health check ধীর করে দিতে পারে না। এটা আমাদের একটা drain সুইচও দেয়। কোনো node-কে সেবা থেকে সরাতে চাইলে /lb-health দিয়ে 503 ফেরত পাঠাই। load balancer প্রায় ৩০ সেকেন্ডের মধ্যে তার কাছে নতুন request পাঠানো বন্ধ করে দেয়, আর যে request আগে থেকে চলছে সেগুলো স্বাভাবিকভাবে শেষ হয়।
Autoscale test আর বাসি খবরের metric
৫ অক্টোবর, রাত ৯টা ১৭ থেকে ৯টা ৩৩ মিনিটের মধ্যে আমরা autoscaling-টাকেই test করলাম। k6 পরীক্ষার পিক-সময়ের আসল request মিক্স চালাল, শুধু GET request, যাতে কিছুই লেখা না হয়: সেকেন্ডে ৩০০ থেকে ৪৫০ request, তারপর ৬ মিনিট ধরে সেকেন্ডে প্রায় ৭৫০ request।
যা দেখলাম
ফল
Backend pool
রাত ৯টা ২৯-এ ২ থেকে ৩ node, যখন pool-এর গড় 75% ছুঁল
Frontend pool
রাত ৯টা ৩০-এ ২ থেকে ৩ node, 71%-এ
Error ও timeout
server error ০, timeout ০; নতুন node-গুলো নিজে থেকেই load balancer-এ যোগ দিল
সামগ্রিক p95
132 থেকে 153 ms
API p95
320 থেকে 705 ms, তৃতীয় node যোগ দেওয়ার আগে backend প্রায় 90%-এ ছিল
HTTP 429
প্রায় 4%: পুরো load এসেছিল একটাই test IP থেকে, তাই per-IP limit নিজের কাজ করেছে
API p95-টা দেখুন: ৩২০ থেকে ৭০৫ ms। অপেক্ষার দাম এটাই। তৃতীয় node যোগ দেওয়া পর্যন্ত backend প্রায় ৯০% CPU-তে ছিল, কারণ DigitalOcean-এর CPU metric আসল load থেকে ৫ থেকে ৮ মিনিট পিছিয়ে থাকে। আগের backend test-এ CPU ৯৯% ছোঁয়ার প্রায় ৭ মিনিট পরে প্রথম বাড়তি node এসেছিল। autoscaler ভাঙা নয়, সে শুধু বাসি খবর পড়ছে।
এই দেরি উল্টো দিকেও কাজ করে। load থামার পরও metric উঁচু দেখাচ্ছিল, আর backend সাময়িকভাবে ৪টা node-এ উঠে গেল। ঠিক যেন গোসলের কল: কিছু বদলাচ্ছে না দেখে আপনি আরও ঘোরান, আর তারপর গরম জল একসঙ্গে এসে পড়ে।
load একটা ধাপ, metric একটা ধীর বক্ররেখা, আর autoscaler চলে বক্ররেখার পেছনে। এটা প্রভাবটা বোঝানোর schematic, রেকর্ড করা ডেটা নয়।
সাধারণ যুক্তিটা আছে ট্রাফিক স্পাইকে autoscaling লেখায়; এখানে তার পেছনে আছে আমাদের নিজের সংখ্যা। নির্ধারিত পরীক্ষার জন্য আমরা প্রায় ৪৫ মিনিট আগে minimum বাড়াই, আর কমাই শুধু পরে। Autoscaling চালু থাকে অপ্রত্যাশিতের জন্য নিরাপত্তা-জাল হিসেবে। test-এর অবস্থা: Tested।
Deploy হবে বদলে দিয়ে, হাতে সম্পাদনা করে নয়
pool-এর node ফেলনা জিনিস, তাই কেউ হাতে সেটা বদলায় না: পরের scale-in-এ সেটা উবে যাবে, আর পরের নতুন node বুট হবে snapshot থেকে, সেই হাতের বদল থেকে নয়। আমাদের ধাপগুলো:
একটা node বদলাই এবং তা test করি।
সেটার snapshot নিই।
pool template-কে সেই snapshot-এর দিকে ঘুরিয়ে দিই।
DigitalOcean নতুন node বানায়, তারপর পুরোনোগুলো মুছে ফেলে।
rollback-এর জন্য শেষ ৩টা ভালো image রেখে দিই, আর পরীক্ষার সময়ে কখনো template roll করি না। প্রতিটি pool update-এ সব সেটিং আবার পাঠাই, যাতে কিছু চুপচাপ ডিফল্টে ফিরে না যায়। অ্যাকাউন্টের droplet সীমা ছিল ২৫। rollout-এর সময় পুরোনো আর নতুন node একসঙ্গে থাকে, তাই দুই pool-ই যাতে সর্বোচ্চ আকারে পৌঁছাতে পারে, সেজন্য সীমাটা বাড়াতে হয়েছে।
503-র ঝলক, আর guarded rollout
প্রথম backend rollout ছিল নেহাত একটা সাধারণ template বদল, আর তাতে অল্প সময়ের জন্য 503 error এসে গেল। DigitalOcean পুরোনো droplet মুছে ফেলেছিল, অথচ load balancer তখনো সেগুলোর দিকে request পাঠাচ্ছিল। যেন পুরোনো খাবার ঘর বন্ধ করে দেওয়া হলো, আর হোস্ট তখনো সেখানে অতিথি বসাচ্ছেন।
সমাধান ছিল provider-এর ঘটনা-ক্রমের ওপর ভরসা ছেড়ে rollout-টাকে একটা সুরক্ষিত ধাপ-পরম্পরা হিসেবে নিজেরা চালানো। Figure ২-এ সেটা দেখানো আছে।
মুছে ফেলার আগে পুরোনো node drain করা হয়, তাই load balancer কখনো এমন node-এ request পাঠায় না যেটা মিলিয়ে যেতে চলেছে।
pool template-এ শুধু image বদলাই।
প্রতিটি নতুন node /lb-health-এ সাড়া না দেওয়া পর্যন্ত অপেক্ষা করি।
load balancer নতুন node-গুলোকে ঢুকিয়ে নেবে, এজন্য প্রায় ৪০ সেকেন্ড অপেক্ষা করি।
আগে পুরোনো node drain করি: তাদের /lb-health 503 ফেরত দেয়।
DigitalOcean তার cooldown-এর পর পুরোনো node সরিয়ে দেয়, তখন আর সেবা দেওয়ার কিছু বাকি থাকে না।
পরে ৭ অক্টোবর দুই pool-এর পুরো rollout চলেছে প্রতি ২ সেকেন্ডে আসল পেজে uptime probe চালিয়ে। ফল: ২১৯টা probe, error ০। অবস্থা: Tested, তারপর fix।
প্রতিটি node-এ swap ও hardening
frontend-এ নীরব ঝুঁকি ছিল memory। ওই node-গুলোতে swap ছিল না, অথচ ৪ GB-এর node-এ প্রতিটি container ১.৫ GB পর্যন্ত নিতে পারে। ৭ অক্টোবর আমরা swappiness ১০ দিয়ে ২ GB-এর একটা swap ফাইল যোগ করলাম, যাতে kernel RAM-কেই আগে বেছে নেয়, আর সেটা frontend image-এর ভেতরে বসিয়ে দিলাম। backend node-এ আগে থেকেই ৪ GB swap ছিল। একই দফার কাজে প্রতিটি node-এর hardening-ও ছিল।
ক্ষেত্র
কী আছে
Access
শুধু key দিয়ে SSH আর সব node-এ Fail2Ban
Network
tag ধরে cloud firewall: frontend node শুধু নিজের load balancer থেকে port 80 নেয়; backend node শুধু নিজের load balancer থেকে 80 ও 443 নেয়
Process
container-এ request সামলানো প্রসেসগুলো non-root user হিসেবে চলে
Secret
secret env ফাইলের mode 600
Database
app user-এর শুধু SELECT, INSERT, UPDATE ও DELETE অধিকার, DDL নেই
firewall চলে tag ধরে, তাই মাঝরাতে pool যে node বানায়, তৈরির মুহূর্তেই সে তার নিয়ম পেয়ে যায়। কাউকে কিছু মনে রাখতে হয় না। অবস্থা: ৭ অক্টোবর Implemented।
যা শিখলাম
সার্ভার বাড়ানো architecture নয়। limiter আর Valkey-র সমস্যা মিটেছে কোড বদল ও data-স্তরের ঠিকঠাকে; আরও node শুধু সমস্যাটার কপি বাড়াত।
Warm pool নেই? উষ্ণতা নিজেই বানান। আগে থেকে কাজ করা বাড়তি ক্ষমতা, আর বড় পরীক্ষার প্রায় ৪৫ মিনিট আগে বাড়ানো minimum।
নির্ধারিত পরীক্ষার জন্য pre-scale করুন। CPU metric ৫ থেকে ৮ মিনিট পিছিয়ে থাকে, তাই reactive autoscaling নিরাপত্তা-জাল, পরিকল্পনা নয়।
node-কে ফেলনা করুন। stateless web node, ID-র বদলে tag, আর হাতে সম্পাদনার বদলে image দিয়ে বদল।
মোছার আগে drain করুন। সুরক্ষিত rollout একটা পদ্ধতি, ভরসা নয়: ২১৯টা probe, error ০।
পুরোনোটা রাখুন, যতক্ষণ তার traffic না সরে। TTL একটা অনুরোধ; এক ঘণ্টা পরেও পুরোনো সার্ভার ৩৬% পাচ্ছিল।
আরও একটা নিয়ম, যা শিখেছি এক সত্যিকারের পরীক্ষার সন্ধ্যায় আর বলা হবে পরের কোনো পর্বে: scale-in নিজে থেকে drain করে না।
কোন অংশ এখন কোথায়
অংশ
অবস্থা
দুটো load balancer, দুটো autoscale pool, golden-image deploy, swap ও hardening
Implemented
প্রায় 750 request/s-এ autoscale test; প্রথম rollout ও তার 503 ঝলক
Tested, তারপর fix
দুই স্তরের জন্য একটাই load balancer
Rejected
কয়েক সেকেন্ডের warm standby; Kubernetes (DOKS)
Considered; DOKS পরের জন্য
CDN থেকে static asset; frontend node-এ Fluent Bit (যাদের nginx log node-এর সঙ্গেই হারিয়ে যায়)
Planned
application layer এখন কারও টের না পাওয়ার মতো করে বাড়তে ও বদলাতে পারে। পরের প্রশ্ন, একজন শিক্ষার্থীর উত্তর টিকে থাকবে কি না: database। পর্ব ৩-এ আমরা MySQL সরাব পরিকল্পিত ২৬ মিনিটের একটা জানালায়, আর দেখা পাব সেই পুরোনো admin panel-এর, যে ভুল database-এ লিখেই যাচ্ছিল।