Go-তে আপনার অ্যাপে আরও ভালো পারফরম্যান্সের জন্য যন্ত্র (পার্ট 1: ট্রেস)

১. ভূমিকা

505827108874614d.png

সর্বশেষ হালনাগাদ: ২০২২-০৭-১৫

অ্যাপ্লিকেশনটির পর্যবেক্ষণযোগ্যতা

পর্যবেক্ষণযোগ্যতা এবং ওপেনটেলিমেট্রি

অবজার্ভেবিলিটি হলো একটি সিস্টেমের বৈশিষ্ট্য বর্ণনা করার জন্য ব্যবহৃত পরিভাষা। অবজার্ভেবিলিটি সম্পন্ন একটি সিস্টেম টিমগুলোকে তাদের সিস্টেম সক্রিয়ভাবে ডিবাগ করার সুযোগ দেয়। সেই প্রেক্ষাপটে, অবজার্ভেবিলিটির তিনটি স্তম্ভ হলো লগ, মেট্রিক্স এবং ট্রেস, যা একটি সিস্টেমের অবজার্ভেবিলিটি অর্জনের জন্য মৌলিক উপকরণ হিসেবে কাজ করে।

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

এছাড়াও অবজার্ভেবিলিটির তিনটি স্তম্ভের পাশাপাশি, কন্টিনিউয়াস প্রোফাইলিং হলো এর আরেকটি মূল উপাদান এবং এটি ইন্ডাস্ট্রিতে এর ব্যবহারকারীর সংখ্যা বাড়িয়ে চলেছে । ক্লাউড প্রোফাইলার এর অন্যতম প্রবর্তক এবং এটি অ্যাপ্লিকেশন কল স্ট্যাকের পারফরম্যান্স মেট্রিক্স গভীরভাবে বিশ্লেষণ করার জন্য একটি সহজ ইন্টারফেস প্রদান করে।

এই কোডল্যাবটি সিরিজের প্রথম পর্ব এবং এতে ওপেনটেলিমেট্রি ও ক্লাউড ট্রেস ব্যবহার করে মাইক্রোসার্ভিসে ডিস্ট্রিবিউটেড ট্রেস ইন্সট্রুমেন্ট করার পদ্ধতি আলোচনা করা হয়েছে। দ্বিতীয় পর্বে ক্লাউড প্রোফাইলার ব্যবহার করে কন্টিনিউয়াস প্রোফাইলিং নিয়ে আলোচনা করা হবে।

বিতরণকৃত ট্রেস

লগ, মেট্রিক্স এবং ট্রেসের মধ্যে, ট্রেস হলো সেই টেলিমেট্রি যা সিস্টেমের কোনো প্রসেসের একটি নির্দিষ্ট অংশের ল্যাটেন্সি সম্পর্কে জানায়। বিশেষ করে মাইক্রোসার্ভিসের যুগে, সামগ্রিক ডিস্ট্রিবিউটেড সিস্টেমের ল্যাটেন্সি বাধাগুলো খুঁজে বের করার জন্য ডিস্ট্রিবিউটেড ট্রেস একটি শক্তিশালী চালিকাশক্তি।

ডিস্ট্রিবিউটেড ট্রেস বিশ্লেষণ করার সময়, এক নজরে সামগ্রিক সিস্টেম লেটেন্সি বোঝার জন্য ট্রেস ডেটা ভিজ্যুয়ালাইজেশনই হলো মূল চাবিকাঠি। ডিস্ট্রিবিউটেড ট্রেসে, আমরা সিস্টেম এন্ট্রিপয়েন্টে একটিমাত্র অনুরোধ প্রসেস করার জন্য একাধিক স্প্যান সম্বলিত একটি ট্রেসের আকারে একগুচ্ছ কল পরিচালনা করি।

স্প্যান একটি ডিস্ট্রিবিউটেড সিস্টেমে সম্পাদিত কাজের একটি স্বতন্ত্র একককে প্রতিনিধিত্ব করে এবং এর শুরু ও শেষের সময় রেকর্ড করে। স্প্যানগুলোর মধ্যে প্রায়শই একটি শ্রেণিবদ্ধ সম্পর্ক থাকে — নিচের ছবিতে সমস্ত ছোট স্প্যানগুলো একটি বড় /messages স্প্যানের চাইল্ড স্প্যান, এবং এগুলোকে একত্রিত করে একটি একক ট্রেস তৈরি করা হয়েছে যা একটি সিস্টেমের মধ্য দিয়ে কাজের পথ দেখায়।

একটি চিহ্ন

গুগল ক্লাউড ট্রেস হলো ডিস্ট্রিবিউটেড ট্রেস ব্যাকএন্ডের অন্যতম একটি বিকল্প এবং এটি গুগল ক্লাউডের অন্যান্য প্রোডাক্টের সাথে ভালোভাবে সমন্বিত।

আপনি যা তৈরি করবেন

এই কোডল্যাবে, আপনি গুগল কুবারনেটিস ইঞ্জিন ক্লাস্টারে চলমান "শেক্সপিয়ার অ্যাপ্লিকেশন" (সংক্ষেপে শেকসঅ্যাপ) নামক সার্ভিসগুলোতে ট্রেস তথ্য ইন্সট্রুমেন্ট করবেন। শেকসঅ্যাপের আর্কিটেকচার নিচে বর্ণনা করা হলো:

44e243182ced442f.png

  • লোডজেন HTTP-এর মাধ্যমে ক্লায়েন্টের কাছে একটি কোয়েরি স্ট্রিং পাঠায়।
  • ক্লায়েন্টরা gRPC-এর মাধ্যমে লোডজেন থেকে সার্ভারে কোয়েরিটি প্রেরণ করে।
  • সার্ভার ক্লায়েন্টের কাছ থেকে কোয়েরি গ্রহণ করে, গুগল ক্লাউড স্টোরেজ থেকে টেক্সট ফরম্যাটে শেক্সপিয়রের সমস্ত রচনা সংগ্রহ করে, কোয়েরিযুক্ত লাইনগুলো অনুসন্ধান করে এবং মিলে যাওয়া লাইনের নম্বরটি ক্লায়েন্টকে ফেরত পাঠায়।

আপনি অনুরোধ জুড়ে ট্রেস তথ্য পরিমাপ করবেন। এরপর, আপনি সার্ভারে একটি প্রোফাইলার এজেন্ট স্থাপন করে প্রতিবন্ধকতাটি অনুসন্ধান করবেন।

আপনি যা শিখবেন

  • Go প্রজেক্টে OpenTelemetry Trace লাইব্রেরিগুলো দিয়ে কীভাবে কাজ শুরু করবেন
  • লাইব্রেরি ব্যবহার করে কীভাবে স্প্যান তৈরি করবেন
  • অ্যাপ কম্পোনেন্টগুলোর মধ্যে নেটওয়ার্কের মাধ্যমে স্প্যান কনটেক্সট কীভাবে প্রচার করা যায়
  • ক্লাউড ট্রেসে ট্রেস ডেটা কীভাবে পাঠাবেন
  • ক্লাউড ট্রেসে ট্রেস কীভাবে বিশ্লেষণ করবেন

এই কোডল্যাবটি আপনার মাইক্রোসার্ভিসগুলোকে কীভাবে ইন্সট্রুমেন্ট করতে হয় তা ব্যাখ্যা করে। বোঝার সুবিধার জন্য, এই উদাহরণটিতে মাত্র ৩টি কম্পোনেন্ট (লোড জেনারেটর, ক্লায়েন্ট এবং সার্ভার) রয়েছে, কিন্তু এই কোডল্যাবে বর্ণিত একই প্রক্রিয়াটি আপনি আরও জটিল এবং বড় সিস্টেমের ক্ষেত্রেও প্রয়োগ করতে পারেন।

আপনার যা যা লাগবে

  • গো সম্পর্কে প্রাথমিক জ্ঞান
  • কুবারনেটিস সম্পর্কে প্রাথমিক জ্ঞান

২. সেটআপ এবং প্রয়োজনীয়তা

স্ব-গতিতে পরিবেশ সেটআপ

আপনার যদি আগে থেকে কোনো গুগল অ্যাকাউন্ট (জিমেইল বা গুগল অ্যাপস) না থাকে, তবে আপনাকে অবশ্যই একটি তৈরি করতে হবে। গুগল ক্লাউড প্ল্যাটফর্ম কনসোলে ( console.cloud.google.com ) সাইন-ইন করুন এবং একটি নতুন প্রজেক্ট তৈরি করুন।

আপনার যদি আগে থেকেই কোনো প্রজেক্ট থাকে, তাহলে কনসোলের উপরের বাম দিকের প্রজেক্ট সিলেকশন পুল-ডাউন মেনুতে ক্লিক করুন:

7a32e5469db69e9.png

এবং একটি নতুন প্রজেক্ট তৈরি করতে, প্রাপ্ত ডায়ালগ বক্সে থাকা 'NEW PROJECT' বোতামটিতে ক্লিক করুন:

7136b3ee36ebaf89.png

আপনার যদি আগে থেকে কোনো প্রজেক্ট না থাকে, তাহলে আপনার প্রথম প্রজেক্টটি তৈরি করার জন্য এইরকম একটি ডায়ালগ বক্স দেখতে পাবেন:

870a3cbd6541ee86.png

পরবর্তী প্রজেক্ট তৈরির ডায়ালগ বক্সে আপনি আপনার নতুন প্রজেক্টের বিবরণ লিখতে পারবেন:

affdc444517ba805.png

প্রজেক্ট আইডিটি মনে রাখবেন, যা সমস্ত গুগল ক্লাউড প্রজেক্ট জুড়ে একটি অনন্য নাম (উপরের নামটি ইতিমধ্যে ব্যবহৃত হয়েছে এবং আপনার জন্য কাজ করবে না, দুঃখিত!)। এই কোডল্যাবে এটিকে পরবর্তীতে PROJECT_ID হিসাবে উল্লেখ করা হবে।

