ডেভেলপারদের জন্য Docker: ইমেজ, কন্টেইনার এবং Compose
আপনি একটি প্রজেক্ট ক্লোন করেছেন, সেটআপ নির্দেশাবলী চালিয়েছেন, এবং একটি বাধার সম্মুখীন হয়েছেন: একটি অনুপস্থিত সিস্টেম লাইব্রেরি, একটি ডাটাবেস যা শুরু হচ্ছে না, অথবা একটি Node সংস্করণের অমিল। Docker এই ধরনের সমস্যা দূর করার জন্যই বিদ্যমান। আপনার মেশিনে সরাসরি সার্ভিস ইনস্টল করার পরিবর্তে, আপনি সেগুলি একবার বর্ণনা করেন এবং আইসোলেটেড কন্টেইনারে চালান যা সব জায়গায় একইভাবে আচরণ করে।
এই গাইডটি তিনটি ধারণা ব্যাখ্যা করে যা আপনার আসলে প্রয়োজন: ইমেজ, কন্টেইনার, এবং Compose। এটি ব্যবহারিক থাকে, এমন কমান্ড সহ যা আপনি পড়ার সময় চালাতে পারেন।
ইমেজ বনাম কন্টেইনার: মানসিক মডেল
একটি ইমেজ হলো একটি রিড-অনলি টেমপ্লেট। এতে একটি ফাইলসিস্টেম স্ন্যাপশট এবং ডিফল্ট কমান্ড ও এক্সপোজড পোর্টের মতো মেটাডেটা থাকে। একটি কন্টেইনার হলো সেই ইমেজের একটি চলমান (বা বন্ধ) ইনস্ট্যান্স যার উপরে একটি পাতলা রাইটেবল লেয়ার থাকে।
যদি আপনি অবজেক্ট-ওরিয়েন্টেড প্রোগ্রামিং থেকে আসেন, উপমাটি কাছাকাছি: একটি ইমেজ হলো একটি ক্লাস, একটি কন্টেইনার হলো একটি অবজেক্ট। আপনি একটি ইমেজ থেকে অনেক কন্টেইনার শুরু করতে পারেন, এবং একটি কন্টেইনারের ভিতরের পরিবর্তনগুলি ইমেজ পরিবর্তন করে না।
| ধারণা | এটি কী | সাধারণ কমান্ড |
|---|---|---|
| ইমেজ | অপরিবর্তনীয় টেমপ্লেট | docker build, docker pull |
| কন্টেইনার | চলমান ইনস্ট্যান্স | docker run, docker ps |
| ভলিউম | স্থায়ী স্টোরেজ | docker volume create |
| Compose | মাল্টি-কন্টেইনার সংজ্ঞা | docker compose up |
একটি Dockerfile দিয়ে আপনার প্রথম ইমেজ
একটি Dockerfile হলো একটি রেসিপি। প্রতিটি নির্দেশ একটি লেয়ার তৈরি করে, এবং Docker লেয়ারগুলি ক্যাশ করে যাতে কিছু পরিবর্তন না হলে পুনর্নির্মাণ দ্রুত হয়। এখানে একটি ছোট Node.js উদাহরণ:
FROM node:20-alpine\nWORKDIR /app\nCOPY package*.json ./\nRUN npm ci --omit=dev\nCOPY . .\nEXPOSE 3000\nCMD ["node", "server.js"]এখানে দুটি অভ্যাস গুরুত্বপূর্ণ। প্রথমত, বাকি সোর্সের আগে ডিপেন্ডেন্সি ম্যানিফেস্ট কপি করুন যাতে শুধুমাত্র অ্যাপ্লিকেশন কোড পরিবর্তন হলে npm ci লেয়ার ক্যাশ হয়। দ্বিতীয়ত, আকার এবং অ্যাটাক সারফেস কমাতে alpine-এর মতো একটি স্লিম বেস ইমেজ পছন্দ করুন।
এটি বিল্ড এবং চালান:
docker build -t myapp:1.0 .\ndocker run --rm -p 3000:3000 myapp:1.0-p 3000:3000 ফ্ল্যাগটি হোস্ট পোর্ট 3000 কে কন্টেইনার পোর্ট 3000-এ ম্যাপ করে। --rm কন্টেইনারটি বন্ধ হয়ে গেলে মুছে ফেলে, যা ডেভেলপমেন্টের সময় আপনার মেশিন পরিষ্কার রাখে।
প্রতিদিনের কন্টেইনার কমান্ড
এগুলি বেশিরভাগ দৈনন্দিন কাজ কভার করে:
docker ps— চলমান কন্টেইনার তালিকা করে; বন্ধ থাকা কন্টেইনার অন্তর্ভুক্ত করতে-aযোগ করুন।docker logs -f <container>— অ্যাপ্লিকেশন আউটপুট স্ট্রিম করে।docker exec -it <container> sh— একটি চলমান কন্টেইনারের ভিতরে একটি শেল খোলে।docker stop <container>— গ্রেসফুল শাটডাউন।docker images— স্থানীয় ইমেজ এবং তাদের আকার তালিকা করে।docker system prune— অব্যবহৃত কন্টেইনার, নেটওয়ার্ক এবং ড্যাংলিং ইমেজ সরিয়ে দেয়।
যখন একটি কন্টেইনার সাথে সাথে বন্ধ হয়ে যায়, প্রথমে লগ পড়ুন। সবচেয়ে সাধারণ কারণগুলি হলো ভুল CMD, একটি অনুপস্থিত এনভায়রনমেন্ট ভেরিয়েবল, অথবা কন্টেইনারের ভিতরে প্রসেসটি 0.0.0.0 এর পরিবর্তে 127.0.0.1-এ বাইন্ড করা।
Compose কেন বিদ্যমান
একটি বাস্তব অ্যাপ্লিকেশনের সাধারণত একাধিক প্রসেস প্রয়োজন: একটি API, একটি ডাটাবেস, হয়তো একটি ক্যাশে। দীর্ঘ ফ্ল্যাগ তালিকা সহ তিনটি docker run কমান্ড চালানো ত্রুটি-প্রবণ। Docker Compose আপনাকে একটি YAML ফাইলে সমস্ত সার্ভিস ঘোষণা করতে এবং সেগুলি একসাথে শুরু করতে দেয়।
services:\n api:\n build: .\n ports:\n - "3000:3000"\n environment:\n DATABASE_URL: postgres://app:secret@db:5432/app\n depends_on:\n - db\n db:\n image: postgres:16-alpine\n environment:\n POSTGRES_USER: app\n POSTGRES_PASSWORD: secret\n POSTGRES_DB: app\n volumes:\n - db_data:/var/lib/postgresql/data\nvolumes:\n db_data:লক্ষ্য করুন যে API db-এর সাথে সংযুক্ত হয়, localhost-এর সাথে নয়। Compose একটি প্রাইভেট নেটওয়ার্ক তৈরি করে যেখানে প্রতিটি সার্ভিস তার সার্ভিস নাম দ্বারা পৌঁছানো যায়। এটি নতুনদের জন্য বিভ্রান্তির একক সবচেয়ে সাধারণ উৎস।
docker compose up দিয়ে সবকিছু শুরু করুন, অথবা ব্যাকগ্রাউন্ডে চালানোর জন্য docker compose up -d। কোড পরিবর্তনের পরে docker compose up --build দিয়ে পুনর্নির্মাণ করুন। docker compose down দিয়ে টিয়ার ডাউন করুন, এবং যদি আপনি নামযুক্ত ভলিউমগুলিও মুছতে চান তবে -v যোগ করুন।
ভলিউম, বাইন্ড মাউন্ট এবং লাইভ রিলোড
কন্টেইনারগুলি ক্ষণস্থায়ী; সেগুলির ভিতরের ডেটা সরিয়ে ফেললে অদৃশ্য হয়ে যায়। দুটি প্রক্রিয়া এটি সমাধান করে:
- নামযুক্ত ভলিউম — Docker দ্বারা পরিচালিত, ডাটাবেস ফাইলের জন্য আদর্শ। এগুলি কন্টেইনার পুনরায় চালু করার পরেও স্থায়ী হয় এবং
volumes:বিভাগে ঘোষণা করা হয়। - বাইন্ড মাউন্ট — একটি হোস্ট ডিরেক্টরি কন্টেইনারে ম্যাপ করে, ডেভেলপমেন্টের সময় সোর্স কোডের জন্য আদর্শ।
services:\n api:\n build: .\n volumes:\n - ./src:/app/srcএকটি বাইন্ড মাউন্টের সাথে, আপনার হোস্টে ফাইল সম্পাদনা কন্টেইনারের ভিতরে প্রতিফলিত হয়। এটি একটি ফাইল ওয়াচারের (nodemon, uvicorn --reload, air) সাথে একত্রিত করুন এবং আপনি ইমেজ পুনর্নির্মাণ ছাড়াই লাইভ রিলোড পাবেন।
একটি ব্যবহারিক ডেভেলপমেন্ট ওয়ার্কফ্লো
- আপনার অ্যাপের জন্য একটি Dockerfile লিখুন এবং এটি ন্যূনতম রাখুন।
- অ্যাপ এবং তার ডিপেন্ডেন্সি সহ একটি
docker-compose.ymlযোগ করুন। - সোর্স কোডের জন্য বাইন্ড মাউন্ট এবং ডেটার জন্য নামযুক্ত ভলিউম ব্যবহার করুন।
- সিক্রেট ফাইল থেকে দূরে রাখুন; একটি
.envফাইল ব্যবহার করুন এবং${VAR}দিয়ে ভেরিয়েবল রেফারেন্স করুন। node_modules,.git, এবং বিল্ড আউটপুট ধারণকারী একটি.dockerignoreযোগ করুন বিল্ড গতি বাড়ানোর জন্য।docker compose up --buildচালান এবং পুনরাবৃত্তি করুন।
যদি আপনার সার্ভিস লগ তৈরি করে যা আপনার পরে পরিদর্শন করতে হয়, মনে রাখবেন কন্টেইনার লগগুলি শুধু টেক্সট। সার্ভার লগ পার্স করে এমন টুলস, যেমন একটি nginx log analyzer, কন্টেইনারগুলির সাথে ভালভাবে জোড়া লাগে কারণ আপনি হোস্টে কিছু ইনস্টল না করেই লগ পাইপ বা কপি করতে পারেন।
সাধারণ সমস্যা
- মোটা ইমেজ: একটি স্টেজে কম্পাইল করতে এবং শুধুমাত্র আর্টিফ্যাক্টগুলি একটি স্লিম রানটাইম ইমেজে কপি করতে মাল্টি-স্টেজ বিল্ড ব্যবহার করুন।
- সবকিছু পুনর্নির্মাণ: Dockerfile নির্দেশাবলী সর্বনিম্ন থেকে সর্বাধিক ঘন ঘন পরিবর্তনশীল ক্রমে সাজান।
- হার্ডকোডেড হোস্ট: Compose-এর ভিতরে, সার্ভিস নাম ব্যবহার করুন; বাইরে, এনভায়রনমেন্ট ভেরিয়েবল ব্যবহার করুন।
- পরিষ্কার উপেক্ষা: ড্যাংলিং ইমেজ এবং বন্ধ কন্টেইনার জমা হয়। পর্যায়ক্রমে
docker system pruneচালান। - রুট হিসেবে চালানো: প্রোডাকশন ইমেজে একটি নন-রুট
USERযোগ করুন।
সাধারণ প্রশ্নোত্তর
একটি ইমেজ এবং একটি কন্টেইনারের মধ্যে পার্থক্য কী?
একটি ইমেজ হলো একটি অপরিবর্তনীয় টেমপ্লেট যা একটি ফাইলসিস্টেম এবং মেটাডেটা ধারণ করে। একটি কন্টেইনার হলো সেই ইমেজের একটি চলমান ইনস্ট্যান্স যার একটি অতিরিক্ত রাইটেবল লেয়ার রয়েছে। আপনি একটি ইমেজ থেকে অনেক কন্টেইনার শুরু করতে পারেন।
একটি একক সার্ভিসের জন্য আমার কি Docker Compose প্রয়োজন?
না। একটি কন্টেইনারের জন্য একটি docker run কমান্ডই যথেষ্ট। Compose মূল্যবান হয়ে ওঠে যখন আপনার একাধিক সার্ভিস, শেয়ার্ড নেটওয়ার্ক, বা এনভায়রনমেন্ট ভেরিয়েবল থাকে যা আপনি এক জায়গায় ঘোষণা করতে চান।
কন্টেইনারগুলি একে অপরের সাথে কীভাবে যোগাযোগ করে?
Compose সমস্ত সার্ভিসকে একটি শেয়ার্ড নেটওয়ার্কে রাখে যেখানে প্রতিটি সার্ভিস তার সার্ভিস নাম দ্বারা পৌঁছানো যায়। সেই নামটি হোস্টনাম হিসেবে ব্যবহার করুন, উদাহরণস্বরূপ db:5432, localhost-এর পরিবর্তে।
Docker একটি ছোট প্রাথমিক বিনিয়োগের পুরস্কার দেয়। একবার ইমেজ, কন্টেইনার এবং Compose বুঝে গেলে, একজন টিমমেটকে অনবোর্ড করা বা মেশিন পরিবর্তন করা একটি সেটআপ আচার হওয়া বন্ধ করে এবং একটি একক কমান্ড হয়ে যায়।