paint-brush
গো-তে গ্রেসফুল শাটডাউনগুলি আয়ত্ত করা: কুবারনেটসের জন্য একটি ব্যাপক নির্দেশিকা৷দ্বারা@gopher

গো-তে গ্রেসফুল শাটডাউনগুলি আয়ত্ত করা: কুবারনেটসের জন্য একটি ব্যাপক নির্দেশিকা৷

দ্বারা Alex6m2024/08/14
Read on Terminal Reader

অতিদীর্ঘ; পড়তে

এই নির্দেশিকাটি আকর্ষণীয় শাটডাউনের জগতের সন্ধান করবে, বিশেষ করে কুবারনেটসে চলমান Go অ্যাপ্লিকেশনগুলিতে তাদের বাস্তবায়নের উপর ফোকাস করবে।
featured image - গো-তে গ্রেসফুল শাটডাউনগুলি আয়ত্ত করা: কুবারনেটসের জন্য একটি ব্যাপক নির্দেশিকা৷
Alex HackerNoon profile picture

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


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


এই নির্দেশিকাটি আকর্ষণীয় শাটডাউনের জগতের সন্ধান করবে, বিশেষ করে Kubernetes-এ চলমান Go অ্যাপ্লিকেশনগুলিতে তাদের বাস্তবায়নের উপর ফোকাস করবে।

ইউনিক্স সিস্টেমে সংকেত

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


অনেক সংকেত আছে, এবং আপনি সেগুলি এখানে খুঁজে পেতে পারেন, কিন্তু আমাদের উদ্বেগ শুধুমাত্র শাটডাউন সংকেত:

  • SIGTERM - এটির সমাপ্তির অনুরোধ করার জন্য একটি প্রক্রিয়াতে পাঠানো হয়েছে৷ সর্বাধিক ব্যবহৃত হয়, এবং আমরা পরে এটিতে ফোকাস করব৷
  • SIGKILL - "অবিলম্বে প্রস্থান করুন", হস্তক্ষেপ করা যাবে না।
  • SIGINT - বাধা সংকেত (যেমন Ctrl+C)
  • SIGQUIT - প্রস্থান সংকেত (যেমন Ctrl+D)


এই সংকেতগুলি ব্যবহারকারীর কাছ থেকে (Ctrl+C / Ctrl+D), অন্য একটি প্রোগ্রাম/প্রসেস বা সিস্টেম থেকে (কার্নেল/OS) পাঠানো যেতে পারে, উদাহরণস্বরূপ, একটি SIGSEGV ওরফে সেগমেন্টেশন ফল্ট OS দ্বারা পাঠানো হয়।


আমাদের গিনি পিগ পরিষেবা

একটি ব্যবহারিক সেটিংয়ে মনোমুগ্ধকর শাটডাউনের বিশ্ব অন্বেষণ করতে, আসুন একটি সাধারণ পরিষেবা তৈরি করি যা আমরা পরীক্ষা করতে পারি৷ এই "গিনিপিগ" পরিষেবাটির একটি একক শেষ পয়েন্ট থাকবে যা Redis-এর INCR কমান্ডকে কল করে কিছু বাস্তব-বিশ্বের কাজ (আমরা সামান্য বিলম্ব যোগ করব) অনুকরণ করে৷ প্ল্যাটফর্মটি কীভাবে সমাপ্তির সংকেতগুলি পরিচালনা করে তা পরীক্ষা করার জন্য আমরা একটি মৌলিক Kubernetes কনফিগারেশনও প্রদান করব।


চূড়ান্ত লক্ষ্য: নিশ্চিত করুন যে আমাদের পরিষেবাটি কোনো অনুরোধ/ডেটা হারানো ছাড়াই শাটডাউনগুলিকে সুন্দরভাবে পরিচালনা করে। Redis-এ চূড়ান্ত কাউন্টার মানের সাথে সমান্তরালভাবে পাঠানো অনুরোধের সংখ্যার তুলনা করে, আমরা আমাদের সুন্দর শাটডাউন বাস্তবায়ন সফল কিনা তা যাচাই করতে সক্ষম হব।

আমরা Kubernetes ক্লাস্টার এবং Redis সেট আপ করার বিশদ বিবরণে যাব না, তবে আপনি আমাদের Github সংগ্রহস্থলে সম্পূর্ণ সেটআপটি খুঁজে পেতে পারেন।


যাচাইকরণ প্রক্রিয়াটি নিম্নরূপ:

  1. কুবারনেটসে রেডিস এবং গো অ্যাপ্লিকেশন স্থাপন করুন।
  2. 1000টি অনুরোধ পাঠাতে vegeta ব্যবহার করুন (40 সেকেন্ডের বেশি 25/s)।
  3. যখন ভেজিটা চলছে, তখন ইমেজ ট্যাগ আপডেট করে কুবারনেটস রোলিং আপডেট শুরু করুন।
  4. "কাউন্টার" যাচাই করতে Redis এর সাথে সংযোগ করুন, এটি 1000 হওয়া উচিত।


আমাদের বেস গো HTTP সার্ভার দিয়ে শুরু করা যাক।

hard-shutdown/main.go

 package main import ( "net/http" "os" "time" "github.com/go-redis/redis" ) func main() { redisdb := redis.NewClient(&redis.Options{ Addr: os.Getenv("REDIS_ADDR"), }) server := http.Server{ Addr: ":8080", } http.HandleFunc("/incr", func(w http.ResponseWriter, r *http.Request) { go processRequest(redisdb) w.WriteHeader(http.StatusOK) }) server.ListenAndServe() } func processRequest(redisdb *redis.Client) { // simulate some business logic here time.Sleep(time.Second * 5) redisdb.Incr("counter") }

যখন আমরা এই কোডটি ব্যবহার করে আমাদের যাচাইকরণ পদ্ধতি চালাই তখন আমরা দেখতে পাব যে কিছু অনুরোধ ব্যর্থ হয় এবং কাউন্টারটি 1000-এর কম হয় (প্রতিবারে সংখ্যাটি আলাদা হতে পারে)।


যার স্পষ্ট অর্থ হল রোলিং আপডেটের সময় আমরা কিছু ডেটা হারিয়েছি। 😢

Go-তে সংকেত পরিচালনা করা

Go একটি সিগন্যাল প্যাকেজ প্রদান করে যা আপনাকে ইউনিক্স সিগন্যাল পরিচালনা করতে দেয়। এটি লক্ষ্য করা গুরুত্বপূর্ণ যে ডিফল্টরূপে, SIGINT এবং SIGTERM সংকেতগুলি Go প্রোগ্রাম থেকে প্রস্থান করে। এবং আমাদের গো অ্যাপ্লিকেশন যাতে হঠাৎ করে প্রস্থান না করে, তার জন্য আমাদের আগত সংকেতগুলি পরিচালনা করতে হবে।

এটি করার জন্য দুটি বিকল্প রয়েছে।


চ্যানেল ব্যবহার করে:

 c := make(chan os.Signal, 1) signal.Notify(c, syscall.SIGTERM)


প্রসঙ্গ ব্যবহার করে (আজকাল পছন্দের পদ্ধতি):

 ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop()


NotifyContext মূল প্রসঙ্গটির একটি অনুলিপি প্রদান করে যাকে সম্পন্ন হিসাবে চিহ্নিত করা হয়েছে (এর সম্পন্ন চ্যানেল বন্ধ) যখন তালিকাভুক্ত সংকেতগুলির মধ্যে একটি আসে, যখন ফেরানো স্টপ() ফাংশনটি কল করা হয়, বা যখন অভিভাবক প্রসঙ্গটির সম্পন্ন চ্যানেল বন্ধ করা হয়, যেটি প্রথমে ঘটবে .


HTTP সার্ভারের আমাদের বর্তমান বাস্তবায়নে কিছু সমস্যা রয়েছে:

  1. আমাদের একটি ধীর প্রসেস রিকোয়েস্ট গোরুটিন আছে, এবং যেহেতু আমরা টার্মিনেশন সিগন্যালটি পরিচালনা করি না, প্রোগ্রামটি স্বয়ংক্রিয়ভাবে প্রস্থান করে, যার অর্থ সমস্ত চলমান গোরুটিনগুলিও বন্ধ হয়ে যায়।
  2. প্রোগ্রাম কোনো সংযোগ বন্ধ করে না.


এর আবার লিখুন.


graceful-shutdown/main.go

 package main // imports var wg sync.WaitGroup func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGTERM) defer stop() // redisdb, server http.HandleFunc("/incr", func(w http.ResponseWriter, r *http.Request) { wg.Add(1) go processRequest(redisdb) w.WriteHeader(http.StatusOK) }) // make it a goroutine go server.ListenAndServe() // listen for the interrupt signal <-ctx.Done() // stop the server if err := server.Shutdown(context.Background()); err != nil { log.Fatalf("could not shutdown: %v\n", err) } // wait for all goroutines to finish wg.Wait() // close redis connection redisdb.Close() os.Exit(0) } func processRequest(redisdb *redis.Client) { defer wg.Done() // simulate some business logic here time.Sleep(time.Second * 5) redisdb.Incr("counter") }


এখানে আপডেটের সারসংক্ষেপ রয়েছে:

  • SIGTERM সমাপ্তি সংকেত শোনার জন্য সংকেত যোগ করা হয়েছে।NotifyContext।
  • ইন-ফ্লাইট অনুরোধগুলি (প্রসেসরিকোয়েস্ট গোরুটিন) ট্র্যাক করার জন্য একটি সিঙ্ক. ওয়েটগ্রুপ প্রবর্তন করেছে৷
  • সার্ভারটিকে একটি গোরুটিনে মোড়ানো এবং সার্ভার ব্যবহার করা হয়েছে৷ নতুন সংযোগ গ্রহণ করা বন্ধ করার জন্য প্রসঙ্গ সহ শাটডাউন৷
  • সমস্ত ইন-ফ্লাইট অনুরোধ (প্রসেসরিকোয়েস্ট গোরুটিন) এগিয়ে যাওয়ার আগে শেষ করার জন্য wg.Wait() ব্যবহার করা হয়েছে।
  • রিসোর্স ক্লিনআপ: প্রস্থান করার আগে Redis সংযোগটি সঠিকভাবে বন্ধ করতে redisdb.Close() যোগ করা হয়েছে।
  • ক্লিন এক্সিট: একটি সফল সমাপ্তি নির্দেশ করতে os.Exit(0) ব্যবহার করা হয়েছে।

এখন, যদি আমরা আমাদের যাচাইকরণ প্রক্রিয়াটি পুনরাবৃত্তি করি তাহলে আমরা দেখতে পাব যে সমস্ত 1000টি অনুরোধ সঠিকভাবে প্রক্রিয়া করা হয়েছে। 🎉


ওয়েব ফ্রেমওয়ার্ক / HTTP লাইব্রেরি

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


যখন আপনি একটি SIGTERM সংকেতকে বাধা দেন এবং আপনার ফ্রেমওয়ার্ককে সুন্দরভাবে শাটডাউন করতে বলেন, তখন 2টি গুরুত্বপূর্ণ জিনিস ঘটে (অতি সরল করার জন্য):

  • আপনার ফ্রেমওয়ার্ক ইনকামিং অনুরোধগুলি গ্রহণ করা বন্ধ করে দেয়
  • এটি যেকোন বিদ্যমান আগত অনুরোধগুলি শেষ হওয়ার জন্য অপেক্ষা করে (গরুটিনগুলি শেষ হওয়ার জন্য অন্তর্নিহিতভাবে অপেক্ষা করা)।


দ্রষ্টব্য: Kubernetes লোডব্যালেন্সার থেকে আপনার পডে ইনকামিং ট্র্যাফিকের নির্দেশনা বন্ধ করে দেয় একবার এটিকে টার্মিনেটিং হিসাবে লেবেল করে দেয়।

ঐচ্ছিক: শাটডাউন টাইমআউট

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


 shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() go func() { if err := server.Shutdown(shutdownCtx); err != nil { log.Fatalf("could not shutdown: %v\n", err) } }() select { case <-shutdownCtx.Done(): if shutdownCtx.Err() == context.DeadlineExceeded { log.Fatalln("timeout exceeded, forcing shutdown") } os.Exit(0) }

কুবারনেটস টার্মিনেশন লাইফসাইকেল

যেহেতু আমরা আমাদের পরিষেবা স্থাপনের জন্য কুবারনেটস ব্যবহার করেছি, আসুন এটি কীভাবে পডগুলিকে শেষ করে তা আরও গভীরে ঢোকা যাক৷ একবার কুবারনেটস পডটি বন্ধ করার সিদ্ধান্ত নিলে, নিম্নলিখিত ঘটনাগুলি ঘটবে:

  1. পড "সমাপ্ত" অবস্থায় সেট করা হয়েছে এবং সমস্ত পরিষেবার শেষ পয়েন্ট তালিকা থেকে সরানো হয়েছে।
  2. সংজ্ঞায়িত হলে preStop হুক কার্যকর করা হয়।
  3. SIGTERM সংকেত পডে পাঠানো হয়। কিন্তু আরে, এখন আমাদের আবেদন জানে কী করতে হবে!
  4. Kubernetes একটি গ্রেস পিরিয়ডের জন্য অপেক্ষা করে ( terminationGracePeriodSeconds ), যা ডিফল্টরূপে 30s।
  5. SIGKILL সংকেত পডে পাঠানো হয়, এবং পড সরানো হয়।

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

উপসংহার

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

আপনি আমাদের Github সংগ্রহস্থলে Go কোড এবং Kubernetes ম্যানিফেস্টগুলি খুঁজে পেতে পারেন।

সম্পদ