Docker là gì: một chuyến tham quan ngắn gọn về lịch sử và những khái niệm trừu tượng cơ bản

Bắt đầu vào ngày 10 tháng XNUMX tại Slurm Khóa học video về Docker, trong đó chúng tôi phân tích nó một cách đầy đủ - từ những khái niệm trừu tượng cơ bản đến các tham số mạng.

Trong bài viết này, chúng ta sẽ nói về lịch sử của Docker và các khái niệm trừu tượng chính của nó: Image, Cli, Dockerfile. Bài giảng dành cho người mới bắt đầu nên khó có thể thu hút được sự quan tâm của những người dùng có kinh nghiệm. Sẽ không có máu, ruột thừa hoặc ngâm sâu. Những điều rất cơ bản.

Docker là gì: một chuyến tham quan ngắn gọn về lịch sử và những khái niệm trừu tượng cơ bản

Docker là gì

Hãy xem định nghĩa của Docker từ Wikipedia.

Docker là phần mềm tự động hóa việc triển khai và quản lý ứng dụng trong môi trường được đóng gói.

Không có gì rõ ràng từ định nghĩa này. Đặc biệt là không rõ “trong môi trường hỗ trợ việc đóng gói bằng container” nghĩa là gì. Để tìm hiểu, chúng ta hãy quay ngược thời gian. Hãy bắt đầu với thời đại mà tôi thường gọi là “Kỷ nguyên đá nguyên khối”.

Thời đại nguyên khối

Kỷ nguyên nguyên khối là đầu những năm 2000, khi tất cả các ứng dụng đều nguyên khối, với rất nhiều phần phụ thuộc. Sự phát triển mất một thời gian dài. Đồng thời, không có nhiều máy chủ, tất cả chúng tôi đều biết tên và theo dõi chúng. Có một sự so sánh buồn cười như sau:

Thú cưng là vật nuôi trong nhà. Trong kỷ nguyên nguyên khối, chúng tôi đối xử với máy chủ của mình như thú cưng, được chải chuốt và nâng niu, thổi bay những hạt bụi. Và để quản lý tài nguyên tốt hơn, chúng tôi đã sử dụng ảo hóa: chúng tôi lấy một máy chủ và cắt nó thành nhiều máy ảo, nhờ đó đảm bảo sự tách biệt với môi trường.

Hệ thống ảo hóa dựa trên Hypervisor

Mọi người có lẽ đã nghe nói về các hệ thống ảo hóa: VMware, VirtualBox, Hyper-V, Qemu KVM, v.v. Chúng cung cấp khả năng cách ly ứng dụng và quản lý tài nguyên, nhưng chúng cũng có những nhược điểm. Để thực hiện ảo hóa, bạn cần một bộ ảo hóa. Và hypervisor là một chi phí tài nguyên. Và bản thân máy ảo thường là một khối khổng lồ - một hình ảnh nặng nề chứa hệ điều hành, Nginx, Apache và có thể cả MySQL. Hình ảnh lớn và máy ảo hoạt động bất tiện. Kết quả là, làm việc với máy ảo có thể bị chậm. Để giải quyết vấn đề này, các hệ thống ảo hóa đã được tạo ở cấp kernel.

Hệ thống ảo hóa cấp hạt nhân

Ảo hóa cấp hạt nhân được hỗ trợ bởi các hệ thống OpenVZ, Systemd-nspawn, LXC. Một ví dụ nổi bật về ảo hóa như vậy là LXC (Linux Container).

LXC là một hệ thống ảo hóa cấp hệ điều hành để chạy nhiều phiên bản riêng biệt của hệ điều hành Linux trên một nút. LXC không sử dụng máy ảo mà tạo ra một môi trường ảo với không gian xử lý và ngăn xếp mạng riêng.

Về cơ bản LXC tạo ra các thùng chứa. Sự khác biệt giữa máy ảo và container là gì?

Docker là gì: một chuyến tham quan ngắn gọn về lịch sử và những khái niệm trừu tượng cơ bản

Vùng chứa không phù hợp để cô lập các tiến trình: các lỗ hổng được tìm thấy trong các hệ thống ảo hóa ở cấp kernel cho phép chúng thoát khỏi vùng chứa đến máy chủ. Vì vậy, nếu cần cô lập một thứ gì đó, tốt hơn hết bạn nên sử dụng máy ảo.

Sự khác biệt giữa ảo hóa và container hóa có thể được nhìn thấy trong sơ đồ.
Có các trình ảo hóa phần cứng, các trình ảo hóa trên hệ điều hành và các bộ chứa.

Docker là gì: một chuyến tham quan ngắn gọn về lịch sử và những khái niệm trừu tượng cơ bản

Trình ảo hóa phần cứng rất tuyệt nếu bạn thực sự muốn tách biệt thứ gì đó. Bởi vì có thể cô lập ở cấp độ trang bộ nhớ và bộ xử lý.

Có những chương trình ảo hóa như một chương trình, có những thùng chứa và chúng ta sẽ nói thêm về chúng. Các hệ thống container hóa không có bộ ảo hóa nhưng có Công cụ vùng chứa để tạo và quản lý các vùng chứa. Thứ này nhẹ hơn, do đó do làm việc với lõi nên sẽ tốn ít chi phí hơn hoặc không có gì cả.

Những gì được sử dụng để chứa ở cấp kernel

Các công nghệ chính cho phép bạn tạo vùng chứa tách biệt với các quy trình khác là Không gian tên và Nhóm điều khiển.

Không gian tên: PID, Mạng, Mount và Người dùng. Còn nhiều nữa, nhưng để dễ hiểu chúng ta sẽ tập trung vào những điều này.

Các quy trình giới hạn không gian tên PID. Ví dụ: khi chúng ta tạo một Không gian tên PID và đặt một tiến trình ở đó, nó sẽ trở thành PID 1. Thông thường trong các hệ thống PID 1 là systemd hoặc init. Theo đó, khi chúng ta đặt một tiến trình vào một không gian tên mới, nó cũng nhận được PID 1.

Không gian tên mạng cho phép bạn giới hạn/cô lập mạng và đặt các giao diện của riêng bạn vào bên trong. Mount là một giới hạn của hệ thống tập tin. Người dùng—hạn chế đối với người dùng.

Nhóm điều khiển: Bộ nhớ, CPU, IOPS, Mạng - tổng cộng khoảng 12 cài đặt. Ngoài ra chúng còn được gọi là Cgroups (“C-groups”).

Nhóm kiểm soát quản lý tài nguyên cho một vùng chứa. Thông qua Nhóm kiểm soát, chúng tôi có thể nói rằng vùng chứa không nên tiêu thụ nhiều hơn một lượng tài nguyên nhất định.

Để quá trình đóng gói hoạt động hoàn chỉnh, các công nghệ bổ sung sẽ được sử dụng: Khả năng, Sao chép khi ghi và các công nghệ khác.

Khả năng là khi chúng ta nói với một quy trình những gì nó có thể và không thể làm. Ở cấp độ kernel, đây chỉ đơn giản là các ảnh bitmap có nhiều tham số. Ví dụ: người dùng root có đầy đủ đặc quyền và có thể làm mọi thứ. Máy chủ thời gian có thể thay đổi thời gian của hệ thống: nó có các khả năng trên Time Capsule, và chỉ vậy thôi. Bằng cách sử dụng các đặc quyền, bạn có thể cấu hình linh hoạt các hạn chế cho các quy trình và từ đó bảo vệ chính mình.

Hệ thống Copy-on-write cho phép chúng tôi làm việc với Docker image và sử dụng chúng hiệu quả hơn.

Docker hiện có vấn đề tương thích với Cgroups v2 nên bài viết này tập trung cụ thể vào Cgroups v1.

Nhưng chúng ta hãy quay trở lại lịch sử.

Khi các hệ thống ảo hóa xuất hiện ở cấp kernel, chúng bắt đầu được sử dụng tích cực. Chi phí trên hypervisor đã biến mất, nhưng vẫn còn một số vấn đề:

  • hình ảnh lớn: họ đẩy một hệ điều hành, thư viện, một loạt phần mềm khác nhau vào cùng một OpenVZ, và cuối cùng hình ảnh vẫn có kích thước khá lớn;
  • Không có tiêu chuẩn thông thường cho việc đóng gói và giao hàng, vì vậy vấn đề phụ thuộc vẫn còn. Có những trường hợp hai đoạn mã sử dụng cùng một thư viện nhưng có các phiên bản khác nhau. Có thể có xung đột giữa họ.

Để giải quyết tất cả những vấn đề này, thời đại tiếp theo đã đến.

kỷ nguyên container

Khi Kỷ nguyên của Container đến, triết lý làm việc với chúng đã thay đổi:

  • Một quy trình - một container.
  • Chúng tôi cung cấp tất cả các phần phụ thuộc mà quy trình cần vào vùng chứa của nó. Điều này đòi hỏi phải cắt các khối nguyên khối thành các dịch vụ vi mô.
  • Hình ảnh càng nhỏ thì càng tốt - có ít lỗ hổng hơn, nó xuất hiện nhanh hơn, v.v.
  • Các trường hợp trở nên phù du.

Hãy nhớ những gì tôi đã nói về vật nuôi và gia súc? Trước đây, các cá thể giống như vật nuôi trong nhà, nhưng bây giờ chúng đã trở nên giống như gia súc. Trước đây, có một khối - một ứng dụng. Bây giờ là 100 microservice, 100 container. Một số vùng chứa có thể có 2-3 bản sao. Việc kiểm soát mọi thùng chứa trở nên ít quan trọng hơn đối với chúng tôi. Điều quan trọng hơn đối với chúng tôi là tính sẵn có của dịch vụ: bộ container này làm gì. Điều này thay đổi cách tiếp cận để giám sát.

Vào năm 2014-2015, Docker phát triển mạnh mẽ - công nghệ mà chúng ta sẽ nói đến bây giờ.

Docker đã thay đổi triết lý và tiêu chuẩn hóa việc đóng gói ứng dụng. Sử dụng Docker, chúng ta có thể đóng gói một ứng dụng, gửi nó đến kho lưu trữ, tải xuống từ đó và triển khai nó.

Chúng tôi đặt mọi thứ chúng tôi cần vào vùng chứa Docker, vì vậy vấn đề phụ thuộc đã được giải quyết. Docker đảm bảo khả năng tái tạo. Tôi nghĩ nhiều người đã gặp phải tình trạng không thể tái tạo được: mọi thứ đều phù hợp với bạn, bạn đẩy nó vào sản xuất và ở đó nó ngừng hoạt động. Với Docker vấn đề này sẽ biến mất. Nếu vùng chứa Docker của bạn khởi động và thực hiện những gì nó cần làm thì khả năng cao là nó sẽ bắt đầu được đưa vào sản xuất và thực hiện tương tự ở đó.

Lạc đề về chi phí

Luôn có những tranh chấp về chi phí chung. Một số người tin rằng Docker không mang thêm tải vì nó sử dụng nhân Linux và tất cả các quy trình cần thiết cho quá trình chứa. Giống như, “nếu bạn nói rằng Docker có chi phí cao thì nhân Linux cũng có chi phí cao.”

Mặt khác, nếu bạn đi sâu hơn, thực sự có một số thứ trong Docker mà nếu xét rộng ra thì có thể nói là vượt quá chi phí.

Đầu tiên là không gian tên PID. Khi chúng ta đặt một tiến trình vào một không gian tên, nó được gán PID 1. Đồng thời, tiến trình này có một PID khác, nằm trên không gian tên máy chủ, bên ngoài vùng chứa. Ví dụ: chúng tôi đã khởi chạy Nginx trong một vùng chứa, nó trở thành PID 1 (quy trình chính). Và trên máy chủ, nó có PID 12623. Và thật khó để nói chi phí hoạt động của nó là bao nhiêu.

Điều thứ hai là Cgroups. Hãy lấy Cgroups theo bộ nhớ, tức là khả năng giới hạn bộ nhớ của vùng chứa. Khi nó được bật, bộ đếm và tính toán bộ nhớ sẽ được kích hoạt: kernel cần hiểu có bao nhiêu trang đã được phân bổ và bao nhiêu trang vẫn còn trống cho vùng chứa này. Đây có thể là chi phí chung, nhưng tôi chưa thấy nghiên cứu chính xác nào về việc nó ảnh hưởng đến hiệu suất như thế nào. Và bản thân tôi cũng không nhận thấy rằng ứng dụng chạy trong Docker đột nhiên bị giảm hiệu suất rõ rệt.

Và một lưu ý nữa về hiệu suất. Một số tham số kernel được truyền từ máy chủ đến vùng chứa. Đặc biệt, một số thông số mạng. Do đó, nếu bạn muốn chạy thứ gì đó hiệu suất cao trong Docker, chẳng hạn như thứ gì đó sẽ tích cực sử dụng mạng, thì ít nhất bạn cần phải điều chỉnh các tham số này. Một số nf_conntrack chẳng hạn.

Giới thiệu về khái niệm Docker

Docker bao gồm một số thành phần:

  1. Docker Daemon là Công cụ chứa tương tự; phóng container.
  2. Docker CII là một tiện ích quản lý Docker.
  3. Dockerfile - hướng dẫn cách xây dựng hình ảnh.
  4. Hình ảnh - hình ảnh mà container được triển khai.
  5. Thùng đựng hàng.
  6. Docker đăng ký là một kho lưu trữ hình ảnh.

Về mặt sơ đồ, nó trông giống như thế này:

Docker là gì: một chuyến tham quan ngắn gọn về lịch sử và những khái niệm trừu tượng cơ bản

Trình nền Docker chạy trên Docker_host và khởi chạy các container. Có Client gửi lệnh: build image, tải image, khởi chạy container. Docker daemon đi tới sổ đăng ký và thực thi chúng. Máy khách Docker có thể truy cập cả cục bộ (vào ổ cắm Unix) và thông qua TCP từ máy chủ từ xa.

Chúng ta hãy đi qua từng thành phần.

Trình nền Docker - đây là phần máy chủ, nó hoạt động trên máy chủ: tải hình ảnh và khởi chạy các vùng chứa từ chúng, tạo mạng giữa các vùng chứa, thu thập nhật ký. Khi chúng ta nói “tạo ra một hình ảnh”, ma quỷ cũng đang làm điều đó.

docker CLI — Phần máy khách Docker, tiện ích bảng điều khiển để làm việc với daemon. Tôi nhắc lại, nó có thể hoạt động không chỉ cục bộ mà còn qua mạng.

Các lệnh cơ bản:

docker ps - hiển thị các container hiện đang chạy trên máy chủ Docker.
hình ảnh docker - hiển thị hình ảnh được tải xuống cục bộ.
docker search <> - tìm kiếm hình ảnh trong sổ đăng ký.
docker pull <> - tải hình ảnh từ sổ đăng ký xuống máy.
xây dựng docker < > - thu thập hình ảnh.
docker run <> - khởi chạy container.
docker rm <> - loại bỏ vùng chứa.
nhật ký docker <> - nhật ký container
docker start/stop/restart <> - làm việc với container

Nếu bạn thành thạo các lệnh này và tự tin sử dụng chúng, hãy coi như bạn thành thạo 70% về Docker ở cấp độ người dùng.

Dockerfile - hướng dẫn tạo hình ảnh. Hầu hết mọi lệnh hướng dẫn đều là một lớp mới. Hãy xem một ví dụ.

Docker là gì: một chuyến tham quan ngắn gọn về lịch sử và những khái niệm trừu tượng cơ bản

Dockerfile trông như thế này: các lệnh ở bên trái, các đối số ở bên phải. Mỗi lệnh ở đây (và thường được viết trong Dockerfile) sẽ tạo một lớp mới trong Image.

Ngay cả khi nhìn sang bên trái, bạn cũng có thể hiểu đại khái chuyện gì đang xảy ra. Chúng tôi nói: “tạo một thư mục cho chúng tôi” - đây là một lớp. “Làm cho thư mục hoạt động” là một lớp khác, v.v. Lớp bánh làm cho cuộc sống dễ dàng hơn. Nếu tôi tạo một Dockerfile khác và thay đổi nội dung nào đó ở dòng cuối cùng - tôi chạy thứ gì đó không phải là “python” “main.py” hoặc cài đặt các phần phụ thuộc từ một tệp khác - thì các lớp trước đó sẽ được sử dụng lại làm bộ đệm.

Hình ảnh - đây là bao bì container; các container được khởi chạy từ hình ảnh. Nếu chúng ta nhìn Docker từ quan điểm của một người quản lý gói (như thể chúng ta đang làm việc với các gói deb hoặc vòng/phút), thì hình ảnh về cơ bản là một gói vòng/phút. Thông qua yum install chúng ta có thể cài đặt ứng dụng, xóa nó, tìm nó trong kho lưu trữ và tải xuống. Ở đây cũng tương tự: các thùng chứa được khởi chạy từ hình ảnh, chúng được lưu trữ trong sổ đăng ký Docker (tương tự như yum, trong kho lưu trữ) và mỗi hình ảnh có hàm băm SHA-256, tên và thẻ.

Image được build theo hướng dẫn từ Dockerfile. Mỗi lệnh từ Dockerfile sẽ tạo một lớp mới. Các lớp có thể được tái sử dụng.

Đăng ký Docker là kho lưu trữ hình ảnh Docker. Tương tự như HĐH, Docker có sổ đăng ký tiêu chuẩn công cộng - dockerhub. Nhưng bạn có thể xây dựng kho lưu trữ của riêng mình, sổ đăng ký Docker của riêng bạn.

Container - những gì được đưa ra từ hình ảnh. Chúng ta build image theo hướng dẫn từ Dockerfile, sau đó chúng ta khởi chạy nó từ image này. Vùng chứa này được cách ly với các vùng chứa khác và phải chứa mọi thứ cần thiết để ứng dụng hoạt động. Trong trường hợp này, một container - một quy trình. Điều đó xảy ra là bạn phải thực hiện hai quy trình, nhưng điều này hơi trái ngược với hệ tư tưởng của Docker.

Yêu cầu "một vùng chứa, một quy trình" có liên quan đến Không gian tên PID. Khi một tiến trình có PID 1 bắt đầu trong Không gian tên, nếu nó đột ngột chết thì toàn bộ vùng chứa cũng chết theo. Nếu có hai tiến trình đang chạy ở đó: một tiến trình còn hoạt động và tiến trình kia đã chết thì vùng chứa sẽ vẫn tiếp tục hoạt động. Nhưng đây là câu hỏi về Các phương pháp thực hành tốt nhất, chúng ta sẽ nói về chúng trong các tài liệu khác.

Để nghiên cứu chi tiết hơn các tính năng và chương trình đầy đủ của khóa học, vui lòng truy cập liên kết: “Khóa học video về Docker'.

Tác giả: Marcel Ibraev, quản trị viên Kubernetes được chứng nhận, kỹ sư thực hành tại Southbridge, diễn giả và nhà phát triển các khóa học Slurm.

Nguồn: www.habr.com

Thêm một lời nhận xét