পাঠ ৪৩ · ৫৭-এর মধ্যে · মডিউল ১০
Home / Courses / Cloud Computing & DevOps / ডিস্ট্রিবিউটেড ট্রেসিং ও অবজারভেবিলিটির তিন স্তম্ভ

ডিস্ট্রিবিউটেড ট্রেসিং ও অবজারভেবিলিটির তিন স্তম্ভ

Distributed tracing & the three pillars of observability
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ডিস্ট্রিবিউটেড ট্রেসিং কী সমস্যা সমাধান করে এবং কীভাবে এটি কাজ করে (trace ID, span)
  • কেন একটি জটিল মাইক্রোসার্ভিস কল-চেইনে ট্রেসিং প্রায়ই প্রথম ডিবাগিং হাতিয়ার
  • অবজারভেবিলিটির তিন স্তম্ভ — মেট্রিক্স, লগ ও ট্রেস — কীভাবে একে অপরের পরিপূরক
  • Python দিয়ে একটি ছোট্ট সিমুলেটেড ট্রেস তৈরি ও বিশ্লেষণ করে বটলনেক খুঁজে বের করা

১ · সমস্যা — একটি রিকোয়েস্ট, অনেক সার্ভিস

একটি আধুনিক মাইক্রোসার্ভিস আর্কিটেকচারে একটি একক ইউজার রিকোয়েস্ট (যেমন "একটি অর্ডার দিন") প্রায়ই পাঁচ-দশটি ভিন্ন সার্ভিস স্পর্শ করে — API গেটওয়ে, অথ সার্ভিস, অর্ডার সার্ভিস, ইনভেন্টরি সার্ভিস, পেমেন্ট সার্ভিস, ইত্যাদি। L42-এর সেন্ট্রালাইজড লগিং প্রতিটি সার্ভিসের আলাদা লগ এক জায়গায় জমা করে, কিন্তু "এই নির্দিষ্ট একটি রিকোয়েস্টের জন্য কোন সার্ভিসে কতটুকু সময় গেল" — এই প্রশ্নের উত্তর লগ থেকে একত্র করা কঠিন, কারণ লগ এন্ট্রিগুলো স্বাভাবিকভাবেই একে অপরের সাথে "এই একই রিকোয়েস্টের অংশ" হিসেবে সংযুক্ত থাকে না।

২ · ডিস্ট্রিবিউটেড ট্রেসিং — trace ID ও span

ডিস্ট্রিবিউটেড ট্রেসিংDistributed Tracingএকটি শেয়ার্ড trace ID ব্যবহার করে একটি একক রিকোয়েস্ট যে সব সার্ভিস দিয়ে গেছে তার সম্পূর্ণ টাইমলাইন পুনর্গঠন করার কৌশল। সমাধান করে এই সমস্যা — যখন একটি রিকোয়েস্ট প্রথম সিস্টেমে ঢোকে, তাকে একটি অনন্য trace IDTrace IDএকটি রিকোয়েস্টকে সিস্টেমের ভেতর দিয়ে সব সার্ভিস জুড়ে অনুসরণ করার জন্য ব্যবহৃত অনন্য আইডেন্টিফায়ার — প্রতিটি ডাউনস্ট্রিম কলে বহন করা হয়। দেওয়া হয়, এবং সেই ID প্রতিটি ডাউনস্ট্রিম সার্ভিস কলে বহন করা হয়। প্রতিটি সার্ভিস তার নিজের কাজের অংশটুকু একটি span (সার্ভিসের নাম, অপারেশনের নাম, শুরু ও শেষ সময়) হিসেবে রেকর্ড করে। সব span একই trace ID-তে যুক্ত থাকায়, একটি ট্রেসিং সিস্টেম সেগুলো একত্র করে পুরো রিকোয়েস্টের একটি সম্পূর্ণ টাইমলাইন দেখাতে পারে — কোন সার্ভিস কতক্ষণ সময় নিলো, কোন ধাপে সবচেয়ে বেশি লেটেন্সি যোগ হলো।

কেন এটি প্রায়ই প্রথম হাতিয়ার

একটি প্রোডাকশন ইনসিডেন্টে ("এই রিকোয়েস্টগুলো হঠাৎ ধীর হয়ে গেছে") — মেট্রিক্স আপনাকে বলবে যে কিছু একটা ভুল হয়েছে (লেটেন্সি বেড়েছে), কিন্তু কোথায় তা নির্দিষ্টভাবে বলবে না। একটি একক ধীর রিকোয়েস্টের ট্রেস খুললেই সাথে সাথে দেখা যায় ঠিক কোন সার্ভিসের কোন কল অস্বাভাবিক সময় নিয়েছে — তাই ট্রেসিং প্রায়ই ইনসিডেন্ট তদন্তে প্রথম যে টুল খোলা হয় (M10/L44-এর অন-কল প্র্যাকটিসের সরাসরি সঙ্গী)।

৩ · অবজারভেবিলিটির তিন স্তম্ভ

এই কোর্সের মনিটরিং/লগিং/অবজারভেবিলিটি মডিউলের (M10) তিনটি পাঠ একসাথে অবজারভেবিলিটির প্রথাগত "তিন স্তম্ভ" গঠন করে —

মেট্রিক্স (L41)
সময়ের সাথে সংগৃহীত সংখ্যাসূচক পরিমাপ — সামগ্রিক স্বাস্থ্য/ট্রেন্ড বোঝায়, কিন্তু বিস্তারিত নয়।
লগ (L42)
বিচ্ছিন্ন, বিস্তারিত ঘটনা রেকর্ড — ঠিক কী ঘটেছিল তার প্রমাণ, কিন্তু রিকোয়েস্ট-জোড়া দৃষ্টিভঙ্গি নয়।
ট্রেস (এই পাঠ)
একটি একক রিকোয়েস্টের সম্পূর্ণ কল-চেইন-স্তরের বিস্তারিত — ঠিক কোথায় সময় গেল তা দেখায়।

কোনো একটি স্তম্ভ একা সম্পূর্ণ চিত্র দেয় না — মেট্রিক্স আপনাকে সতর্ক করে (কিছু একটা ভুল), লগ আপনাকে প্রাসঙ্গিক বিস্তারিত ঘটনা দেখায় (কী ভুল হয়েছে), আর ট্রেস দেখায় ঠিক কোথায় ও কেন (কোন সার্ভিস, কোন কল)। তিনটি একসাথেই একটি প্রোডাকশন সিস্টেমকে সত্যিকারের "পর্যবেক্ষণযোগ্য" করে তোলে।

Python
# একটি সিমুলেটেড রিকোয়েস্টের ডিস্ট্রিবিউটেড ট্রেস — একটি ভুয়া ইনক্রিমেন্টিং ক্লক ব্যবহার করে
spans = []
clock = {"tick": 0}

def add_span(service_name, span_name, duration_ms):
    """একটি span রেকর্ড করে — শুরু ও শেষের সময় ভুয়া ক্লক থেকে নেওয়া, তারপর ক্লক এগিয়ে দেওয়া হয়।"""
    start = clock["tick"]
    clock["tick"] += duration_ms
    end = clock["tick"]
    spans.append({"service": service_name, "span": span_name, "start": start, "end": end})

def handle_request(trace_id):
    add_span("api-gateway", "route_request", 2)
    add_span("auth-service", "verify_token", 5)
    add_span("order-service", "validate_order", 3)
    add_span("inventory-service", "check_stock", 40)   # অস্বাভাবিক ধীর — সন্দেহভাজন বটলনেক
    add_span("payment-service", "charge_card", 12)
    add_span("order-service", "finalize_order", 2)
    return trace_id

handle_request("trace-9f21")

print(f"{'সার্ভিস':20}{'স্প্যান':22}{'শুরু':>6}{'শেষ':>6}{'সময়(ms)':>10}")
for s in spans:
    dur = s["end"] - s["start"]
    print(f"{s['service']:20}{s['span']:22}{s['start']:>6}{s['end']:>6}{dur:>10}")

total_time = spans[-1]["end"]
slowest = max(spans, key=lambda s: s["end"] - s["start"])
print(f"\nমোট রিকোয়েস্ট সময়: {total_time}ms")
print(f"সবচেয়ে বেশি সময় নিয়েছে: {slowest['service']} → {slowest['span']} ({slowest['end']-slowest['start']}ms)")

    
লক্ষ্য করুন inventory-service-এর check_stock স্প্যানই সবচেয়ে বেশি সময় (৪০ms) নিয়েছে — মোট ৬৪ms রিকোয়েস্ট সময়ের প্রায় ৬২%। শুধু "মোট রিকোয়েস্ট ধীর হয়েছে" এই মেট্রিক্স জানলে কোথায় সমস্যা তা বোঝা যেত না — ট্রেসের প্রতিটি span আলাদা করে দেখেই ঠিক কোন সার্ভিসে অপ্টিমাইজেশন দরকার তা নির্দিষ্ট করা গেল।
মূল কথা · Key takeaway

ডিস্ট্রিবিউটেড ট্রেসিং একটি রিকোয়েস্টের ভেতরের "কালো বাক্স" খুলে দেয় — দেখায় কোন সার্ভিস, কোন অপারেশন সময় নিয়েছে। মেট্রিক্স, লগ ও ট্রেস — এই তিনটি স্তম্ভ একসাথে ব্যবহার করলেই একজন ইঞ্জিনিয়ার দ্রুত একটি প্রোডাকশন সমস্যার মূল কারণ পর্যন্ত পৌঁছাতে পারেন — পরের পাঠে (L44) আমরা দেখব এই সিগন্যাল থেকে কীভাবে সঠিকভাবে একজন মানুষকে সতর্ক করা হয়।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ যদি কোনো একটি সার্ভিস ডাউনস্ট্রিম কলে trace ID পাস করতে ভুলে যায়, তাহলে ট্রেসের কী ক্ষতি হবে?

