Bài 39.2: Docker architecture và components

Trong thế giới phát triển web hiện đại, Docker đã trở thành một công cụ không thể thiếu. Nó giúp chúng ta đóng gói ứng dụng và môi trường chạy của chúng vào các đơn vị nhỏ gọn, dễ dàng di chuyển và nhất quán - gọi là containers. Nhưng đằng sau sự tiện lợi đó là một kiến trúc mạnh mẽ với các thành phần hoạt động nhịp nhàng cùng nhau. Hiểu rõ kiến trúc này sẽ giúp bạn sử dụng Docker hiệu quả hơn, đặc biệt là khi triển khai các ứng dụng web phức tạp.

Hãy cùng đi sâu vào kiến trúc và các thành phần chính của Docker!

Kiến trúc Client-Server của Docker

Về cơ bản, Docker hoạt động dựa trên kiến trúc Client-Server. Đây là cách các thành phần tương tác với nhau:

  1. Docker Daemon (dockerd): Đây là "trái tim" của Docker, chạy ngầm trên hệ điều hành của bạn (hoặc trên một máy chủ từ xa). Docker Daemon lắng nghe các yêu cầu từ Docker Client và quản lý các đối tượng Docker như images, containers, volumes, và networks. Daemon chịu trách nhiệm xây dựng (build), chạy (run), và phân phối (distribute) các Docker container của bạn.
  2. Docker Client: Đây là cách người dùng tương tác với Docker. Client (thường là giao diện dòng lệnh CLI - Command Line Interface) gửi các lệnh đến Docker Daemon thông qua REST API. Khi bạn gõ docker run hay docker build trong terminal, bạn đang sử dụng Docker Client để nói chuyện với Daemon. Client có thể chạy trên cùng một máy với Daemon hoặc trên một máy khác.

Mối quan hệ này cho phép sự linh hoạt: bạn có thể điều khiển một Docker Daemon đang chạy trên máy chủ cloud từ máy tính xách tay của mình thông qua Docker Client.

Các Thành phần Cốt lõi của Docker

Bây giờ, hãy tìm hiểu sâu hơn về các đối tượng chính mà Docker Daemon quản lý:

1. Docker Images
  • Docker Images là các template (khuôn mẫu) chỉ đọc (read-only). Chúng chứa tất cả mọi thứ cần thiết để chạy một ứng dụng cụ thể: mã nguồn, thư viện, công cụ hệ thống, môi trường chạy, cấu hình, v.v. Tưởng tượng một image như một "bản chụp" (snapshot) của một hệ thống file và cấu hình sẵn sàng để chạy.
  • Images được xây dựng từ một tệp tin đặc biệt gọi là Dockerfile. Mỗi lệnh trong Dockerfile tạo ra một lớp (layer) chỉ đọc mới trong image. Khi chạy một container, các lớp này được xếp chồng lên nhau.
  • Tại sao lại quan trọng cho web dev? Bạn có thể tạo ra các image chứa sẵn môi trường chạy cho ứng dụng web của mình, ví dụ: một image có Node.js và tất cả dependencies cho ứng dụng React/NextJs backend, hoặc một image có Nginx để phục vụ các file tĩnh của frontend.

  • Ví dụ đơn giản về cách tạo image: Giả sử bạn có một Dockerfile (chúng ta sẽ nói về nó kỹ hơn sau). Để xây dựng một image từ Dockerfile đó, bạn dùng lệnh:

    docker build -t ten_image_cua_ban:latest .
    
    • Giải thích:
      • docker build: Lệnh nói với Docker Client rằng bạn muốn xây dựng một image.
      • -t ten_image_cua_ban:latest: Gắn thẻ (tag) cho image vừa tạo. ten_image_cua_ban là tên bạn đặt, :latest là phiên bản (tag).
      • .: Chỉ ra đường dẫn đến thư mục chứa Dockerfile (trong trường hợp này là thư mục hiện tại). Docker Daemon sẽ đọc Dockerfile ở đây để thực hiện các bước xây dựng image.
2. Docker Containers
  • Docker Containers là các thể hiện đang chạy (running instances) của một Docker Image. Khi bạn chạy một image, Docker tạo ra một container từ image đó. Khác với image chỉ đọc, container có một lớp ghi được (writable layer) ở trên cùng. Mọi thay đổi bạn thực hiện khi container đang chạy (ghi file mới, thay đổi cấu hình...) đều được lưu vào lớp này.
  • Mỗi container là một môi trường cô lập (isolated). Nó có hệ thống file riêng, mạng riêng, quy trình riêng, tách biệt hoàn toàn với hệ điều hành gốc và các container khác (trừ khi bạn cấu hình cho chúng giao tiếp).
  • Tại sao lại quan trọng cho web dev? Bạn có thể chạy nhiều instance của ứng dụng web (ví dụ: nhiều server Node.js), hoặc chạy database (MySQL, PostgreSQL) trong một container riêng biệt, thậm chí chạy một container Nginx/Apache để làm reverse proxy, tất cả trên cùng một máy nhưng hoàn toàn cô lập với nhau. Điều này giúp tránh xung đột môi trường và dễ dàng quản lý.

  • Ví dụ đơn giản về cách chạy container: Để chạy một container từ image bạn vừa build (hoặc từ một image có sẵn trên Docker Hub):

    docker run -p 80:80 --name ten_container_cua_ban ten_image_cua_ban:latest
    
    • Giải thích:
      • docker run: Lệnh nói với Docker Client rằng bạn muốn chạy một container.
      • -p 80:80: Ánh xạ cổng (port mapping). Cổng 80 trên máy chủ (host) sẽ được ánh xạ tới cổng 80 bên trong container. Điều này cho phép bạn truy cập ứng dụng web đang chạy trong container từ trình duyệt của máy chủ.
      • --name ten_container_cua_ban: Đặt tên cho container để dễ quản lý hơn sau này.
      • ten_image_cua_ban:latest: Chỉ định image mà container sẽ chạy từ đó.
3. Dockerfile
  • Dockerfile là một tệp tin văn bản chứa các lệnh (instructions) mà Docker Daemon sử dụng để xây dựng một Docker Image. Mỗi lệnh trong Dockerfile (như FROM, RUN, COPY, CMD, EXPOSE, v.v.) thực hiện một hành động cụ thể và tạo ra một lớp mới trong image.
  • Dockerfile mô tả từng bước để thiết lập môi trường và cài đặt ứng dụng của bạn một cách tự động. Điều này giúp đảm bảo quá trình xây dựng image luôn nhất quán và có thể lặp lại.
  • Tại sao lại quan trọng cho web dev? Dockerfile chính là "công thức" để đóng gói ứng dụng web của bạn. Bạn viết Dockerfile một lần và có thể sử dụng nó để tạo ra image chạy được ở bất cứ đâu có Docker, đảm bảo môi trường phát triển, staging, và production là giống nhau.

  • Ví dụ đơn giản về Dockerfile cho ứng dụng Node.js:

    # Sử dụng image Node.js chính thức làm nền tảng
    FROM node:18
    
    # Tạo thư mục làm việc bên trong container
    WORKDIR /app
    
    # Sao chép file package*.json để cài đặt dependencies
    COPY package*.json ./
    
    # Cài đặt dependencies
    RUN npm install
    
    # Sao chép toàn bộ mã nguồn ứng dụng
    COPY . .
    
    # Mở cổng mà ứng dụng Node.js lắng nghe
    EXPOSE 3000
    
    # Lệnh mặc định để chạy ứng dụng khi container khởi động
    CMD ["npm", "start"]
    
    • Giải thích:
      • FROM node:18: Bắt đầu từ một image Node.js version 18 đã có sẵn (tải từ Docker Hub).
      • WORKDIR /app: Đặt thư mục /app làm thư mục làm việc mặc định cho các lệnh tiếp theo.
      • COPY package*.json ./: Sao chép các file package.jsonpackage-lock.json (hoặc yarn.lock) từ máy chủ vào thư mục /app trong image.
      • RUN npm install: Chạy lệnh npm install để cài đặt các thư viện cần thiết (được định nghĩa trong package.json). Bước này tạo ra một lớp riêng, và Docker sẽ cache nó nếu package*.json không thay đổi, giúp quá trình build nhanh hơn.
      • COPY . .: Sao chép toàn bộ các file còn lại trong thư mục hiện tại của máy chủ vào thư mục /app trong image.
      • EXPOSE 3000: Thông báo rằng container sẽ lắng nghe trên cổng 3000 khi chạy (chỉ là tài liệu, cần kết hợp với -p khi docker run để ánh xạ ra ngoài).
      • CMD ["npm", "start"]: Thiết lập lệnh mặc định sẽ được chạy khi container khởi động.
4. Docker Registry (e.g., Docker Hub)
  • Docker Registry là nơi lưu trữ các Docker Images. Nó hoạt động như một kho chứa tập trung, cho phép bạn tải về (pull) các image có sẵn do người khác tạo ra và đẩy lên (push) các image của riêng bạn để chia sẻ hoặc lưu trữ.
  • Docker Hub là Registry công cộng lớn nhất và phổ biến nhất. Ngoài ra, các công ty có thể thiết lập Registry riêng (Private Registry) để lưu trữ các image nội bộ.
  • Tại sao lại quan trọng cho web dev? Bạn có thể dễ dàng sử dụng các image nền tảng chuẩn (như node, nginx, ubuntu, mysql) từ Docker Hub mà không cần tự xây dựng từ đầu. Bạn cũng có thể đẩy image ứng dụng web của mình lên Registry để nhóm làm việc cùng sử dụng hoặc để hệ thống CI/CD/triển khai tự động kéo về.

  • Ví dụ đơn giản về kéo và đẩy image: Để tải một image từ Docker Hub:

    docker pull nginx:latest
    

    Để đẩy image của bạn lên Docker Hub (sau khi đã đăng nhập và đặt tên image theo định dạng your_dockerhub_username/ten_image_cua_ban:tag):

    docker push your_dockerhub_username/ten_image_cua_ban:latest
    
    • Giải thích:
      • docker pull nginx:latest: Tải image Nginx phiên bản latest từ Docker Hub về máy cục bộ của bạn.
      • docker push ...: Đẩy image ten_image_cua_ban với tag latest từ máy cục bộ của bạn lên kho lưu trữ trên Docker Hub dưới tài khoản your_dockerhub_username.
Cách các thành phần hoạt động cùng nhau trong một quy trình điển hình

Một quy trình làm việc thông thường với Docker cho ứng dụng web sẽ diễn ra như sau:

  1. Bạn viết mã nguồn ứng dụng web của mình (ví dụ: code React, code Node.js backend).
  2. Bạn tạo một Dockerfile mô tả cách xây dựng môi trường cho ứng dụng đó (dựa trên image nền tảng từ Docker Registry, sao chép code, cài đặt dependencies, cấu hình...).
  3. Sử dụng Docker Client, bạn chạy lệnh docker build để gửi Dockerfile và các file cần thiết tới Docker Daemon.
  4. Docker Daemon đọc Dockerfile, thực hiện từng lệnh để tạo ra các lớp, và cuối cùng đóng gói chúng lại thành một Docker Image mới.
  5. Bạn có thể tùy chọn sử dụng Docker Client chạy lệnh docker push để đẩy image vừa tạo lên một Docker Registry (như Docker Hub) để lưu trữ hoặc chia sẻ.
  6. Để chạy ứng dụng, bạn dùng Docker Client chạy lệnh docker run. Nếu image chưa có trên máy cục bộ, Docker Daemon sẽ tự động kéo (pull) image đó từ Docker Registry về.
  7. Docker Daemon tạo ra một Docker Container từ image và chạy ứng dụng của bạn bên trong môi trường cô lập đó.

Hiểu được các thành phần này và cách chúng tương tác là nền tảng vững chắc để bạn có thể khai thác tối đa sức mạnh của Docker trong quá trình phát triển và triển khai các ứng dụng web. Docker không chỉ giúp đơn giản hóa môi trường mà còn mở ra cánh cửa cho các kiến trúc hiện đại hơn như microservices và CI/CD (Tích hợp Liên tục / Triển khai Liên tục).

Comments

There are no comments at the moment.