একটি ফুল-স্ট্যাক অ্যাপ কন্টেইনারাইজ করা
এই পাঠে যা শিখবেন
- কন্টেইনার আসলে কী সমস্যা সমাধান করে — আইসোলেশন ও রিপ্রোডিউসিবিলিটি
- একটি কন্টেইনার "ম্যানিফেস্ট"-এ কী কী তথ্য থাকা দরকার (বেস ইমেজ, এনভায়রনমেন্ট ভ্যারিয়েবল, পোর্ট, স্টার্ট কমান্ড)
- বিল্ড শুরুর আগে একটি ম্যানিফেস্ট বৈধ কি না তা প্রোগ্রাম্যাটিকভাবে যাচাই করা
- এই কোর্স ও
../cloud-devops/কোর্সের মধ্যে দায়িত্বের সীমারেখা
১ · একটি কন্টেইনার আসলে কী দেয়
একটি ফুল-স্ট্যাক অ্যাপ ডেভেলপারের মেশিনে চলে ঠিকভাবে, কিন্তু সার্ভারে ডিপ্লয় করার পর প্রায়ই ব্যর্থ হয় — কারণ সার্ভারে হয়তো ভিন্ন Python ভার্সন আছে, একটি সিস্টেম লাইব্রেরি অনুপস্থিত, বা একটি এনভায়রনমেন্ট ভ্যারিয়েবল মিসিং। কন্টেইনারাইজেশন এই সমস্যা সমাধান করে অ্যাপ্লিকেশনকে তার সম্পূর্ণ রানটাইম পরিবেশ-সহ একটি একক, পোর্টেবল ইউনিটে প্যাক করে।
একই হোস্ট মেশিনে চলা একাধিক কন্টেইনার একে অপরের প্রসেস, মেমরি বা নেটওয়ার্ক পোর্টে হস্তক্ষেপ করে না — প্রতিটি নিজের বিচ্ছিন্ন জগতে চলে।
প্রতিটি কন্টেইনারের নিজস্ব ফাইলসিস্টেম থাকে (ইমেজ থেকে তৈরি) — হোস্টের বা অন্য কন্টেইনারের ফাইল দুর্ঘটনাক্রমে পরিবর্তন করতে পারে না।
একই ইমেজ থেকে তৈরি কন্টেইনার dev, staging, production — সবখানে হুবহু একই রানটাইম, লাইব্রেরি ভার্সন ও কনফিগারেশন ব্যবহার করে।
docker build, docker run, ও একটি সত্যিকারের Dockerfile-এর
লাইন-বাই-লাইন সিনট্যাক্স ইতিমধ্যে ../cloud-devops/ কোর্সে বিস্তারিতভাবে শেখানো হয়েছে। এই পাঠ
সেই কমান্ডগুলো পুনরাবৃত্তি করবে না — বরং ফুল-স্ট্যাক অ্যাপ কন্টেইনারাইজ করার সময় "কী কী তথ্য জানা দরকার এবং
কেন" তার উপর মনোযোগ দেবে।
২ · কন্টেইনার "ম্যানিফেস্ট" ও বিল্ডের আগে যাচাই
একটি কন্টেইনার তৈরি করতে একটি Dockerfile-এ (বা সমতুল্য কনফিগে) কমপক্ষে চারটি তথ্য স্পষ্টভাবে থাকা দরকার —
কোন বেস ইমেজ দিয়ে শুরু হবে, কোন এনভায়রনমেন্ট ভ্যারিয়েবল সেট থাকবে, কোন
পোর্ট এক্সপোজ করা হবে, আর কন্টেইনার চালু হওয়ার সময় কোন স্টার্ট কমান্ড
চলবে। নিচের কোড সেলে এই চারটি তথ্যকে একটি সাধারণ Python ডিকশনারি — একটি "ম্যানিফেস্ট" — হিসেবে মডেল করা
হয়েছে, এবং একটি সত্যিকারের validate_manifest() ফাংশন লেখা হয়েছে যা বিল্ড শুরুর আগেই সব
প্রয়োজনীয় ফিল্ড উপস্থিত ও সঠিক টাইপের কি না যাচাই করে।
# বিল্ড শুরু করার আগে একটি কন্টেইনার ম্যানিফেস্টে কোন কোন ফিল্ড
# বাধ্যতামূলক এবং তাদের প্রত্যাশিত টাইপ কী -- তা ঘোষণা করা হলো
REQUIRED_FIELDS = {
"base_image": str,
"env_vars": dict,
"exposed_port": int,
"start_command": str,
}
def validate_manifest(manifest):
"""manifest ডিকশনারিতে সব প্রয়োজনীয় ফিল্ড আছে কিনা এবং প্রতিটির টাইপ সঠিক
কিনা যাচাই করে। বৈধ হলে (True, বার্তা), অবৈধ হলে (False, নির্দিষ্ট এরর বার্তা)
রিটার্ন করে -- অবৈধ হলে আসল বিল্ড কখনো শুরু হবে না।"""
for field, expected_type in REQUIRED_FIELDS.items():
if field not in manifest:
return False, f"প্রয়োজনীয় ফিল্ড অনুপস্থিত: '{field}'"
if not isinstance(manifest[field], expected_type):
actual = type(manifest[field]).__name__
return False, (
f"ফিল্ড '{field}'-এর টাইপ ভুল: {expected_type.__name__} প্রত্যাশিত, "
f"পাওয়া গেছে {actual}"
)
return True, "manifest বৈধ -- বিল্ড শুরু করা যাবে"
# সম্পূর্ণ, বৈধ ম্যানিফেস্ট
complete_manifest = {
"base_image": "python:3.12-slim",
"env_vars": {"APP_ENV": "production", "PORT": "8000"},
"exposed_port": 8000,
"start_command": "gunicorn app:server --bind 0.0.0.0:8000",
}
# ইচ্ছাকৃতভাবে অসম্পূর্ণ ম্যানিফেস্ট -- 'exposed_port' বাদ দেওয়া হয়েছে
incomplete_manifest = {
"base_image": "python:3.12-slim",
"env_vars": {"APP_ENV": "production"},
"start_command": "gunicorn app:server --bind 0.0.0.0:8000",
}
test_cases = [
("সম্পূর্ণ manifest", complete_manifest),
("অসম্পূর্ণ manifest", incomplete_manifest),
]
for label, manifest in test_cases:
ok, message = validate_manifest(manifest)
status = "PASS" if ok else "FAIL"
print(f"[{status}] {label}: {message}")
if ok:
print(f" -> বিল্ড করা হচ্ছে: {manifest['base_image']} থেকে, পোর্ট {manifest['exposed_port']} এক্সপোজ করে")
else:
print(" -> বিল্ড বাতিল করা হলো, ম্যানিফেস্ট আগে ঠিক করতে হবে")
validate_manifest() ফাংশনটি REQUIRED_FIELDS-এর ক্রম অনুযায়ী একে একে ফিল্ড চেক
করে, তাই প্রথম যে ফিল্ড অনুপস্থিত পাওয়া যায় সেখানেই থেমে সুনির্দিষ্ট এরর বার্তা রিটার্ন করে —
incomplete_manifest-এ base_image ও env_vars ঠিক আছে, কিন্তু
exposed_port সম্পূর্ণ অনুপস্থিত বলে ঠিক সেই ফিল্ডের নাম-সহ ব্যর্থতা রিপোর্ট হয় — একটি অস্পষ্ট
"বিল্ড ফেইল হয়েছে" বার্তার বদলে।
কন্টেইনারাইজেশনের মূল মূল্য দুটো জায়গায় — আইসোলেশন (একটি কন্টেইনার অন্যটির সাথে সংঘর্ষ করে না) এবং
রিপ্রোডিউসিবিলিটি (একই ইমেজ সবখানে একই আচরণ করে)। বাস্তবে এটি অর্জন করতে একটি সঠিক, সম্পূর্ণ ম্যানিফেস্ট/
Dockerfile দরকার — এবং বিল্ড শুরুর আগেই সেটি প্রোগ্রাম্যাটিকভাবে যাচাই করলে অসম্পূর্ণ কনফিগ নিয়ে
প্রোডাকশনে ডিপ্লয় করার ঝুঁকি অনেক কমে যায়। Docker-এর প্রকৃত টুলিং ../cloud-devops/ কোর্সে
রয়েছে।
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।
প্র ০১ "আমার মেশিনে তো চলে" সমস্যাটা আসলে কী, আর কন্টেইনার এটা কীভাবে সমাধান করে?
এই সমস্যা হয় যখন একজন ডেভেলপারের লোকাল মেশিনের রানটাইম এনভায়রনমেন্ট (Python ভার্সন, ইনস্টল করা সিস্টেম লাইব্রেরি, এনভায়রনমেন্ট ভ্যারিয়েবল) সার্ভারের এনভায়রনমেন্ট থেকে সূক্ষ্মভাবে আলাদা হয় — ফলে কোড লোকালে কাজ করলেও সার্ভারে ব্যর্থ হয়। কন্টেইনার পুরো রানটাইম এনভায়রনমেন্টকেই ইমেজের ভেতরে প্যাক করে দেয়, তাই "কোথায় চলছে" সেটা আর গুরুত্বপূর্ণ থাকে না — একই ইমেজ সবখানে হুবহু একই এনভায়রনমেন্ট নিয়ে চলে।
প্র ০২ একটি ফুল-স্ট্যাক অ্যাপে ফ্রন্ট-এন্ড, ব্যাক-এন্ড ও ডেটাবেস — এই তিনটি স্তরের জন্য কি একটাই কন্টেইনার ব্যবহার করা উচিত, নাকি আলাদা আলাদা?
সাধারণ ভালো প্র্যাকটিস হলো প্রতিটি স্তরের জন্য আলাদা কন্টেইনার — কারণ প্রতিটি স্তরের রানটাইম প্রয়োজন
ভিন্ন (ব্যাক-এন্ডের Python/Node রানটাইম, ডেটাবেসের নিজস্ব ডেটাবেস ইঞ্জিন), এবং আলাদা রাখলে প্রতিটি
স্তর স্বাধীনভাবে স্কেল ও আপডেট করা যায় (যেমন শুধু ব্যাক-এন্ড কন্টেইনারের নতুন সংস্করণ ডিপ্লয় করা,
ডেটাবেস কন্টেইনার স্পর্শ না করেই)। একাধিক কন্টেইনার একসাথে চালানো ও সংযুক্ত করার অর্কেস্ট্রেশন
../cloud-devops/ কোর্সে কভার করা হয়েছে।
প্র ০৩
উপরের কোডে incomplete_manifest-এ env_vars ফিল্ড আছে, কিন্তু তার মান যদি
একটি স্ট্রিং হতো (ডিকশনারি না) — তাহলে validate_manifest() কী এরর বার্তা দিত?
তখন field not in manifest চেক পাস করে যেত (কারণ env_vars কী উপস্থিত), কিন্তু
পরের isinstance(manifest[field], expected_type) চেক ব্যর্থ হতো (কারণ str,
dict নয়) — তাই ফাংশনটি রিটার্ন করত: "ফিল্ড 'env_vars'-এর টাইপ ভুল: dict প্রত্যাশিত,
পাওয়া গেছে str"। এটাই দেখায় ফাংশনটি শুধু ফিল্ড উপস্থিতি নয়, তার টাইপও সঠিকভাবে যাচাই করে।
অনুশীলন
-
চিন্তা করুন:
REQUIRED_FIELDS-এ আরও কোন কোন ফিল্ড বাস্তবে গুরুত্বপূর্ণ হতে পারে যা এই সরলীকৃত উদাহরণে নেই (যেমন CPU/মেমরি সীমা, ভলিউম মাউন্ট)?বাস্তব ম্যানিফেস্ট/অর্কেস্ট্রেশন কনফিগে সাধারণত আরও থাকে — রিসোর্স সীমা (
cpu_limit,memory_limit, যাতে একটি কন্টেইনার পুরো হোস্টের রিসোর্স খেয়ে না ফেলে), পার্সিস্টেন্ট ডেটা রাখার জন্য ভলিউম মাউন্ট পাথ, হেলথ-চেক এন্ডপয়েন্ট (কন্টেইনার সত্যিই সুস্থভাবে চলছে কি না তা যাচাই করার জন্য), এবং রিস্টার্ট পলিসি (কন্টেইনার ক্র্যাশ করলে আবার চালু হবে কি না)। এই ধরনের অর্কেস্ট্রেশন-স্তরের কনফিগারেশন বিস্তারিতভাবে../cloud-devops/কোর্সে কভার করা হয়েছে। -
পরীক্ষা করুন: উপরের কোড সেলে
REQUIRED_FIELDS-এ একটি নতুন বাধ্যতামূলক ফিল্ড"health_check_path": strযোগ করুন, তারপরcomplete_manifest-এ সেটি ("/health"দিয়ে) যোগ করুন কিন্তুincomplete_manifest-এ যোগ করবেন না, ও Run চেপে দেখুন এখন কী ফলাফল আসে।REQUIRED_FIELDS-এ নতুন ফিল্ড যোগ হওয়ার পরcomplete_manifest-এ যেহেতুhealth_check_pathযোগ করা হয়েছে, সেটি এখনও PASS দেখাবে। কিন্তুincomplete_manifest-এ ইতিমধ্যেexposed_portঅনুপস্থিত, তাই যেহেতুREQUIRED_FIELDS-এexposed_port,health_check_path-এর আগে আসে, ফলাফল একই থাকবে — এখনওexposed_port-এর অনুপস্থিতি নিয়েই প্রথমে ব্যর্থ হবে (ফাংশনটি ক্রমানুসারে চেক করে, প্রথম অনুপস্থিত ফিল্ডেই থেমে যায়)।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- কোর্সের সম্পূর্ণ সিলেবাস দেখুন ৫৮টি পাঠ আর্কিটেকচার প্যাটার্ন, ফ্রন্ট-এন্ড/ব্যাক-এন্ড ফ্রেমওয়ার্ক ফান্ডামেন্টাল, স্টেট ম্যানেজমেন্ট, REST API, ORM, অথেন্টিকেশন, রেন্ডারিং স্ট্র্যাটেজি ও ডিপ্লয়মেন্ট — বাকি পাঠগুলো শীঘ্রই যুক্ত হবে।
- Cloud Computing & DevOps কোর্স সহোদর কোর্স Docker কমান্ড, Dockerfile সিনট্যাক্স ও কন্টেইনার অর্কেস্ট্রেশনের সম্পূর্ণ বিস্তারিত সেই কোর্সে কভার করা হয়েছে।
- সব Courses দেখুন ABCL TECH C, C++, Python, Java, JavaScript, DSA, DBMS, Discrete Mathematics, System Design, Cybersecurity, Cloud Computing & DevOps, Computer Networks, Operating Systems, Computer Architecture, Programming Languages & Compiler Design, Software Engineering & Git, Theory of Computation, Engineering Economics ও Full-Stack Web Frameworks — সব এক জায়গায়।