১. সংক্ষিপ্ত বিবরণ
এই ল্যাবটি এমন সব ফিচার ও সক্ষমতা প্রদর্শন করে, যা কন্টেইনারাইজড পরিবেশে জাভা অ্যাপ্লিকেশন তৈরির দায়িত্বে থাকা সফটওয়্যার ইঞ্জিনিয়ারদের জন্য ডেভেলপমেন্ট ওয়ার্কফ্লোকে সহজ ও সাবলীল করার উদ্দেশ্যে ডিজাইন করা হয়েছে। সাধারণ কন্টেইনার ডেভেলপমেন্টের জন্য ব্যবহারকারীকে কন্টেইনার এবং কন্টেইনার বিল্ড প্রসেসের খুঁটিনাটি বিষয়গুলো বুঝতে হয়। এছাড়াও, ডেভেলপারদের প্রায়শই তাদের কাজের ধারাবাহিকতায় ছেদ ঘটাতে হয় এবং দূরবর্তী পরিবেশে অ্যাপ্লিকেশন পরীক্ষা ও ডিবাগ করার জন্য IDE থেকে বেরিয়ে যেতে হয়। এই টিউটোরিয়ালে উল্লিখিত টুলস এবং প্রযুক্তিগুলোর সাহায্যে ডেভেলপাররা তাদের IDE না ছেড়েই কন্টেইনারাইজড অ্যাপ্লিকেশন নিয়ে কার্যকরভাবে কাজ করতে পারবেন।
আপনি যা শিখবেন
এই ল্যাবে আপনি GCP-তে কন্টেইনার ব্যবহার করে ডেভেলপ করার পদ্ধতিগুলো শিখবেন, যার মধ্যে অন্তর্ভুক্ত রয়েছে:
- ক্লাউড ওয়ার্কস্টেশন সহ ইনারলুপ উন্নয়ন
- একটি নতুন জাভা স্টার্টার অ্যাপ্লিকেশন তৈরি করা
- উন্নয়ন প্রক্রিয়ার মধ্য দিয়ে হেঁটে যাওয়া
- একটি সহজ CRUD রেস্ট সার্ভিস তৈরি করা
- GKE ক্লাস্টারে অ্যাপ্লিকেশন ডিবাগিং
- অ্যাপ্লিকেশনকে ক্লাউডএসকিউএল ডেটাবেসের সাথে সংযুক্ত করা হচ্ছে

২. সেটআপ এবং প্রয়োজনীয়তা
স্ব-গতিতে পরিবেশ সেটআপ
- Google Cloud Console- এ সাইন-ইন করুন এবং একটি নতুন প্রজেক্ট তৈরি করুন অথবা বিদ্যমান কোনো প্রজেক্ট পুনরায় ব্যবহার করুন। যদি আপনার আগে থেকে Gmail বা Google Workspace অ্যাকাউন্ট না থাকে, তবে আপনাকে অবশ্যই একটি তৈরি করতে হবে।



- প্রজেক্টের নামটি হলো এই প্রজেক্টের অংশগ্রহণকারীদের প্রদর্শিত নাম। এটি একটি ক্যারেক্টার স্ট্রিং যা গুগল এপিআই ব্যবহার করে না। আপনি যেকোনো সময় এটি আপডেট করতে পারেন।
- প্রজেক্ট আইডি সমস্ত গুগল ক্লাউড প্রজেক্ট জুড়ে অনন্য এবং অপরিবর্তনীয় (একবার সেট করার পর এটি পরিবর্তন করা যায় না)। ক্লাউড কনসোল স্বয়ংক্রিয়ভাবে একটি অনন্য স্ট্রিং তৈরি করে; সাধারণত এটি কী তা নিয়ে আপনার মাথা ঘামানোর প্রয়োজন হয় না। বেশিরভাগ কোডল্যাবে, আপনাকে প্রজেক্ট আইডি উল্লেখ করতে হবে (এটি সাধারণত
PROJECT_IDহিসাবে চিহ্নিত করা হয়)। তৈরি করা আইডিটি আপনার পছন্দ না হলে, আপনি এলোমেলোভাবে আরেকটি তৈরি করতে পারেন। বিকল্পভাবে, আপনি নিজের আইডি দিয়ে চেষ্টা করে দেখতে পারেন যে সেটি উপলব্ধ আছে কিনা। এই ধাপের পরে এটি পরিবর্তন করা যাবে না এবং প্রজেক্টের পুরো সময়কাল জুড়ে এটি অপরিবর্তিত থাকবে। - আপনার অবগতির জন্য জানাচ্ছি যে, তৃতীয় একটি ভ্যালু রয়েছে, যা হলো প্রজেক্ট নাম্বার এবং কিছু এপিআই এটি ব্যবহার করে। ডকুমেন্টেশনে এই তিনটি ভ্যালু সম্পর্কে আরও বিস্তারিত জানুন।
- এরপর, ক্লাউড রিসোর্স/এপিআই ব্যবহার করার জন্য আপনাকে ক্লাউড কনসোলে বিলিং চালু করতে হবে। এই কোডল্যাবটি সম্পন্ন করতে খুব বেশি খরচ হওয়ার কথা নয়, এমনকি আদৌ কোনো খরচ নাও হতে পারে। এই টিউটোরিয়ালের পর যাতে কোনো বিলিং না হয়, সেজন্য রিসোর্সগুলো বন্ধ করতে আপনি আপনার তৈরি করা রিসোর্সগুলো অথবা পুরো প্রজেক্টটিই ডিলিট করে দিতে পারেন। গুগল ক্লাউডের নতুন ব্যবহারকারীরা ৩০০ মার্কিন ডলারের ফ্রি ট্রায়াল প্রোগ্রামের জন্য যোগ্য।
ক্লাউডশেল এডিটর শুরু করুন
এই ল্যাবটি গুগল ক্লাউড শেল এডিটর-এর সাথে ব্যবহারের জন্য ডিজাইন ও পরীক্ষা করা হয়েছে। এডিটরটি অ্যাক্সেস করতে,
- আপনার গুগল প্রজেক্টটি https://console.cloud.google.com -এ অ্যাক্সেস করুন।
- উপরের ডান কোণায় ক্লাউড শেল এডিটর আইকনে ক্লিক করুন

- আপনার জানালার নিচের দিকে একটি নতুন কাচ খুলবে।
- ওপেন এডিটর বোতামে ক্লিক করুন

- এডিটরটি ডানদিকে একটি এক্সপ্লোরার এবং মাঝখানে এডিটর সহ খুলবে।
- স্ক্রিনের নীচে একটি টার্মিনাল পেইনও থাকা উচিত।
- টার্মিনাল খোলা না থাকলে, নতুন টার্মিনাল উইন্ডো খোলার জন্য `ctrl+` কী-সংমিশ্রণটি ব্যবহার করুন।
gcloud সেট আপ করুন
ক্লাউড শেলে, আপনার প্রজেক্ট আইডি এবং যে অঞ্চলে আপনার অ্যাপ্লিকেশনটি ডেপ্লয় করতে চান, তা সেট করুন। এগুলোকে PROJECT_ID এবং REGION ভেরিয়েবল হিসেবে সেভ করুন।
export REGION=us-central1
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
সোর্স কোড ক্লোন করুন
এই ল্যাবের সোর্স কোডটি গিটহাবের GoogleCloudPlatform-এর `container-developer-workshop`-এ অবস্থিত। নিচের কমান্ডটি দিয়ে এটি ক্লোন করুন, তারপর ডিরেক্টরিতে প্রবেশ করুন।
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git
cd container-developer-workshop/labs/spring-boot
এই ল্যাবে ব্যবহৃত অবকাঠামো সরবরাহ করুন।
এই ল্যাবে আপনি GKE-তে কোড ডেপ্লয় করবেন এবং একটি CloudSQL ডেটাবেসে সংরক্ষিত ডেটা অ্যাক্সেস করবেন। নিচের সেটআপ স্ক্রিপ্টটি আপনার জন্য এই পরিকাঠামো প্রস্তুত করে দেবে। প্রোভিশনিং প্রক্রিয়াটিতে ২৫ মিনিটের বেশি সময় লাগবে। পরবর্তী বিভাগে যাওয়ার আগে স্ক্রিপ্টটি সম্পূর্ণ হওয়া পর্যন্ত অপেক্ষা করুন।
./setup_with_cw.sh &
ক্লাউড ওয়ার্কস্টেশন ক্লাস্টার
ক্লাউড কনসোলে ক্লাউড ওয়ার্কস্টেশনগুলো খুলুন। ক্লাস্টারটি READY স্ট্যাটাসে আসা পর্যন্ত অপেক্ষা করুন।
ওয়ার্কস্টেশন কনফিগারেশন তৈরি করুন
আপনার ক্লাউড শেল সেশনটি সংযোগ বিচ্ছিন্ন হয়ে গেলে, "পুনরায় সংযোগ করুন" (Reconnect) বোতামে ক্লিক করুন এবং তারপরে প্রজেক্ট আইডি সেট করার জন্য gcloud cli কমান্ডটি চালান। কমান্ডটি চালানোর আগে, নীচের নমুনা প্রজেক্ট আইডিটি আপনার কুইকল্যাবস প্রজেক্ট আইডি দিয়ে প্রতিস্থাপন করুন।
gcloud config set project qwiklabs-gcp-project-id
ক্লাউড ওয়ার্কস্টেশন কনফিগারেশন তৈরি করতে টার্মিনালে নিচের স্ক্রিপ্টটি চালান।
cd ~/container-developer-workshop/labs/spring-boot
./workstation_config_setup.sh
কনফিগারেশন বিভাগে ফলাফল যাচাই করুন। READY স্ট্যাটাসে রূপান্তরিত হতে ২ মিনিট সময় লাগবে।

কনসোলে ক্লাউড ওয়ার্কস্টেশন খুলুন এবং নতুন ইনস্ট্যান্স তৈরি করুন।

নাম পরিবর্তন করে my-workstation করুন এবং বিদ্যমান কনফিগারেশন codeoss-java নির্বাচন করুন।

ওয়ার্কস্টেশন বিভাগে ফলাফল যাচাই করুন।

লঞ্চ ওয়ার্কস্টেশন
ওয়ার্কস্টেশনটি চালু করুন।

অ্যাড্রেস বারের আইকনটিতে ক্লিক করে থার্ড পার্টি কুকিজের অনুমতি দিন। 

'সাইট কাজ করছে না?'-এ ক্লিক করুন।

"কুকিজের অনুমতি দিন"-এ ক্লিক করুন।

ওয়ার্কস্টেশনটি চালু হলে আপনি Code OSS IDE দেখতে পাবেন। ওয়ার্কস্টেশন IDE-এর Getting Started পৃষ্ঠায় "Mark Done"-এ ক্লিক করুন।

৩. একটি নতুন জাভা স্টার্টার অ্যাপ্লিকেশন তৈরি করা
এই অংশে আপনি spring.io দ্বারা প্রদত্ত একটি নমুনা অ্যাপ্লিকেশন ব্যবহার করে একেবারে শুরু থেকে একটি নতুন জাভা স্প্রিং বুট অ্যাপ্লিকেশন তৈরি করবেন। একটি নতুন টার্মিনাল খুলুন।

