Docker শেখার শুরুতে docker run করা খুব সহজ।
একটি Ubuntu image নিন, container চালান, ভিতরে ঢুকুন—তারপর application চালিয়ে দিন।
কিন্তু software engineering-এর আসল প্রশ্নটা একটু ভিন্ন:
Can someone else reproduce exactly what you just built?
যদি উত্তর হয় “না, আমাকে একে একে সব command বলে দিতে হবে”—তাহলে আমাদের workflow-এ এখনও একটি সমস্যা আছে।
এই article-এ আমরা একটি ছোট Go HTTP server-কে Docker container-এর ভিতরে চালানোর মাধ্যমে সেই সমস্যাটি বুঝব।
প্রথমে সবকিছু manually করব।
তারপর docker commit দিয়ে সেই environment-এর একটি image বানাব।
এরপর দেখব কেন এই approach দীর্ঘমেয়াদে inconvenient।
সবশেষে একই process-কে একটি Dockerfile-এ লিখে পুরো setup-টিকে reproducible এবং automated করে তুলব।
এটাই মূল transition:
Manual Configuration
↓
docker commit
↓
Problem
↓
Dockerfile
↓
Reproducible Image
↓
Containerized Application
1. The Application We Want to Containerize
আমাদের application একটি খুব simple Go HTTP server।
server.go:
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World!")
})
fmt.Println("Server is running on :8080")
http.ListenAndServe(":8080", nil)
}
Application-এর কাজ খুব straightforward:
- HTTP server start করবে
-
8080port-এ listen করবে -
/endpoint-এ request এলেHello World!response দেবে
অর্থাৎ আমাদের লক্ষ্য:
Browser / Client
│
│ HTTP Request
▼
Go Server
│
▼
:8080
│
▼
"Hello World!"
এখন প্রশ্ন হলো—এই application-টিকে Docker container-এর মধ্যে কীভাবে চালাব?
2. First Attempt: Do Everything Manually
শুরু করি Ubuntu দিয়ে।
প্রথমে image pull:
docker pull ubuntu:24.04
তারপর interactive container:
docker run -it ubuntu:24.04 bash
এখন আমরা Ubuntu container-এর ভিতরে।
Reference workflow-এও প্রথমে Ubuntu image pull করে interactive container চালানো হয়েছে।
এখন আমাদের container একটি basic Ubuntu environment।
কিন্তু এখানে Go নেই।
তাই পরবর্তী কাজ—Go install করা।
3. Install Go Inside the Container
প্রথমে package index update:
apt update
তারপর:
apt install -y golang
Installation verify করতে:
go version
যদি version output পাওয়া যায়, তাহলে Go environment ready।
Reference workflow-এও একইভাবে apt update, apt install -y golang, এবং go version ব্যবহার করা হয়েছে।
এখন আমাদের container-এর ভিতরে Go আছে।
কিন্তু application কোথায়?
4. Create an Application Directory
Application-এর জন্য একটি dedicated directory তৈরি করি:
mkdir /app
cd /app
এখন আমাদের container structure conceptually এমন:
/
├── bin/
├── etc/
├── usr/
├── var/
└── app/
আমরা application-এর জন্য /app ব্যবহার করছি।
Reference workflow-এও application code রাখার জন্য /app directory তৈরি করা হয়েছে।
5. Move server.go into the Container
এখন একটি গুরুত্বপূর্ণ বিষয় আছে।
আমাদের server.go host machine-এ আছে।
কিন্তু Go runtime চলছে container-এর ভিতরে।
তাই host থেকে container-এ file নিতে হবে।
Docker-এর জন্য এই কাজের একটি command হলো:
docker cp server.go <CONTAINER_ID>:/app/server.go
Conceptually:
HOST MACHINE
│
│ server.go
│
│ docker cp
▼
DOCKER CONTAINER
│
└── /app/server.go
docker cp host এবং container-এর মধ্যে file copy করার জন্য ব্যবহৃত হয়।
এখন container-এর ভিতরে:
cd /app
ls
আমরা দেখতে পাব:
server.go
6. Run the Go Server
এখন application চালানো যাক:
go run server.go
Output:
Server is running on :8080
আমাদের Go server এখন container-এর ভিতরে চলছে।
Application test করার জন্য container-এর ভিতর থেকেই:
curl http://localhost:8080
করলে response:
Hello World!
Reference workflow-এও go run server.go দিয়ে server চালিয়ে curl http://localhost:8080 দিয়ে Hello World! response verify করা হয়েছে।
আমরা সফল।
কিন্তু আমাদের journey এখানেই শেষ নয়।
বরং এখান থেকেই আসল problem শুরু।
7. We Built It. But Can We Rebuild It?
একবার চিন্তা করুন, আমরা কী কী করেছি:
1. Pull Ubuntu
2. Run container
3. Update packages
4. Install Go
5. Create /app
6. Copy server.go
7. Run the server
8. Test the application
এখন যদি container delete হয়ে যায়?
আমাদের আবার সব করতে হবে।
যদি server.go পরিবর্তন হয়?
আবার setup করতে হবে।
যদি আরেকজন developer একই environment চায়?
তাকে আমাদের সব command দিতে হবে।
এখানে একটা fundamental engineering problem আছে:
The environment exists, but the process used to create the environment is not properly encoded.
এই problem থেকেই Dockerfile-এর প্রয়োজনীয়তা বোঝা যায়।
8. docker commit: Take a Snapshot
আমরা চাই আমাদের configured container-এর বর্তমান state preserve করতে।
এখানে ব্যবহার করা যায়:
docker commit <CONTAINER_ID> go_server
এটি container-এর current filesystem state থেকে একটি নতুন image তৈরি করে।
তারপর:
docker run -it go_server bash
চালালে নতুন container-এর মধ্যে আগের setup পাওয়া যাবে।
অর্থাৎ:
Configured Container
│
│ docker commit
▼
go_server
Image
│
│ docker run
▼
New Container
docker commit দিয়ে configured container থেকে image তৈরি করার এই workflow-টিই reference material-এ দেখানো হয়েছে।
এটি useful।
কিন্তু production engineering-এর জন্য এখানেও একটি বড় প্রশ্ন রয়ে যায়।
9. The Problem with docker commit
ধরুন server.go-তে আজ একটি পরিবর্তন এসেছে।
আমাদের manual workflow আবার:
Ubuntu
↓
Install Go
↓
Create /app
↓
Copy new server.go
↓
Run application
↓
docker commit
আবার একই কাজ।
এটি repetitive।
আরও গুরুত্বপূর্ণ হলো, image দেখে সহজে বোঝা যায় না কীভাবে environment-টি তৈরি হয়েছিল।
কোন base image?
কোন package?
কোন directory?
কোন source file?
কোন setup command?
Manual process-এর এই traceability এবং repeatability limitation-ই Dockerfile-এর দিকে যাওয়ার মূল কারণ।
10. Enter the Dockerfile
এবার আমরা একটি গুরুত্বপূর্ণ engineering principle ব্যবহার করব:
If you have to repeat a process, turn the process into code.
আমরা এতক্ষণ যে কাজগুলো terminal-এ manually করেছি, সেগুলো এখন একটি Dockerfile-এ লিখব।
আমাদের project structure:
docker-learning/
│
├── Dockerfile
└── server.go
এখন Dockerfile:
FROM ubuntu:24.04
RUN apt update
RUN apt install -y golang
WORKDIR /app
COPY ./server.go ./server.go
এটাই আমাদের manual workflow-এর declarative version।
11. Understanding the Dockerfile
Dockerfile-এর প্রতিটি instruction-এর একটি নির্দিষ্ট responsibility আছে।
FROM
FROM ubuntu:24.04
এটি আমাদের base image নির্ধারণ করে।
অর্থাৎ:
Start with Ubuntu 24.04.
Docker image তৈরির foundation এখান থেকেই শুরু হচ্ছে।
RUN
RUN apt update
Image build-এর সময় package index update হবে।
তারপর:
RUN apt install -y golang
Build-এর সময় Go install হবে।
Manual workflow-এ আমরা এগুলো নিজের হাতে করেছিলাম।
Dockerfile-এ আমরা instructions লিখে দিলাম।
Reference material-এ RUN instructions-এর মাধ্যমে package update এবং Golang installation automate করার একই workflow দেখানো হয়েছে।
12. WORKDIR: A Better Way to Handle /app
Manual approach-এ আমাদের লিখতে হয়েছিল:
mkdir /app
cd /app
Dockerfile-এ:
WORKDIR /app
এই একটি instruction /app-কে working directory হিসেবে set করে।
এর ফলে পরবর্তী instructions-এর relative context /app হয়ে যায়।
Reference material-এও উল্লেখ করা হয়েছে যে WORKDIR /app ব্যবহার করলে আলাদা করে mkdir /app এবং cd /app করার প্রয়োজন হয় না।
এটি Dockerfile-কে অনেক cleaner করে।
13. COPY: Bring the Application into the Image
এখন:
COPY ./server.go ./server.go
এর মাধ্যমে build context-এর server.go image-এর working directory-তে copy হবে।
আমাদের WORKDIR যেহেতু:
WORKDIR /app
তাই application file পাওয়া যাবে:
/app/server.go
অর্থাৎ:
Host
│
└── server.go
│
│ COPY
▼
Container Image
│
└── /app/server.go
Reference workflow-এও COPY দিয়ে host-এর server.go application directory-তে অন্তর্ভুক্ত করা হয়েছে।
14. Manual Process vs Dockerfile
এখন সবচেয়ে গুরুত্বপূর্ণ comparison:
| Manual Process | Dockerfile |
|---|---|
| Pull Ubuntu | FROM ubuntu:24.04 |
apt update |
RUN apt update |
| Install Go | RUN apt install -y golang |
mkdir /app |
WORKDIR /app |
docker cp server.go |
COPY ./server.go ./server.go |
docker commit |
docker build |
খেয়াল করুন কী হয়েছে।
আমরা শুধু কয়েকটি command replace করিনি।
আমরা পুরো environment creation process-টাকে code-এ পরিণত করেছি।
15. Build the Image
এখন Dockerfile থেকে একটি image তৈরি করি:
docker build -t new_go_server:1.0.0 .
এখানে:
docker build
→ Dockerfile instructions অনুসরণ করে image তৈরি করবে।
-t new_go_server:1.0.0
→ image-এর name এবং version tag।
.
→ current directory-কে build context হিসেবে ব্যবহার করবে।
Reference workflow-এও docker build -t new_go_server:1.0.0 . ব্যবহার করা হয়েছে।
16. What Happens During docker build?
আমাদের Dockerfile:
FROM ubuntu:24.04
RUN apt update
RUN apt install -y golang
WORKDIR /app
COPY ./server.go ./server.go
Docker build করার সময় conceptually:
FROM ubuntu:24.04
↓
Base Image
↓
RUN apt update
↓
Install Go
↓
WORKDIR /app
↓
COPY server.go
↓
Final Image
Docker প্রতিটি instruction অনুসরণ করে একটি নতুন image তৈরি করে।
এখানে সবচেয়ে গুরুত্বপূর্ণ পরিবর্তন হলো:
আগে আমরা environment তৈরি করছিলাম manually।
এখন Docker environment তৈরি করছে Dockerfile-এর instructions অনুসরণ করে।
17. Run the Custom Image
Image তৈরি হয়ে গেছে।
এখন:
docker run -it new_go_server:1.0.0 bash
Container-এর ভিতরে ঢুকলে আমরা /app directory-তে থাকব।
কারণ Dockerfile-এ:
WORKDIR /app
দেওয়া আছে।
এখন:
ls
দিলে:
server.go
দেখা যাবে।
তারপর:
go run server.go
Output:
Server is running on :8080
অর্থাৎ আমাদের image-এর ভিতরে ইতিমধ্যেই:
- Ubuntu আছে
- Go আছে
-
/appআছে -
server.goআছে
আমাদের শুধু application run করতে হচ্ছে।
Reference workflow-এও custom image run করার পর /app-এ server.go পাওয়া এবং go run server.go দিয়ে application চালানোর বিষয়টি দেখানো হয়েছে।
18. The Workflow Has Completely Changed
আগের workflow:
Manual Setup
↓
Manual Configuration
↓
Manual Copy
↓
Manual Testing
↓
docker commit
এখন:
Dockerfile
↓
docker build
↓
Image
↓
docker run
↓
Application
এটাই Docker-এর real engineering advantage।
19. What If the Code Changes?
ধরুন server.go-তে আমরা পরিবর্তন করলাম।
Manual approach-এ আবার পুরো environment setup করতে হতে পারে।
Dockerfile approach-এ:
docker build -t new_go_server:1.0.1 .
নতুন image তৈরি করা যায়।
Version tag-ও পরিবর্তন করা যায়:
new_go_server:1.0.0
new_go_server:1.0.1
new_go_server:1.0.2
এতে application image-এর version track করা সহজ হয়।
Reference comparison-এ Dockerfile workflow-এর এই repeatable build এবং version-control-friendly nature-কে manual docker commit workflow-এর তুলনায় বড় সুবিধা হিসেবে দেখানো হয়েছে।
20. Why This Matters in Real Engineering
এখন বিষয়টা শুধু Docker শেখার মধ্যে সীমাবদ্ধ নয়।
ধরুন একটি team-এ পাঁচজন developer আছে।
Manual workflow হলে প্রত্যেক developer-এর environment setup আলাদা হতে পারে।
একজনের:
Go version A
অন্যজনের:
Go version B
কেউ অন্য package install করেছে।
কেউ কিছু configuration পরিবর্তন করেছে।
ফলে classic problem:
“It works on my machine.”
Dockerfile এই সমস্যাকে পুরোপুরি magic-এর মাধ্যমে solve করে না, কিন্তু environment creation-কে explicit, repeatable এবং version-controlled করার একটি শক্তিশালী mechanism দেয়।
একজন developer Dockerfile পাবে।
একটি image build করবে।
তারপর একই defined environment-এর ভিত্তিতে application চালাবে।
21. Dockerfile Is Infrastructure as Code Thinking
Dockerfile-এর সবচেয়ে গুরুত্বপূর্ণ lesson শুধু Docker syntax নয়।
এটি একটি broader engineering mindset শেখায়:
Infrastructure and environment configuration should be reproducible.
আমরা আগে environment তৈরি করছিলাম command-by-command।
এখন environment definition একটি file-এ:
FROM ubuntu:24.04
RUN apt update
RUN apt install -y golang
WORKDIR /app
COPY ./server.go ./server.go
এই file Git repository-তে রাখা যায়।
Code review করা যায়।
Change track করা যায়।
একজন নতুন developer repository clone করে একই environment build করতে পারে।
এখান থেকেই Docker CI/CD এবং DevOps workflow-এর সঙ্গে naturally connect করতে শুরু করে।
Reference material-এও Dockerfile-কে version control এবং production/CI-CD workflow-এর জন্য বেশি উপযোগী হিসেবে আলাদা করা হয়েছে।
22. One Important Distinction: Image vs Container
এই পুরো article-এর মধ্যে একটি mental model সবচেয়ে গুরুত্বপূর্ণ।
Dockerfile
│
│ docker build
▼
Docker Image
│
│ docker run
▼
Docker Container
│
▼
Go Application
Dockerfile
Environment কীভাবে তৈরি হবে তার definition।
Image
সেই definition থেকে তৈরি packaged environment।
Container
সেই image-এর running instance।
এই distinction পরিষ্কার থাকলে Docker-এর অনেক concept অনেক সহজ হয়ে যায়।
23. The Complete Example
আমাদের project:
docker-learning/
│
├── Dockerfile
└── server.go
server.go
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World!")
})
fmt.Println("Server is running on :8080")
http.ListenAndServe(":8080", nil)
}
Dockerfile
FROM ubuntu:24.04
RUN apt update
RUN apt install -y golang
WORKDIR /app
COPY ./server.go ./server.go
Build
docker build -t new_go_server:1.0.0 .
Run
docker run -it new_go_server:1.0.0 bash
Start the application
go run server.go
Test
curl http://localhost:8080
Expected:
Hello World!
24. A Small but Important Production Note
উপরের Dockerfile একটি learning-oriented example।
এখানে আমরা ইচ্ছাকৃতভাবে Ubuntu-এর ভিতরে Go install করছি, যাতে FROM, RUN, WORKDIR, এবং COPY—এই fundamental Dockerfile instructions পরিষ্কারভাবে বোঝা যায়।
Production environment-এ একই Go application-এর জন্য আরও optimized approach ব্যবহার করা যায়—যেমন:
- multi-stage builds
- smaller runtime images
- compiled Go binary
- explicit
CMDবাENTRYPOINT - dependency caching
- non-root execution
- health checks
অর্থাৎ এই Dockerfile-কে production-ready final architecture হিসেবে নয়, বরং Dockerfile fundamentals বোঝার foundation হিসেবে দেখা উচিত।
25. The Bigger Picture
এই ছোট Go server example-এর মধ্যেই Docker-এর একটি বড় philosophy লুকিয়ে আছে।
আমরা শুরু করেছিলাম:
“আমি নিজে container configure করব।”
তারপর:
“আমার configuration-এর একটা snapshot নিয়ে রাখি।”
docker commit আমাদের সেই capability দেয়।
কিন্তু তারপর প্রশ্ন আসে:
“আমি কি এই configuration-টাকে লিখে রাখতে পারি?”
উত্তর:
Yes. Dockerfile.
তারপর:
Dockerfile
↓
docker build
↓
Image
↓
docker run
↓
Container
এখন environment creation আর developer-এর memory-এর ওপর নির্ভর করছে না।
এটি একটি file-এর মধ্যে definition হিসেবে আছে।
Final Takeaway
Docker শেখার সময় শুধু command মুখস্থ করা যথেষ্ট নয়।
সবচেয়ে গুরুত্বপূর্ণ হলো এই transformation বোঝা:
Manual Work
↓
Automation
↓
Reproducibility
↓
Consistency
আমরা প্রথমে একটি Ubuntu container manually configure করেছি।
Go install করেছি।
server.go copy করেছি।
Application চালিয়েছি।
তারপর docker commit দিয়ে সেই configured environment-এর snapshot নিয়েছি।
কিন্তু বুঝেছি—এভাবে প্রতিবার environment তৈরি করা scalable নয়।
তাই একই process-কে Dockerfile-এ লিখেছি:
FROM ubuntu:24.04
RUN apt update
RUN apt install -y golang
WORKDIR /app
COPY ./server.go ./server.go
তারপর:
docker build -t new_go_server:1.0.0 .
এবং একটি reproducible image তৈরি করেছি।
শেষ পর্যন্ত:
Source Code
↓
Dockerfile
↓
Docker Image
↓
Docker Container
↓
Go Server
↓
HTTP Response
এটাই Docker-এর সবচেয়ে গুরুত্বপূর্ণ engineering lesson:
Don't just build an environment. Define how to build it.
কারণ manual setup একজন developer-এর knowledge হতে পারে।
কিন্তু Dockerfile সেই knowledge-কে code-এ পরিণত করে।
And once your environment becomes code, it becomes much easier to reproduce, version, review, automate, and ship.
🐳 That is where Docker starts becoming an engineering tool—not just a container runtime.
Top comments (0)