容器化的概念很早就有了。2013 年 Docker 引擎 的出現(xiàn)使應(yīng)用程序容器化變得更加容易。
根據(jù) Stack Overflow 開發(fā)者調(diào)查-2020,Docker 是開發(fā)者 #1 最想要的平臺、#2 最喜歡的平臺,以及 #3 最流行的平臺。
盡管 Docker 功能強大,但上手確并不容易。因此,本文將介紹從基礎(chǔ)知識到更高層次容器化的的所有內(nèi)容。讀完本文之后,你應(yīng)該能夠:
- 容器化(幾乎)任何應(yīng)用程序
- 將自定義 Docker 鏡像上傳到在線倉庫
- 使用 Docker Compose 處理多個容器
前提
- 熟悉 Linux 終端操作
- 熟悉 JavaScript(稍后的的演示項目用到了 JavaScript)
容器化和 Docker 簡介
摘自 IBM,
容器化意味著封裝或打包軟件代碼及其所有依賴項,以便它可以在任何基礎(chǔ)架構(gòu)上統(tǒng)一且一致地運行。
換句話說,容器化可以將軟件及其所有依賴項打包在一個自包含的軟件包中,這樣就可以省略麻煩的配置,直接運行。
舉一個現(xiàn)實生活的場景。假設(shè)你已經(jīng)開發(fā)了一個很棒的圖書管理應(yīng)用程序,該應(yīng)用程序可以存儲所有圖書的信息,還可以為別人提供圖書借閱服務(wù)。
如果列出依賴項,如下所示:
- Node.js
- Express.js
- SQLite3
理論上應(yīng)該是這樣。但是實際上還要搞定其他一些事情。 Node.js 使用了 node-gyp 構(gòu)建工具來構(gòu)建原生加載項。根據(jù) 官方存儲庫 中的 安裝說明,此構(gòu)建工具需要 Python 2 或 3 和相應(yīng)的的 C/C ++ 編譯器工具鏈。
考慮到所有這些因素,最終的依賴關(guān)系列表如下:
- Node.js
- Express.js
- SQLite3
- Python 2 or 3
- C/C++ tool-chain
無論使用什么平臺,安裝 Python 2 或 3 都非常簡單。在 Linux 上,設(shè)置 C/C ++ 工具鏈也非常容易,但是在 Windows 和 Mac 上,這是一項繁重的工作。
在 Windows 上,C++ 構(gòu)建工具包有數(shù) GB 之大,安裝需要花費相當長的時間。在 Mac 上,可以安裝龐大的 Xcode 應(yīng)用程序,也可以安裝小巧的 Xcode 命令行工具 包。
不管安裝了哪一種,它都可能會在 OS 更新時中斷。實際上,該問題非常普遍,甚至連官方倉庫都專門提供了 macOS Catalina 的安裝說明。
這里假設(shè)你已經(jīng)解決了設(shè)置依賴項的所有麻煩,并且已經(jīng)準備好開始。這是否意味著現(xiàn)在開始就一帆風順了?當然不是。
如果你使用 Linux 而同事使用 Windows 該怎么辦?現(xiàn)在,必須考慮如何處理這兩個不同的操作系統(tǒng)不一致的路徑,或諸如 nginx 之類的流行技術(shù)在 Windows 上未得到很好的優(yōu)化的事實,以及諸如 Redis 之類的某些技術(shù)甚至都不是針對 Windows 預(yù)先構(gòu)建的。
即使你完成了整個開發(fā),如果負責管理服務(wù)器的人員部署流程搞錯了,該怎么辦?
所有這些問題都可以通過以下方式解決:
- 在與最終部署環(huán)境匹配的隔離環(huán)境(稱為容器)中開發(fā)和運行應(yīng)用程序。
- 將你的應(yīng)用程序及其所有依賴項和必要的部署配置放入一個文件(稱為鏡像)中。
- 并通過具有適當授權(quán)的任何人都可以訪問的中央服務(wù)器(稱為倉庫)共享該鏡像。
然后,你的同事就可以從倉庫中下載鏡像,可以在沒有平臺沖突的隔離環(huán)境中運行應(yīng)用,甚至可以直接在服務(wù)器上進行部署,因為該鏡像也可以進行生產(chǎn)環(huán)境配置。
這就是容器化背后的想法:將應(yīng)用程序放在一個獨立的程序包中,使其在各種環(huán)境中都可移植且可回溯。
現(xiàn)在的問題是:Docker 在這里扮演什么角色?
正如我之前講的,容器化是一種將一切統(tǒng)一放入盒子中來解決軟件開發(fā)過程中的問題的思想。
這個想法有很多實現(xiàn)。Docker 就是這樣的實現(xiàn)。這是一個開放源代碼的容器化平臺,可讓你對應(yīng)用程序進行容器化,使用公共或私有倉庫共享它們,也可以編排它們。
目前,Docker 并不是市場上唯一的容器化工具,卻是最受歡迎的容器化工具。我喜歡的另一個容器化引擎是 Red Hat 開發(fā)的 Podman。其他工具,例如 Google 的 Kaniko,CoreOS 的 rkt 都很棒,但和 Docker 還是有差距。
此外,如果你想了解容器的歷史,可以閱讀 A Brief History of Containers: From the 1970s Till Now,它描述了該技術(shù)的很多重要節(jié)點。
怎樣安裝 Docker
Docker 的安裝因使用的操作系統(tǒng)而異。但這整個過程都非常簡單。
Docker可在 Mac、Windows 和 Linux 這三個主要平臺上完美運行。在這三者中,在 Mac 上的安裝過程是最簡單的,因此我們從這里開始。
怎樣在 macOS 里安裝 Docker
在 Mac 上,要做的就是跳轉(zhuǎn)到官方的下載頁面,然后單擊Download for Mac(stable)按鈕。
你會看到一個常規(guī)的 Apple Disk Image 文件,在該文件的內(nèi)有 Docker 應(yīng)用程序。所要做的就是將文件拖放到 Applications 目錄中。

