DEV Community

Cover image for From Manual Containers to Dockerfile: Containerizing a Go Server the Right Way
Ragib Hasan
Ragib Hasan Subscriber

Posted on

From Manual Containers to Dockerfile: Containerizing a Go Server the Right Way

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
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

Application-এর কাজ খুব straightforward:

  • HTTP server start করবে
  • 8080 port-এ listen করবে
  • / endpoint-এ request এলে Hello World! response দেবে

অর্থাৎ আমাদের লক্ষ্য:

Browser / Client
       │
       │ HTTP Request
       ▼
Go Server
       │
       ▼
:8080
       │
       ▼
"Hello World!"
Enter fullscreen mode Exit fullscreen mode

এখন প্রশ্ন হলো—এই application-টিকে Docker container-এর মধ্যে কীভাবে চালাব?


2. First Attempt: Do Everything Manually

শুরু করি Ubuntu দিয়ে।

প্রথমে image pull:

docker pull ubuntu:24.04
Enter fullscreen mode Exit fullscreen mode

তারপর interactive container:

docker run -it ubuntu:24.04 bash
Enter fullscreen mode Exit fullscreen mode

এখন আমরা 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
Enter fullscreen mode Exit fullscreen mode

তারপর:

apt install -y golang
Enter fullscreen mode Exit fullscreen mode

Installation verify করতে:

go version
Enter fullscreen mode Exit fullscreen mode

যদি 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
Enter fullscreen mode Exit fullscreen mode

এখন আমাদের container structure conceptually এমন:

/
├── bin/
├── etc/
├── usr/
├── var/
└── app/
Enter fullscreen mode Exit fullscreen mode

আমরা 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
Enter fullscreen mode Exit fullscreen mode

Conceptually:

HOST MACHINE
│
│  server.go
│
│ docker cp
▼
DOCKER CONTAINER
│
└── /app/server.go
Enter fullscreen mode Exit fullscreen mode

docker cp host এবং container-এর মধ্যে file copy করার জন্য ব্যবহৃত হয়।

এখন container-এর ভিতরে:

cd /app
ls
Enter fullscreen mode Exit fullscreen mode

আমরা দেখতে পাব:

server.go
Enter fullscreen mode Exit fullscreen mode

6. Run the Go Server

এখন application চালানো যাক:

go run server.go
Enter fullscreen mode Exit fullscreen mode

Output:

Server is running on :8080
Enter fullscreen mode Exit fullscreen mode

আমাদের Go server এখন container-এর ভিতরে চলছে।

Application test করার জন্য container-এর ভিতর থেকেই:

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

করলে response:

Hello World!
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

এখন যদি 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
Enter fullscreen mode Exit fullscreen mode

এটি container-এর current filesystem state থেকে একটি নতুন image তৈরি করে।

তারপর:

docker run -it go_server bash
Enter fullscreen mode Exit fullscreen mode

চালালে নতুন container-এর মধ্যে আগের setup পাওয়া যাবে।

অর্থাৎ:

Configured Container
        │
        │ docker commit
        ▼
    go_server
      Image
        │
        │ docker run
        ▼
New Container
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

আবার একই কাজ।

এটি 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
Enter fullscreen mode Exit fullscreen mode

এখন Dockerfile:

FROM ubuntu:24.04

RUN apt update

RUN apt install -y golang

WORKDIR /app

COPY ./server.go ./server.go
Enter fullscreen mode Exit fullscreen mode

এটাই আমাদের manual workflow-এর declarative version।


11. Understanding the Dockerfile

Dockerfile-এর প্রতিটি instruction-এর একটি নির্দিষ্ট responsibility আছে।

FROM

FROM ubuntu:24.04
Enter fullscreen mode Exit fullscreen mode

এটি আমাদের base image নির্ধারণ করে।

অর্থাৎ:

Start with Ubuntu 24.04.

Docker image তৈরির foundation এখান থেকেই শুরু হচ্ছে।


RUN

RUN apt update
Enter fullscreen mode Exit fullscreen mode

Image build-এর সময় package index update হবে।

তারপর:

RUN apt install -y golang
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Dockerfile-এ:

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode

এই একটি 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
Enter fullscreen mode Exit fullscreen mode

এর মাধ্যমে build context-এর server.go image-এর working directory-তে copy হবে।

আমাদের WORKDIR যেহেতু:

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode

তাই application file পাওয়া যাবে:

/app/server.go
Enter fullscreen mode Exit fullscreen mode

অর্থাৎ:

Host
│
└── server.go
       │
       │ COPY
       ▼
Container Image
│
└── /app/server.go
Enter fullscreen mode Exit fullscreen mode

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 .
Enter fullscreen mode Exit fullscreen mode

এখানে:

docker build
Enter fullscreen mode Exit fullscreen mode

→ Dockerfile instructions অনুসরণ করে image তৈরি করবে।

-t new_go_server:1.0.0
Enter fullscreen mode Exit fullscreen mode

→ image-এর name এবং version tag।

.
Enter fullscreen mode Exit fullscreen mode

→ 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
Enter fullscreen mode Exit fullscreen mode

Docker build করার সময় conceptually:

FROM ubuntu:24.04
        ↓
Base Image
        ↓
RUN apt update
        ↓
Install Go
        ↓
WORKDIR /app
        ↓
COPY server.go
        ↓
Final Image
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Container-এর ভিতরে ঢুকলে আমরা /app directory-তে থাকব।

কারণ Dockerfile-এ:

WORKDIR /app
Enter fullscreen mode Exit fullscreen mode

দেওয়া আছে।

এখন:

ls
Enter fullscreen mode Exit fullscreen mode

দিলে:

server.go
Enter fullscreen mode Exit fullscreen mode

দেখা যাবে।

তারপর:

go run server.go
Enter fullscreen mode Exit fullscreen mode

Output:

Server is running on :8080
Enter fullscreen mode Exit fullscreen mode

অর্থাৎ আমাদের 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
Enter fullscreen mode Exit fullscreen mode

এখন:

Dockerfile
     ↓
docker build
     ↓
Image
     ↓
docker run
     ↓
Application
Enter fullscreen mode Exit fullscreen mode

এটাই 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 .
Enter fullscreen mode Exit fullscreen mode

নতুন image তৈরি করা যায়।

Version tag-ও পরিবর্তন করা যায়:

new_go_server:1.0.0
new_go_server:1.0.1
new_go_server:1.0.2
Enter fullscreen mode Exit fullscreen mode

এতে 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
Enter fullscreen mode Exit fullscreen mode

অন্যজনের:

Go version B
Enter fullscreen mode Exit fullscreen mode

কেউ অন্য 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
Enter fullscreen mode Exit fullscreen mode

এই 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
Enter fullscreen mode Exit fullscreen mode

Dockerfile

Environment কীভাবে তৈরি হবে তার definition।

Image

সেই definition থেকে তৈরি packaged environment।

Container

সেই image-এর running instance।

এই distinction পরিষ্কার থাকলে Docker-এর অনেক concept অনেক সহজ হয়ে যায়।


23. The Complete Example

আমাদের project:

docker-learning/
│
├── Dockerfile
└── server.go
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

Dockerfile

FROM ubuntu:24.04

RUN apt update

RUN apt install -y golang

WORKDIR /app

COPY ./server.go ./server.go
Enter fullscreen mode Exit fullscreen mode

Build

docker build -t new_go_server:1.0.0 .
Enter fullscreen mode Exit fullscreen mode

Run

docker run -it new_go_server:1.0.0 bash
Enter fullscreen mode Exit fullscreen mode

Start the application

go run server.go
Enter fullscreen mode Exit fullscreen mode

Test

curl http://localhost:8080
Enter fullscreen mode Exit fullscreen mode

Expected:

Hello World!
Enter fullscreen mode Exit fullscreen mode

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 করব।”
Enter fullscreen mode Exit fullscreen mode

তারপর:

“আমার configuration-এর একটা snapshot নিয়ে রাখি।”
Enter fullscreen mode Exit fullscreen mode

docker commit আমাদের সেই capability দেয়।

কিন্তু তারপর প্রশ্ন আসে:

“আমি কি এই configuration-টাকে লিখে রাখতে পারি?”
Enter fullscreen mode Exit fullscreen mode

উত্তর:

Yes. Dockerfile.

তারপর:

Dockerfile
    ↓
docker build
    ↓
Image
    ↓
docker run
    ↓
Container
Enter fullscreen mode Exit fullscreen mode

এখন environment creation আর developer-এর memory-এর ওপর নির্ভর করছে না।

এটি একটি file-এর মধ্যে definition হিসেবে আছে।


Final Takeaway

Docker শেখার সময় শুধু command মুখস্থ করা যথেষ্ট নয়।

সবচেয়ে গুরুত্বপূর্ণ হলো এই transformation বোঝা:

Manual Work
    ↓
Automation
    ↓
Reproducibility
    ↓
Consistency
Enter fullscreen mode Exit fullscreen mode

আমরা প্রথমে একটি 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
Enter fullscreen mode Exit fullscreen mode

তারপর:

docker build -t new_go_server:1.0.0 .
Enter fullscreen mode Exit fullscreen mode

এবং একটি reproducible image তৈরি করেছি।

শেষ পর্যন্ত:

Source Code
     ↓
Dockerfile
     ↓
Docker Image
     ↓
Docker Container
     ↓
Go Server
     ↓
HTTP Response
Enter fullscreen mode Exit fullscreen mode

এটাই 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)