সেই সার্ভিসের পরের span-গুলো মূল trace ID-এর সাথে সংযুক্ত থাকবে না — ফলে সেগুলো ট্রেসিং সিস্টেমে একটি সম্পূর্ণ আলাদা, বিচ্ছিন্ন ট্রেস হিসেবে দেখা যাবে (বা একেবারেই সংযুক্ত হবে না)। এতে সম্পূর্ণ রিকোয়েস্ট টাইমলাইন ভেঙে যায় — ট্রেসিংয়ের পুরো সুবিধা নির্ভর করে প্রতিটি সার্ভিস সঠিকভাবে ID ফরওয়ার্ড করার উপর, তাই এটি সাধারণত একটি শেয়ার্ড লাইব্রেরি/মিডলওয়্যার দিয়ে স্বয়ংক্রিয় করা হয়, প্রতিটি ডেভেলপারকে হাতে মনে রাখতে হয় না।

প্র ০২ একজন ইঞ্জিনিয়ার শুধু মেট্রিক্স ড্যাশবোর্ড দেখেই বলতে পারবেন "কোন নির্দিষ্ট সার্ভিস কল ধীর হয়েছে" — নাকি এর জন্য ট্রেসিং প্রয়োজন?

সাধারণত না। মেট্রিক্স সাধারণত অ্যাগ্রিগেট (গড়/পার্সেন্টাইল) আকারে থাকে — যেমন "গড় রেসপন্স টাইম বেড়েছে" — কিন্তু এটি বলে না ঠিক কোন ডাউনস্ট্রিম কল এই বৃদ্ধির জন্য দায়ী। এই নির্দিষ্ট, রিকোয়েস্ট- স্তরের কল-চেইন বিস্তারিত পাওয়ার জন্যই ট্রেসিং প্রয়োজন — মেট্রিক্স "কিছু একটা ভুল" বলে, ট্রেস "ঠিক কোথায় ভুল" বলে।

প্র ০৩ তিন স্তম্ভের মধ্যে যেকোনো একটি ছেড়ে দিলে (যেমন শুধু মেট্রিক্স ও লগ রাখলে, ট্রেস বাদ দিলে) কী হারানো যায়?

মেট্রিক্স ও লগ দিয়ে আপনি জানতে পারবেন কিছু একটা ভুল হয়েছে এবং কোন কোন ইভেন্ট ঘটেছে, কিন্তু একটি জটিল, বহু-সার্ভিস কল-চেইনে ঠিক কোন সার্ভিসের কোন কল সময় বা ত্রুটি যোগ করেছে তা পুনর্গঠন করা কঠিন হয়ে পড়বে — বিশেষ করে যখন লগগুলো ভিন্ন সার্ভিসে ভিন্ন সময়ে লেখা হয়েছে এবং একে অপরের সাথে স্পষ্টভাবে সংযুক্ত নয়। ট্রেসিং ছাড়া রুট-কজ বিশ্লেষণ অনেক বেশি ম্যানুয়াল অনুমান-নির্ভর হয়ে পড়ে।

অনুশীলন

  1. চিন্তা করুন: উপরের ট্রেসে যদি inventory-service-এর check_stock-কে ৪০ms-এর বদলে ৫ms করা হয়, তাহলে নতুন "সবচেয়ে ধীর" span কোনটি হবে বলে মনে হয়?

    তাহলে payment-service-এর charge_card span (১২ms) সবচেয়ে ধীর হয়ে যাবে, কারণ বাকি span-গুলোর মধ্যে এটিই এখন সর্বোচ্চ সময়। এটি দেখায় কীভাবে একটি বটলনেক ঠিক করার পর মনোযোগ স্বয়ংক্রিয়ভাবে পরবর্তী সবচেয়ে বড় সমস্যায় সরে যায় — অপ্টিমাইজেশন একটি চলমান, পুনরাবৃত্তিমূলক প্রক্রিয়া।

  2. পরীক্ষা করুন: handle_request-এ একটি নতুন span যোগ করুন — add_span("notification-service", "send_confirmation", 8) — এবং কোডটি আবার চালিয়ে নতুন মোট সময় ও স্লোয়েস্ট span দেখুন।

    মোট রিকোয়েস্ট সময় ৮ms বেড়ে যাবে (৬৪ থেকে ৭২ms), কিন্তু inventory-service-এর ৪০ms span-ই এখনও স্লোয়েস্ট থাকবে যেহেতু ৮ms তার চেয়ে কম। এটি দেখায় প্রতিটি নতুন span মোট সময়ে যোগ হয়, কিন্তু "সবচেয়ে বড় সমস্যা" খুঁজতে প্রতিটি স্প্যানকে আলাদাভাবে তুলনা করতে হয়, শুধু মোট সময় দেখলে যথেষ্ট নয়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
L42 · সেন্ট্রালাইজড লগিং