只需雙擊應(yīng)用程序圖標即可啟動 Docker。應(yīng)用程序啟動后,將看到 Docker 圖標出現(xiàn)在菜單欄上。

現(xiàn)在,打開終端并執(zhí)行 docker --version 和 docker-compose --version 以驗證是否安裝成功。
怎樣在 Windows 上安裝 Docker
在 Windows 上,步驟幾乎相同,當然還需要執(zhí)行一些額外的操作。安裝步驟如下:
- 跳轉(zhuǎn)到此站點,然后按照說明在 Windows 10 上安裝 WSL2。
- 然后跳轉(zhuǎn)到官方下載頁面 并單擊 Download for Windows(stable) 按鈕。
- 雙擊下載的安裝程序,然后使用默認設(shè)置進行安裝。
安裝完成后,從開始菜單或桌面啟動 Docker Desktop。Docker 圖標應(yīng)顯示在任務(wù)欄上。

現(xiàn)在,打開 Ubuntu 或從 Microsoft Store 安裝的任何發(fā)行版。執(zhí)行 docker --version 和 docker-compose --version 命令以確保安裝成功。

也可以從常規(guī)命令提示符或 PowerShell 訪問 Docker,只是我更喜歡使用 WSL2。
怎樣在 Linux 上安裝 Docker
在 Linux 上安裝 Docker 的過程有所不同,具體操作取決于你所使用的發(fā)行版,它們之間差異可能更大。但老實說,安裝與其他兩個平臺一樣容易(如果不能算更容易的話)。
Windows 或 Mac 上的 Docker Desktop 軟件包是一系列工具的集合,例如Docker Engine、Docker Compose、Docker Dashboard、Kubernetes 和其他一些好東西。
但是,在 Linux 上,沒有得到這樣的捆綁包??梢允謩影惭b所需的所有必要工具。 不同發(fā)行版的安裝過程如下:
- 如果你使用的是 Ubuntu,則可以遵循官方文檔中的 在 Ubuntu 上安裝 Docker 引擎 部分。
- 對于其他發(fā)行版,官方文檔中提供了 不同發(fā)行版的安裝指南。
- 如果你使用的發(fā)行版未在文檔中列出,則可以參考從二進制文件安裝 Docker 引擎指南。
- 無論參考什么程序,都必須完成一些非常重要的 Linux 的安裝后續(xù)步驟。
- 完成 docker 安裝后,必須安裝另一個名為 Docker Compose 的工具。 可以參考官方文檔中的 Install Docker Compose 指南。
安裝完成后,打開終端并執(zhí)行 docker --version 和 docker-compose --version 以確保安裝成功。

盡管無論使用哪個平臺,Docker 的性能都很好,但與其他平臺相比,我更喜歡 Linux。在本文中,我將使用Ubuntu 20.10 或者 Fedora 33。
一開始就需要闡明的另一件事是,在本文中,我不會使用任何 GUI 工具操作 Docker。
我在各個平臺用過很多不錯的 GUI 工具,但是介紹常見的 docker 命令是本文的主要目標之一。
初識 Docker - 介紹 Docker 基本知識
已經(jīng)在計算機上啟動并運行了 Docker,現(xiàn)在該運行第一個容器了。打開終端并執(zhí)行以下命令:
docker run hello-world
# 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/
hello-world 鏡像是使用 Docker 進行最小化容器化的一個示例。它有一個從 hello.c 文件編譯的程序,負責打印出終端看到的消息。
現(xiàn)在,在終端中,可以使用 docker ps -a 命令查看當前正在運行或過去運行的所有容器:
docker ps -a
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# 128ec8ceab71 hello-world "/hello" 14 seconds ago Exited (0) 13 seconds ago exciting_chebyshev
在輸出中,使用 hello-world 鏡像運行了名為 exciting_chebyshev 的容器,其容器標識為 128ec8ceab71。它已經(jīng)在 Exited (0) 13 seconds ago,其中 (0) 退出代碼表示在容器運行時未發(fā)生任何錯誤。
現(xiàn)在,為了了解背后發(fā)生的事情,必須熟悉 Docker 體系結(jié)構(gòu)和三個非?;镜娜萜骰拍睿缦滤荆?/p>
- 容器
- 鏡像
- 倉庫
我已經(jīng)按字母順序列出了這三個概念,并且將從列表中的第一個開始介紹。
什么是容器?
在容器化世界中,沒有什么比容器的概念更基礎(chǔ)的了。
官方 Docker resources 網(wǎng)站說 -
容器是應(yīng)用程序?qū)拥某橄螅梢詫⒋a和依賴項打包在一起。容器不虛擬化整個物理機,僅虛擬化主機操作系統(tǒng)。
可以認為容器是下一代虛擬機。
就像虛擬機一樣,容器是與主機系統(tǒng)是彼此之間完全隔離的環(huán)境。它也比傳統(tǒng)虛擬機輕量得多,因此可以同時運行大量容器,而不會影響主機系統(tǒng)的性能。
容器和虛擬機實際上是虛擬化物理硬件的不同方法。兩者之間的主要區(qū)別是虛擬化方式。
虛擬機通常由稱為虛擬機監(jiān)控器的程序創(chuàng)建和管理,例如 Oracle VM VirtualBox,VMware Workstation,KVM,Microsoft Hyper-V 等等。 該虛擬機監(jiān)控程序通常位于主機操作系統(tǒng)和虛擬機之間,充當通信介質(zhì)。

每個虛擬機都有自己的 guest 操作系統(tǒng),該操作系統(tǒng)與主機操作系統(tǒng)一樣消耗資源。
在虛擬機內(nèi)部運行的應(yīng)用程序與 guest 操作系統(tǒng)進行通信,該 guest 操作系統(tǒng)在與虛擬機監(jiān)控器進行通信,后者隨后又與主機操作系統(tǒng)進行通信,以將必要的資源從物理基礎(chǔ)設(shè)施分配給正在運行的應(yīng)用程序。
虛擬機內(nèi)部運行的應(yīng)用程序與物理基礎(chǔ)設(shè)施之間存在很長的通信鏈。在虛擬機內(nèi)部運行的應(yīng)用程序可能只擁有少量資源,因為 guest 操作系統(tǒng)會占用很大的開銷。
與虛擬機不同,容器以更智能的方式完成虛擬化工作。在容器內(nèi)部沒有完整的 guest 操作系統(tǒng),它只是通過容器運行時使用主機操作系統(tǒng),同時保持隔離 – 就像傳統(tǒng)的虛擬機一樣。

容器運行時(即 Docker)位于容器和主機操作系統(tǒng)之間,而不是虛擬機監(jiān)控器中。容器與容器運行時進行通信,容器運行時再與主機操作系統(tǒng)進行通信,以從物理基礎(chǔ)設(shè)施中獲取必要的資源。
由于消除了整個主機操作系統(tǒng)層,因此與傳統(tǒng)的虛擬機相比,容器的更輕量,資源占用更少。
為了說明這一點,請看下面的代碼片段:
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
在上面的代碼片段中,在主機操作系統(tǒng)上執(zhí)行了 uname -a 命令以打印出內(nèi)核詳細信息。然后在下一行,我在運行 Alpine Linux 的容器內(nèi)執(zhí)行了相同的命令。
從輸出中可以看到,該容器確實正在使用主機操作系統(tǒng)中的內(nèi)核。這證明了容器虛擬化主機操作系統(tǒng)而不是擁有自己的操作系統(tǒng)這一點。
如果你使用的是 Windows 計算機,則會發(fā)現(xiàn)所有容器都使用 WSL2 內(nèi)核。發(fā)生這種情況是因為 WSL2 充當了 Windows 上 Docker 的后端。在 macOS 上,默認后端是在 HyperKit 虛擬機管理程序上運行的 VM。
什么是 Docker 鏡像?
鏡像是分層的自包含文件,充當創(chuàng)建容器的模板。它們就像容器的凍結(jié)只讀副本。 鏡像可以通過倉庫進行共享。
過去,不同的容器引擎具有不同的鏡像格式。但是后來,開放式容器計劃(OCI)定義了容器鏡像的標準規(guī)范,該規(guī)范被主要的容器化引擎所遵循。這意味著使用 Docker 構(gòu)建的映像可以與 Podman 等其他運行時一起使用,而不會有兼容性問題。
容器只是處于運行狀態(tài)的鏡像。當從互聯(lián)網(wǎng)上獲取鏡像并使用該鏡像運行容器時,實際上是在先前的只讀層之上創(chuàng)建了另一個臨時可寫層。
在本文的后續(xù)部分中,這一概念將變得更加清晰。但就目前而言,請記住,鏡像是分層只讀文件,其中保留著應(yīng)用程序所需的狀態(tài)。
什么是倉庫?
已經(jīng)了解了這個難題的兩個非常重要的部分,即 Containers 和 Images 。 最后一個是 Registry。
鏡像倉庫是一個集中式的位置,可以在其中上傳鏡像,也可以下載其他人創(chuàng)建的鏡像。 Docker Hub 是 Docker 的默認公共倉庫。另一個非常流行的鏡像倉庫是 Red Hat 的 Quay。
在本文中,我將使用 Docker Hub 作為首選倉庫。

可以免費在 Docker Hub 上共享任意數(shù)量的公共鏡像。供世界各地的人們下載免費使用。
除了 Docker Hub 或 Quay,還可以創(chuàng)建自己的鏡像倉庫來托管私有鏡像。計算機中還運行著一個本地倉庫,該倉庫緩存從遠程倉庫提取的鏡像。
Docker 架構(gòu)概述
既然已經(jīng)熟悉了有關(guān)容器化和 Docker 的大多數(shù)基本概念,那么現(xiàn)在是時候了解 Docker 作為軟件的架構(gòu)了。
該引擎包括三個主要組件:
-
Docker 守護程序: 守護程序(
dockerd)是一個始終在后臺運行并等待來自客戶端的命令的進程。守護程序能夠管理各種 Docker 對象。 -
Docker 客戶端: 客戶端(
docker)是一個命令行界面程序,主要負責傳輸用戶發(fā)出的命令。 - REST API: REST API 充當守護程序和客戶端之間的橋梁。使用客戶端發(fā)出的任何命令都將通過 API 傳遞,最終到達守護程序。
根據(jù)官方 文檔,
“ Docker 使用客戶端-服務(wù)器體系結(jié)構(gòu)。Docker client 與 Docker daemon 對話,daemon 繁重地構(gòu)建、運行和分發(fā) Docker 容器”。
作為用戶,通常將使用客戶端組件執(zhí)行命令。然后,客戶端使用 REST API 來訪問長期運行的守護程序并完成工作。
全景圖
好吧,說的夠多了。 現(xiàn)在是時候了解剛剛學習的所有這些知識如何和諧地工作了。在深入解釋運行 docker run hello-world 命令時實際發(fā)生的情況之前,看一下下面的圖片:

該圖像是在官方文檔中找到的圖像的略微修改版本。 執(zhí)行命令時發(fā)生的事件如下: