什麼是 Docker:簡要回顧歷史和基本抽象

10月XNUMX日在Slurm開始 Docker 視訊課程,其中我們對其進行了全面分析 - 從基本抽像到網路參數。

在本文中,我們將討論 Docker 的歷史及其主要抽象:Image、Cli、Dockerfile。 該講座面向初學者,因此經驗豐富的使用者不太可能感興趣。 不會有血液、闌尾或深度浸沒。 非常基礎的知識。

什麼是 Docker:簡要回顧歷史和基本抽象

什麼是 Docker

讓我們來看看維基百科上對Docker的定義。

Docker 是用於在容器化環境中自動部署和管理應用程式的軟體。

從這個定義來看,沒有什麼是清楚的。 尤其不清楚「在支持容器化的環境中」的含義。 為了找到答案,讓我們回到過去。 讓我們從我通常稱為“整體時代”的時代開始。

單體時代

單體時代是 2000 年代初,當時所有應用程式都是單體的,並且具有大量依賴項。 開發花了很長時間。 同時,伺服器並不多;我們都知道它們的名字並監控它們。 有這樣一個有趣的對比:

寵物是家畜。 在單體時代,我們對待伺服器就像對待寵物一樣,精心呵護、精心照顧,吹走灰塵。 為了更好的資源管理,我們使用了虛擬化:我們將一台伺服器切割成多個虛擬機,從而確保環境的隔離。

基於管理程式的虛擬化系統

大家可能都聽過虛擬化系統:VMware、VirtualBox、Hyper-V、Qemu KVM等。它們提供應用程式隔離和資源管理,但也有缺點。 要進行虛擬化,您需要一個虛擬機器管理程式。 而虛擬機器管理程式是一種資源開銷。 虛擬機器本身通常是一個完整的龐然大物——一個包含作業系統、Nginx、Apache,可能還包含 MySQL 的重型映像。 鏡像較大,虛擬機器操作不方便。 因此,使用虛擬機器可能會很慢。 為了解決這個問題,在核心層級創建了虛擬化系統。

核心級虛擬化系統

OpenVZ、Systemd-nspawn、LXC 系統支援核心級虛擬化。 這種虛擬化的一個顯著例子是 LXC(Linux 容器)。

LXC 是一個作業系統級虛擬化系統,用於在單一節點上執行 Linux 作業系統的多個隔離實例。 LXC不使用虛擬機,而是創建一個具有自己的進程空間和網路堆疊的虛擬環境。

本質上 LXC 建立容器。 虛擬機器和容器有什麼差別?

什麼是 Docker:簡要回顧歷史和基本抽象

容器不適合隔離進程:在核心層級的虛擬化系統中發現了漏洞,使它們能夠從容器逃逸到主機。 因此,如果需要隔離某些東西,最好使用虛擬機器。

虛擬化和容器化之間的差異可以從圖中看出。
有硬體管理程式、作業系統之上的管理程式和容器。

什麼是 Docker:簡要回顧歷史和基本抽象

如果你確實想隔離某些東西,硬體虛擬機器管理程式很酷。 因為可以在記憶體頁面和處理器層級進行隔離。

有作為程序的虛擬機器管理程序,也有容器,我們將進一步討論它們。 容器化系統沒有虛擬機器管理程序,但有一個容器引擎來建立和管理容器。 這個東西更輕量級,因此由於與核心一起工作,開銷更少或根本沒有。

核心層級的容器化是用什麼來實現的

允許您建立與其他進程隔離的容器的主要技術是命名空間和控制組。

命名空間:PID、網路、安裝和使用者。 還有更多,但為了便於理解,我們將重點放在這些上。

PID 命名空間限制流程。 例如,當我們建立一個 PID 命名空間並在那裡放置一個程序時,它的 PID 為 1。通常在系統中,PID 1 是 systemd 或 init。 因此,當我們將進程放置在新的命名空間中時,它也會收到 PID 1。

網路命名空間可讓您限制/隔離網路並在其中放置您自己的介面。 掛載是檔案系統的限制。 用戶——對用戶的限制。

控制組:記憶體、CPU、IOPS、網路 - 總共約 12 個設定。 否則,它們也稱為 Cgroup(“C-groups”)。

控制組管理容器的資源。 透過控制組,我們可以說容器消耗的資源不應超過一定數量。

為了使容器化充分發揮作用,需要使用其他技術:功能、寫入時複製等。

能力是我們告訴流程它能做什麼、不能做什麼。 在核心級別,這些只是帶有許多參數的位圖。 例如,root 使用者擁有完全權限並且可以執行所有操作。 時間伺服器可以更改系統時間:它具有 Time Capsule 上的功能,僅此而已。 透過權限,您可以靈活配置進程限制,從而保護自己。

Copy-on-write 系統允許我們處理 Docker 映像並更有效地使用它們。

Docker 目前存在與 Cgroups v2 的兼容性問題,因此本文專門關注 Cgroups v1。

但讓我們回到歷史。

當虛擬化系統出現在核心層級時,它們就開始被積極使用。 虛擬機器管理程式的開銷消失了,但仍存在一些問題:

  • 大鏡像:他們將作業系統、函式庫、一堆不同的軟體推送到同一個 OpenVZ 中,最終鏡像仍然很大;
  • 打包和交付沒有正常的標準,因此依賴問題仍然存在。 在某些情況下,兩段程式碼使用相同的函式庫,但版本不同。 他們之間可能會發生衝突。

為了解決所有這些問題,下一個時代已經來臨。

容器時代

當容器時代到來時,與之合作的理念發生了變化:

  • 一個進程 - 一個容器。
  • 我們將流程所需的所有依賴項傳遞給其容器。 這需要將單體應用切割成微服務。
  • 鏡像越小越好——可能的漏洞更少,推出速度更快等等。
  • 實例變得短暫。

還記得我說過的寵物與牛的說法嗎? 以前,實例就像家畜,但現在它們變得像牛一樣。 以前,有一個整體——一個應用程式。 現在有 100 個微服務、100 個容器。 某些容器可能有 2-3 個副本。 對我們來說控制每個容器變得不那麼重要了。 對我們來說更重要的是服務本身的可用性:這組容器的作用。 這改變了監控方法。

2014-2015 年,Docker 蓬勃發展——我們現在要討論的技術。

Docker 改變了理念並標準化了應用程式打包。 使用 Docker,我們可以打包應用程序,將其發送到儲存庫,從那裡下載並部署它。

我們把需要的東西都放到了Docker容器中,這樣依賴問題就解決了。 Docker 保證可重複性。 我想很多人都遇到過不可重複性:一切都適合你,你把它推到生產中,然後它就停止工作了。 有了 Docker,這個問題就迎刃而解了。 如果您的 Docker 容器啟動並執行其需要執行的操作,那麼它很可能會在生產環境中啟動並執行相同的操作。

關於開銷的題外話

關於管理費用總是存在爭議。 有些人認為 Docker 不會帶來額外的負載,因為它使用 Linux 核心及其容器化所需的所有進程。 就像,“如果你說 Docker 是開銷,那麼 Linux 核心也是開銷。”

另一方面,如果深入的話,Docker 中確實有幾個東西,拉長一點,可以說是開銷很大的。

第一個是 PID 命名空間。 當我們將一個行程放置在命名空間中時,它會被指派 PID 1。同時,該行程還有另一個 PID,該資料位於容器外部的主機命名空間上。 例如,我們在容器中啟動Nginx,它的PID變成1(主流程)。 在主機上它的 PID 為 12623。而且很難說它有多少開銷。

第二件事是Cgroup。 我們以記憶體來理解Cgroups,也就是限制容器記憶體的能力。 啟用後,計數器和記憶體統計將被啟動:核心需要了解已分配了多少頁以及該容器仍有多少頁可用。 這可能是一種開銷,但我還沒有看到任何關於它如何影響性能的精確研究。 而我自己並沒有註意到,運行在Docker中的應用程式突然出現了效能的急劇下降。

還有一個關於性能的說明。 一些核心參數從主機傳遞到容器。 特別是一些網路參數。 因此,如果你想在Docker中運行一些高效能的東西,例如,一些會主動使用網路的東西,那麼你至少需要調整這些參數。 例如,一些 nf_conntrack。

關於 Docker 概念

Docker 由幾個元件組成:

  1. Docker Daemon 是同一個容器引擎; 啟動容器。
  2. Docker CII 是一個 Docker 管理實用程式。
  3. Dockerfile - 有關如何建立映像的說明。
  4. 映像 — 推出容器的映像。
  5. 容器。
  6. Docker 註冊表是一個映像儲存庫。

從原理上講,它看起來像這樣:

什麼是 Docker:簡要回顧歷史和基本抽象

Docker 守護程式在 Docker_host 上執行並啟動容器。 有一個客戶端發送命令:建立映像、下載映像、啟動容器。 Docker 守護程式進入註冊表並執行它們。 Docker 用戶端可以在本地端(Unix 套接字)進行訪問,也可以透過 TCP 從遠端主機進行存取。

讓我們詳細了解每個組件。

Docker 守護程式 - 這是伺服器部分,它在主機上工作:下載映像並從中啟動容器,在容器之間建立網絡,收集日誌。 當我們說「創造一個形象」時,惡魔也在這樣做。

碼頭工人命令行界面 — Docker 用戶端部分,用於使用守護程序的控制台實用程式。 我再說一遍,它不僅可以在本地運行,還可以透過網路運行。

基本指令:

docker ps - 顯示目前在 Docker 主機上執行的容器。
docker images - 顯示本機下載的映像。
docker search <> - 在註冊表中搜尋映像。
docker pull <> - 將映像從註冊表下載到機器上。
碼頭工人建造 < > - 收集影像。
docker run <> - 啟動容器。
docker rm <> - 刪除容器。
docker log <> - 容器日誌
docker start/stop/restart <> - 使用容器

如果你掌握了這些指令並且有信心使用它們,那麼你就認為自己在使用者層級對 Docker 的熟練程度達到了 70%。

Dockerfile - 建立圖像的說明。 幾乎每個指令指令都是一個新層。 讓我們來看一個例子。

什麼是 Docker:簡要回顧歷史和基本抽象

Dockerfile 是這樣的:指令在左邊,參數在右邊。 這裡的每個命令(通常寫在 Dockerfile 中)都會在 Image 中建立一個新層。

即使看左邊,你也能大致明白發生了什麼事。 我們說:「為我們建立一個資料夾」 - 這是一層。 「讓資料夾正常運作」是另一層,依此類推。 夾心蛋糕讓生活更輕鬆。 如果我創建另一個Dockerfile 並更改最後一行中的某些內容- 我運行“python”“main.py”以外的其他內容,或者從另一個文件安裝依賴項- 那麼前面的層將被重新用作緩存。

圖片 - 這是容器包裝;容器是從鏡像啟動的。 如果我們從套件管理器的角度來看 Docker(就好像我們正在使用 deb 或 rpm 套件一樣),那麼映像本質上就是一個 rpm 套件。 透過 yum install 我們可以安裝應用程式、刪除它、在儲存庫中尋找它並下載它。 這裡的情況大致相同:容器從映像啟動,它們儲存在 Docker 註冊表中(類似於儲存庫中的 yum),每個映像都有一個 SHA-256 雜湊、一個名稱和一個標籤。

映像像是根據 Dockerfile 的指令建構的。 Dockerfile 中的每個指令都會建立一個新圖層。 圖層可以重複使用。

Docker 註冊表 是一個 Docker 映像儲存庫。 與作業系統類似,Docker 有一個公共標準註冊表 - dockerhub。 但您可以建立自己的儲存庫、自己的 Docker 註冊表。

容器 - 從影像啟動的內容。 我們根據 Dockerfile 的說明建立了一個映像,然後從該映像啟動它。 該容器與其他容器隔離,並且必須包含應用程式運行所需的一切。 在這種情況下,一個容器 - 一個進程。 碰巧你必須做兩個進程,但這有點違背Docker的思想。

「一容器,一個行程」的要求與 PID 命名空間有關。 當 PID 為 1 的進程在 Namespace 中啟動時,如果它突然死亡,那麼整個容器也會死亡。 如果那裡正在運行兩個進程:一個是存活的,另一個是死亡的,那麼容器仍然會繼續存活。 但這是最佳實踐的問題,我們將在其他材料中討論它們。

要更詳細地了解該課程的特點和完整計劃,請點擊連結:“Docker 視頻課程“。

作者:Marcel Ibraev,經過認證的 Kubernetes 管理員、Southbridge 的執業工程師、Slurm 課程的演講者和開發者。

來源: www.habr.com

添加評論