天天看點

Docker實戰:初探Docker

關注【異步圖書】微信公衆号,

本文主要内容

  • Docker是什麼
  • Docker的使用以及它如何能節省時間和金錢
  • 容器與鏡像之間的差別
  • Docker的分層特性
  • 使用Docker建構并運作一個to-do應用程式

Docker是一個允許使用者“在任何地方建構、分發及運作任何應用”的平台。它在極短的時間内發展壯大,目前已經被視為解決軟體中最昂貴的方面之一——部署的一個标準方法。

在Docker出現之前,開發流水線通常由用于管理軟體活動的不同技術組合而成,如虛拟機、配置管理工具、不同的包管理系統以及各類依賴庫複雜的網站。所有這些工具需要由專業的工程師管理和維護,并且多數工具都具有自己獨特的配置方式。

Docker改變了這一切,允許不同的工程師參與到這個過程中,有效地使用同一門語言,這讓協作變得輕而易舉。所有東西通過一個共同的流水線轉變成可以在任何目标平台上使用的單一的産出——無須繼續維護一堆讓人眼花缭亂的工具配置,如圖1-1所示。

與此同時,隻要現存的軟體技術棧依然有效,使用者就無須抛棄它——使用者可以将其原樣打包到一個Docker容器内供其他人使用。由此獲得的額外好處是,使用者能看到這些容器是如何建構的,是以如果需要深入其細節,完全沒問題。

在本文中,讀者将了解到Docker是什麼、為什麼它很重要,并開始使用它。

Docker實戰:初探Docker

圖1-1 Docker如何消除了工具維護的負擔

1.1 Docker是什麼以及為什麼用Docker

在動手實踐之前,我們将對Docker稍做讨論,以便讀者了解它的背景、“Docker”名字的來曆以及為什麼使用它!

1.1.1 Docker是什麼

要了解Docker是什麼,從一個比喻開始會比技術性解釋來得簡單,而且這個Docker的比喻非常有說服力。Docker原本是指在船隻停靠港口之後将商品移進或移出的勞工。箱子和物品的大小和形狀各異,而有經驗的碼頭勞工能以合算的方式手工将商品裝入船隻,因而他們倍受青睐(見圖1-2)。雇人搬東西并不便宜,但除此之外别無選擇。

對在軟體行業工作的人來說,這聽起來應該很熟悉。大量時間和精力被花在将奇形怪狀的軟體放置到裝滿了其他奇形怪狀軟體、大小各異的船隻上,以便将其賣給其他地方的使用者或商業機構。

Docker實戰:初探Docker

圖1-2 标準化集裝箱前後的航運對比

在Docker出現之前,部署軟體到不同環境所需的工作量巨大。即使不是采用手工運作腳本的方式在不同機器上進行軟體配備(還是有很多人這麼做),使用者也不得不全力應付那些配置管理工具,它們掌管着渴求資源且快速變化的環境的狀态。即便将這些工作封裝到虛拟機中,還是需要花費大量時間來部署這些虛拟機、等待它們啟動并管理它們所産生的額外的資源開銷。

使用Docker,配置工作從資源管理中分離了出來,而部署工作則是微不足道的:運作​

​docker run​

​,環境的鏡像會被拉取下來并準備運作,所消耗的資源更少并且是内含的,是以不會幹擾其他環境。

讀者無須擔心容器是将被分發到Red Hat機器、Ubuntu機器還是CentOS虛拟機鏡像中,隻要上面有Docker,就沒有問題。

圖1-3展示了使用Docker概念時如何能節省時間和金錢。

Docker實戰:初探Docker

圖1-3 使用Docker前後軟體傳遞的對比

1.1.2 Docker有什麼好處

幾個重要的實際問題出現了:為什麼要使用Docker,Docker用在什麼地方?針對“為什麼”的簡要答案是:隻需要一點點付出,Docker就能快速為企業節省大量金錢。部分方法(肯定不是所有的)将在随後的幾節中讨論。我們已經在實際工作環境中切身體會到所有這些益處。

1.替代虛拟機(VM)

Docker可以在很多情況下替代虛拟機。如果使用者隻關心應用程式,而不是作業系統,可以用Docker替代虛拟機,并将作業系統交給其他人去考慮。Docker不僅啟動速度比虛拟機快,遷移時也更為輕量,同時得益于它的分層檔案系統,與其他人分享變更時更簡單、更快捷。而且,它牢牢地紮根在指令行中,非常适合腳本化。

2.軟體原型

如果想快速體驗軟體,同時避免幹擾目前的設定或配備一個虛拟機的麻煩,Docker可以在幾毫秒内提供一個沙箱環境。在親身體驗之前,很難感受到這種解放的效果。

3.打包軟體

因為對Linux使用者而言,Docker鏡像實際上沒有依賴,是以非常适合用于打包軟體。使用者可以建構鏡像,并確定它可以運作在任何現代Linux機器上——就像Java一樣,但不需要JVM。

4.讓微服務架構成為可能

Docker 有助于将一個複雜系統分解成一系列可組合的部分,這讓使用者可以用更離散的方式來思考其服務。使用者可以在不影響全局的前提下重組軟體使其各部分更易于管理和可插拔。

5.網絡模組化

由于可以在一台機器上啟動數百個(甚至數千個)隔離的容器,是以對網絡進行模組化輕而易舉。這對于現實世界場景的測試非常有用,而且所費無幾。

6.離線時啟用全棧生産力

因為可以将系統的所有部分捆綁在Docker容器中,是以使用者可以将其編排運作在筆記本電腦中移動辦公,即便在離線時也沒問題。

7.降低調試支出

不同團隊之間關于軟體傳遞的複雜談判在業内司空見慣。我們親身經曆過不計其數的這類讨論:失效的庫、有問題的依賴、更新被錯誤實施或是執行順序有誤,甚至可能根本沒執行以及無法重制的錯誤等。估計讀者也遇到過。Docker讓使用者可以清晰地說明(即便是腳本的形式)在一個屬性已知的系統上調試問題的步驟,錯誤和環境重制變得更簡單,而且通常與所提供的主控端環境是分離的。

8.文檔化軟體依賴及接觸點

通過使用結構化方式建構鏡像,為遷移到不同環境做好準備,Docker 強制使用者從一個基本出發點開始明确地記錄軟體依賴。即使使用者不打算在所有地方都使用Docker,這種對文檔記錄的需要也有助于在其他地方安裝軟體。

9.啟用持續傳遞

持續傳遞(continuous delivery,CD)是一個基于流水線的軟體傳遞範型,該流水線通過一個自動化(或半自動化)流程在每次變動時重新建構系統然後傳遞到生産環境中。

因為使用者可以更準确地控制建構環境的狀态,Docker 建構比傳統軟體建構方法更具有可重制性和可複制性。使持續傳遞的實作變得更容易。通過實作一個以Docker為中心的可重制的建構過程,标準的持續傳遞技術,如藍/綠部署(blue/green deployment,在生産環境中維護“生産”和“最新”部署)和鳳凰部署(phoenix deployment,每次釋出時都重新建構整個系統)變得很簡單。

現在,讀者對Docker如何能夠提供幫助有了一定了解。在進入一個真實示例之前,讓我們來了解一下幾個核心概念。

1.1.3 關鍵的概念

在本節中,我們将介紹一些關鍵的Docker概念,如圖1-4所示。

Docker實戰:初探Docker

圖1-4 Docker的核心概念

在開始運作Docker指令之前,将鏡像、容器及層的概念牢記于心是極其有用的。簡而言之,​

​容器​

​運作着由​

​鏡像​

​定義的系統。這些鏡像由一個或多個​

​層​

​(或差異集)加上一些Docker的中繼資料組成。

讓我們來看一些核心的Docker指令。我們将把鏡像轉變成容器,修改它們,并添加層到我們即将送出的新鏡像中。如果這一切聽上去有點兒混亂,不用太擔心。在本文結束時,一切都将更加清晰。

1.關鍵的Docker指令

Docker的中心功能是建構、分發及在任何具有Docker的地方運作軟體。

對終端使用者而言,Docker是一個用于運作的指令行程式。就像git(或任何源代碼控制工具)一樣,這個程式具有用于執行不同操作的子指令。

表1-1中列出了将在主控端上使用的主要的Docker子指令。

表1-1 Docker子指令

命  令 目  的

​docker build​

建構一個Docker鏡像

​docker run​

以容器形式運作一個Docker鏡像

​docker commit ​

将一個Docker容器作為一個鏡像送出

​docker tag​

給一個Docker鏡像打标簽
2.鏡像與容器

如果讀者不熟悉Docker,這可能是第一次聽說本文所說的“容器”和“鏡像”這兩個詞。它們很可能是Docker中最重要的概念,是以有必要花點兒時間明确其差異。