এরপরে, যদি আপনি আগে থেকে তা না করে থাকেন, তাহলে Google Cloud রিসোর্স ব্যবহার করতে এবং Cloud Trace API চালু করতে আপনাকে ডেভেলপার কনসোলে বিলিং সক্ষম করতে হবে।

15d0ef27a8fbab27.png

এই কোডল্যাবটি চালাতে আপনার কয়েক ডলারের বেশি খরচ হওয়ার কথা নয়, কিন্তু আপনি যদি আরও রিসোর্স ব্যবহার করার সিদ্ধান্ত নেন অথবা সেগুলোকে চালু রাখেন, তাহলে খরচ আরও বেশি হতে পারে (এই ডকুমেন্টের শেষে 'ক্লিনআপ' অংশটি দেখুন)। Google Cloud Trace, Google Kubernetes Engine এবং Google Artifact Registry-এর মূল্যতালিকা অফিসিয়াল ডকুমেন্টেশনে উল্লেখ করা আছে।

গুগল ক্লাউড প্ল্যাটফর্মের নতুন ব্যবহারকারীরা ৩০০ ডলারের একটি ফ্রি ট্রায়ালের জন্য যোগ্য, যার ফলে এই কোডল্যাবটি সম্পূর্ণ বিনামূল্যে পাওয়া যাবে।

গুগল ক্লাউড শেল সেটআপ

যদিও গুগল ক্লাউড এবং গুগল ক্লাউড ট্রেস আপনার ল্যাপটপ থেকে দূরবর্তীভাবে পরিচালনা করা যায়, এই কোডল্যাবে আমরা গুগল ক্লাউড শেল ব্যবহার করব, যা ক্লাউডে চালিত একটি কমান্ড লাইন পরিবেশ।

এই ডেবিয়ান-ভিত্তিক ভার্চুয়াল মেশিনটিতে আপনার প্রয়োজনীয় সমস্ত ডেভেলপমেন্ট টুলস লোড করা আছে। এটি একটি স্থায়ী ৫ জিবি হোম ডিরেক্টরি প্রদান করে এবং গুগল ক্লাউডে চলে, যা নেটওয়ার্ক পারফরম্যান্স ও অথেনটিকেশনকে ব্যাপকভাবে উন্নত করে। এর মানে হলো, এই কোডল্যাবের জন্য আপনার শুধু একটি ব্রাউজার প্রয়োজন হবে (হ্যাঁ, এটি ক্রোমবুকেও কাজ করে)।

ক্লাউড কনসোল থেকে ক্লাউড শেল সক্রিয় করতে, কেবল 'Activate Cloud Shell'-এ ক্লিক করুন। gcLMt5IuEcJJNnMId-Bcz3sxCd0rZn7IzT_r95C8UZeqML68Y1efBG_B0VRp7hc7qiZTLAF-TXD7SsOadxn8uadgHhaLeASnVS3ZHK39eOlKJOgj9SJua_oeGhMxRrbOg3qigddS2A (পরিবেশের জন্য ব্যবস্থা করতে এবং সংযোগ স্থাপন করতে মাত্র কয়েক মুহূর্ত সময় লাগা উচিত)।

JjEuRXGg0AYYIY6QZ8d-66gx_Mtc-_jDE9ijmbXLJSAXFvJt-qUpNtsBsYjNpv2W6BQSr Dc1D-ARINNQ-1EkwUhz-iUK-FUCZhJ-NtjvIEx9pIkE-246DomWuCfiGHK78DgoeWkHRw

Screen Shot 2017-06-14 at 10.13.43 PM.png

ক্লাউড শেলে সংযুক্ত হওয়ার পর, আপনি দেখতে পাবেন যে আপনাকে ইতিমধ্যেই প্রমাণীকৃত করা হয়েছে এবং প্রজেক্টটি আপনার PROJECT_ID তে সেট করা আছে।

gcloud auth list

কমান্ড আউটপুট

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

কমান্ড আউটপুট

[core]
project = <PROJECT_ID>

যদি কোনো কারণে প্রজেক্টটি সেট করা না থাকে, তাহলে নিম্নলিখিত কমান্ডটি দিন:

gcloud config set project <PROJECT_ID>

আপনার PROJECT_ID খুঁজছেন? সেটআপের ধাপগুলিতে আপনি কোন আইডি ব্যবহার করেছিলেন তা দেখে নিন অথবা ক্লাউড কনসোল ড্যাশবোর্ডে এটি খুঁজে দেখুন:

158fNPfwSxsFqz9YbtJVZes8viTS3d1bV4CVhij3XPxuzVFOtTObnwsphlm6lYGmgdMFwBJtc-FaLrZU7XHAg_ZYoCrgombMRR3h-eolLPcvO351c5iBv506B3ZwghZoiRg6cz23Qw

ক্লাউড শেল ডিফল্টরূপে কিছু এনভায়রনমেন্ট ভেরিয়েবলও সেট করে, যা ভবিষ্যতে কমান্ড চালানোর সময় কাজে লাগতে পারে।

echo $GOOGLE_CLOUD_PROJECT

কমান্ড আউটপুট

<PROJECT_ID>

অবশেষে, ডিফল্ট জোন এবং প্রজেক্ট কনফিগারেশন সেট করুন।

gcloud config set compute/zone us-central1-f

আপনি বিভিন্ন ধরনের জোন বেছে নিতে পারেন। আরও তথ্যের জন্য, অঞ্চল ও জোন দেখুন।

ভাষা সেটআপে যান

এই কোডল্যাবে আমরা সমস্ত সোর্স কোডের জন্য Go ব্যবহার করেছি। ক্লাউড শেলে নিম্নলিখিত কমান্ডটি চালান এবং Go-এর ভার্সনটি 1.17+ কিনা তা নিশ্চিত করুন।

go version

কমান্ড আউটপুট

go version go1.18.3 linux/amd64

একটি গুগল কুবারনেটিস ক্লাস্টার সেটআপ করুন

এই কোডল্যাবে, আপনি গুগল কুবারনেটিস ইঞ্জিন (GKE)-এ মাইক্রোসার্ভিসের একটি ক্লাস্টার চালাবেন। এই কোডল্যাবের প্রক্রিয়াটি নিম্নরূপ:

  1. বেসলাইন প্রজেক্টটি ক্লাউড শেলে ডাউনলোড করুন
  2. কন্টেইনারে মাইক্রোসার্ভিস তৈরি করুন
  3. কন্টেইনারগুলি গুগল আর্টিফ্যাক্ট রেজিস্ট্রি (GAR)-তে আপলোড করুন
  4. GKE-তে কন্টেইনারগুলি স্থাপন করুন
  5. ট্রেস ইন্সট্রুমেন্টেশনের জন্য সার্ভিসগুলোর সোর্স কোড পরিবর্তন করুন।
  6. ধাপ ২-এ যান

কুবারনেটিস ইঞ্জিন সক্রিয় করুন

প্রথমে, আমরা একটি Kubernetes ক্লাস্টার সেট আপ করব যেখানে Shakesapp, GKE-তে চলবে, তাই আমাদের GKE সক্রিয় করতে হবে। 'Kubernetes Engine' মেনুতে যান এবং ENABLE বোতামটি চাপুন।

548cfd95bc6d344d.png

এখন আপনি একটি কুবারনেটিস ক্লাস্টার তৈরি করার জন্য প্রস্তুত।

কুবারনেটিস ক্লাস্টার তৈরি করুন

ক্লাউড শেলে, একটি কুবারনেটিস ক্লাস্টার তৈরি করতে নিম্নলিখিত কমান্ডটি চালান। অনুগ্রহ করে নিশ্চিত করুন যে জোন ভ্যালুটি সেই অঞ্চলের অধীনে রয়েছে যা আপনি আর্টিফ্যাক্ট রেজিস্ট্রি রিপোজিটরি তৈরির জন্য ব্যবহার করবেন। যদি আপনার রিপোজিটরি অঞ্চলটি এই জোনকে অন্তর্ভুক্ত না করে, তবে জোন ভ্যালু us-central1-f পরিবর্তন করুন।

gcloud container clusters create otel-trace-codelab2 \
--zone us-central1-f \
--release-channel rapid \
--preemptible \
--enable-autoscaling \
--max-nodes 8 \
--no-enable-ip-alias \
--scopes cloud-platform

কমান্ড আউটপুট

Note: Your Pod address range (`--cluster-ipv4-cidr`) can accommodate at most 1008 node(s).
Creating cluster otel-trace-codelab2 in us-central1-f... Cluster is being health-checked (master is healthy)...done.     
Created [https://container.googleapis.com/v1/projects/development-215403/zones/us-central1-f/clusters/otel-trace-codelab2].
To inspect the contents of your cluster, go to: https://console.cloud.google.com/kubernetes/workload_/gcloud/us-central1-f/otel-trace-codelab2?project=development-215403
kubeconfig entry generated for otel-trace-codelab2.
NAME: otel-trace-codelab2
LOCATION: us-central1-f
MASTER_VERSION: 1.23.6-gke.1501
MASTER_IP: 104.154.76.89
MACHINE_TYPE: e2-medium
NODE_VERSION: 1.23.6-gke.1501
NUM_NODES: 3
STATUS: RUNNING

আর্টিফ্যাক্ট রেজিস্ট্রি এবং স্কাফোল্ড সেটআপ

এখন আমাদের কাছে ডেপ্লয়মেন্টের জন্য প্রস্তুত একটি কুবারনেটিস ক্লাস্টার আছে। এরপর আমরা কন্টেইনার পুশ এবং ডেপ্লয় করার জন্য একটি কন্টেইনার রেজিস্ট্রি প্রস্তুত করব। এই ধাপগুলোর জন্য, আমাদের একটি আর্টিফ্যাক্ট রেজিস্ট্রি (GAR) সেট আপ করতে হবে এবং এটি ব্যবহার করার জন্য স্কাফোল্ড তৈরি করতে হবে।

আর্টিফ্যাক্ট রেজিস্ট্রি সেটআপ

'আর্টিফ্যাক্ট রেজিস্ট্রি' মেনুতে যান এবং ENABLE বোতামটি চাপুন।

45e384b87f7cf0db.png

কিছুক্ষণ পর আপনি GAR-এর রিপোজিটরি ব্রাউজারটি দেখতে পাবেন। "CREATE REPOSITORY" বোতামে ক্লিক করুন এবং রিপোজিটরির নাম লিখুন।

d6a70f4cb4ebcbe3.png

এই কোডল্যাবে, আমি নতুন রিপোজিটরিটির নাম দিয়েছি trace-codelab । আর্টিফ্যাক্টের ফরম্যাট হলো "Docker" এবং লোকেশন টাইপ হলো "Region"। আপনি Google Compute Engine-এর ডিফল্ট জোনের জন্য যে অঞ্চলটি সেট করেছেন, তার কাছাকাছি একটি অঞ্চল বেছে নিন। উদাহরণস্বরূপ, উপরের এই উদাহরণে "us-central1-f" বেছে নেওয়া হয়েছে, তাই এখানে আমরা "us-central1 (Iowa)" বেছে নেব। তারপর "CREATE" বোতামে ক্লিক করুন।

9c2d1ce65258ef70.png

এখন আপনি রিপোজিটরি ব্রাউজারে 'trace-codelab' দেখতে পাচ্ছেন।

7a3c1f47346bea15.png

রেজিস্ট্রি পাথটি যাচাই করার জন্য আমরা পরে এখানে ফিরে আসব।

স্ক্যাফোল্ড সেটআপ

Kubernetes-এ চালিত মাইক্রোসার্ভিস তৈরির ক্ষেত্রে Skaffold একটি অত্যন্ত দরকারি টুল। এটি অল্প কিছু কমান্ডের সাহায্যে অ্যাপ্লিকেশনের কন্টেইনার তৈরি, পুশ এবং ডিপ্লয় করার ওয়ার্কফ্লো পরিচালনা করে। Skaffold ডিফল্টভাবে কন্টেইনার রেজিস্ট্রি হিসেবে Docker Registry ব্যবহার করে, তাই কন্টেইনার পুশ করার সময় GAR-কে চেনার জন্য আপনাকে Skaffold কনফিগার করতে হবে।

আবার ক্লাউড শেল খুলুন এবং skaffold ইনস্টল করা আছে কিনা তা নিশ্চিত করুন। (ক্লাউড শেল ডিফল্টরূপে পরিবেশে skaffold ইনস্টল করে।) নিম্নলিখিত কমান্ডটি চালান এবং skaffold সংস্করণটি দেখুন।

skaffold version

কমান্ড আউটপুট

v1.38.0

এখন, আপনি স্কাফোল্ডের ব্যবহারের জন্য ডিফল্ট রিপোজিটরিটি রেজিস্টার করতে পারেন। রেজিস্ট্রি পাথটি পাওয়ার জন্য, আর্টিফ্যাক্ট রেজিস্ট্রি ড্যাশবোর্ডে যান এবং আগের ধাপে সেট আপ করা রিপোজিটরিটির নামে ক্লিক করুন।

7a3c1f47346bea15.png

এরপর আপনি পেজের উপরে ব্রেডক্রাম্ব ট্রেইল দেখতে পাবেন। ক্লিক করুন। e157b1359c3edc06.png রেজিস্ট্রি পাথটি ক্লিপবোর্ডে কপি করার আইকন।

e0f2ae2144880b8b.png

কপি বোতামে ক্লিক করলে, আপনি ব্রাউজারের নীচে এই ধরনের বার্তা সহ একটি ডায়ালগ বক্স দেখতে পাবেন:

"us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab" কপি করা হয়েছে

ক্লাউড শেলে ফিরে যান। ড্যাশবোর্ড থেকে এইমাত্র কপি করা মানটি দিয়ে skaffold config set default-repo কমান্ডটি চালান।

skaffold config set default-repo us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab

কমান্ড আউটপুট

set value default-repo to us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab for context gke_stackdriver-sandbox-3438851889_us-central1-b_stackdriver-sandbox

এছাড়াও, আপনাকে ডকার কনফিগারেশনের জন্য রেজিস্ট্রি কনফিগার করতে হবে। নিম্নলিখিত কমান্ডটি চালান:

gcloud auth configure-docker us-central1-docker.pkg.dev --quiet

কমান্ড আউটপুট

{
  "credHelpers": {
    "gcr.io": "gcloud",
    "us.gcr.io": "gcloud",
    "eu.gcr.io": "gcloud",
    "asia.gcr.io": "gcloud",
    "staging-k8s.gcr.io": "gcloud",
    "marketplace.gcr.io": "gcloud",
    "us-central1-docker.pkg.dev": "gcloud"
  }
}
Adding credentials for: us-central1-docker.pkg.dev

এখন আপনি GKE-তে একটি Kubernetes কন্টেইনার সেট আপ করার পরবর্তী ধাপের জন্য প্রস্তুত।

সারসংক্ষেপ

এই ধাপে, আপনি আপনার কোডল্যাব পরিবেশ সেট আপ করবেন:

  • ক্লাউড শেল সেট আপ করুন
  • কন্টেইনার রেজিস্ট্রির জন্য একটি আর্টিফ্যাক্ট রেজিস্ট্রি রিপোজিটরি তৈরি করা হয়েছে।
  • কন্টেইনার রেজিস্ট্রি ব্যবহার করার জন্য স্কাফোল্ড সেট আপ করুন।
  • একটি কুবারনেটিস ক্লাস্টার তৈরি করা হয়েছে যেখানে কোডল্যাব মাইক্রোসার্ভিসগুলো চলে।

এরপরে

পরবর্তী ধাপে, আপনি আপনার মাইক্রোসার্ভিসগুলো বিল্ড করে ক্লাস্টারে পুশ ও ডিপ্লয় করবেন।

৩. মাইক্রোসার্ভিসগুলো বিল্ড, পুশ এবং ডিপ্লয় করুন

কোডল্যাব উপকরণ ডাউনলোড করুন

পূর্ববর্তী ধাপে, আমরা এই কোডল্যাবের জন্য সমস্ত পূর্বশর্ত প্রস্তুত করেছি। এখন আপনি সেগুলোর উপর সম্পূর্ণ মাইক্রোসার্ভিসগুলো চালানোর জন্য প্রস্তুত। কোডল্যাবের উপাদানগুলো গিটহাবে হোস্ট করা আছে, তাই নিম্নলিখিত গিট কমান্ডের মাধ্যমে সেগুলো ক্লাউড শেল এনভায়রনমেন্টে ডাউনলোড করুন।

cd ~
git clone https://github.com/ymotongpoo/opentelemetry-trace-codelab-go.git
cd opentelemetry-trace-codelab-go

প্রকল্পটির ডিরেক্টরি কাঠামোটি নিম্নরূপ:

.
├── README.md
├── step0
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step1
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step2
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step3
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step4
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
├── step5
│   ├── manifests
│   ├── proto
│   ├── skaffold.yaml
│   └── src
└── step6
    ├── manifests
    ├── proto
    ├── skaffold.yaml
    └── src
  • ম্যানিফেস্ট: কুবারনেটিস ম্যানিফেস্ট ফাইল
  • প্রোটো: ক্লায়েন্ট এবং সার্ভারের মধ্যে যোগাযোগের জন্য প্রোটো সংজ্ঞা।
  • src: প্রতিটি সার্ভিসের সোর্স কোডের জন্য ডিরেক্টরি
  • skaffold.yaml: skaffold-এর কনফিগারেশন ফাইল

এই কোডল্যাবে, আপনাকে step0 ফোল্ডারের অধীনে থাকা সোর্স কোড আপডেট করতে হবে। পরবর্তী ধাপগুলোর উত্তরের জন্য আপনি step[1-6] ফোল্ডারের সোর্স কোডও দেখতে পারেন। (পার্ট ১-এ step0 থেকে step4 এবং পার্ট ২-এ step 5 ও 6 অন্তর্ভুক্ত)

skaffold কমান্ড চালান

অবশেষে আপনি আপনার সদ্য তৈরি করা কুবারনেটিস ক্লাস্টারে সম্পূর্ণ কন্টেন্ট বিল্ড, পুশ এবং ডিপ্লয় করার জন্য প্রস্তুত। শুনে মনে হতে পারে এতে একাধিক ধাপ রয়েছে, কিন্তু আসল ব্যাপার হলো স্কাফোল্ড আপনার জন্য সবকিছু করে দেয়। চলুন, নিচের কমান্ডটি দিয়ে তা চেষ্টা করে দেখা যাক:

cd step0
skaffold dev

কমান্ডটি চালানোর সাথে সাথেই আপনি docker build এর লগ আউটপুট দেখতে পাবেন এবং নিশ্চিত হতে পারবেন যে সেগুলি সফলভাবে রেজিস্ট্রি-তে পুশ করা হয়েছে।

কমান্ড আউটপুট

...
---> Running in c39b3ea8692b
 ---> 90932a583ab6
Successfully built 90932a583ab6
Successfully tagged us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step1
The push refers to repository [us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice]
cc8f5a05df4a: Preparing
5bf719419ee2: Preparing
2901929ad341: Preparing
88d9943798ba: Preparing
b0fdf826a39a: Preparing
3c9c1e0b1647: Preparing
f3427ce9393d: Preparing
14a1ca976738: Preparing
f3427ce9393d: Waiting
14a1ca976738: Waiting
3c9c1e0b1647: Waiting
b0fdf826a39a: Layer already exists
88d9943798ba: Layer already exists
f3427ce9393d: Layer already exists
3c9c1e0b1647: Layer already exists
14a1ca976738: Layer already exists
2901929ad341: Pushed
5bf719419ee2: Pushed
cc8f5a05df4a: Pushed
step1: digest: sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe size: 2001

সমস্ত সার্ভিস কন্টেইনার পুশ করার পর, কুবারনেটিস ডিপ্লয়মেন্ট স্বয়ংক্রিয়ভাবে শুরু হয়ে যায়।

কমান্ড আউটপুট

sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8 size: 1997
Tags used in deployment:
 - serverservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/serverservice:step4@sha256:8acdbe3a453001f120fb22c11c4f6d64c2451347732f4f271d746c2e4d193bbe
 - clientservice -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/clientservice:step4@sha256:b71fce0a96cea08075dc20758ae561cf78c83ff656b04d211ffa00cedb77edf8
 - loadgen -> us-central1-docker.pkg.dev/psychic-order-307806/trace-codelab/loadgen:step4@sha256:eea2e5bc8463ecf886f958a86906cab896e9e2e380a0eb143deaeaca40f7888a
Starting deploy...
 - deployment.apps/clientservice created
 - service/clientservice created
 - deployment.apps/loadgen created
 - deployment.apps/serverservice created
 - service/serverservice created

ডেপ্লয়মেন্টের পরে, আপনি প্রতিটি কন্টেইনারের stdout-এ প্রকৃত অ্যাপ্লিকেশন লগগুলি এইভাবে নির্গত হতে দেখবেন:

কমান্ড আউটপুট

[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:15 {"match_count":3040}
[loadgen] 2022/07/14 06:33:15 query 'love': matched 3040
[client] 2022/07/14 06:33:16 {"match_count":3040}
[loadgen] 2022/07/14 06:33:16 query 'love': matched 3040
[client] 2022/07/14 06:33:19 {"match_count":463}
[loadgen] 2022/07/14 06:33:19 query 'tear': matched 463
[loadgen] 2022/07/14 06:33:20 query 'world': matched 728
[client] 2022/07/14 06:33:20 {"match_count":728}
[client] 2022/07/14 06:33:22 {"match_count":463}
[loadgen] 2022/07/14 06:33:22 query 'tear': matched 463

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

সার্ভিসটি ইন্সট্রুমেন্টেশন শুরু করার আগে, অনুগ্রহ করে Ctrl-C চেপে আপনার ক্লাস্টারটি শাট ডাউন করুন।

কমান্ড আউটপুট

...
[client] 2022/07/14 06:34:57 {"match_count":1}
[loadgen] 2022/07/14 06:34:57 query 'what's past is prologue': matched 1
^CCleaning up...
 - W0714 06:34:58.464305   28078 gcp.go:120] WARNING: the gcp auth plugin is deprecated in v1.22+, unavailable in v1.25+; use gcloud instead.
 - To learn more, consult https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

সারসংক্ষেপ

এই ধাপে, আপনি আপনার পরিবেশে কোডল্যাবের উপকরণ প্রস্তুত করেছেন এবং নিশ্চিত করেছেন যে স্ক্যাফোল্ড প্রত্যাশা অনুযায়ী চলছে।

এরপরে

পরবর্তী ধাপে, আপনি ট্রেস তথ্য ইন্সট্রুমেন্ট করার জন্য লোডজেন সার্ভিসের সোর্স কোড পরিবর্তন করবেন।

৪. HTTP এর জন্য ইন্সট্রুমেন্টেশন

ট্রেস ইনস্ট্রুমেন্টেশন এবং প্রসারণের ধারণা

সোর্স কোড সম্পাদনা করার আগে, একটি সহজ ডায়াগ্রামের মাধ্যমে ডিস্ট্রিবিউটেড ট্রেস কীভাবে কাজ করে তা সংক্ষেপে ব্যাখ্যা করি।

6be42e353b9bfd1d.png

এই উদাহরণে, আমরা কোডটিকে এমনভাবে সাজিয়েছি যাতে এটি ক্লাউড ট্রেস-এ ট্রেস এবং স্প্যান তথ্য এক্সপোর্ট করে এবং লোডজেন সার্ভিস থেকে সার্ভার সার্ভিসে পাঠানো রিকোয়েস্ট জুড়ে ট্রেস কনটেক্সট ছড়িয়ে দেয়।

অ্যাপ্লিকেশনগুলিকে ট্রেস মেটাডেটা, যেমন ট্রেস আইডি এবং স্প্যান আইডি, পাঠাতে হবে যাতে ক্লাউড ট্রেস একই ট্রেস আইডিযুক্ত সমস্ত স্প্যানকে একটি ট্রেসে একত্রিত করতে পারে। এছাড়াও, অ্যাপ্লিকেশনটিকে অনুরোধকারী ডাউনস্ট্রিম পরিষেবাগুলিতে ট্রেস কনটেক্সট (প্যারেন্ট স্প্যানের ট্রেস আইডি এবং স্প্যান আইডির সমন্বয়) প্রচার করতে হবে, যাতে তারা জানতে পারে যে তারা কোন ট্রেস কনটেক্সটটি পরিচালনা করছে।

ওপেনটেলিমেট্রি আপনাকে সাহায্য করে:

  • অনন্য ট্রেস আইডি এবং স্প্যান আইডি তৈরি করতে
  • ব্যাকএন্ডে ট্রেস আইডি এবং স্প্যান আইডি রপ্তানি করতে
  • অন্যান্য পরিষেবাগুলিতে ট্রেস প্রসঙ্গগুলি প্রচার করতে
  • ট্রেস বিশ্লেষণে সহায়ক অতিরিক্ত মেটাডেটা এম্বেড করতে

ওপেনটেলিমেট্রি ট্রেস-এর উপাদানসমূহ

b01f7bb90188db0d.png

OpenTelemetry ব্যবহার করে অ্যাপ্লিকেশন ট্রেস ইন্সট্রুমেন্ট করার প্রক্রিয়াটি নিম্নরূপ:

  1. একজন রপ্তানিকারক তৈরি করুন
  2. ১-এ থাকা এক্সপোর্টারটিকে বাইন্ডিং করে একটি TracerProvider তৈরি করুন এবং এটিকে গ্লোবাল হিসেবে সেট করুন।
  3. প্রচার পদ্ধতি নির্ধারণ করতে TextMapPropagaror সেট করুন।
  4. TracerProvider থেকে Tracer-টি সংগ্রহ করুন।
  5. ট্রেসার থেকে স্প্যান তৈরি করুন

এই মুহূর্তে আপনার প্রতিটি উপাদানের বিস্তারিত বৈশিষ্ট্যগুলো বোঝার প্রয়োজন নেই, তবে মনে রাখার সবচেয়ে গুরুত্বপূর্ণ বিষয়গুলো হলো:

  • এখানকার এক্সপোর্টারটি TracerProvider-এ প্লাগেবল।
  • ট্রেস স্যাম্পলিং এবং এক্সপোর্ট সংক্রান্ত সমস্ত কনফিগারেশন TracerProvider-এ থাকে।
  • সমস্ত ট্রেস Tracer অবজেক্টে একত্রিত করা হয়।

এই বিষয়টি বুঝে, চলুন এবার মূল কোডিংয়ের কাজে এগিয়ে যাওয়া যাক।

যন্ত্রের প্রথম স্প্যান

ইন্সট্রুমেন্ট লোড জেনারেটর পরিষেবা

বাটনটি চেপে ক্লাউড শেল এডিটর খুলুন 776a11bfb2122549.png ক্লাউড শেলের উপরের ডানদিকে। বাম দিকের প্যানেলের এক্সপ্লোরার থেকে step0/src/loadgen/main.go ফাইলটি খুলুন এবং main ফাংশনটি খুঁজুন।

step0/src/loadgen/main.go

func main() {
        ...
        for range t.C {
                log.Printf("simulating client requests, round %d", i)
                if err := run(numWorkers, numConcurrency); err != nil {
                        log.Printf("aborted round with error: %v", err)
                }
                log.Printf("simulated %d requests", numWorkers)
                if numRounds != 0 && i > numRounds {
                        break
                }
                i++
        }
}

মেইন ফাংশনের ভেতরে আপনি ফাংশনটিকে কল করা লুপটি run দেখবেন। বর্তমান ইমপ্লিমেন্টেশনে, এই অংশে ২টি লগ লাইন আছে যা ফাংশন কলের শুরু এবং শেষ রেকর্ড করে। এখন চলুন ফাংশন কলের ল্যাটেন্সি ট্র্যাক করার জন্য স্প্যান (Span) তথ্য ইন্সট্রুমেন্ট করি।

প্রথমে, পূর্ববর্তী বিভাগে যেমন উল্লেখ করা হয়েছে, চলুন OpenTelemetry-এর জন্য সম্পূর্ণ কনফিগারেশন সেট আপ করি। নিম্নরূপে OpenTelemetry প্যাকেজগুলি যোগ করুন:

step0/src/loadgen/main.go

import (
        "context" // step1. add packages
        "encoding/json"
        "fmt"
        "io"
        "log"
        "math/rand"
        "net/http"
        "net/url"
        "time"
        // step1. add packages
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "go.opentelemetry.io/otel/trace"
        // step1. end add packages
)

পাঠযোগ্যতার জন্য, আমরা initTracer নামে একটি সেটআপ ফাংশন তৈরি করি এবং main ফাংশনে এটিকে কল করি।

step0/src/loadgen/main.go

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

আপনি হয়তো লক্ষ্য করেছেন যে ওপেনটেলিমেট্রি সেট আপ করার পদ্ধতিটি পূর্ববর্তী বিভাগে বর্ণিত পদ্ধতির মতোই। এই বাস্তবায়নে, আমরা একটি stdout এক্সপোর্টার ব্যবহার করি যা সমস্ত ট্রেস তথ্যকে একটি কাঠামোগত বিন্যাসে stdout-এ রপ্তানি করে।

তারপর আপনি মেইন ফাংশন থেকে এটিকে কল করবেন। initTracer() কল করুন এবং অ্যাপ্লিকেশনটি বন্ধ করার সময় TracerProvider.Shutdown() কল করতে ভুলবেন না।

step0/src/loadgen/main.go

func main() {
        // step1. setup OpenTelemetry
        tp, err := initTracer()
        if err != nil {
                log.Fatalf("failed to initialize TracerProvider: %v", err)
        }
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step1. end setup

        log.Printf("starting worder with %d workers in %d concurrency", numWorkers, numConcurrency)
        log.Printf("number of rounds: %d (0 is inifinite)", numRounds)
        ...

সেটআপ শেষ করার পর, আপনাকে একটি অনন্য ট্রেস আইডি এবং স্প্যান আইডি দিয়ে একটি স্প্যান তৈরি করতে হবে। ওপেনটেলিমেট্রি এর জন্য একটি সুবিধাজনক লাইব্রেরি সরবরাহ করে। ইন্সট্রুমেন্ট HTTP ক্লায়েন্টে অতিরিক্ত নতুন প্যাকেজ যোগ করুন।

step0/src/loadgen/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "math/rand"
        "net/http"
        "net/http/httptrace" // step1. add packages
        "net/url"
        "time"
        // step1. add packages
        "go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        // step1. end add packages
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
        "go.opentelemetry.io/otel/trace"
)

যেহেতু লোড জেনারেটরটি runQuery ফাংশনে net/http ব্যবহার করে HTTP-এর মাধ্যমে ক্লায়েন্ট সার্ভিসকে কল করে, তাই আমরা net/http এর জন্য contrib প্যাকেজ ব্যবহার করি এবং httptraceotelhttp প্যাকেজের এক্সটেনশনের মাধ্যমে ইন্সট্রুমেন্টেশন সক্রিয় করি।

প্রথমে ইন্সট্রুমেন্টেড ক্লায়েন্টের মাধ্যমে HTTP অনুরোধ কল করার জন্য httpClient নামে একটি প্যাকেজ গ্লোবাল ভেরিয়েবল যোগ করতে হবে।

step0/src/loadgen/main.go

var httpClient = http.Client{
        Transport: otelhttp.NewTransport(http.DefaultTransport)
}

এরপরে, OpenTelemetry ব্যবহার করে কাস্টম স্প্যান এবং কাস্টম HTTP ক্লায়েন্ট থেকে স্বয়ংক্রিয়ভাবে তৈরি স্প্যান তৈরি করার জন্য runQuery ফাংশনে ইন্সট্রুমেন্টেশন যোগ করুন। আপনাকে যা করতে হবে তা হলো:

  1. otel.Tracer() ব্যবহার করে গ্লোবাল TracerProvider থেকে একটি Tracer পান।
  2. Tracer.Start() পদ্ধতি ব্যবহার করে একটি রুট স্প্যান তৈরি করুন।
  3. যেকোনো সময়ে রুট স্প্যানটি শেষ করুন (এই ক্ষেত্রে, runQuery ফাংশনের শেষে)।

step0/src/loadgen/main.go

        reqURL.RawQuery = v.Encode()
        // step1. replace http.Get() with custom client call
        // resp, err := http.Get(reqURL.String())

        // step1. instrument trace
        ctx := context.Background()
        tr := otel.Tracer("loadgen")
        ctx, span := tr.Start(ctx, "query.request", trace.WithAttributes(
                semconv.TelemetrySDKLanguageGo,
                semconv.ServiceNameKey.String("loadgen.runQuery"),
                attribute.Key("query").String(s),
        ))
        defer span.End()
        ctx = httptrace.WithClientTrace(ctx, otelhttptrace.NewClientTrace(ctx))
        req, err := http.NewRequestWithContext(ctx, "GET", reqURL.String(), nil)
        if err != nil {
                return -1, fmt.Errorf("error creating HTTP request object: %v", err)
        }
        resp, err := httpClient.Do(req)
        // step1. end instrumentation
        if err != nil {
                return -1, fmt.Errorf("error sending request to %v: %v", reqURL.String(), err)
        }

এখন আপনার লোডজেন (HTTP ক্লায়েন্ট অ্যাপ্লিকেশন)-এর ইন্সট্রুমেন্টেশনের কাজ শেষ। অনুগ্রহ করে go mod কমান্ড ব্যবহার করে আপনার go.mod এবং go.sum ফাইল দুটি আপডেট করে নিন।

go mod tidy

যন্ত্র গ্রাহক পরিষেবা

পূর্ববর্তী অংশে, আমরা নীচের চিত্রে লাল আয়তক্ষেত্র দ্বারা আবদ্ধ অংশটিকে ইনস্ট্রুমেন্ট করেছিলাম। আমরা লোড জেনারেটর সার্ভিসে স্প্যান তথ্য ইনস্ট্রুমেন্ট করেছিলাম। লোড জেনারেটর সার্ভিসের মতোই, এখন আমাদের ক্লায়েন্ট সার্ভিসকে ইনস্ট্রুমেন্ট করতে হবে। লোড জেনারেটর সার্ভিসের সাথে পার্থক্য হলো, ক্লায়েন্ট সার্ভিসকে HTTP হেডারে লোড জেনারেটর সার্ভিস থেকে প্রেরিত ট্রেস আইডি (Trace ID) তথ্য বের করতে হবে এবং সেই আইডি ব্যবহার করে স্প্যান তৈরি করতে হবে।

bcaccd06691269f8.png

ক্লাউড শেল এডিটর খুলুন এবং লোড জেনারেটর সার্ভিসের জন্য আমরা যেভাবে করেছিলাম, সেভাবে প্রয়োজনীয় প্যাকেজগুলো যোগ করুন।

step0/src/client/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "net/url"
        "os"
        "time"

        "opentelemetry-trace-codelab-go/client/shakesapp"
        // step1. add new import
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "go.opentelemetry.io/otel/trace"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
        // step1. end new import
)

আবার, আমাদের ওপেনটেলিমেট্রি সেট আপ করতে হবে। শুধু লোডজেন (loadgen) থেকে initTracer ফাংশনটি কপি করে পেস্ট করুন এবং ক্লায়েন্ট সার্ভিসের main ফাংশনেও এটিকে কল করুন।

step0/src/client/main.go

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

এখন স্প্যানগুলোকে ইন্সট্রুমেন্ট করার সময়। যেহেতু ক্লায়েন্ট সার্ভিসকে লোডজেন সার্ভিস থেকে HTTP রিকোয়েস্ট গ্রহণ করতে হয়, তাই এর হ্যান্ডলারটিকে ইন্সট্রুমেন্ট করতে হবে। ক্লায়েন্ট সার্ভিসের HTTP সার্ভারটি net/http দিয়ে ইমপ্লিমেন্ট করা হয়েছে, এবং লোডজেনে আমরা যেমনটা করেছিলাম, ঠিক সেভাবেই আপনি otelhttp প্যাকেজটি ব্যবহার করতে পারেন।

প্রথমে, আমরা হ্যান্ডলার রেজিস্ট্রেশনকে otelhttp Handler দিয়ে প্রতিস্থাপন করব। main ফাংশনের মধ্যে, সেই লাইনগুলো খুঁজুন যেখানে http.HandleFunc() দিয়ে HTTP হ্যান্ডলারটি রেজিস্টার করা হয়েছে।

step0/src/client/main.go

        // step1. change handler to intercept OpenTelemetry related headers
        // http.HandleFunc("/", svc.handler)
        otelHandler := otelhttp.NewHandler(http.HandlerFunc(svc.handler), "client.handler")
        http.Handle("/", otelHandler)
        // step1. end intercepter setting
        http.HandleFunc("/_genki", svc.health)

এরপর, আমরা হ্যান্ডলারের ভিতরে প্রকৃত স্প্যানটিকে ইন্সট্রুমেন্ট করি। `func (*clientService) handler()` ফাংশনটি খুঁজুন এবং trace.SpanFromContext() ব্যবহার করে স্প্যান ইন্সট্রুমেন্টেশন যোগ করুন।

step0/src/client/main.go

func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
        ...
        ctx := r.Context()
        ctx, cancel := context.WithCancel(ctx)
        defer cancel()
        // step1. instrument trace
        span := trace.SpanFromContext(ctx)
        defer span.End()
        // step1. end instrument
        ...

এই ইন্সট্রুমেন্টেশনের মাধ্যমে, আপনি handler মেথডের শুরু থেকে শেষ পর্যন্ত স্প্যানগুলো পাবেন। স্প্যানগুলো বিশ্লেষণ করা সহজ করার জন্য, কোয়েরিতে একটি অতিরিক্ত অ্যাট্রিবিউট যোগ করুন যা মিলে যাওয়া সংখ্যার হিসাব সংরক্ষণ করবে। লগ লাইনের ঠিক আগে, নিম্নলিখিত কোডটি যোগ করুন।

step0/src/client/main.go

