Sổ tay Docker: Giới thiệu khái niệm cơ bản của Docker

Ở bài viết trước, chúng tôi đã hướng dẫn cách cài đặt Docker. Nếu bạn chưa cài đặt Docker thì tốt nhất bạn nên cài đặt Docker theo hướng dẫn dưới đây trước khi đọc bài viết này.

Sổ tay Docker: Hướng dẫn cài đặt Docker
Hướng dẫn này sẽ hướng dẫn bạn chi tiết từng bước để cài đặt thành công Docker trên macOS, Windows và Linux.

Bây giờ bạn đã cài đặt và chạy Docker, đã đến lúc bạn chạy container đầu tiên của mình. Mở terminal và chạy lệnh sau:

docker run hello-world

Kết quả trông sẽ như thế này:

# Unable to find image 'hello-world:latest' locally
# latest: Pulling from library/hello-world
# 0e03bdcc26d7: Pull complete 
# Digest: sha256:4cf9c47f86df71d48364001ede3a4fcd85ae80ce02ebad74156906caff5378bc
# Status: Downloaded newer image for hello-world:latest
# 
# Hello from Docker!
# This message shows that your installation appears to be working correctly.
# 
# To generate this message, Docker took the following steps:
#  1. The Docker client contacted the Docker daemon.
#  2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
#     (amd64)
#  3. The Docker daemon created a new container from that image which runs the
#     executable that produces the output you are currently reading.
#  4. The Docker daemon streamed that output to the Docker client, which sent it
#     to your terminal.
#
# To try something more ambitious, you can run an Ubuntu container with:
#  $ docker run -it ubuntu bash
# 
# Share images, automate workflows, and more with a free Docker ID:
#  https://hub.docker.com/
#
# For more examples and ideas, visit:
#  https://docs.docker.com/get-started/

Image hello-world là một ví dụ về container hóa tối thiểu với Docker. Nó có một chương trình duy nhất được biên dịch từ tệp hello.c chịu trách nhiệm in ra thông báo bạn đang thấy trên terminal của mình.

Bây giờ trong terminal của bạn, bạn có thể sử dụng lệnh docker ps -a để xem tất cả các container hiện đang chạy hoặc đã chạy trong quá khứ:

docker ps -a

Kết quả trông sẽ như thế này:

# CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
# 128ec8ceab71        hello-world         "/hello"            14 seconds ago      Exited (0) 13 seconds ago                      exciting_chebyshev

Trong kết quả đầu ra, một container có tên exciting_chebyshev đã được chạy với container id là 128ec8ceab71 bằng cách sử dụng image hello-world. Nó có Exited (0) 13 seconds ago ở cột STATUS, exit code (0) có nghĩa là không có lỗi trong thời gian chạy của container.

Bây giờ để hiểu những gì vừa xảy ra ở đằng sau, bạn sẽ phải làm quen với kiến trúc Docker và ba khái niệm rất cơ bản về container hóa nói chung, như sau:

  • Container
  • Image
  • Registry

Tôi đã liệt kê ba khái niệm quan trọng trong Docker và sẽ bắt đầu giải thích bằng khái niệm đầu tiên trong danh sách.

Docker Container là gì?

Trong thế giới container hóa, không thể có điều gì cơ bản hơn khái niệm container.

Trang web tài nguyên chính thức của Docker cho biết:

Container là phần trừu tượng ở lớp ứng dụng đóng gói mã và các phần phụ thuộc lại với nhau. Thay vì ảo hóa toàn bộ máy vật lý, các container chỉ ảo hóa hệ điều hành máy chủ.

Bạn có thể coi container là thế hệ tiếp theo của máy ảo.

Cũng giống như máy ảo, container là môi trường cách ly hoàn toàn với hệ thống máy chủ cũng như cách ly với nhau. Chúng cũng nhẹ hơn rất nhiều so với máy ảo truyền thống, do đó, một số lượng lớn các container có thể được chạy đồng thời mà không ảnh hưởng đến hiệu suất của hệ thống máy chủ .‌

Container và máy ảo thực sự là những cách khác nhau để ảo hóa phần cứng vật lý của bạn. Sự khác biệt chính giữa hai phương pháp này là phương pháp ảo hóa.

Máy ảo thường được tạo và quản lý bởi một chương trình được gọi là hypervisor (siêu giám sát), như Oracle VM VirtualBox, VMware Workstation, KVM, Microsoft Hyper-V, v.v. Chương trình hypervisor này thường nằm giữa hệ điều hành chủ (Host OS) và các máy ảo để hoạt động như một phương tiện giao tiếp.

Sơ đồ kiến trúc ảo hóa sử dụng Máy ảo

Mỗi máy ảo đi kèm với hệ điều hành khách (Guest OS) của riêng nó, nặng tương đương với hệ điều hành chủ.

Ứng dụng chạy bên trong máy ảo giao tiếp với hệ điều hành khách, hệ điều hành này sẽ nói chuyện với hypervisor, sau đó sẽ nói chuyện với hệ điều hành chủ để phân bổ tài nguyên cần thiết từ cơ sở hạ tầng vật lý cho ứng dụng đang chạy.

Như bạn có thể thấy, có một chuỗi liên lạc dài giữa các ứng dụng chạy bên trong máy ảo và cơ sở hạ tầng vật lý. Ứng dụng chạy bên trong máy ảo có thể chỉ chiếm một lượng nhỏ tài nguyên, nhưng hệ điều hành khách sẽ bổ sung thêm chi phí đáng chú ý.

Không giống như một máy ảo, một container thực hiện công việc ảo hóa theo cách thông minh hơn. Thay vì có một hệ điều hành khách hoàn chỉnh bên trong một container, nó chỉ sử dụng hệ điều hành máy chủ thông qua container runtime trong khi duy trì sự cô lập - giống như một máy ảo truyền thống.

Sơ đồ kiến trúc ảo hóa sử dụng Docker

Container runtime được trình bày ở phần trên chính là Docker, nằm giữa các container và hệ điều hành máy chủ thay vì một hypervisor. Sau đó, các container giao tiếp với container runtime, container runtime giao tiếp với hệ điều hành máy chủ để nhận các tài nguyên cần thiết từ cơ sở hạ tầng vật lý.

Kết quả của việc loại bỏ toàn bộ lớp hệ điều hành khách, các container nhẹ hơn và ít tốn tài nguyên hơn nhiều so với các máy ảo truyền thống.

Để minh chứng cho quan điểm, hãy xem khối mã sau:

uname -a
# Linux alpha-centauri 5.8.0-22-generic #23-Ubuntu SMP Fri Oct 9 00:34:40 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

docker run alpine uname -a
# Linux f08dbbe9199b 5.8.0-22-generic #23-Ubuntu SMP Fri Oct 9 00:34:40 UTC 2020 x86_64 Linux

Trong khối mã ở trên, tôi đã thực thi lệnh uname -a trên hệ điều hành chủ của mình để in ra thông tin chi tiết kernel. Sau đó, trên dòng tiếp theo, tôi đã thực hiện lệnh tương tự bên trong một container chạy Alpine Linux.

Như bạn có thể thấy trong đầu ra, container thực sự đang sử dụng kernel từ hệ điều hành máy chủ của tôi. Điều này chứng minh quan điểm rằng các container ảo hóa hệ điều hành chủ thay vì có một hệ điều hành của riêng chúng.

Nếu bạn đang sử dụng máy Windows, bạn sẽ thấy rằng tất cả các container đều sử dụng nhân WSL2. Nó xảy ra vì WSL2 đóng vai trò là back-end cho Docker trên Windows. Trên macOS, back-end mặc định là một máy ảo chạy trên siêu giám sát HyperKit.

Docker Image là gì?

Image là các tệp chứa nhiều lớp đóng vai trò là template để tạo container. Chúng giống như một bản sao cố định, chỉ đọc của một container. Image có thể được trao đổi thông qua Registry.

Trong quá khứ, các công cụ container khác nhau có các định dạng image khác nhau. Nhưng sau đó, Open Container Initiative (OCI) đã định nghĩa một tiêu chuẩn kỹ thuật cho container image được tuân thủ bởi các công cụ container hóa lớn. Điều này có nghĩa là một image được xây dựng bằng Docker có thể được sử dụng với một container runtime khác như Podman mà không gặp bất kỳ rắc rối nào.

Container chỉ là image ở trạng thái đang chạy. Khi bạn lấy một image từ internet và chạy một container bằng image đó, về cơ bản bạn tạo một lớp có thể ghi tạm thời khác trên lớp chỉ đọc trước đó.

Khái niệm này sẽ trở nên rõ ràng hơn rất nhiều trong các phần sắp tới của loạt bài sổ tay Docker này. Nhưng hiện tại, chỉ cần lưu ý rằng image là các tệp chỉ đọc nhiều lớp chứa ứng dụng của bạn ở trạng thái mong muốn bên trong chúng.

Docker Registry là gì?

Bạn đã tìm hiểu về hai phần rất quan trọng là ContainerImage. Phần này sẽ trình bày về Registry.

Sổ đăng ký hình ảnh (image registry) là nơi tập trung image, nơi bạn có thể tải lên image của mình và cũng có thể tải xuống image do người khác tạo. Docker Hub là cơ quan đăng ký công khai mặc định cho Docker. Một sổ đăng ký hình ảnh rất phổ biến khác là Quay của Red Hat.

Trong loạt bài này, tôi sẽ sử dụng Docker Hub làm sổ đăng ký.

Docker Registry là gì?

Bạn có thể chia sẻ miễn phí bất kỳ image công khai nào trên Docker Hub. Mọi người trên khắp thế giới sẽ có thể tải xuống và sử dụng chúng một cách tự do.

Ngoài Docker Hub hoặc Quay, bạn cũng có thể tạo sổ đăng ký hình ảnh của riêng mình để lưu trữ image riêng tư. Ngoài ra còn có một sổ đăng ký cục bộ chạy trong máy tính của bạn để lưu trữ các image được lấy từ các cơ quan đăng ký từ xa.

Tổng quan về kiến ​​trúc Docker

Bây giờ bạn đã làm quen với hầu hết các khái niệm cơ bản liên quan đến container và Docker, đã đến lúc bạn hiểu Docker như một phần mềm được thiết kế như thế nào.

Docker engine bao gồm ba thành phần chính:

  1. Docker Daemon: Daemon (dockerd) là một tiến trình chạy liên tục ở chế độ nền và đợi lệnh từ client. Daemon có khả năng quản lý các đối tượng Docker khác nhau.
  2. Docker Client: Client (docker) là một chương trình giao diện dòng lệnh chủ yếu chịu trách nhiệm vận chuyển các lệnh do người dùng đưa ra.
  3. REST API: REST API hoạt động như một cầu nối giữa daemon và client. Bất kỳ lệnh nào được đưa ra bởi client đều đi qua API để cuối cùng đến được daemon.

Theo tài liệu chính thức:

"Docker sử dụng kiến ​​trúc client-server. Ứng dụng client của Docker sẽ nói chuyện với Docker daemon, trình nền này thực hiện công việc xây dựng, chạy và phân phối Docker container của bạn".

Bạn là người dùng thường sẽ thực hiện các lệnh bằng cách sử dụng thành phần client. Sau đó, client sử dụng API REST để tiếp cận với daemon đang hoạt động liên tục và hoàn thành công việc của bạn.

Bức tranh toàn cảnh

Được rồi, nói đủ rồi. Bây giờ là lúc để bạn hiểu tất cả những mảnh ghép mà bạn vừa học được về công việc hòa hợp với nhau như thế nào. Trước khi tôi đi sâu vào giải thích về những gì thực sự xảy ra khi bạn chạy lệnh docker run hello-world, hãy để tôi chỉ cho bạn một sơ đồ nhỏ mà tôi đã thực hiện:

Sơ đồ này là một phiên bản sửa đổi một chút của sơ đồ được tìm thấy trong tài liệu chính thức. Các sự kiện xảy ra khi bạn thực hiện lệnh như sau:

  1. Bạn thực hiện lệnh docker run hello-world trong đó hello-world là tên của một image.
  2. Ứng dụng Docker Client tiếp cận với Docker Daemon, yêu cầu nó lấy image hello-world và chạy một container từ đó.
  3. Docker Daemon tìm kiếm image trong kho lưu trữ cục bộ của bạn và nhận ra rằng nó không có ở đó, kết quả là Unable to find image 'hello-world:latest' locally được in trong terminal của bạn.
  4. Sau đó, Docker Daemon tiếp cận Registry công khai mặc định là Docker Hub và lấy bản sao mới nhất của image hello-world, được biểu thị bằng dòng latest: Pulling from library/hello-world trong terminal của bạn.
  5. Docker Daemon sau đó tạo một container mới từ image mới được tải xuống.
  6. Cuối cùng, Docker Daemon chạy container được tạo bằng cách sử dụng image hello-world và in thông điệp trong terminal của bạn.

Đây là hành vi mặc định của Docker Daemon để tìm kiếm các image không có ở máy cục bộ trong Docker Hub. Nhưng khi một image đã được tìm nạp, nó sẽ ở trong bộ nhớ cache cục bộ. Vì vậy, nếu bạn thực hiện lại lệnh, bạn sẽ không thấy các dòng sau trong đầu ra:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
Status: Downloaded newer image for hello-world:latest

Nếu có phiên bản mới hơn của image trên sổ đăng ký công khai, Daemon sẽ tìm nạp lại image đó. Đó là do thẻ :latest. Image thường có các thẻ có ý nghĩa để chỉ ra các phiên bản hoặc bản dựng. Bạn sẽ tìm hiểu về điều này một cách chi tiết hơn sau này.

Ở phần tiếp theo bạn sẽ tìm hiểu về các thao tác Docker Container cơ bản:

Sổ tay Docker: Thao tác Docker Container cơ bản
Trong phần này, bạn sẽ tìm hiểu về các thao tác container như: chạy một container, publish port cho container, chế độ detached, liệt kê các container, ...

Bài viết này được dịch từ cuốn sách The Docker Handbook của Farhan Hasin Chowdhury:

The Docker Handbook – 2021 Edition
The concept of containerization itself is pretty old. But the emergence of the Docker Engine [https://docs.docker.com/get-started/overview/#docker-engine] in2013 has made it much easier to containerize your applications. According to the Stack Overflow Developer Survey - 2020[https://insights.stackoverflow.com/survey/2020#overview…
DockerDevOps
Bài Viết Liên Quan:
Deploy ứng dụng ASP.NET Core bằng Docker
Trung Nguyen 29/03/2021
Deploy ứng dụng ASP.NET Core bằng Docker

Bài viết này sẽ hướng dẫn bạn chi tiết cách deploy ứng dụng ASP.NET Core bằng Docker.

Sổ tay Docker: Cách sử dụng Docker Compose
Trung Nguyen 20/03/2021
Sổ tay Docker: Cách sử dụng Docker Compose

Trong hướng dẫn này, bạn sẽ tìm hiểu các kiến thức cơ bản về Docker Compose, cách chạy và quản lý các dịch vụ sử dụng Docker Compose.

Sổ tay Docker: Cách chạy ứng dụng JavaScript trên nhiều Container
Trung Nguyen 20/03/2021
Sổ tay Docker: Cách chạy ứng dụng JavaScript trên nhiều Container

Trong hướng dẫn này, bạn sẽ học cách triển khai ứng dụng trên nhiều container trong Docker.

Sổ tay Docker: Thao tác Docker Network cơ bản
Trung Nguyen 19/03/2021
Sổ tay Docker: Thao tác Docker Network cơ bản

Bạn sẽ tìm hiểu các kiến thức cơ bản về Docker Network, các thao tác mạng cơ bản như: tạo và xóa mạng, gắn container vào mạng, gỡ container khỏi mạng.