Tình cờ đọc được một bài viết về Docker của tác giả Demiro Massessi trên Medium, thấy hay quá nên dịch lại cho nhớ.
Link bài viết gốc: https://medium.com/coinmonks/docker-in-a-nutshell-6b3985a58d68
Trong bài viết, tác giả trả lời 2 câu hỏi:
- Docker là gì
- Tại sao lại dùng Docker ?
1. Docker là gì ?
Trước tiên, hãy xem hình ảnh sau:
Dám cá 10 tỷ Trump là cứ 10 người trong số chúng ta thì phải đến 11 người đã từng làm những bước này khi cài phần mềm:
- Download bộ cài (installer)
- Chạy bộ cài
- Chạy ngon đến hết bước cuối cùng thì tốt quá ! Cơ mà nhiều khi lại dính lỗi
- Tìm lỗi trên mạng, xem bọn Tây sửa dư lào
- Fix lỗi
- Chạy lại installer
- Yay ! Fix được lỗi rùi ...
- ... Ơ lại lỗi :(
- Lại google tiếp ...
- ...
- Thôi không cài nữa :v
Và ngay cả khi bạn cài được phần mềm chạy trên máy bạn, thì cũng không có gì đảm bảo là bạn có thể cài được phần mềm đó chạy trên máy khác. "Ơ rõ ràng nó chạy trên máy em mà !!!".
Docker sẽ giúp bạn giải quyết vấn đề này.
Với Docker, bạn có thể say goodbye với đống quy trình hầm bà lằng kia được rồi
Ví dụ nhé, giả sử máy bạn đã cài Docker rồi, và giờ bạn muốn cài đặt PostgreSQL trên máy bạn, tất cả những gì bạn cần làm chỉ là chạy dòng lệnh sau trên terminal:
docker run --name demo_postgres -p 5432:5432 postgres:11-alpine
và đây là kết quả:
Khỏi cần installer, cũng khỏi cần ấn Next liên tọi và hi vọng không có lỗi xảy ra. Chỉ cần 1 câu lệnh Docker là chúng ta đã có PostgrSQL chạy ngon lành
OK, có vẻ cool đấy ! Nhưng rốt cuộc Docker là gì ?
Mỗi khi bạn nghe ai đó nói rằng "Dự án của tôi có sử dụng Docker", điều mà họ thực sự nói tới là dự án của họ đang dùng Docker Client hoặc Docker Server, hoặc cũng có thể là Docker Hub hay Docker Compose
Tất cả các tool và phần mềm trong ảnh trên góp phần tạo nên hệ sinh thái Docker. Hệ sinh thái này xoay quanh một "diễn viên" chính: Docker Container
Vậy thì Docker Container là gì ?
Trước khi trả lời câu hỏi này, hãy cùng nhau xem lại câu lệnh Docker mà chúng ta đã chạy ở ví dụ trước:
docker run --name demo_postgres -p 5432:5432 postgres:11-alpine
Khi chúng ta gõ đoạn lệnh trên vào terminal và ấn Enter:
- Terminal gửi đoạn lệnh vào Docker CLI
- Docker CLI sau khi nhận đoạn lệnh trên sẽ gửi nó đến Docker Server (hay còn gọi là Docker Daemon). Chúng ta không bao giờ tương tác trực tiếp với Docker Daemon; mọi thao tác đều sẽ phải thông qua Docker CLI
- Docker Daemon, sau khi nhận yêu cầu từ Docker CLI, biết rằng chúng ta đang muốn khởi tạo một Docker Container từ Docker Image của PostgreSQL. Việc đầu tiên Docker Daemon làm đó là kiểm tra xem trong Image Cache đã tồn tại file Docker Image của PostgreSQL chưa. Trong trường hợp này, vì đây là lần đầu tiên chúng ta chạy câu lệnh Docker do đó trong Image Cache sẽ không có gì cả, khi đó Docker Daemon sẽ gửi lên Docker Hub một request yêu cầu download một Docker Image của PostgreSQL. Docker Image là một file chứa tất cả các dependencies và cấu hình cần thiết để chạy một program. Còn Docker Hub là một nơi lưu trữ tất cả các Docker Image mà bạn có thể download miễn phí
- Sau khi Docker Image của PostgreSQL được lưu vào ổ cứng, Docker Daemon sẽ sử dụng Docker Image này để tạo ra một Docker Container chạy PostgreSQL. Docker Container này bản chất là một program được máy tính cấp cho một vùng nhớ riêng, một vùng ổ cứng riêng (nguyên văn là "its own little space of memory, its own little space of networking technology and its own little space of hard drive")
Ở những lần chạy sau, do trong Image Cache đã có sẵn Docker Image của PostgreSQL, chúng ta không cần phải gửi download request lên Docker Hub nữa, khi đó quá trình chạy PostgreSQL sẽ diễn ra nhanh hơn nhiều.
OK, sau khi chạy thử ví dụ trên, chúng ta có thể đúc kết 2 điều:
- Docker Image là một file chứa tất cả các dependencies và cấu hình cần thiết để chạy một program ...
- ... Và mỗi program được khởi tạo từ Docker Image được gọi là Docker Container.
Nếu như định nghĩa về Docker Container chỉ dừng lại ở mức "là một program được tạo ra từ Docker Image" thì tôi có cảm giác hơi hụt hẫng. Rất may là Demiro Massessi đã không dừng lại ở đây, tác giả còn đi sâu xuống mức độ operating system (hệ điều hành) để giải thích về Docker Container
Trước tiên, hãy điểm qua một số khái niệm liên quan đến hệ điều hành
Hầu hết các hệ điều hành đều có 1 thành phần gọi là kernel. Kernel là một chương trình đóng vai trò như một cầu nối giữa các thiết bị phần cứng (CPU, memory, ổ cứng) và các chương trình khác trong hệ thống
Ví dụ như ở hình trên, giả sử chúng ta đang xài NodeJS để tạo một file lưu trên ổ cứng thì về bản chất, không phải NodeJS đang trực tiếp tương tác với ổ cứng, nó chỉ đang gửi yêu cầu đến kernel, chính kernel mới là đối tượng thực hiện việc tạo file trên ổ cứng
Một điểm nữa cần lưu ý, đó là các chương trình trong hệ thống tương tác với kernel thông qua system call. Kernel sẽ tạo ra các end points, mỗi end point tương ứng với một tác vụ khác nhau trên hardware. Mỗi chương trình khi muốn thực hiện một tác vụ trên hardware sẽ thực hiện một system call đến end point tương ứng trên kernel.
Namespacing
Giả sử có một tình huống thế này: Máy bạn đang chạy 2 programs đang chạy là Chrome và NodeJS. Chrome chỉ chạy được nếu có Python 2 còn NodeJS chỉ chạy được nếu có Python 3, và bài toán đặt ra ở đây là làm thế nào để có cả Python 2 và 3 cùng được cài đặt trên ổ cứng.
Chúng ta có thể sử dụng một tính năng của Linux kernel, đó là namespacing. Với namespacing, chúng ta có thể tạo 1 vùng trên ổ cứng dành riêng cho Python 2, và một vùng khác dành riêng cho Python 3. Và mỗi khi Chrome hoặc NodeJS thực hiện một system call đến kernel, kernel sẽ xác định xem program nào đang thực hiện request: Nếu đó là Chrome, kernel sẽ điều hướng request này đến vùng ổ cứng chạy Python 2; còn nếu đó là NodeJS, kernel sẽ điều hướng request này đến vùng ổ cứng chạy Python 3
Control groups
Một khái niệm rất gần gũi với Namespacing, đó là Control groups. Nếu như namespacing được sử dụng để phân chia ổ cứng thành từng vùng, mỗi vùng dành cho một process (hoặc một nhóm các process); thì control groups được dùng để giới hạn tài nguyên (memory, CPU, network bandwidth) mà một process có thể sử dụng. Namespacing và control groups, khi kết hợp với nhau, sẽ giúp chúng ta "cô lập" (isolate) một process, giới hạn tài nguyên mà process đó có thể sử dụng
Docker Container
Docker Container bản chất là một process (hoặc một nhóm các process) được kernel cấp phát cho một lượng resource nhất định để sử dụng. Mỗi khi Docker Container thực hiện 1 system call đến kernel, kernel sẽ điều hướng nó đến một phân vùng nhất định trên ổ cứng với các tài nguyên (RAM, CPU, network bandwidth) tương ứng.
Docker Image
Docker Image = File System snapshot + Startup Command
trong đó:
- File System snapshot: hiểu nôm na là một bản copy paste của một tập hợp các file và thư mục nhất định
- Startup Command: khởi tạo một Docker Container từ image
Khi chúng ta tạo một Docker Container từ một Docker Image thì:
- Kernel sẽ dành riêng (isolate) một vùng ổ cứng cho container này, cùng với đó là một lượng nhất định các resource (CPU, RAM, network bandwidth,...) mà container có thể sử dụng
- Sau đó, File System snapshot của Docker Image được đặt vào trong phân vùng ổ cứng vừa được tạo
- Docker Daemon chạy câu lệnh Startup Command để khới tạo Container. Một process được tạo ra trên phân vùng ổ cứng và với một lượng resource nhất định dành riêng cho process đó
Phewww !!! Như vậy là chúng ta đã đi đến phần cuối bài viết về Docker
tuy nhiên, có một điều nữa tôi muốn chia sẻ
Ở phần trước, chúng ta nói đến việc Docker sử dụng 2 chức năng quan trọng của kernel là Namespacing và Control groups để tạo nên các Docker Container. Nhưng, có một vấn đề là không phải hệ điều hành nào cũng có 2 chức năng này. Cụ thể hơn, 2 chức năng này chỉ có trên hệ điều hành Linux
Vậy thì, làm thế nào mà Docker vẫn có thể chạy được trên cả Windows và MacOS ?
Câu trả lời là: Khi chúng ta cài Docker trên Windows hay Mac OS, bản chất là chúng ta đã cài luôn một máy ảo chạy Linux. Bên trong máy ảo Linux này có một Linux kernel với đầy đủ các chức năng Namespacing và Control groups để từ đó chạy được các Docker Container
Bình luận