নমুনা অ্যাপ্লিকেশনটি ক্লোন করুন
- একটি স্টার্টার অ্যাপ্লিকেশন তৈরি করুন
curl https://start.spring.io/starter.zip -d dependencies=web -d type=maven-project -d javaVersion=17 -d packageName=com.example.springboot -o sample-app.zip
এই বার্তাটি দেখতে পেলে Allow বাটনে ক্লিক করুন, যাতে আপনি ওয়ার্কস্টেশনে কপি পেস্ট করতে পারেন।

- অ্যাপ্লিকেশনটি আনজিপ করুন
unzip sample-app.zip -d sample-app
- 'sample-app' ফোল্ডারটি খুলুন
cd sample-app && code-oss-cloud-workstations -r --folder-uri="$PWD"
spring-boot-devtools ও Jib যোগ করুন
Spring Boot DevTools চালু করতে, আপনার এডিটরের এক্সপ্লোরার থেকে pom.xml ফাইলটি খুঁজুন এবং খুলুন। এরপর, <description>Demo project for Spring Boot</description> এই description লাইনের পরে নিম্নলিখিত কোডটি পেস্ট করুন।
- pom.xml-এ spring-boot-devtools যোগ করুন
প্রজেক্টের রুটে থাকা pom.xml ফাইলটি খুলুন। Description এন্ট্রির পরে নিম্নলিখিত কনফিগারেশনটি যোগ করুন।
pom.xml
<!-- Spring profiles-->
<profiles>
<profile>
<id>sync</id>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
- pom.xml-এ jib-maven-plugin সক্রিয় করুন।
জিব হলো গুগলের একটি ওপেন-সোর্স জাভা কন্টেইনারাইজিং টুল, যা জাভা ডেভেলপারদের তাদের পরিচিত জাভা টুল ব্যবহার করে কন্টেইনার তৈরি করতে সাহায্য করে। জিব একটি দ্রুত ও সহজ কন্টেইনার ইমেজ বিল্ডার, যা আপনার অ্যাপ্লিকেশনকে একটি কন্টেইনার ইমেজে প্যাকেজ করার সমস্ত ধাপ পরিচালনা করে। এর জন্য আপনাকে ডকারফাইল লিখতে বা ডকার ইনস্টল করার প্রয়োজন হয় না এবং এটি সরাসরি মেভেন ও গ্রেডলের সাথে ইন্টিগ্রেটেড।
pom.xml ফাইলটিতে নিচে স্ক্রল করুন এবং Jib প্লাগইনটি অন্তর্ভুক্ত করার জন্য Build সেকশনটি আপডেট করুন। কাজ সম্পন্ন হলে build সেকশনটি নিচের মতো দেখতে হবে।
pom.xml
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!-- Jib Plugin-->
<plugin>
<groupId>com.google.cloud.tools</groupId>
<artifactId>jib-maven-plugin</artifactId>
<version>3.2.0</version>
</plugin>
<!-- Maven Resources Plugin-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.1.0</version>
</plugin>
</plugins>
</build>
ম্যানিফেস্ট তৈরি করুন
স্ক্যাফোল্ড কন্টেইনার ডেভেলপমেন্ট সহজ করার জন্য সমন্বিত টুল সরবরাহ করে। এই ধাপে আপনি স্ক্যাফোল্ড ইনিশিয়ালাইজ করবেন, যা স্বয়ংক্রিয়ভাবে বেস কুবারনেটিস YAML ফাইল তৈরি করবে। এই প্রক্রিয়াটি ডকারফাইলের মতো কন্টেইনার ইমেজ ডেফিনিশনযুক্ত ডিরেক্টরিগুলো শনাক্ত করার চেষ্টা করে এবং তারপর প্রতিটির জন্য একটি ডিপ্লয়মেন্ট ও সার্ভিস ম্যানিফেস্ট তৈরি করে।
প্রক্রিয়াটি শুরু করতে টার্মিনালে নিচের কমান্ডটি চালান।

- টার্মিনালে নিম্নলিখিত কমান্ডটি চালান।
skaffold init --generate-manifests
- জিজ্ঞাসা করা হলে:
- আপনার কার্সারটি
Jib Maven Pluginএ নিয়ে যেতে তীর চিহ্নগুলো ব্যবহার করুন। - বিকল্পটি নির্বাচন করতে স্পেসবার চাপুন।
- চালিয়ে যেতে এন্টার চাপুন।
- পোর্টের জন্য 8080 লিখুন।
- কনফিগারেশন সংরক্ষণ করতে y লিখুন।
ওয়ার্কস্পেসে skaffold.yaml এবং deployment.yaml দুটি ফাইল যোগ করা হয়েছে।
স্ক্যাফোল্ড আউটপুট:

অ্যাপের নাম আপডেট করুন
কনফিগারেশনে অন্তর্ভুক্ত ডিফল্ট মানগুলো বর্তমানে আপনার অ্যাপ্লিকেশনের নামের সাথে মিলছে না। ডিফল্ট মানগুলোর পরিবর্তে আপনার অ্যাপ্লিকেশনের নাম উল্লেখ করার জন্য ফাইলগুলো আপডেট করুন।
- স্ক্যাফোল্ড কনফিগে এন্ট্রি পরিবর্তন করুন
-
skaffold.yamlখুলুন - বর্তমানে
pom-xml-imageহিসেবে সেট করা ছবির নামটি নির্বাচন করুন। - ডান ক্লিক করুন এবং সমস্ত ঘটনা পরিবর্তন করুন নির্বাচন করুন
- নতুন নাম হিসেবে
demo-appটাইপ করুন।
- Kubernetes কনফিগে এন্ট্রি পরিবর্তন করুন
-
deployment.yamlফাইলটি খুলুন - বর্তমানে
pom-xml-imageহিসেবে সেট করা ছবির নামটি নির্বাচন করুন। - ডান ক্লিক করুন এবং সমস্ত ঘটনা পরিবর্তন করুন নির্বাচন করুন
- নতুন নাম হিসেবে
demo-appটাইপ করুন।
অটো সিঙ্ক মোড চালু করুন
একটি অপ্টিমাইজড হট রিলোড অভিজ্ঞতা সহজতর করতে আপনি Jib দ্বারা প্রদত্ত Sync ফিচারটি ব্যবহার করবেন। এই ধাপে আপনি বিল্ড প্রক্রিয়ায় সেই ফিচারটি ব্যবহার করার জন্য Skaffold কনফিগার করবেন।
উল্লেখ্য যে, আপনি Skaffold কনফিগারেশনে যে 'sync' প্রোফাইলটি কনফিগার করছেন, সেটি পূর্ববর্তী ধাপে আপনার কনফিগার করা Spring 'sync' প্রোফাইলটিকেই ব্যবহার করে, যেখানে আপনি spring-dev-tools-এর জন্য সাপোর্ট এনাবল করেছেন।
- স্ক্যাফোল্ড কনফিগারেশন আপডেট করুন
skaffold.yaml ফাইলে, ফাইলটির সম্পূর্ণ বিল্ড সেকশনটি নিম্নলিখিত স্পেসিফিকেশন দ্বারা প্রতিস্থাপন করুন। ফাইলটির অন্য কোনো সেকশন পরিবর্তন করবেন না।
skaffold.yaml
build:
artifacts:
- image: demo-app
jib:
project: com.example:demo
type: maven
args:
- --no-transfer-progress
- -Psync
fromImage: gcr.io/distroless/java17-debian11:debug
sync:
auto: true
একটি ডিফল্ট রুট যোগ করুন
/src/main/java/com/example/springboot/ ফোল্ডারে HelloController.java নামে একটি ফাইল তৈরি করুন।

একটি ডিফল্ট http রুট তৈরি করতে ফাইলটিতে নিম্নলিখিত বিষয়বস্তু পেস্ট করুন।
HelloController.java
package com.example.springboot;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.beans.factory.annotation.Value;
@RestController
public class HelloController {
@Value("${target:local}")
String target;
@GetMapping("/")
public String hello()
{
return String.format("Hello from your %s environment!", target);
}
}
৪. উন্নয়ন প্রক্রিয়াটি পর্যালোচনা করা
এই অংশে আপনি ক্লাউড কোড প্লাগইন ব্যবহার করে কয়েকটি ধাপ অনুসরণ করার মাধ্যমে মৌলিক প্রক্রিয়াগুলো শিখবেন এবং আপনার স্টার্টার অ্যাপ্লিকেশনের কনফিগারেশন ও সেটআপ যাচাই করবেন।
ক্লাউড কোড আপনার ডেভেলপমেন্ট প্রক্রিয়াকে আরও সহজ করতে স্ক্যাফোল্ডের সাথে সমন্বিত হয়। যখন আপনি পরবর্তী ধাপগুলোতে GKE-তে ডিপ্লয় করবেন, ক্লাউড কোড এবং স্ক্যাফোল্ড স্বয়ংক্রিয়ভাবে আপনার কন্টেইনার ইমেজ তৈরি করবে, সেটিকে একটি কন্টেইনার রেজিস্ট্রি-তে পুশ করবে এবং তারপর আপনার অ্যাপ্লিকেশনটি GKE-তে ডিপ্লয় করবে। এই পুরো প্রক্রিয়াটি নেপথ্যে সম্পন্ন হয়, যা ডেভেলপারের কাজের ধারা থেকে খুঁটিনাটি বিষয়গুলো আড়াল করে রাখে। এছাড়াও, ক্লাউড কোড কন্টেইনার-ভিত্তিক ডেভেলপমেন্টে প্রচলিত ডিবাগ এবং হটসিঙ্ক সুবিধা প্রদান করে আপনার ডেভেলপমেন্ট প্রক্রিয়াকে আরও উন্নত করে।
গুগল ক্লাউডে সাইন ইন করুন
ক্লাউড কোড আইকনে ক্লিক করুন এবং 'গুগল ক্লাউডে সাইন ইন করুন' নির্বাচন করুন:

'সাইন ইন করতে এগিয়ে যান'-এ ক্লিক করুন।

টার্মিনালে আউটপুটটি দেখুন এবং লিঙ্কটি খুলুন:

আপনার কুইকল্যাবস ছাত্র পরিচয়পত্র দিয়ে লগইন করুন।

"অনুমতি দিন" নির্বাচন করুন:

যাচাইকরণ কোডটি কপি করে ওয়ার্কস্টেশন ট্যাবে ফিরে যান।

যাচাইকরণ কোডটি পেস্ট করে এন্টার চাপুন।

কুবারনেটিস ক্লাস্টার যোগ করুন
- একটি ক্লাস্টার যোগ করুন

- গুগল কুবারনেটিস ইঞ্জিন নির্বাচন করুন:

- প্রকল্প নির্বাচন করুন।

- প্রাথমিক সেটআপে তৈরি করা 'quote-cluster' নির্বাচন করুন।


gcloud cli ব্যবহার করে বর্তমান প্রজেক্ট আইডি সেট করুন
qwiklabs পৃষ্ঠা থেকে এই ল্যাবের প্রজেক্ট আইডি কপি করুন।

প্রজেক্ট আইডি সেট করতে gcloud cli কমান্ডটি চালান। কমান্ডটি চালানোর আগে নমুনা প্রজেক্ট আইডিটি পরিবর্তন করে নিন।
gcloud config set project qwiklabs-gcp-project-id
নমুনা আউটপুট:

কুবারনেটিসে ডিবাগ করুন
- বাম দিকের প্যানেলের নীচে ক্লাউড কোড নির্বাচন করুন।

- ডেভেলপমেন্ট সেশনস (DEVELOPMENT SESSIONS)-এর নিচে যে প্যানেলটি দেখা যাবে, সেখানে কুবারনেটিস-এ ডিবাগ (Debug on Kubernetes) নির্বাচন করুন।
বিকল্পটি দেখা না গেলে নিচে স্ক্রল করুন।

- বর্তমান প্রেক্ষাপট ব্যবহার করতে 'হ্যাঁ' নির্বাচন করুন।

- প্রাথমিক সেটআপের সময় তৈরি করা 'quote-cluster' নির্বাচন করুন।

- কন্টেইনার রিপোজিটরি নির্বাচন করুন।

- অগ্রগতি এবং বিজ্ঞপ্তি দেখতে নিচের প্যানেলে আউটপুট ট্যাবটি নির্বাচন করুন।
- কন্টেইনারগুলো থেকে সরাসরি সম্প্রচারিত অতিরিক্ত বিবরণ এবং লগ দেখতে ডানদিকের চ্যানেল ড্রপ-ডাউন থেকে 'Kubernetes: Run/Debug - Detailed' নির্বাচন করুন।

অ্যাপ্লিকেশনটি ডেপ্লয় হওয়ার জন্য অপেক্ষা করুন।

- ক্লাউড কনসোলে GKE-তে ডেপ্লয় করা অ্যাপ্লিকেশনটি পর্যালোচনা করুন।

- আউটপুট ট্যাবের ড্রপডাউন থেকে "Kubernetes: Run/Debug" নির্বাচন করে সরলীকৃত ভিউতে ফিরে যান।
- যখন বিল্ড এবং টেস্ট সম্পন্ন হয়, তখন আউটপুট ট্যাবে লেখা থাকে:
Resource deployment/demo-app status completed successfully, এবং একটি ইউআরএল তালিকাভুক্ত হয়: "Forwarded URL from service demo-app: http://localhost:8080" - ক্লাউড কোড টার্মিনালে, আউটপুটে থাকা URL-টির (http://localhost:8080) উপর মাউস রাখুন এবং তারপরে যে টুলটিপটি প্রদর্শিত হবে, সেখান থেকে ‘Follow link’ নির্বাচন করুন।

একটি নতুন ট্যাব খোলা হবে এবং আপনি নিচে আউটপুট দেখতে পাবেন:

ব্রেকপয়েন্ট ব্যবহার করুন
-
/src/main/java/com/example/springboot/HelloController.javaতে অবস্থিতHelloController.javaঅ্যাপ্লিকেশনটি খুলুন। - রুট পাথের জন্য রিটার্ন স্টেটমেন্টটি খুঁজুন, যেটি হলো
return String.format("Hello from your %s environment!", target); - লাইন নম্বরের বাম পাশের ফাঁকা জায়গায় ক্লিক করে ওই লাইনে একটি ব্রেকপয়েন্ট যোগ করুন। ব্রেকপয়েন্ট সেট করা হয়েছে তা বোঝাতে একটি লাল ইন্ডিকেটর দেখা যাবে।

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

- ভেরিয়েবল সেকশনে ক্লিক করতে করতে 'Target' ভেরিয়েবলটি খুঁজে বের করুন।
- বর্তমান মানটিকে 'স্থানীয়' হিসাবে পর্যবেক্ষণ করুন।

- 'target' ভেরিয়েবল নামের উপর ডাবল ক্লিক করুন এবং পপআপে,
মানটি 'ক্লাউড ওয়ার্কস্টেশন'-এ পরিবর্তন করুন

- ডিবাগ কন্ট্রোল প্যানেলে থাকা কন্টিনিউ বাটনে ক্লিক করুন।

- আপনার ব্রাউজারে প্রাপ্ত প্রতিক্রিয়াটি পর্যালোচনা করুন, যেখানে এখন আপনার এইমাত্র প্রবেশ করানো হালনাগাদ মানটি দেখাচ্ছে।

- লাইন নম্বরের বাম দিকে থাকা লাল ইন্ডিকেটরটিতে ক্লিক করে ব্রেকপয়েন্টটি সরিয়ে ফেলুন। এর ফলে, এই ল্যাবে কাজ এগিয়ে নিয়ে যাওয়ার সময় আপনার কোড এই লাইনে থেমে যাবে না।
হট রিলোড
- স্টেটমেন্টটি পরিবর্তন করে একটি ভিন্ন মান রিটার্ন করার ব্যবস্থা করুন, যেমন "Hello from %s Code"।
- ফাইলটি GKE-এর রিমোট কন্টেইনারগুলিতে স্বয়ংক্রিয়ভাবে সংরক্ষিত এবং সিঙ্ক হয়ে যায়।
- হালনাগাদ ফলাফল দেখতে আপনার ব্রাউজার রিফ্রেশ করুন।
- ডিবাগ টুলবারের লাল বর্গক্ষেত্রটিতে ক্লিক করে ডিবাগিং সেশনটি বন্ধ করুন।


"প্রতিটি রানের পর পরিষ্কার করুন" নির্বাচন করুন।

৫. একটি সহজ CRUD রেস্ট সার্ভিস তৈরি করা
এই পর্যায়ে আপনার অ্যাপ্লিকেশনটি কন্টেইনারাইজড ডেভেলপমেন্টের জন্য সম্পূর্ণরূপে কনফিগার করা হয়েছে এবং আপনি ক্লাউড কোড ব্যবহার করে বেসিক ডেভেলপমেন্ট ওয়ার্কফ্লোটি সম্পন্ন করেছেন। পরবর্তী বিভাগগুলিতে, আপনি গুগল ক্লাউডের একটি ম্যানেজড ডেটাবেসের সাথে সংযোগকারী REST সার্ভিস এন্ডপয়েন্ট যুক্ত করার মাধ্যমে আপনার শেখা বিষয়গুলো অনুশীলন করবেন।
নির্ভরতা কনফিগার করুন
অ্যাপ্লিকেশন কোডটি রেস্ট সার্ভিসের ডেটা সংরক্ষণের জন্য একটি ডেটাবেস ব্যবহার করে। pom.xl ফাইলে নিম্নলিখিতটি যোগ করে ডিপেন্ডেন্সিগুলো উপলব্ধ আছে কিনা তা নিশ্চিত করুন।
-
pom.xmlফাইলটি খুলুন এবং config-এর dependencies সেকশনে নিম্নলিখিতটি যোগ করুন।
pom.xml
<!-- Database dependencies-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>javax.persistence-api</artifactId>
<version>2.2</version>
</dependency>
কোড REST পরিষেবা
Quote.java
/src/main/java/com/example/springboot/ ফোল্ডারে Quote.java নামে একটি ফাইল তৈরি করুন এবং নিচের কোডটি সেখানে কপি করুন। এটি অ্যাপ্লিকেশনে ব্যবহৃত Quote অবজেক্টের জন্য এনটিটি মডেল (Entity model) নির্ধারণ করে।
package com.example.springboot;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import java.util.Objects;
@Entity
@Table(name = "quotes")
public class Quote
{
@Id
@Column(name = "id")
private Integer id;
@Column(name="quote")
private String quote;
@Column(name="author")
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getQuote() {
return quote;
}
public void setQuote(String quote) {
this.quote = quote;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Quote quote1 = (Quote) o;
return Objects.equals(id, quote1.id) &&
Objects.equals(quote, quote1.quote) &&
Objects.equals(author, quote1.author);
}
@Override
public int hashCode() {
return Objects.hash(id, quote, author);
}
}
QuoteRepository.java
src/main/java/com/example/springboot এ QuoteRepository.java নামে একটি ফাইল তৈরি করুন এবং নিম্নলিখিত কোডটি কপি করে সেখানে পেস্ট করুন।
package com.example.springboot;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
public interface QuoteRepository extends JpaRepository<Quote,Integer> {
@Query( nativeQuery = true, value =
"SELECT id,quote,author FROM quotes ORDER BY RANDOM() LIMIT 1")
Quote findRandomQuote();
}
এই কোডটি ডেটা সংরক্ষণের জন্য JPA ব্যবহার করে। ক্লাসটি Spring JPARepository ইন্টারফেসকে এক্সটেন্ড করে এবং কাস্টম কোড তৈরির সুযোগ দেয়। কোডটিতে আপনি একটি findRandomQuote কাস্টম মেথড যোগ করেছেন।
QuoteController.java
সার্ভিসটির এন্ডপয়েন্ট উন্মুক্ত করার জন্য, একটি QuoteController ক্লাস এই কার্যকারিতাটি প্রদান করবে।
src/main/java/com/example/springboot এ QuoteController.java নামে একটি ফাইল তৈরি করুন এবং নিম্নলিখিত বিষয়বস্তু কপি করে তাতে পেস্ট করুন।
package com.example.springboot;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class QuoteController {
private final QuoteRepository quoteRepository;
public QuoteController(QuoteRepository quoteRepository) {
this.quoteRepository = quoteRepository;
}
@GetMapping("/random-quote")
public Quote randomQuote()
{
return quoteRepository.findRandomQuote();
}
@GetMapping("/quotes")
public ResponseEntity<List<Quote>> allQuotes()
{
try {
List<Quote> quotes = new ArrayList<Quote>();
quoteRepository.findAll().forEach(quotes::add);
if (quotes.size()==0 || quotes.isEmpty())
return new ResponseEntity<List<Quote>>(HttpStatus.NO_CONTENT);
return new ResponseEntity<List<Quote>>(quotes, HttpStatus.OK);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<List<Quote>>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PostMapping("/quotes")
public ResponseEntity<Quote> createQuote(@RequestBody Quote quote) {
try {
Quote saved = quoteRepository.save(quote);
return new ResponseEntity<Quote>(saved, HttpStatus.CREATED);
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/quotes/{id}")
public ResponseEntity<Quote> updateQuote(@PathVariable("id") Integer id, @RequestBody Quote quote) {
try {
Optional<Quote> existingQuote = quoteRepository.findById(id);
if(existingQuote.isPresent()){
Quote updatedQuote = existingQuote.get();
updatedQuote.setAuthor(quote.getAuthor());
updatedQuote.setQuote(quote.getQuote());
return new ResponseEntity<Quote>(updatedQuote, HttpStatus.OK);
} else {
return new ResponseEntity<Quote>(HttpStatus.NOT_FOUND);
}
} catch (Exception e) {
System.out.println(e.getMessage());
return new ResponseEntity<Quote>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
Optional<Quote> quote = quoteRepository.findById(id);
if (quote.isPresent()) {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
ডাটাবেস কনফিগারেশন যোগ করুন
application.yaml
সার্ভিসটি যে ব্যাকএন্ড ডাটাবেস অ্যাক্সেস করে, তার জন্য কনফিগারেশন যোগ করুন। src/main/resources এর অধীনে থাকা application.yaml ফাইলটি সম্পাদনা করুন (না থাকলে তৈরি করুন) এবং ব্যাকএন্ডের জন্য একটি প্যারামিটারাইজড স্প্রিং কনফিগারেশন যোগ করুন।
target: local
spring:
config:
activate:
on-profile: cloud-dev
datasource:
url: 'jdbc:postgresql://${DB_HOST:127.0.0.1}/${DB_NAME:quote_db}'
username: '${DB_USER:user}'
password: '${DB_PASS:password}'
jpa:
properties:
hibernate:
jdbc:
lob:
non_contextual_creation: true
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
ডাটাবেস মাইগ্রেশন যোগ করুন
src/main/resources এর অধীনে db/migration ফোল্ডার তৈরি করুন
একটি SQL ফাইল তৈরি করুন: V1__create_quotes_table.sql
ফাইলটিতে নিম্নলিখিত বিষয়বস্তু পেস্ট করুন।
V1__create_quotes_table.sql
CREATE TABLE quotes(
id INTEGER PRIMARY KEY,
quote VARCHAR(1024),
author VARCHAR(256)
);
INSERT INTO quotes (id,quote,author) VALUES (1,'Never, never, never give up','Winston Churchill');
INSERT INTO quotes (id,quote,author) VALUES (2,'While there''s life, there''s hope','Marcus Tullius Cicero');
INSERT INTO quotes (id,quote,author) VALUES (3,'Failure is success in progress','Anonymous');
INSERT INTO quotes (id,quote,author) VALUES (4,'Success demands singleness of purpose','Vincent Lombardi');
INSERT INTO quotes (id,quote,author) VALUES (5,'The shortest answer is doing','Lord Herbert');
কুবারনেটিস কনফিগারেশন
deployment.yaml ফাইলে নিম্নলিখিত সংযোজনগুলো অ্যাপ্লিকেশনটিকে CloudSQL ইনস্ট্যান্সগুলোর সাথে সংযোগ স্থাপন করতে সক্ষম করে।
- টার্গেট - অ্যাপটি যে পরিবেশে চালানো হবে তা নির্দেশ করার জন্য ভেরিয়েবলটি কনফিগার করে।
- SPRING_PROFILES_ACTIVE - সক্রিয় Spring প্রোফাইলটি দেখায়, যা
cloud-devএর জন্য কনফিগার করা হবে। - DB_HOST - এটি হলো ডেটাবেসের প্রাইভেট আইপি, যা ডেটাবেস ইনস্ট্যান্স তৈরি করার সময় অথবা গুগল ক্লাউড কনসোলের নেভিগেশন মেনুতে
SQLক্লিক করে নোট করা হয়েছে - অনুগ্রহ করে এর মান পরিবর্তন করুন! - DB_USER এবং DB_PASS - যা CloudSQL ইনস্ট্যান্স কনফিগারেশনে সেট করা থাকে এবং GCP-তে একটি Secret হিসেবে সংরক্ষিত থাকে।
নিচের বিষয়বস্তু দিয়ে আপনার deployment.yaml ফাইলটি আপডেট করুন।
deployment.yaml
apiVersion: v1
kind: Service
metadata:
name: demo-app
labels:
app: demo-app
spec:
ports:
- port: 8080
protocol: TCP
clusterIP: None
selector:
app: demo-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo-app
labels:
app: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: demo-app
template:
metadata:
labels:
app: demo-app
spec:
containers:
- name: demo-app
image: demo-app
env:
- name: PORT
value: "8080"
- name: TARGET
value: "Local Dev - CloudSQL Database - K8s Cluster"
- name: SPRING_PROFILES_ACTIVE
value: cloud-dev
- name: DB_HOST
value: ${DB_INSTANCE_IP}
- name: DB_PORT
value: "5432"
- name: DB_USER
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: username
- name: DB_PASS
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: password
- name: DB_NAME
valueFrom:
secretKeyRef:
name: gke-cloud-sql-secrets
key: database
টার্মিনালে নিচের কমান্ডগুলো চালিয়ে DB_HOST ভ্যালুটি আপনার ডাটাবেসের অ্যাড্রেস দিয়ে প্রতিস্থাপন করুন:
export DB_INSTANCE_IP=$(gcloud sql instances describe quote-db-instance \
--format=json | jq \
--raw-output ".ipAddresses[].ipAddress")
envsubst < deployment.yaml > deployment.new && mv deployment.new deployment.yaml
deployment.yaml ফাইলটি খুলুন এবং যাচাই করুন যে DB_HOST ভ্যালুটি Instance IP দিয়ে আপডেট করা হয়েছে।

অ্যাপ্লিকেশন স্থাপন এবং যাচাই করুন
- Cloud Shell Editor-এর নিচের প্যানে Cloud Code নির্বাচন করুন, তারপর স্ক্রিনের উপরে Debug on Kubernetes নির্বাচন করুন।

- যখন বিল্ড এবং টেস্ট সম্পন্ন হয়, তখন আউটপুট ট্যাবে লেখা থাকে:
Resource deployment/demo-app status completed successfully, এবং একটি ইউআরএল তালিকাভুক্ত হয়: "Forwarded URL from service demo-app: http://localhost:8080 "। উল্লেখ্য যে, কখনও কখনও পোর্টটি ভিন্ন হতে পারে, যেমন ৮০৮১। যদি তাই হয়, তবে উপযুক্ত মান সেট করুন। টার্মিনালে ইউআরএল-এর মান সেট করুন।
export URL=localhost:8080
- এলোমেলো উক্তি দেখুন
টার্মিনাল থেকে, random-quote এন্ডপয়েন্টে নিচের কমান্ডটি একাধিকবার চালান। লক্ষ্য করুন, বারবার কল করার ফলে ভিন্ন ভিন্ন কোট ফেরত আসছে।
curl $URL/random-quote | jq
- একটি উদ্ধৃতি যোগ করুন
নিচে দেওয়া কমান্ডটি ব্যবহার করে id=6 সহ একটি নতুন কোট তৈরি করুন এবং অনুরোধটি প্রতিধ্বনিত হতে দেখুন।
curl -H 'Content-Type: application/json' -d '{"id":"6","author":"Henry David Thoreau","quote":"Go confidently in the direction of your dreams! Live the life you have imagined"}' -X POST $URL/quotes
- একটি উদ্ধৃতি মুছে ফেলুন
এখন delete মেথড ব্যবহার করে আপনার যোগ করা কোটেশন চিহ্নটি মুছে ফেলুন এবং HTTP/1.1 204 রেসপন্স কোডটি পর্যবেক্ষণ করুন।
curl -v -X DELETE $URL/quotes/6
- সার্ভার ত্রুটি
এন্ট্রিটি মুছে ফেলার পরেও শেষ অনুরোধটি পুনরায় চালালে একটি ত্রুটিপূর্ণ অবস্থার সম্মুখীন হবেন।
curl -v -X DELETE $URL/quotes/6
লক্ষ্য করুন, প্রতিক্রিয়াটি একটি HTTP:500 Internal Server Error দেখাচ্ছে।
অ্যাপ্লিকেশনটি ডিবাগ করুন
পূর্ববর্তী অংশে, ডাটাবেসে নেই এমন একটি এন্ট্রি ডিলিট করার চেষ্টা করার সময় আপনি অ্যাপ্লিকেশনে একটি ত্রুটি খুঁজে পেয়েছিলেন। এই অংশে, সমস্যাটি সনাক্ত করার জন্য আপনি একটি ব্রেকপয়েন্ট সেট করবেন। ত্রুটিটি DELETE অপারেশনে ঘটেছে, তাই আপনি QuoteController ক্লাসটি নিয়ে কাজ করবেন।
-
src/main/java/com/example/springboot/QuoteController.javaখুলুন। -
deleteQuote()পদ্ধতিটি খুঁজুন - এই লাইনটি খুঁজুন:
Optional<Quote> quote = quoteRepository.findById(id); - লাইন নম্বরের বাম পাশের ফাঁকা জায়গায় ক্লিক করে ওই লাইনে একটি ব্রেকপয়েন্ট সেট করুন।
- একটি লাল সূচক দেখা যাবে যা নির্দেশ করে যে ব্রেকপয়েন্ট সেট করা হয়েছে।
-
deleteকমান্ডটি আবার চালান।
curl -v -X DELETE $URL/quotes/6
- বাম কলামের আইকনটিতে ক্লিক করে ডিবাগ ভিউতে ফিরে যান।
- QuoteController ক্লাসে থেমে থাকা ডিবাগ লাইনটি লক্ষ্য করুন।
- ডিবাগারে,
step overআইকনে ক্লিক করুন।
- লক্ষ্য করুন যে, একটি কোড ক্লায়েন্টকে ইন্টারনাল সার্ভার এরর (HTTP 500) ফেরত পাঠাচ্ছে, যা কাম্য নয়।
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 500 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
কোডটি আপডেট করুন
কোডটি ভুল এবং else ব্লকটিকে রিফ্যাক্টর করে একটি HTTP 404 নট ফাউন্ড স্ট্যাটাস কোড ফেরত পাঠানোর ব্যবস্থা করা উচিত।
ভুলটি সংশোধন করুন।
- ডিবাগ সেশন চালু থাকা অবস্থায়, ডিবাগ কন্ট্রোল প্যানেলে থাকা 'continue' বাটনটি চেপে অনুরোধটি সম্পূর্ণ করুন।
- এরপর
elseব্লকটি এই কোডে পরিবর্তন করুন:
else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
পদ্ধতিটি নিম্নলিখিতের মতো হওয়া উচিত।
@DeleteMapping("/quotes/{id}")
public ResponseEntity<HttpStatus> deleteQuote(@PathVariable("id") Integer id) {
Optional<Quote> quote = quoteRepository.findById(id);
if (quote.isPresent()) {
quoteRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} else {
return new ResponseEntity<HttpStatus>(HttpStatus.NOT_FOUND);
}
}
- ডিলিট কমান্ডটি পুনরায় চালান।
curl -v -X DELETE $URL/quotes/6
- ডিবাগারের মাধ্যমে ধাপে ধাপে এগিয়ে যান এবং কলারকে ফেরত আসা HTTP 404 Not Found বার্তাটি পর্যবেক্ষণ করুন।
Trying 127.0.0.1:8080... * Connected to 127.0.0.1 (127.0.0.1) port 8080 (#0) > DELETE /quotes/6 HTTP/1.1 > Host: 127.0.0.1:8080 > User-Agent: curl/7.74.0 > Accept: */* > * Mark bundle as not supporting multiuse < HTTP/1.1 404 < Content-Length: 0 < Date: < * Connection #0 to host 127.0.0.1 left intact
- ডিবাগ টুলবারের লাল বর্গক্ষেত্রটিতে ক্লিক করে ডিবাগিং সেশনটি বন্ধ করুন।


৬. অভিনন্দন
অভিনন্দন! এই ল্যাবে আপনি একেবারে শুরু থেকে একটি নতুন জাভা অ্যাপ্লিকেশন তৈরি করেছেন এবং এটিকে কন্টেইনারের সাথে কার্যকরভাবে কাজ করার জন্য কনফিগার করেছেন। এরপর আপনি প্রচলিত অ্যাপ্লিকেশন স্ট্যাকে পাওয়া ডেভেলপার ফ্লো অনুসরণ করে আপনার অ্যাপ্লিকেশনটি একটি রিমোট GKE ক্লাস্টারে ডেপ্লয় ও ডিবাগ করেছেন।
আপনি যা শিখেছেন
- ক্লাউড ওয়ার্কস্টেশন সহ ইনারলুপ উন্নয়ন
- একটি নতুন জাভা স্টার্টার অ্যাপ্লিকেশন তৈরি করা
- উন্নয়ন প্রক্রিয়ার মধ্য দিয়ে হেঁটে যাওয়া
- একটি সহজ CRUD REST পরিষেবা তৈরি করা
- GKE ক্লাস্টারে অ্যাপ্লিকেশন ডিবাগিং
- অ্যাপ্লিকেশনকে ক্লাউডএসকিউএল ডেটাবেসের সাথে সংযুক্ত করা হচ্ছে
