Trong thế giới phát triển phần mềm hiện đại ngày nay, Docker đã trở thành một công nghệ không thể thiếu, mang lại cuộc cách mạng trong việc xây dựng, vận chuyển và chạy ứng dụng. Nhưng, bạn đã biết chưa, trái tim của cuộc cách mạng đó chính là hai khái niệm "core": Image và Container. Mặc dù thường được nhắc đến cùng nhau, chúng lại đóng những vai trò riêng biệt nhưng không thể tách rời.
Bài viết này sẽ đưa bạn vào một hành trình khám phá toàn diện, từ khái niệm cơ bản đến các ví dụ thực tế, giúp bạn giải mã hoàn toàn cặp đôi này và hiểu rõ tại sao chúng lại quan trọng đến vậy.
Docker Image và Container là gì?
Để dễ hình dung, hãy tưởng tượng bạn đang làm bánh.
-
Docker Image giống như công thức làm bánh. Nó là một bản thiết kế chi tiết, bất biến (chỉ đọc), chứa đựng tất cả những gì cần thiết để tạo ra chiếc bánh: danh sách nguyên liệu (mã nguồn, thư viện, dependencies), các bước thực hiện (câu lệnh cài đặt), và môi trường cần thiết (hệ điều hành tối giản). Công thức này một khi đã viết ra thì không thể thay đổi.
-
Docker Container chính là chiếc bánh đã được làm ra từ công thức đó. Nó là một thực thể sống, một phiên bản đang chạy của Image. Từ một công thức (Image), bạn có thể tạo ra vô số những chiếc bánh (Container) hoàn toàn giống hệt nhau. Mỗi chiếc bánh là một môi trường biệt lập, chứa đựng ứng dụng và sẵn sàng để "thưởng thức" (sử dụng).
Một cách ví von khác trong lập trình, Image là một Class, còn Container là một instance (đối tượng) của Class đó.
So sánh chi tiết: Sự khác biệt cốt lõi
Dưới đây là bảng so sánh Docker Image và Container theo các tiêu chí:
Tiêu chí | Docker Image | Docker Container |
---|---|---|
Bản chất | Một template tĩnh, chỉ đọc (read-only) | Một thực thể sống, đang chạy (running instance) |
Trạng thái | Bất biến (Immutable) | Khả biến (Mutable), có trạng thái (stateful) |
Chức năng | Đóng gói ứng dụng và các thành phần phụ thuộc | Thực thi ứng dụng trong một môi trường cô lập |
Tạo ra từ | Được xây dựng từ một Dockerfile hoặc commit từ một container | Được khởi chạy từ một Docker Image |
Lưu trữ | Tồn tại dưới dạng các tệp tin trong Docker registry (như Docker Hub) hoặc máy local | Tồn tại trong bộ nhớ và CPU khi đang chạy, có thể bị xóa đi |
Cấu trúc của Docker Image: Sức mạnh của các "layer"
Điều làm nên sự hiệu quả và linh hoạt của Docker Image chính là cấu trúc phân lớp (layered architecture). Mỗi chỉ dẫn trong tệp Dockerfile
(tệp văn bản chứa hướng dẫn xây dựng Image) sẽ tạo ra một "lớp" (layer) mới.
Hãy tưởng tượng bạn đang xếp các tấm kính trong suốt lên nhau. Mỗi tấm kính là một layer, chứa một sự thay đổi nhất định (ví dụ: cài đặt một thư viện, sao chép mã nguồn). Khi nhìn qua tất cả các lớp, bạn sẽ thấy được hình ảnh hoàn chỉnh của ứng dụng.
Lợi ích của cấu trúc layer:
- Tái sử dụng và Tiết kiệm dung lượng: Nếu nhiều Image cùng sử dụng chung một lớp cơ sở (ví dụ, cùng dùng hệ điều hành Ubuntu), Docker sẽ chỉ lưu trữ lớp đó một lần duy nhất.
- Build nhanh hơn: Khi bạn xây dựng lại Image, Docker sẽ tận dụng bộ đệm (cache). Nó chỉ cần xây dựng lại những lớp đã có sự thay đổi, giúp tiết kiệm thời gian đáng kể.
- Quản lý phiên bản hiệu quả: Việc cập nhật ứng dụng chỉ đơn giản là thêm hoặc thay thế một vài lớp trên cùng mà không cần phải xây dựng lại toàn bộ từ đầu.
Vòng đời của Docker Container
Một Container không chỉ đơn giản là "chạy" hoặc "dừng". Nó có một vòng đời rõ ràng với các trạng thái khác nhau, được quản lý bởi các câu lệnh Docker tương ứng.
-
Created (Đã tạo): Container đã được tạo từ một Image nhưng chưa được khởi chạy. Trạng thái này giống như bạn đã chuẩn bị xong mọi thứ nhưng chưa bật bếp.
- Lệnh:
docker create <image_name>
- Lệnh:
-
Running (Đang chạy): Container đang hoạt động và ứng dụng bên trong nó đang được thực thi.
- Lệnh:
docker start <container_id_or_name>
hoặcdocker run <image_name>
(lệnhrun
là sự kết hợp củacreate
vàstart
).
- Lệnh:
-
Paused (Tạm dừng): Toàn bộ các tiến trình bên trong Container bị "đóng băng". Trạng thái này hữu ích khi bạn muốn tạm ngưng hoạt động để kiểm tra mà không làm mất trạng thái hiện tại.
- Lệnh:
docker pause <container_id_or_name>
- Để tiếp tục:
docker unpause <container_id_or_name>
- Lệnh:
-
Stopped (Đã dừng): Container đã dừng hoạt động. Tất cả các tiến trình chính đã kết thúc, nhưng hệ thống tệp của nó vẫn còn được lưu trữ. Bạn có thể khởi động lại nó sau này.
- Lệnh:
docker stop <container_id_or_name>
- Lệnh:
-
Deleted (Đã xóa): Container bị xóa hoàn toàn khỏi hệ thống, giải phóng tài nguyên. Bạn không thể phục hồi một container đã bị xóa.
- Lệnh:
docker rm <container_id_or_name>
- Lệnh:
Ví dụ thực tế: "Docker hóa" một ứng dụng Node.js đơn giản
Hãy cùng xem cách Image và Container phối hợp với nhau qua một ví dụ thực tế. Giả sử chúng ta có một ứng dụng web server đơn giản bằng Node.js.
Bước 1: Chuẩn bị ứng dụng
Tạo một thư mục dự án, bên trong có 2 tệp:
server.js
: Mã nguồn ứng dụng.package.json
: Khai báo thông tin và các gói phụ thuộc.
Bước 2: Viết Dockerfile
(Công thức)
Tạo một tệp tên là Dockerfile
trong cùng thư mục với nội dung sau:
# Bước 1: Chọn một Image nền
FROM node:18-alpine
# Bước 2: Tạo thư mục làm việc bên trong Image
WORKDIR /app
# Bước 3: Sao chép tệp package.json và cài đặt dependencies
COPY package*.json ./
RUN npm install
# Bước 4: Sao chép toàn bộ mã nguồn vào
COPY . .
# Bước 5: Mở cổng 8080 để bên ngoài có thể truy cập
EXPOSE 8080
# Bước 6: Lệnh để chạy ứng dụng khi Container khởi động
CMD [ "node", "server.js" ]
Tệp Dockerfile
này chính là công thức chi tiết để tạo ra Image cho ứng dụng của chúng ta.
Bước 3: Build Image (Tạo bản thiết kế)
Mở terminal trong thư mục dự án và chạy lệnh:
docker build -t my-nodejs-app .
Docker sẽ đọc Dockerfile
, thực thi từng bước để tạo ra các layer và cuối cùng tạo ra một Image mới có tên là my-nodejs-app
.
Bước 4: Run Container (Làm ra chiếc bánh)
Bây giờ, từ Image vừa tạo, chúng ta sẽ khởi chạy một Container:
docker run -p 3000:8080 -d --name my-running-app my-nodejs-app
-p 3000:8080
: Ánh xạ cổng 3000 trên máy của bạn vào cổng 8080 bên trong container.-d
: Chạy container ở chế độ nền.--name my-running-app
: Đặt tên cho container.
Lúc này, bạn đã có một chiếc bánh hoàn chỉnh - một ứng dụng Node.js đang chạy biệt lập bên trong một container. Bạn có thể truy cập vào http://localhost:3000
trên trình duyệt để xem kết quả. Từ cùng một Image my-nodejs-app
, bạn có thể tạo ra nhiều container khác nhau nếu muốn.
Lời kết: Image và Container là các khái niệm "core"
Image và Container là hai mặt của một đồng xu trong hệ sinh thái Docker. Image cung cấp một bản thiết kế bất biến, đóng gói mọi thứ cần thiết cho ứng dụng, đảm bảo tính nhất quán trên mọi môi trường. Trong khi đó, Container là hiện thân sống động của bản thiết kế đó, cung cấp một môi trường thực thi nhẹ, cô lập và linh hoạt.
Hiểu rõ mối quan hệ "khuôn mẫu và thực thể" này không chỉ là nền tảng để làm chủ Docker mà còn mở ra cánh cửa để xây dựng và triển khai các ứng dụng hiện đại một cách hiệu quả, nhanh chóng và đáng tin cậy hơn bao giờ hết.