func (cs *clientService) handler(w http.ResponseWriter, r *http.Request) {
        ...
        // step1. add span specific attribute
        span.SetAttributes(attribute.Key("matched").Int64(resp.MatchCount))
        // step1. end adding attribute
        log.Println(string(ret))
        ...

উপরের সমস্ত ইন্সট্রুমেন্টেশনের মাধ্যমে, আপনি লোডজেন এবং ক্লায়েন্টের মধ্যে ট্রেস ইন্সট্রুমেন্টেশন সম্পন্ন করেছেন। চলুন দেখি এটি কীভাবে কাজ করে। স্ক্যাফোল্ড দিয়ে কোডটি আবার চালান।

skaffold dev

GKE ক্লাস্টারে সার্ভিসগুলো কিছুক্ষণ চালানোর পর, আপনি এই ধরনের বিপুল পরিমাণ লগ মেসেজ দেখতে পাবেন:

কমান্ড আউটপুট

[loadgen] {
[loadgen]       "Name": "query.request",
[loadgen]       "SpanContext": {
[loadgen]               "TraceID": "cfa22247a542beeb55a3434392d46b89",
[loadgen]               "SpanID": "18b06404b10c418b",
[loadgen]               "TraceFlags": "01",
[loadgen]               "TraceState": "",
[loadgen]               "Remote": false
[loadgen]       },
[loadgen]       "Parent": {
[loadgen]               "TraceID": "00000000000000000000000000000000",
[loadgen]               "SpanID": "0000000000000000",
[loadgen]               "TraceFlags": "00",
[loadgen]               "TraceState": "",
[loadgen]               "Remote": false
[loadgen]       },
[loadgen]       "SpanKind": 1,
[loadgen]       "StartTime": "2022-07-14T13:13:36.686751087Z",
[loadgen]       "EndTime": "2022-07-14T13:14:31.849601964Z",
[loadgen]       "Attributes": [
[loadgen]               {
[loadgen]                       "Key": "telemetry.sdk.language",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "go"
[loadgen]                       }
[loadgen]               },
[loadgen]               {
[loadgen]                       "Key": "service.name",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "loadgen.runQuery"
[loadgen]                       }
[loadgen]               },
[loadgen]               {
[loadgen]                       "Key": "query",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "faith"
[loadgen]                       }
[loadgen]               }
[loadgen]       ],
[loadgen]       "Events": null,
[loadgen]       "Links": null,
[loadgen]       "Status": {
[loadgen]               "Code": "Unset",
[loadgen]               "Description": ""
[loadgen]       },
[loadgen]       "DroppedAttributes": 0,
[loadgen]       "DroppedEvents": 0,
[loadgen]       "DroppedLinks": 0,
[loadgen]       "ChildSpanCount": 5,
[loadgen]       "Resource": [
[loadgen]               {
[loadgen]                       "Key": "service.name",
[loadgen]                       "Value": {
[loadgen]                               "Type": "STRING",
[loadgen]                               "Value": "unknown_service:loadgen"
...

stdout এক্সপোর্টার এই বার্তাগুলো নির্গত করে। আপনি লক্ষ্য করবেন যে loadgen দ্বারা তৈরি সমস্ত স্প্যানের প্যারেন্টদের TraceID: 00000000000000000000000000000000 , কারণ এটি হলো রুট স্প্যান, অর্থাৎ ট্রেসের প্রথম স্প্যান। এছাড়াও আপনি দেখবেন যে "query" এমবেড অ্যাট্রিবিউটে সেই কোয়েরি স্ট্রিংটি রয়েছে যা ক্লায়েন্ট সার্ভিসে পাঠানো হয়।

সারসংক্ষেপ

এই ধাপে, আপনি HTTP-তে যোগাযোগকারী লোড জেনারেটর সার্ভিস এবং ক্লায়েন্ট সার্ভিসকে ইনস্ট্রুমেন্ট করেছেন এবং নিশ্চিত করেছেন যে আপনি সফলভাবে সার্ভিসগুলোর মধ্যে ট্রেস কনটেক্সট প্রচার করতে এবং উভয় সার্ভিস থেকে stdout-এ স্প্যান তথ্য এক্সপোর্ট করতে পেরেছেন।

এরপরে

পরবর্তী ধাপে, gRPC-এর মাধ্যমে কীভাবে ট্রেস কনটেক্সট (Trace Context) প্রেরণ করা যায় তা নিশ্চিত করার জন্য আপনি ক্লায়েন্ট সার্ভিস এবং সার্ভার সার্ভিসকে ইন্সট্রুমেন্ট করবেন।

৫. gRPC-এর জন্য যন্ত্রপাতি

পূর্ববর্তী ধাপে, আমরা এই মাইক্রোসার্ভিসগুলোতে অনুরোধের প্রথম অর্ধেককে ইনস্ট্রুমেন্ট করেছিলাম। এই ধাপে, আমরা ক্লায়েন্ট সার্ভিস এবং সার্ভার সার্ভিসের মধ্যে gRPC যোগাযোগকে ইনস্ট্রুমেন্ট করার চেষ্টা করব। (নীচের ছবিতে সবুজ এবং বেগুনি আয়তক্ষেত্র)

75310d8e0e3b1a30.png

gRPC ক্লায়েন্টের জন্য পূর্ব-নির্মিত ইন্সট্রুমেন্টেশন

ওপেনটেলিমেট্রির ইকোসিস্টেমে অনেকগুলো দরকারি লাইব্রেরি রয়েছে যা ডেভেলপারদের অ্যাপ্লিকেশন ইন্সট্রুমেন্ট করতে সাহায্য করে। আগের ধাপে, আমরা net/http প্যাকেজের জন্য আগে থেকে তৈরি ইন্সট্রুমেন্টেশন ব্যবহার করেছিলাম। এই ধাপে, যেহেতু আমরা gRPC-এর মাধ্যমে ট্রেস কনটেক্সট প্রচার করার চেষ্টা করছি, তাই আমরা এর জন্য লাইব্রেরিটি ব্যবহার করছি।

প্রথমে, আপনাকে otelgrpc নামের আগে থেকে তৈরি gRPC প্যাকেজটি ইম্পোর্ট করতে হবে।

step0/src/client/main.go

import (
        "context"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "net/url"
        "os"
        "time"

        "opentelemetry-trace-codelab-go/client/shakesapp"
        // step2. add prebuilt gRPC package (otelgrpc) 
        "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
        "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
        "go.opentelemetry.io/otel"
        "go.opentelemetry.io/otel/attribute"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "go.opentelemetry.io/otel/trace"
        "google.golang.org/grpc"
        "google.golang.org/grpc/credentials/insecure"
)

এবার, ক্লায়েন্ট সার্ভিসটি হলো সার্ভার সার্ভিসের বিরুদ্ধে একটি gRPC ক্লায়েন্ট, তাই আপনাকে gRPC ক্লায়েন্টটিকে ইনস্ট্রুমেন্ট করতে হবে। mustConnGRPC ফাংশনটি খুঁজুন এবং gRPC ইন্টারসেপ্টর যোগ করুন, যা ক্লায়েন্ট প্রতিবার সার্ভারে অনুরোধ করার সময় নতুন স্প্যানগুলোকে ইনস্ট্রুমেন্ট করবে।

step0/src/client/main.go

// Helper function for gRPC connections: Dial and create client once, reuse.
func mustConnGRPC(ctx context.Context, conn **grpc.ClientConn, addr string) {
        var err error
        // step2. add gRPC interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        *conn, err = grpc.DialContext(ctx, addr,
                grpc.WithTransportCredentials(insecure.NewCredentials()),
                grpc.WithUnaryInterceptor(otelgrpc.UnaryClientInterceptor(interceptorOpt)),
                grpc.WithStreamInterceptor(otelgrpc.StreamClientInterceptor(interceptorOpt)),
                grpc.WithTimeout(time.Second*3),
        )
        // step2: end adding interceptor
        if err != nil {
                panic(fmt.Sprintf("Error %s grpc: failed to connect %s", err, addr))
        }
}

যেহেতু আপনি আগের অংশে OpenTelemetry সেট আপ করে ফেলেছেন, তাই আপনাকে এটি আবার করতে হবে না।

gRPC সার্ভারের জন্য পূর্ব-নির্মিত ইন্সট্রুমেন্টেশন

gRPC ক্লায়েন্টের জন্য আমরা যা করেছিলাম, ঠিক সেভাবেই gRPC সার্ভারের জন্য আগে থেকে তৈরি ইন্সট্রুমেন্টেশন কল করি। ইম্পোর্ট সেকশনে নতুন প্যাকেজটি এভাবে যোগ করুন:

step0/src/server/main.go

import (
        "context"
        "fmt"
        "io/ioutil"
        "log"
        "net"
        "os"
        "regexp"
        "strings"

        "opentelemetry-trace-codelab-go/server/shakesapp"

        "cloud.google.com/go/storage"
        // step2. add OpenTelemetry packages including otelgrpc
        "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
        "go.opentelemetry.io/otel"
        stdout "go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
        "go.opentelemetry.io/otel/propagation"
        sdktrace "go.opentelemetry.io/otel/sdk/trace"
        "google.golang.org/api/iterator"
        "google.golang.org/api/option"
        "google.golang.org/grpc"
        healthpb "google.golang.org/grpc/health/grpc_health_v1"
)

যেহেতু এটি প্রথমবার সার্ভার ইন্সট্রুমেন্ট করা হচ্ছে, তাই লোডজেন এবং ক্লায়েন্ট সার্ভিসের মতো আপনাকেও প্রথমে ওপেনটেলিমেট্রি সেট আপ করতে হবে।

step0/src/server/main.go

// step2. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // create a stdout exporter to show collected spans out to stdout.
        exporter, err := stdout.New(stdout.WithPrettyPrint())
        if err != nil {
                return nil, err
        }
        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

func main() {
        ...

        // step2. setup OpenTelemetry
        tp, err := initTracer()
        if err != nil {
                log.Fatalf("failed to initialize TracerProvider: %v", err)
        }
        defer func() {
                if err := tp.Shutdown(context.Background()); err != nil {
                        log.Fatalf("error shutting down TracerProvider: %v", err)
                }
        }()
        // step2. end setup
        ...

এরপরে, আপনাকে সার্ভার ইন্টারসেপ্টর যোগ করতে হবে। main ফাংশনের মধ্যে, যেখানে grpc.NewServer() কল করা হয়েছে, সেই জায়গাটি খুঁজে বের করুন এবং ফাংশনটিতে ইন্টারসেপ্টর যোগ করুন।

step0/src/server/main.go

func main() {
        ...
        svc := NewServerService()
        // step2: add interceptor
        interceptorOpt := otelgrpc.WithTracerProvider(otel.GetTracerProvider())
        srv := grpc.NewServer(
                grpc.UnaryInterceptor(otelgrpc.UnaryServerInterceptor(interceptorOpt)),
                grpc.StreamInterceptor(otelgrpc.StreamServerInterceptor(interceptorOpt)),
        )
        // step2: end adding interceptor
        shakesapp.RegisterShakespeareServiceServer(srv, svc)
        ...

মাইক্রোসার্ভিসটি চালান এবং ট্রেসটি নিশ্চিত করুন।

এরপর skaffold কমান্ড দিয়ে আপনার পরিবর্তিত কোডটি চালান।

skaffold dev

এখনও আপনি stdout-এ অনেকগুলো স্প্যান তথ্য দেখতে পাচ্ছেন।

কমান্ড আউটপুট

...
[server] {
[server]        "Name": "shakesapp.ShakespeareService/GetMatchCount",
[server]        "SpanContext": {
[server]                "TraceID": "89b472f213a400cf975e0a0041649667",
[server]                "SpanID": "96030dbad0061b3f",
[server]                "TraceFlags": "01",
[server]                "TraceState": "",
[server]                "Remote": false
[server]        },
[server]        "Parent": {
[server]                "TraceID": "89b472f213a400cf975e0a0041649667",
[server]                "SpanID": "cd90cc3859b73890",
[server]                "TraceFlags": "01",
[server]                "TraceState": "",
[server]                "Remote": true
[server]        },
[server]        "SpanKind": 2,
[server]        "StartTime": "2022-07-14T14:05:55.74822525Z",
[server]        "EndTime": "2022-07-14T14:06:03.449258891Z",
[server]        "Attributes": [
...
[server]        ],
[server]        "Events": [
[server]                {
[server]                        "Name": "message",
[server]                        "Attributes": [
...
[server]                        ],
[server]                        "DroppedAttributeCount": 0,
[server]                        "Time": "2022-07-14T14:05:55.748235489Z"
[server]                },
[server]                {
[server]                        "Name": "message",
[server]                        "Attributes": [
...
[server]                        ],
[server]                        "DroppedAttributeCount": 0,
[server]                        "Time": "2022-07-14T14:06:03.449255889Z"
[server]                }
[server]        ],
[server]        "Links": null,
[server]        "Status": {
[server]                "Code": "Unset",
[server]                "Description": ""
[server]        },
[server]        "DroppedAttributes": 0,
[server]        "DroppedEvents": 0,
[server]        "DroppedLinks": 0,
[server]        "ChildSpanCount": 0,
[server]        "Resource": [
[server]                {
...
[server]        ],
[server]        "InstrumentationLibrary": {
[server]                "Name": "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc",
[server]                "Version": "semver:0.33.0",
[server]                "SchemaURL": ""
[server]        }
[server] }
...

আপনি লক্ষ্য করেন যে আপনি কোনো স্প্যান নাম এমবেড করেননি এবং trace.Start() বা span.SpanFromContext() ব্যবহার করে ম্যানুয়ালি স্প্যান তৈরি করেছেন। তবুও আপনি প্রচুর সংখ্যক স্প্যান পাচ্ছেন, কারণ gRPC ইন্টারসেপ্টরগুলোই সেগুলো তৈরি করেছে।

সারসংক্ষেপ

এই ধাপে, আপনি OpenTelemetry ইকোসিস্টেম লাইব্রেরির সহায়তায় gRPC ভিত্তিক যোগাযোগ ব্যবস্থা স্থাপন করেছেন।

এরপরে

পরবর্তী ধাপে, আপনি অবশেষে ক্লাউড ট্রেস ব্যবহার করে ট্রেসটি দেখতে পাবেন এবং সংগৃহীত স্প্যানগুলো কীভাবে বিশ্লেষণ করতে হয় তা শিখবেন।

৬. ক্লাউড ট্রেস ব্যবহার করে ট্রেসটি দৃশ্যমান করুন

আপনি ওপেনটেলিমেট্রি ব্যবহার করে পুরো সিস্টেমে ট্রেস ইন্সট্রুমেন্ট করেছেন। আপনি এখন পর্যন্ত HTTP এবং gRPC সার্ভিসগুলো কীভাবে ইন্সট্রুমেন্ট করতে হয় তা শিখেছেন। যদিও আপনি এগুলো ইন্সট্রুমেন্ট করা শিখেছেন, তবুও আপনি এগুলো বিশ্লেষণ করা শেখেননি। এই অংশে, আপনি stdout এক্সপোর্টারগুলোকে ক্লাউড ট্রেস এক্সপোর্টার দিয়ে প্রতিস্থাপন করবেন এবং আপনার ট্রেসগুলো কীভাবে বিশ্লেষণ করতে হয় তা শিখবেন।

ক্লাউড ট্রেস এক্সপোর্টার ব্যবহার করুন

ওপেনটেলিমেট্রির অন্যতম শক্তিশালী বৈশিষ্ট্য হলো এর প্লাগেবিলিটি। আপনার ইনস্ট্রুমেন্টেশন দ্বারা সংগৃহীত সমস্ত স্প্যান ভিজ্যুয়ালাইজ করার জন্য, আপনাকে শুধু stdout এক্সপোর্টারটিকে ক্লাউড ট্রেস এক্সপোর্টার দিয়ে প্রতিস্থাপন করতে হবে।

প্রতিটি সার্ভিসের main.go ফাইল খুলুন এবং initTracer() ফাংশনটি খুঁজুন। stdout এক্সপোর্টার তৈরি করার লাইনটি মুছে দিন এবং এর পরিবর্তে Cloud Trace এক্সপোর্টার তৈরি করুন।

step0/src/loadgen/main.go

import (
        ...
        // step3. add OpenTelemetry for Cloud Trace package
        cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace"
)

// step1. add OpenTelemetry initialization function
func initTracer() (*sdktrace.TracerProvider, error) {
        // step3. replace stdout exporter with Cloud Trace exporter
        // cloudtrace.New() finds the credentials to Cloud Trace automatically following the
        // rules defined by golang.org/x/oauth2/google.findDefaultCredentailsWithParams.
        // https://pkg.go.dev/golang.org/x/oauth2/google#FindDefaultCredentialsWithParams
        exporter, err := cloudtrace.New()
        // step3. end replacing exporter
        if err != nil {
                return nil, err
        }

        // for the demonstration, we use AlwaysSmaple sampler to take all spans.
        // do not use this option in production.
        tp := sdktrace.NewTracerProvider(
                sdktrace.WithSampler(sdktrace.AlwaysSample()),
                sdktrace.WithBatcher(exporter),
        )
        otel.SetTracerProvider(tp)
        otel.SetTextMapPropagator(propagation.TraceContext{})
        return tp, nil
}

আপনাকে ক্লায়েন্ট এবং সার্ভার সার্ভিসেও একই ফাংশনটি সম্পাদনা করতে হবে।

মাইক্রোসার্ভিসটি চালান এবং ট্রেসটি নিশ্চিত করুন।

সম্পাদনার পরে, skaffold কমান্ড ব্যবহার করে যথারীতি ক্লাস্টারটি চালান।

skaffold dev

তাহলে এখন আপনি stdout-এ স্ট্রাকচার্ড লগ ফরম্যাটে খুব বেশি স্প্যান তথ্য দেখতে পাবেন না, কারণ আপনি এক্সপোর্টারটিকে ক্লাউড ট্রেসের এক্সপোর্টার দিয়ে প্রতিস্থাপন করেছেন।

কমান্ড আউটপুট

[loadgen] 2022/07/14 15:01:07 simulated 20 requests
[loadgen] 2022/07/14 15:01:07 simulating client requests, round 37
[loadgen] 2022/07/14 15:01:14 query 'sweet': matched 958
[client] 2022/07/14 15:01:14 {"match_count":958}
[client] 2022/07/14 15:01:14 {"match_count":3040}
[loadgen] 2022/07/14 15:01:14 query 'love': matched 3040
[client] 2022/07/14 15:01:15 {"match_count":349}
[loadgen] 2022/07/14 15:01:15 query 'hello': matched 349
[client] 2022/07/14 15:01:15 {"match_count":484}
[loadgen] 2022/07/14 15:01:15 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:15 query 'insolence': matched 14
[client] 2022/07/14 15:01:15 {"match_count":14}
[client] 2022/07/14 15:01:21 {"match_count":484}
[loadgen] 2022/07/14 15:01:21 query 'faith': matched 484
[client] 2022/07/14 15:01:21 {"match_count":728}
[loadgen] 2022/07/14 15:01:21 query 'world': matched 728
[client] 2022/07/14 15:01:22 {"match_count":484}
[loadgen] 2022/07/14 15:01:22 query 'faith': matched 484
[loadgen] 2022/07/14 15:01:22 query 'hello': matched 349
[client] 2022/07/14 15:01:22 {"match_count":349}
[client] 2022/07/14 15:01:23 {"match_count":1036}
[loadgen] 2022/07/14 15:01:23 query 'friend': matched 1036
[loadgen] 2022/07/14 15:01:28 query 'tear': matched 463
...

এখন চলুন নিশ্চিত করা যাক যে সমস্ত স্প্যান ক্লাউড ট্রেস-এ সঠিকভাবে পাঠানো হয়েছে কিনা। ক্লাউড কনসোল অ্যাক্সেস করুন এবং "ট্রেস লিস্ট"-এ যান। সার্চ বক্স থেকে এটি সহজেই অ্যাক্সেস করা যায়। অন্যথায়, আপনি বাম দিকের প্যানেলের মেনুতে ক্লিক করতে পারেন। 8b3f8411bd737e06.png

এরপর আপনি দেখবেন ল্যাটেন্সি গ্রাফ জুড়ে অনেকগুলো নীল বিন্দু ছড়িয়ে আছে। প্রতিটি বিন্দু একটি একক ট্রেসকে প্রতিনিধিত্ব করে।

3ecf131423fc4c40.png

এগুলোর যেকোনো একটিতে ক্লিক করলে আপনি ট্রেসটির ভেতরের বিস্তারিত দেখতে পাবেন। 4fd10960c6648a03.png

এই সাধারণ ও দ্রুত পর্যবেক্ষণ থেকেই আপনি অনেক কিছু জানতে পারছেন। উদাহরণস্বরূপ, ওয়াটারফল গ্রাফ থেকে আপনি দেখতে পাচ্ছেন যে, ল্যাটেন্সির প্রধান কারণ হলো shakesapp.ShakespeareService/GetMatchCount নামের স্প্যানটি। (উপরের ছবিতে ১ দেখুন) আপনি সামারি টেবিল থেকে এটি নিশ্চিত করতে পারেন। (সবচেয়ে ডানদিকের কলামটি প্রতিটি স্প্যানের সময়কাল দেখাচ্ছে।) এছাড়াও, এই ট্রেসটি ছিল 'friend' কোয়েরিটির জন্য। (উপরের ছবিতে ২ দেখুন)

এই সংক্ষিপ্ত বিশ্লেষণগুলো থেকে আপনি হয়তো বুঝতে পারবেন যে, GetMatchCount মেথডের ভেতরের বিষয়গুলো আপনার আরও সূক্ষ্মভাবে জানার প্রয়োজন আছে। stdout তথ্যের তুলনায় ভিজ্যুয়ালাইজেশন অনেক বেশি শক্তিশালী। Cloud Trace-এর বিস্তারিত তথ্য সম্পর্কে আরও জানতে, অনুগ্রহ করে আমাদের অফিশিয়াল ডকুমেন্টেশন দেখুন।

সারসংক্ষেপ

এই ধাপে, আপনি stdout এক্সপোর্টারটিকে Cloud Trace-এর এক্সপোর্টার দিয়ে প্রতিস্থাপন করেছেন এবং Cloud Trace-এ ট্রেসগুলো দেখেছেন। এছাড়াও, আপনি ট্রেসগুলো বিশ্লেষণ করা শুরু করার পদ্ধতি শিখেছেন।

এরপরে

পরবর্তী ধাপে, আপনি GetMatchCount-এ একটি সাব স্প্যান যোগ করার জন্য সার্ভার সার্ভিসের সোর্স কোড পরিবর্তন করবেন।

৭. আরও ভালো বিশ্লেষণের জন্য সাব স্প্যান যোগ করুন।

পূর্ববর্তী ধাপে, আপনি দেখেছেন যে লোডজেন (loadgen) থেকে প্রাপ্ত রাউন্ড ট্রিপ টাইমের কারণ মূলত সার্ভার সার্ভিসের GetMatchCount মেথডের ভেতরের প্রসেস, অর্থাৎ gRPC হ্যান্ডলার। কিন্তু, যেহেতু আমরা হ্যান্ডলারটি ছাড়া আর কিছুই ইন্সট্রুমেন্ট করিনি, তাই আমরা ওয়াটারফল গ্রাফ থেকে এর চেয়ে বেশি কোনো তথ্য খুঁজে পাচ্ছি না। মাইক্রোসার্ভিস ইন্সট্রুমেন্ট করা শুরু করলে এমনটা প্রায়ই ঘটে থাকে।

3b63a1e471dddb8c.png

এই অংশে, আমরা একটি সাব-স্প্যান পরীক্ষা করে দেখব যেখান থেকে সার্ভার গুগল ক্লাউড স্টোরেজকে কল করে। কারণ, প্রায়শই দেখা যায় যে কোনো এক্সটার্নাল নেটওয়ার্ক I/O প্রক্রিয়া সম্পন্ন হতে অনেক সময় নেয় এবং এই কলটিই তার কারণ কিনা তা শনাক্ত করা জরুরি।

সার্ভারে একটি সাব স্প্যান ইন্সট্রুমেন্ট করুন

সার্ভারে main.go খুলুন এবং readFiles ফাংশনটি খুঁজুন। এই ফাংশনটি শেক্সপিয়রের সমস্ত টেক্সট ফাইল আনার জন্য গুগল ক্লাউড স্টোরেজে একটি অনুরোধ পাঠাচ্ছে। এই ফাংশনের ভিতরে, আপনি একটি সাব-স্প্যান তৈরি করতে পারেন, যেমনটি আপনি ক্লায়েন্ট সার্ভিসে HTTP সার্ভার ইন্সট্রুমেন্টেশনের জন্য করেছিলেন।

step0/src/server/main.go

func readFiles(ctx context.Context, bucketName, prefix string) ([]string, error) {
        type resp struct {
                s   string
                err error
        }

        // step4: add an extra span
        span := trace.SpanFromContext(ctx)
        span.SetName("server.readFiles")
        span.SetAttributes(attribute.Key("bucketname").String(bucketName))
        defer span.End()
        // step4: end add span
        ...

নতুন স্প্যান যোগ করার জন্য এটুকুই যথেষ্ট। চলুন অ্যাপটি চালিয়ে দেখি কেমন হয়।

মাইক্রোসার্ভিসটি চালান এবং ট্রেসটি নিশ্চিত করুন।

সম্পাদনার পরে, skaffold কমান্ড ব্যবহার করে যথারীতি ক্লাস্টারটি চালান।

skaffold dev

এবং ট্রেস তালিকা থেকে query.request নামের একটি ট্রেস বেছে নিন। আপনি shakesapp.ShakespeareService/GetMatchCount অধীনে একটি নতুন স্প্যান ছাড়া একই রকম একটি ট্রেস ওয়াটারফল গ্রাফ দেখতে পাবেন। (নিচে লাল আয়তক্ষেত্র দ্বারা আবদ্ধ স্প্যানটি)

3d4a891aa30d7a32.png

এই গ্রাফ থেকে এখন যা বোঝা যাচ্ছে তা হলো, গুগল ক্লাউড স্টোরেজে করা এক্সটার্নাল কলটি একটি বড় পরিমাণ ল্যাটেন্সি দখল করে, কিন্তু বাকি ল্যাটেন্সির সিংহভাগই অন্যান্য কারণে তৈরি হচ্ছে।

ট্রেস ওয়াটারফল গ্রাফটি কয়েকবার দেখেই আপনি ইতিমধ্যে অনেক কিছু বুঝে উঠেছেন। আপনার অ্যাপ্লিকেশনের পারফরম্যান্সের আরও বিস্তারিত তথ্য কীভাবে পাবেন? এখানেই প্রোফাইলারের প্রয়োজন হয়, কিন্তু আপাতত এই কোডল্যাবটি এখানেই শেষ করা যাক এবং প্রোফাইলার সম্পর্কিত সমস্ত টিউটোরিয়াল পার্ট ২-এর জন্য রেখে দেওয়া যাক।

সারসংক্ষেপ

এই ধাপে, আপনি সার্ভার সার্ভিসে আরেকটি স্প্যান ইন্সট্রুমেন্ট করেছেন এবং সিস্টেম লেটেন্সি সম্পর্কে আরও অন্তর্দৃষ্টি লাভ করেছেন।

৮. অভিনন্দন

আপনি OpenTelemery ব্যবহার করে সফলভাবে ডিস্ট্রিবিউটেড ট্রেস তৈরি করেছেন এবং Google Cloud Trace-এ মাইক্রোসার্ভিস জুড়ে রিকোয়েস্ট ল্যাটেন্সি নিশ্চিত করেছেন।

বিস্তারিত অনুশীলনের জন্য, আপনি নিম্নলিখিত বিষয়গুলো নিজে নিজে চেষ্টা করতে পারেন।

  • বর্তমান বাস্তবায়নটি হেলথ চেক দ্বারা তৈরি সমস্ত স্প্যান পাঠিয়ে দেয়। ( grpc.health.v1.Health/Check ) আপনি ক্লাউড ট্রেস থেকে সেই স্প্যানগুলি কীভাবে ফিল্টার করবেন? ইঙ্গিতটি এখানে দেওয়া আছে।
  • ইভেন্ট লগগুলোকে স্প্যানের সাথে সংযুক্ত করুন এবং দেখুন এটি গুগল ক্লাউড ট্রেস ও গুগল ক্লাউড লগিং-এ কীভাবে কাজ করে। ইঙ্গিতটি এখানে দেওয়া আছে
  • কোনো একটি সার্ভিসকে অন্য ভাষার সার্ভিস দিয়ে প্রতিস্থাপন করুন এবং সেই ভাষার জন্য OpenTelemetry ব্যবহার করে সেটিকে ইনস্ট্রুমেন্ট করার চেষ্টা করুন।

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

পরিষ্কার করা

এই কোডল্যাবটির পর, অনুগ্রহ করে কুবারনেটিস ক্লাস্টারটি বন্ধ করুন এবং প্রজেক্টটি ডিলিট করে দিন, যাতে গুগল কুবারনেটিস ইঞ্জিন, গুগল ক্লাউড ট্রেস, গুগল আর্টিফ্যাক্ট রেজিস্ট্রি-তে কোনো অপ্রত্যাশিত চার্জ যুক্ত না হয়।

প্রথমে, ক্লাস্টারটি ডিলিট করুন। আপনি যদি skaffold dev দিয়ে ক্লাস্টারটি চালান, তাহলে শুধু Ctrl-C চাপলেই হবে। আর যদি skaffold run দিয়ে ক্লাস্টারটি চালান, তাহলে নিচের কমান্ডটি চালান:

skaffold delete

কমান্ড আউটপুট

Cleaning up...
 - deployment.apps "clientservice" deleted
 - service "clientservice" deleted
 - deployment.apps "loadgen" deleted
 - deployment.apps "serverservice" deleted
 - service "serverservice" deleted

ক্লাস্টারটি ডিলিট করার পর, মেনু প্যান থেকে "IAM & Admin" > "Settings" নির্বাচন করুন এবং তারপর "SHUT DOWN" বোতামে ক্লিক করুন।

45aa37b7d5e1ddd1.png

এরপর ডায়ালগ বক্সের ফর্মে প্রজেক্ট আইডি (প্রজেক্টের নাম নয়) প্রবেশ করান এবং শাটডাউন নিশ্চিত করুন।