在圖1-5中,讀者将看到這些概念的展示,裡面有從一個基礎鏡像啟動的3個容器。

Docker實戰:初探Docker

圖1-5 Docker鏡像與容器

看待鏡像和容器的一種方式是将它們類比成程式與程序。一個程序可以視為一個被執行的應用程式,同樣,一個Docker容器可以視為一個運作中的Docker鏡像。

如果讀者熟悉面向對象原理,看待鏡像和容器的另一種方法是将鏡像看作類而将容器看作對象。對象是類的具體執行個體,同樣,容器是鏡像的執行個體。使用者可以從單個鏡像建立多個容器,就像對象一樣,它們之間全都是互相隔離的。不論使用者在對象内修改了什麼,都不會影響類的定義——它們從根本上就是不同的東西。

1.2 建構一個Docker應用程式

現在,我們要動手使用Docker來建構一個簡單的“to-do”應用程式(todoapp)鏡像了。在這個過程中,讀者會看到一些關鍵的Docker功能,如Dockerfile、鏡像複用、端口暴露及建構自動化。這是接下來10分鐘讀者将學到的東西:

  • 如何使用Dockerfile來建立Docker鏡像;
  • 如何為Docker鏡像打标簽以便引用;
  • 如何運作建立的Docker鏡像。

to-do應用是協助使用者跟蹤待完成事項的一個應用程式。我們所建構的應用将存儲并顯示可被标記為已完成的資訊的簡短字元串,它以一個簡單的網頁界面呈現。

圖1-6展示了如此操作将得到的結果。

Docker實戰:初探Docker

圖1-6 建構一個Docker應用程式

應用程式的細節不是重點。我們将示範從我們所提供的一個簡短的Dockerfile開始,讀者可以放心地在自己的主控端上使用與我們相同的方法建構、運作、停止和啟動一個應用程式,而無須考慮應用程式的安裝或依賴。這正是 Docker 賦予我們的關鍵部分——可靠地重制并簡便地管理和分享開發環境。這意味着使用者無須再遵循并迷失在那些複雜的或含糊的安裝說明中。

to-do應用程式 這個to-do應用程式将貫穿本文,多次使用,它非常适合用于實踐和示範,是以值得讀者熟悉一下。

1.2.1 建立新的Docker鏡像的方式

建立Docker鏡像有4種标準的方式。表1-2逐一列出了這些方法。

表1-2 建立Docker鏡像的方式

方  法 描  述 詳見技巧
Docker指令/“手工” 使用​

​docker run​

​​啟動一個容器,并在指令行輸入指令來建立鏡像。使用​

​docker commit​

​來建立一個新鏡像
詳見技巧14
Dockerfile 從一個已知基礎鏡像開始建構,并指定一組有限的簡單指令來建構 稍後讨論
Dockerfile及配置管理(configuration management,CM)工具 與Dockerfile相同,不過将建構的控制權交給了更為複雜的CM工具 詳見技巧47
從頭建立鏡像并導入一組檔案 從一個空白鏡像開始,導入一個含有所需檔案的TAR檔案 詳見技巧10

如果使用者所做的是概念驗證以确認安裝過程是否正常,那麼第一種“手工”方式是沒問題的。在這個過程中,使用者應對所采取的步驟做記錄,以便在需要時回到同一點上。

到某個時間點,使用者會想要定義建立鏡像的步驟。這就是第二種方式(也就是我們這裡所用的方式)。

對于更複雜的建構,使用者需要使用第三種方式,特别是在Dockerfile功能還不足以滿足鏡像要求的時候。

最後一種方式從一個空鏡像開始,通過疊加一組運作鏡像所需要的檔案進行建構。如果使用者想導入一組在其他地方建立好的自包含的檔案,這将非常有用,不過這在主流應用中非常罕見。

現在,我們來看一下Dockerfile方法,其他方法将在本文後面再說明。

1.2.2 編寫一個Dockerfile

Dockerfile是一個包含一系列指令的文本檔案。本示例中我們将使用的Dockerfile如下:

FROM node  ⇽--- ❶ 定義基礎鏡像
MAINTAINER [email protected]  ⇽--- ❷ 聲明維護人員
RUN git clone -q https://github.com/docker-in-practice/todo.git  ⇽--- ❸ 克隆todoapp代碼
WORKDIR todo  ⇽--- ❹ 移動到新的克隆目錄
RUN npm install > /dev/null  ⇽--- ❺ 運作node包管理器的安裝指令(npm)
EXPOSE 8000  ⇽--- ❻ 指定從所建構的鏡像啟動的容器需要監聽這個端口
CMD ["npm","start"]  ⇽--- ❼ 指定在啟動時需要運作的指令      

Dockerfile的開始部分是使用​

​FROM​

​指令定義基礎鏡像❶。本示例使用了一個Node.js鏡像以便通路Node.js程式。官方的Node.js鏡像名為​

​node​

​。 接下來,使用​

​MAINTAINER​

​指令聲明維護人員❷。在這裡,我們我們使用的是其中一個人的電子郵件位址,讀者也可以替換成自己的,因為現在它是你的Dockerfile了。這一行不是建立可工作的Docker鏡像所必需的,不過将其包含進來是一個很好的做法。到這個時候,建構已經繼承了node容器的狀态,讀者可以在它上面做操作了。 接下來,使用​

​RUN​

​指令克隆todoapp代碼❸。這裡使用指定的指令擷取應用程式的代碼:在容器内運作​

​git​

​。在這個示例中,Git是安裝在基礎node鏡像裡的,不過讀者不能對這類事情做假定。 現在移動到使用​

​WORKDIR​

​指令新克隆的目錄中❹。這不僅會改變建構環境中的目錄,最後一條​

​WORKDIR​

​指令還決定了從所建構鏡像啟動容器時使用者所處的預設目錄。 接下來,運作node包管理器的安裝指令(​

​npm​

​)❺。這将為應用程式設定依賴。我們對輸出的資訊不感興趣,是以将其重定向到/dev/null上。 由于應用程式使用了8000端口,使用​

​EXPOSE​

​指令告訴Docker從所建構鏡像啟動的容器應該監聽這個端口❻。 最後,使用​

​CMD​

​指令告訴Docker在容器啟動時将運作哪條指令❼。 這個簡單的示例示範了Docker及Dockerfile的幾個核心功能。Dockerfile是一組嚴格按順序運作的有限的指令集的簡單序列。它們影響了最終鏡像的檔案和中繼資料。這裡的​

​RUN​

​指令通過簽出并安裝應用程式影響了檔案系統,而​

​EXPOSE​

​、​

​CMD​

​和​

​WORKDIR​

​指令影響了鏡像的中繼資料。

1.2.3 建構一個Docker鏡像

讀者已經定義了自己的Dockerfile的建構步驟。現在可以鍵入圖1-7所示的指令進而建構Docker鏡像了。

Docker實戰:初探Docker

圖1-7 Docker ​

​build​

​指令

讀者将看到類似下面這樣的輸出:

Sending build context to Docker daemon 178.7 kB      ⇽--- Docker會上傳docker build指定目錄下的檔案和目錄
Sending build context to Docker daemon
Step 0 : FROM node                                ⇽--- 每個建構步驟從 0 開始按順序編号,并與指令一起輸出     
---> fc81e574af43                           ⇽--- 每個指令會導緻一個新鏡像被建立出來,其鏡像ID在此輸出
Step 1 : MAINTAINER [email protected]
---> Running in 21af1aad6950
---> 8f32669fe435
Removing intermediate container 21af1aad6950           ⇽--- 為節省空間,在繼續前每個中間容器會被移除                 
Step 2 : RUN git clone https://github.com/ianmiell/todo.git
---> Running in 0a030ee746ea
Cloning into 'todo'...
---> 783c68b2e3fc
Removing intermediate container 0a030ee746ea
Step 3 : WORKDIR todo
---> Running in 2e59f5df7152
---> 8686b344b124
Removing intermediate container 2e59f5df7152
Step 4 : RUN npm install
---> Running in bdf07a308fca
npm info it worked if it ends with ok               ⇽--- 建構的調試資訊在此輸出(限于篇幅,本代碼清單做了删減)     
[...]
npm info ok
---> 6cf8f3633306
Removing intermediate container bdf07a308fca
Step 5 : RUN chmod -R 777 /todo
---> Running in c03f27789768
---> 2c0ededd3a5e
Removing intermediate container c03f27789768
Step 6 : EXPOSE 8000
---> Running in 46685ea97b8f
---> f1c29feca036
Removing intermediate container 46685ea97b8f
Step 7 : CMD npm start
---> Running in 7b4c1a9ed6af
---> 66c76cea05bb
Removing intermediate container 7b4c1a9ed6af
Successfully built 66c76cea05bb                 ⇽---      此次建構的最終鏡像ID,可用于打标簽      

現在,擁有了一個具有鏡像ID(前面示例中的“66c76cea05bb”,不過讀者的ID會不一樣)的Docker鏡像。總是引用這個ID會很麻煩,可以為其打标簽以友善引用。

輸入圖1-8所示的指令,将66c76cea05bb替換成讀者生成的鏡像ID。

Docker實戰:初探Docker

圖1-8 Docker ​

​tag​

​指令

現在就能從一個Dockerfile建構自己的Docker鏡像副本,并重制别人定義的環境了!

1.2.4 運作一個Docker容器

讀者已經建構出Docker鏡像并為其打上了标簽。現在可以以容器的形式來運作它了:

docker run -p 8000:8000 --name example1 todoapp     ⇽---❶ docker run子指令啟動容器,-p将容器的 8000 端口映射到主控端的8000端口上,--name給容器賦予一個唯一的名字,最後一個參數是鏡像
npm install
npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm info prestart [email protected]
> [email protected] prestart /todo     ⇽--- 容器的啟動程序的輸出被發送到終端中
> make all
npm install
npm info it worked if it ends with ok
npm info using [email protected]
npm info using [email protected]
npm WARN package.json [email protected] No repository field.
npm WARN package.json [email protected] license should be a 
➥ valid SPDX license expression
npm info preinstall [email protected]
npm info package.json [email protected] license should be a valid 
➥ SPDX license expression
npm info package.json [email protected] No license field.
npm info package.json [email protected] No license field.
npm info package.json [email protected] license should be a valid 
➥ SPDX license expression
npm info package.json [email protected] No license field.
npm info build /todo
npm info linkStuff [email protected]
npm info install [email protected]
npm info postinstall [email protected]
npm info prepublish [email protected]
npm info ok
if [ ! -e dist/ ]; then mkdir dist; fi
cp node_modules/react/dist/react.min.js dist/react.min.js
 
LocalTodoApp.js:9:     // TODO: default english version
LocalTodoApp.js:84:               fwdList = 
➥ this.host.get('/TodoList#'+listId); // TODO fn+id sig
TodoApp.js:117:      // TODO scroll into view
TodoApp.js:176:      if (i>=list.length()) { i=list.length()-1; } 
➥ // TODO .length
local.html:30:    <!-- TODO 2-split, 3-split -->
model/TodoList.js:29: 
➥ // TODO one op - repeated spec? long spec?
view/Footer.jsx:61:       // TODO: show the entry's metadata
view/Footer.jsx:80:             todoList.addObject(new TodoItem()); 
➥ // TODO create default
view/Header.jsx:25: 
➥ // TODO list some meaningful header (apart from the id)
 
npm info start [email protected]
> [email protected] start /todo
> node TodoAppServer.js
Swarm server started port 8000
^C                                      ⇽--- ❷ 在此按Ctrl+C終止程序和容器
$ docker ps –a                         ⇽--- ❸ 運作這個指令檢視已經啟動和移除的容器,以及其ID和狀态(就像程序一樣)
CONTAINER ID  IMAGE         COMMAND         CREATED      
➥  STATUS                    PORTS  NAMES
b9db5ada0461  todoapp:latest  "npm start"  2 minutes ago  
➥  Exited (130) 2 minutes ago  example1
$ docker start example1           ⇽--- ❹ 重新啟動容器,這次是在背景運作
example1
$ docker ps -a
CONTAINER ID    IMAGE           COMMAND    CREATED
➥ STATUS           PORTS                  NAMES
b9db5ada0461  todoapp:latest  "npm start"  8 minutes ago
➥ Up 10 seconds  0.0.0.0:8000->8000/tcp  example1             ⇽--- ❺ 再次運作ps指令檢視發生變化的狀态
$ docker diff example1                          ⇽--- ❻ docker diff子指令顯示了自鏡像被執行個體化成一個容器以來哪些檔案受到了影響
C /todo                               ⇽--- ❼ 修改了/todo目錄
A /todo/.swarm
A /todo/.swarm/TodoItem
A /todo/.swarm/TodoItem/1tlOc02+A~4Uzcz           
A /todo/.swarm/_log                                    ⇽--- ❽ 增加了/todo/.swarm目錄
A /todo/dist
A /todo/dist/LocalTodoApp.app.js
A /todo/dist/TodoApp.app.js
A /todo/dist/react.min.js      

​docker run​

​子指令啟動容器❶。​

​-p​

​标志将容器的8000端口映射到主控端的8000端口上,讀者現在應該可以使用浏覽器通路http://localhost:8000來檢視這個應用程式了。​

​--name​

​标志賦予了容器一個唯一的名稱,以便後面引用。最後的參數是鏡像名稱。 一旦容器啟動,我們按下CTRL-C終止程序和容器❷。讀者可以運作​

​ps​

​指令檢視被啟動且未被移除的容器❸。注意,每個容器都具有自己的容器 ID 和狀态,與程序類似。它的狀态是​

​Exited​

​(已退出),不過讀者可以重新啟動它❹。這麼做之後,注意狀态已經改變為​

​Up​

​(運作中),且容器到主控端的端口映射現在也顯示出來了❺。 ​

​docker diff​

​子指令顯示了自鏡像被執行個體化成一個容器以來哪些檔案受到了影響❻。在這個示例中,修改了todo目錄❼,并增加了其他列出的檔案❽。沒有檔案被删除,這是另一種可能性。

如讀者所見,Docker“包含”環境的事實意味着使用者可以将其視為一個實體,在其上執行的動作是可預見的。這賦予了Docker寬廣的能力——使用者可以影響從開發到生産再到維護的整個軟體生命周期。這種改變正是本文所要描述的,在實踐中展示Docker所能完成的東西。

接下來讀者将了解Docker的另一個核心概念——分層。

1.2.5 Docker分層

Docker分層協助使用者管理在大規模使用容器時會遇到的一個大問題。想象一下,如果啟動了數百甚至數千個to-do應用,并且每個應用都需要将檔案的一份副本存儲在某個地方。

可想而知,磁盤空間會迅速消耗光!預設情況下,Docker在内部使用寫時複制(copy-on-write)機制來減少所需的硬碟空間量(見圖 1-9)。每當一個運作中的容器需要寫入一個檔案時,它會通過将該項目複制到磁盤的一個新區域來記錄這一修改。在執行Docker送出時,這塊磁盤新區域将被當機并記錄為具有自身辨別符的一個層。

Docker實戰:初探Docker

圖1-9 啟動時複制與寫時複制對比

這部分解釋了Docker容器為何能如此迅速地啟動——它們不需要複制任何東西,因為所有的資料已經存儲為鏡像。

寫時複制 寫時複制是計算技術中使用的一種标準的優化政策。在從模闆建立一個新的(任意類型)對象時,隻在資料發生變化時才能将其複制進來,而不是複制整個所需的資料集。依據用例的不同,這能省下相當可觀的資源。

圖1-10展示了建構的to-do應用,它具有我們所感興趣的3層。

因為層是靜态的,是以使用者隻需要在想引用的鏡像之上進行建構,變更的内容都在上一層中。在這個to-do應用中,我們從公開可用的node鏡像建構,并将變更疊加在最上層。

Docker實戰:初探Docker

圖1-10 Docker中to-do應用的檔案系統分層

所有這3層都可以被多個運作中的容器共享,就像一個共享庫可以在記憶體中被多個運作中的程序共享一樣。對于運維人員來說,這是一項至關重要的功能,可以在主控端上運作大量基于不同鏡像的容器,而不至于耗盡磁盤空間。

想象一下,你将所運作的to-do應用作為線上服務提供給付費使用者,可以将服務擴散給大量使用者。如果你正在開發中,可以一下在本地機器上啟動多個不同的環境。如果你正在進行測試,可以比之前同時運作更多測試,速度也更快。有了分層,所有這些東西都成為了可能。

通過使用Docker建構和運作一個應用程式,讀者開始見識到Docker能給工作流帶來的威力。重制并分享特定的環境,并能在不同的地方落地,讓使用者在開發過程中兼具靈活性和可控性。

1.3 小結

依據讀者以往的Docker經驗不同,本文可能存在一個陡峭的學習曲線。我們在短時間内讨論了大量的基礎内容。

讀者現在應該:

  • 了解Docker鏡像是什麼;
  • 知道Docker分層是什麼,以及為什麼它很有用;
  • 能夠從一個基礎鏡像送出一個新的Docker鏡像;
  • 知道Dockerfile是什麼。

我們已經使用這項知識:

  • 建立了一個有用的應用程式;
  • 毫不費力地重制了一個應用程式中的狀态。

繼續閱讀