0

Docker & Docker Compose - Phần 1 - Vì sao Docker ra đời, và nó hoạt động như thế nào bên trong?

Đây là phần đầu tiên trong series 3 phần về Docker. Series này không phải là hướng dẫn "gõ lệnh gì", mà tập trung vào hiểu bản chất: vì sao Docker được thiết kế như vậy, và cách các khái niệm cốt lõi liên hệ với nhau. Nếu nắm được phần lý thuyết này, việc học các câu lệnh CLI sau đó sẽ rất nhanh.

Phần 1 sẽ đi qua: vấn đề mà Docker giải quyết, sự khác biệt giữa Virtual Machine và Docker, kiến trúc của Docker, và hai khái niệm quan trọng nhất — Image và Container.

Vấn đề: "It works on my machine"

Chắc hẳn ai cũng từng nghe (hoặc từng nói) câu này ít nhất một lần:

  • Developer A code trên máy chạy Node 20
  • Developer B code trên máy chạy Node 18
  • Server production lại đang chạy Node 22

Cùng một đoạn code, nhưng chạy trên 3 phiên bản runtime khác nhau. Ngoài runtime, còn hàng loạt yếu tố khác có thể lệch nhau: dependency thiếu, hệ điều hành khác, biến môi trường khác... Hậu quả là bug kỳ lạ chỉ xuất hiện ở một số môi trường, deploy phức tạp, người mới onboard mất nhiều thời gian setup. Không ai trong 3 người trên làm sai gì cả. Vấn đề nằm ở chỗ môi trường của họ chưa bao giờ được đảm bảo là giống nhau.

Giải pháp: đóng gói mọi thứ vào một image di động

Docker đóng gói tất cả những gì ứng dụng cần — application, runtime, dependencies, thư viện, cấu hình — vào một image duy nhất, có tính di động (portable). Ứng dụng nhờ đó chạy giống hệt nhau ở local, testing, và production: cả 3 môi trường giờ chạy chung một image, thay vì 3 phiên bản Node khác nhau.

Virtual Machine vs Docker: khác nhau ở đâu

Đây là nội dung quan trọng nhất để hiểu đúng bản chất Docker, vì nhiều người mới hay nhầm container là "VM nhẹ hơn" — thực ra là hai mô hình cách ly khác nhau khá cơ bản.

Mỗi VM mang theo một hệ điều hành đầy đủ (Guest OS) riêng, kể cả kernel — Hypervisor ảo hóa phần cứng để mỗi Guest OS tưởng như chạy trên máy vật lý riêng. Vì cõng theo cả một OS, VM tốn nhiều RAM/ổ đĩa và khởi động chậm, đổi lại cách ly rất mạnh. Container thì khác cơ bản ở chỗ: dùng chung kernel của Host OS, không có Guest OS riêng. Docker Engine chỉ tạo môi trường cách ly (namespace, cgroups) cho tiến trình, không ảo hóa cả một OS. Bỏ được lớp Guest OS là lý do container khởi động trong mili-giây thay vì hàng chục giây tới vài phút, và tốn ít tài nguyên hơn hẳn. Một điểm về bảo mật: vì các container dùng chung kernel, một lỗ hổng ở kernel host có thể ảnh hưởng mọi container trên đó — đây là lý do câu chuyện bảo mật của container khác với VM (VM cách ly kernel-level, container thì không).

Kiến trúc của Docker: ai nói chuyện với ai

Docker hoạt động theo mô hình client-server:

  • Docker Client — CLI bạn gõ lệnh vào (docker run, docker build...)
  • Docker Daemon (dockerd) — tiến trình chạy ngầm, thực sự làm việc: build image, chạy container, quản lý network/volume
  • Images — pull về từ Registry (mặc định Docker Hub; nhiều công ty dùng thêm registry riêng)
  • Containers — instance đang chạy, tạo ra từ image Cách hình dung dễ nhớ: Docker Client giống remote control, Docker Daemon là chiếc TV thực sự xử lý mọi thứ. Khi gõ lệnh Docker, bạn không trực tiếp "làm" gì — bạn gửi yêu cầu tới daemon, và daemon mới là nơi thực thi.

Image vs Container: phân biệt quan trọng nhất

Nếu chỉ nhớ một khái niệm từ bài này, hãy nhớ cặp sau — vì gần như mọi thứ khác của Docker đều xây trên nền tảng này.

Image: giống một bản thiết kế (blueprint), read-only, immutable — không đổi sau khi build. Từ một image, tạo ra nhiều container khác nhau. Container: instance đang chạy của image (tạo bằng docker run), có thêm lớp writable riêng, có thể start/stop/restart/xóa. Ví von dễ nhớ: image giống một class, container giống instance của class đó. Image không bao giờ đổi; mỗi container có state runtime riêng, không ảnh hưởng image gốc lẫn container khác — xóa Container A không hề ảnh hưởng Container B.

Vòng đời của một Container

Image --(docker run)--> Container --> Running --> Stopped --> Removed Điểm nhiều người — kể cả người đã dùng Docker một thời gian — vẫn hay nhầm:

Xóa container KHÔNG xóa image mà nó được tạo ra.
Image vẫn được cache lại, và bạn tạo container mới từ image đó bất cứ lúc nào. Đây cũng là lý do dọn dẹp ổ đĩa (`docker system prune`) là chủ đề tách biệt — container "mồ côi" và image "mồ côi" là hai vấn đề khác nhau. Hai lệnh phân biệt: `docker ps -a` liệt kê container (kể cả đã stop), `docker images` liệt kê image.

Ở Phần 2, mình sẽ đi sâu vào cách Image được xây từ nhiều layer, vì sao thứ tự dòng lệnh trong Dockerfile ảnh hưởng trực tiếp tới tốc độ build, và các instruction cốt lõi (kèm ví dụ thực tế). Ngoài ra sẽ nói về Volumes và Networking — hai chủ đề người mới dùng Docker rất hay gặp lỗi.


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí