一杯茶的時間,上手Docker

NO IMAGE

努力工作,然後進入夢鄉,“工作”和“做夢”之間好像沒有任何關聯;編寫代碼,然後部署應用,這兩者似乎也是天各一邊。然而果真如此嗎?這篇文章將通過《盜夢空間》的方式打開 Docker,讓你實現從“做夢”到“築夢”的實質性轉變。在原先的“做夢”階段(手動配置和部署),一切都充滿了隨機性和不可控性,你有時甚至都無法回憶起具體做的每一步;而在“築夢”階段(藉助 Docker),你將通過自動化、高度可重複且可追蹤的方式輕鬆實現任何配置和部署任務。希望讀完這篇文章的你,也能成為一個優秀的“築夢師”!

準備工作

寫在前面的話

很多朋友跟我們反饋說,“一杯茶”純粹就是忽悠人,寫那麼長,怎麼可能在一杯茶的時間內看完?實際上,“飲茶”的方式因人而異,不同的讀者自有不同的節奏。你完全可以選擇一目十行、甚至只瀏覽一下插圖,幾分鐘的時間便能看完;也可以選擇跟著我們一步一步動手實踐,甚至在有些地方停下來思考一番,雖然需要花更多的時間,但是我們相信這份投入的時間一定是值得的。

其次,我們想確認你是否是這篇文章的受眾:

  1. 如果你已經是每天操縱數以千計容器的 DevOps 大佬,那麼很抱歉打擾了,這篇文章對你來說可能過於簡單;
  2. 如果你已經比較熟悉 Docker 了,想要更多的實戰操作經驗,這篇文章能夠較好地幫助你複習和鞏固關鍵的知識點;
  3. 如果你只聽說過 Docker,但是基本上不會用,那麼這篇文章就是為你準備的!只不過友情提醒:Docker 上手略有難度,想要真正掌握需要投入足夠的時間,認真讀完這篇文章一定能讓你有相當大的進步

最後,每個小節的結構都是實戰演練 + 回憶與昇華。回憶與昇華部分是筆者花了不少時間對優質資源進行蒐集和整合而成,並結合了自身使用容器的經驗,相信能夠進一步加深你的理解,如果你趕時間的話,也可以略過哦。

PS:這篇文章並沒有像常規的 Docker 教程一樣上來就鄭重其事地講 Docker 的背景、概念、優勢(很有可能你已經聽到耳朵生繭了hhh),而是完全通過實踐的方式直觀地理解 Docker。在最後,我們還是會貼出經典的 Docker 架構圖,結合之前的操作體驗,相信你會有了然於胸的感覺。

前提條件

在正式閱讀這篇文章之前,我們希望你已經具備以下條件:

  • 最基本的命令行操作經驗
  • 對計算機網絡有一定的瞭解,特別是應用層中的端口這一概念
  • 最好經歷過配環境、部署項目的痛苦掙扎😭

我們將實現什麼

現在假定你手頭已經有了一個 React 編寫的“夢想清單”項目,如下面這個動圖所示:

一杯茶的時間,上手Docker

我們將在這篇文章中教你一步步用 Docker 將這個應用容器化,用 Nginx 服務器提供構建好的靜態頁面。

你將學會

一杯茶的時間,上手Docker

這篇文章不會涉及 …

當然咯,這篇文章作為一篇入門性質的教程,以下進階內容不會涉及:

  • Docker 網絡機制
  • 數據卷和 Bind Mount 實現數據分享
  • Docker Compose
  • 多階段構建(Multi-stage Build)
  • Docker Machine 工具
  • 容器編排技術,例如 Kubernetes 以及 Docker Swarm

以上進階知識我們會馬上推出相關教程,敬請期待。

安裝 Docker

我們推薦各個平臺用以下方式安裝 Docker(經過我們反覆測試哦)。

Windows

菜鳥教程中詳細介紹了 Win7/8 以及 Win10 的不同推薦安裝方法。注意 Win10 建議開啟 Hyper-V 虛擬化技術。

macOS

可通過點擊官方下載鏈接下載並安裝 DMG 文件(如果速度慢的話可以把鏈接複製進迅雷哦)。安裝完畢之後,點擊 Docker 應用圖標即可打開。

Linux

對於各大 Linux 發行版(Ubuntu、CentOS 等等),我們推薦用官方腳本進行安裝,方便快捷:

curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

然後推薦將 docker 的權限移交給非 root 用戶,這樣使用 docker 就不需要每次都 sudo 了:

sudo usermod -aG docker $USER

註銷用戶或者重啟之後就會生效。然後通過 systemd 服務配置 Docker 開機啟動:

sudo systemctl enable docker

配置鏡像倉庫

默認的鏡像倉庫 Docker Hub 在國外,國內拉取速度比較感人。建議參考這篇文章配置鏡像加速。

鏡像與容器:築夢師的圖紙和夢境

鏡像(Image)和容器(Container)是 Docker 中最為基礎也是最為關鍵的兩個概念,前者就是築夢師的圖紙,根據這張圖紙的內容,就能夠生成完全可預測的夢境(也就是後者)。

提示

如果你覺得這個比喻難以理解,那麼可以通過面向對象編程中“類”(class)和“實例”(instance)這兩個概念進行類比,“類”就相當於“鏡像”,“實例”就相當於“容器”。

小試牛刀:夢開始的地方

在略微接觸了鏡像與容器這兩個基礎概念之後,我們打算暫停理論的講解,而先來一波小實驗讓你快速感受一下。

實驗一:Hello World!

按照歷史慣例,我們運行一下來自 Docker 的 Hello World,命令如下:

docker run hello-world

輸出如下:

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
1b930d010525: Pull complete
Digest: sha256:fb158b7ad66f4d58aa66c4455858230cd2eab4cdf29b13e5c3628a6bfc2e9f05
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
...

不就打印了一個字符串然後退出嗎,有這麼神奇?其實 Docker 為我們默默做了以下事情:

  1. 檢查本地是否有指定的 hello-world:latest 鏡像(latest 是鏡像標籤,後面會細講),如果沒有,執行第 2 步,否則直接執行第 3 步
  2. 本地沒有指定鏡像(Unable to find xxx locally),從 Docker Hub 下載到本地
  3. 根據本地的 hello-world:latest 鏡像創建一個新的容器並運行其中的程序
  4. 運行完畢後,容器退出,控制權返回給用戶

實驗二:運行一個 Nginx 服務器

感覺太簡單?我們來嘗試一個高級一點的:運行一個 Nginx 服務器。運行以下命令

docker run -p 8080:80 nginx

運行之後,你會發現一直卡住,也沒有任何輸出,但放心你的電腦並沒有死機。讓我們打開瀏覽器訪問 localhost:8080

一杯茶的時間,上手Docker

這時候熟悉 Nginx 的朋友可能就坐不住了:就一個簡簡單單的 docker run 命令,就搞定了 Nginx 服務器的安裝和部署??沒錯,你可以繼續訪問一些不存在的路由,比如 localhost:8080/what,同樣會提示 404。這時候我們再看 Docker 容器的輸出,就有內容(服務器日誌)了:

一杯茶的時間,上手Docker

總結一下剛才 Docker 做的事情:

  1. 檢查本地是否有指定的 nginx:latest 鏡像(關於 latest 標籤,後面會細講),如果沒有,執行第 2 步,否則直接執行第 3 步
  2. 本地沒有指定鏡像(Unable to find xxx locally),從 Docker Hub 下載到本地
  3. 根據本地的 nginx:latest 鏡像創建一個新的容器,並通過 -p--publish)參數建立本機的 8080 端口與容器的 80 端口之間的映射,然後運行其中的程序
  4. Nginx 服務器程序保持運行,容器也不會退出

提示

端口映射規則的格式為 <本機端口>:<容器端口>。Nginx 容器默認開放了 80 端口,我們通過設置 8080:80 的端口映射規則,就可以在本機(容器之外)通過訪問 localhost:8080 訪問,甚至可以在同一局域網內通過內網 IP 訪問,這篇文章的最後會演示哦。

實驗三:後臺運行 Nginx

看上去很酷,不過像 Nginx 服務器這樣的進程我們更希望把它拋到後臺一直運行。按 Ctrl + C 退出當前的容器,然後再次運行以下命令:

docker run -p 8080:80 --name my-nginx -d nginx

注意到與之前不同的是,我們:

  • 加了一個參數 --name,用於指定容器名稱為 my-nginx
  • 加了一個選項 -d--detach),表示“後臺運行”

警告

容器的名稱必須是唯一的,如果已經存在同一名稱的容器(即使已經不再運行)就會創建失敗。如果遇到這種情況,可以刪除之前不需要的容器(後面會講解怎麼刪除)。

Docker 會輸出一串長長的 64 位容器 ID,然後把終端的控制權返回給了我們。我們試著訪問 localhost:8080,還能看到那一串熟悉的 Welcome to nginx!,說明服務器真的在後臺運行起來了。

那我們怎麼管理這個服務器呢?就像熟悉的 UNIX ps 命令一樣,docker ps 命令可以讓我們查看當前容器的狀態:

docker ps

輸出結果是這樣的:

一杯茶的時間,上手Docker

提示

由於 docker ps 的輸出比較寬,如果你覺得結果不直觀的話可以把終端(命令行)拉長,如下圖所示:

一杯茶的時間,上手Docker

從這張表中,就可以清晰地看到了我們在後臺運行的 Nginx 服務器容器的一些信息:

  • 容器 ID(Container ID)為 0bddac16b8d8(你機器上的可能不一樣)
  • 所用鏡像(Image)為 nginx
  • 運行命令/程序(Command)為 nginx -g 'daemon of...,這個是 Nginx 鏡像自帶的運行命令,暫時不用關心
  • 創建時間(Created)為 45 seconds ago(45 秒鐘之前)
  • 當前狀態(Status)為 Up 44 seconds(已運行 44 秒鐘)
  • 端口(Ports)為 0.0.0.0:8080->80/tcp,意思是訪問本機的 0.0.0.0:8080 的所有請求會被轉發到該容器的 TCP 80 端口
  • 名稱(Names)為剛才指定的 my-nginx

如果我們要讓容器停下來,通過 docker stop 命令指定容器名稱或 ID 進行操作即可,命令如下:

docker stop my-nginx
# docker stop 0bddac16b8d8

注意

如果指定容器 ID 的話,記得要換成自己機器上真實的 ID 哦。此外,在沒有衝突的情況下,ID 可以只寫前幾位字符,例如寫 0bd 也是可以的。

實驗四:交互式運行

在過了一把 Nginx 服務器的癮之後,我們再來體驗一下 Docker 容器的另一種打開方式:交互式運行。運行以下命令,讓我們進入到一個 Ubuntu 鏡像中:

docker run -it --name dreamland ubuntu

可以看到我們加了 -it 選項,等於是同時指定 -i--interactive,交互式模式)和 -t--tty,分配一個模擬終端) 兩個選項。以上命令的輸出如下:

Unable to find image 'ubuntu:latest' locally
latest: Pulling from library/ubuntu
2746a4a261c9: Pull complete
4c1d20cdee96: Pull complete
0d3160e1d0de: Pull complete
c8e37668deea: Pull complete
Digest: sha256:9207fc49baba2e62841d610598cb2d3107ada610acd4f47252faf73ed4026480
Status: Downloaded newer image for ubuntu:latest
[email protected]:/#

等下,我們怎麼被拋在了一個新的命令行裡面?沒錯,你現在已經在這個 Ubuntu 鏡像構築的“夢境”之中,你可以隨意地“遊走”,運行一些命令:

[email protected]:/# whoami
root
[email protected]:/# ls
bin   dev  home  lib64  mnt  proc  run   srv  tmp  var
boot  etc  lib   media  opt  root  sbin  sys  usr

例如我們在上面運行了 whoamils 命令,你基本上可以確定現在已經在“夢境”(容器)之中了。這時候打開一個新的終端(命令行),運行 docker ps 命令,就可以看到正在運行中的 Ubuntu 鏡像:

一杯茶的時間,上手Docker

回到之前的容器中,按 Ctrl + D (或者輸入 exit 命令)即可退出。你可以在之前查看 docker ps 的終端再次檢查容器是否已經被關閉了。

銷燬容器:聽夢碎的聲音

築夢師難免會有失敗的作品,而我們剛才創建的 Docker 容器也只是用於初步探索,後續不會再用到。由於 Docker 容器是直接存儲在我們本地硬盤上的,及時清理容器也能夠讓我們的硬盤壓力小一些。我們可以通過以下命令查看所有容器(包括已經停止的):

docker ps -a

-a--all)用於顯示所有容器,如果不加的話只會顯示運行中的容器。可以看到輸出如下(這裡我把終端拉寬了,方便你看):

一杯茶的時間,上手Docker

提示

你也許觀察到,之前的實驗一和實驗二中我們沒有指定容器名稱,Docker 為我們取了頗為有趣的默認容器名稱(比如 hardcore_nash),格式是一個隨機的形容詞加上一位著名科學家/程序員的姓氏(運氣好的話,你可能會看到 Linux 之父 torvalds 哦)。

類似 Shell 中的 rm 命令,我們可以通過 docker rm 命令銷燬容器,例如刪除我們之前創建的 dreamland 容器:

docker rm dreamland
# 或者指定容器 ID,記得替換成自己機器上的
# docker rm 94279dbf5d93

但如果我們想要銷燬所有容器怎麼辦?一次次輸入 docker rm 刪除顯然不方便,可以通過以下命令輕鬆刪除所有容器

docker rm $(docker ps -aq)

docker ps -aq 會輸出所有容器的 ID,然後作為參數傳給 docker rm 命令,就可以根據 ID 刪除所有容器啦。

危險!

執行之前一定要仔細檢查是否還有有價值的容器(特別是業務數據),因為容器一旦刪除無法再找回(這裡不討論硬盤恢復這種黑科技)!

回憶與昇華

關於端口映射

可能有些同學還是沒有完全理解“端口映射”的概念,以 8080:80 這一條映射規則為例,我們可以用“傳送門”的比喻來理解(下面的圖是《傳送門2》遊戲的封面):

一杯茶的時間,上手Docker

還是把容器比作“夢境”,把本機環境比作“現實”,通過建立端口映射,訪問本機的 8080 端口的請求就會被“傳送”到容器的 80 端口,是不是很神奇呢。

容器生命週期:夢境地圖

跟著做完上面四個小實驗之後,你或許已經對 Docker 容器有了非常直觀的感受和理解了。是時候祭出這張十(sang)分(xin)經(bing)典(kuang)的 Docker 容器生命週期圖了(來源:docker-saigon.github.io/post/Docker…

一杯茶的時間,上手Docker

這張圖乍一看頗具視覺衝擊力,甚至會讓你感覺不知所措。沒事,我們大致地解讀這張圖裡面的四類元素:

  1. 容器狀態(帶顏色的圓圈):包括已創建(Created)、運行中(Running)、已暫停(Paused)、已停止(Stopped)以及被刪除(Deleted)
  2. Docker 命令(箭頭上以 docker 開頭的文字):包括 docker rundocker createdocker stop 等等
  3. 事件(矩形框):包括 createstartdiestop 還有 OOM(內存耗盡)等等
  4. 還有一個條件判斷,根據重啟策略(Restart Policy)判斷是否需要重新啟動容器

OK,這張圖還是很難一下子理解,不過還記得剛才我們做的四個小實驗嗎?我們實際上走了一共兩條路徑(也是日常使用中走的最多的路),接下來將一一進行分析。

第一條路徑(自然結束)

一杯茶的時間,上手Docker

如上圖所示:

  • 我們先通過 docker run 命令,直接創建(create)並啟動(start)一個容器,進入到運行狀態(Running)
  • 然後程序運行結束(例如輸出 Hello World 之後,或者通過 Ctrl + C 使得程序終止),容器死亡(die)
  • 由於我們沒有設置重啟策略,所以直接進入到停止狀態(Stopped)
  • 最後通過 docker rm 命令銷燬容器,進入到被刪除狀態(Deleted)

第二條路徑(強制結束)

一杯茶的時間,上手Docker

  • 我們還是通過 docker run 命令,直接創建(create)並啟動(start)一個容器,進入到運行狀態(Running)
  • 然後通過 docker stop 命令殺死容器中的程序(die)並停止(stop)容器,最終進入到停止狀態(Stopped)
  • 最後通過 docker rm 命令銷燬容器,進入到被刪除狀態(Deleted)

提示

有些眼尖的讀者可能發現 docker killdocker stop 的功能非常相似,它們之前存在細微的區別: kill 命令向容器內運行的程序直接發出 SIGKILL 信號(或其他指定信號),而 stop 則是先發出 SIGTERM 再發出 SIGKILL 信號,屬於優雅關閉(Graceful Shutdown)。

一條捷徑:刪除運行中的容器

生命週期圖其實有一條捷徑沒有畫出來:直接從運行中(或暫停中)到被刪除,通過給 docker rm 命令加上選項 -f--force,強制執行)就可以實現:

# 假設 dreamland 還在運行中
docker rm -f dreamland

同樣地,我們可以刪除所有容器,無論處於什麼狀態:

docker rm -f $(docker ps -aq)

自由探索

你儘可以自由探索其他我們沒走過的路線,例如嘗試再次啟動之前已經停止的容器(docker start),或者暫停正在運行的容器(docker pause)。幸運的是,docker help 命令可以為我們提供探索的指南針,例如我們想了解 start 命令的使用方法:

$ docker help start
Usage:	docker start [OPTIONS] CONTAINER [CONTAINER...]
Start one or more stopped containers
Options:
-a, --attach                  Attach STDOUT/STDERR and forward signals
--checkpoint string       Restore from this checkpoint
--checkpoint-dir string   Use a custom checkpoint storage directory
--detach-keys string      Override the key sequence for
detaching a container
-i, --interactive             Attach container's STDIN

讀到這裡,相信你已經瞭解瞭如何利用現有的鏡像創造容器,並進行管理。在接下來,我們將帶你創建自己的 Docker 鏡像,開始成為一名標準的“築夢師”!

容器化第一個應用:開啟築夢之旅

在之前的步驟中,我們體驗了別人為我們提前準備好的鏡像(例如 hello-worldnginxubuntu),這些鏡像都可以在 Docker Hub 鏡像倉庫中找到。在這一步,我們將開始築夢之旅:學習如何容器化(Containerization)你的應用。

正如開頭所說,我們將容器化一個全棧的”夢想清單“應用,運行以下命令來獲取代碼,然後進入項目:

git clone -b start-point https://github.com/tuture-dev/docker-dream.git
cd docker-dream

在這一步中,我們將容器化這個用 React 編寫的前端應用,用 Nginx 來提供前端頁面的訪問。

什麼是容器化

容器化包括三個階段:

  • 編寫代碼:我們已經提供了寫好的代碼
  • 構建鏡像:也就是這一節的核心內容,下面會詳細展開
  • 創建和運行容器:通過容器的方式運行我們的應用

構建鏡像

構建 Docker 鏡像主要包括兩種方式:

  1. 手動:根據現有的鏡像創建並運行一個容器,進入其中進行修改,然後運行 docker commit 命令根據修改後的容器創建新的鏡像
  2. 自動:創建 Dockerfile 文件,指定構建鏡像的命令,然後通過 docker build 命令直接創建鏡像

由於篇幅有限,這篇文章只會講解使用最為廣泛的第二種創建鏡像的方式。

一些準備工作

我們先把前端項目 client 構建成一個靜態頁面。確保你的機器上已經安裝 Node 和 npm(點擊這裡下載,或使用 nvm),然後進入到 client 目錄下,安裝所有依賴,並構建項目:

cd client
npm install
npm run build

等待一陣子後,你應該可以看到 client/build 目錄,存放了我們要展示的前端靜態頁面。

創建 Nginx 配置文件 client/config/nginx.conf,代碼如下:

server {
listen 80;
root /www;
index index.html;
sendfile on;
sendfile_max_chunk 1M;
tcp_nopush on;
gzip_static on;
location / {
try_files $uri $uri/ /index.html;
}
}

不熟悉 Nginx 配置的同學不用擔心哦,直接複製粘貼就可以了。上面的配置大致意思是:監聽 80 端口,網頁根目錄在 /www,首頁文件是 index.html,如果訪問 / 則提供文件 index.html

創建 Dockerfile

然後就是這一步驟中最重要的代碼:Dockerfile!創建 client/Dockerfile 文件,代碼如下:

FROM nginx:1.13
# 刪除 Nginx 的默認配置
RUN rm /etc/nginx/conf.d/default.conf
# 添加自定義 Nginx 配置
COPY config/nginx.conf /etc/nginx/conf.d/
# 將前端靜態文件拷貝到容器的 /www 目錄下
COPY build /www

可以看到我們用了 Dockerfile 中的三個指令:

  • FROM 用於指定基礎鏡像,這裡我們基於 nginx:1.13 鏡像作為構建的起點
  • RUN 命令用於在容器內運行任何命令(當然前提是命令必須存在)
  • COPY 命令用於從 Dockerfile 所在的目錄拷貝文件到容器指定的路徑

是時候來構建我們的鏡像了,運行以下命令:

# 如果你已經在 client 目錄中
#(注意最後面有個點,代表當前目錄)
docker build -t dream-client .
# 如果你回到了項目根目錄
docker build -t dream-client client

可以看到我們指定了 -t--tag,容器標籤)為 dream-client,最後指定了構建容器的上下文目錄(也就是 當前目錄 .client)。

運行以上的命令之後,你會發現:

Sending build context to Docker daemon:66.6MB

而且這個數字還在不斷變大,就像黑客科幻電影中的場景一樣,最後應該停在了 290MB 左右。接著運行了一系列的 Step(4 個),然後提示鏡像構建成功。

為啥這個構建上下文(Build Context)這麼大?因為我們把比“黑洞”還“重”的 node_modules 也加進去了!(忍不住想起了下面這張圖)

一杯茶的時間,上手Docker

使用 .dockerignore 忽略不需要的文件

Docker 提供了類似 .gitignore 的機制,讓我們可以在構建鏡像時忽略特定的文件或目錄。創建 client/.dockerignore 文件(注意 dockerignore 前面有一個點):

node_modules

很簡單,我們只想忽略掉可怕的 node_modules。再次運行構建命令:

docker build -t dream-client .

一杯茶的時間,上手Docker

太好了!這次只有 1.386MB,而且速度也明顯快了很多!

運行容器

終於到了容器化的最後一步——創建並運行我們的容器!通過以下命令運行剛才創建的 dream-client 鏡像:

docker run -p 8080:80 --name client -d dream-client

與之前類似,我們還是設定端口映射規則為 8080:80,容器名稱為 client,並且通過 -d 設置為後臺運行。然後訪問 localhost:8080

一杯茶的時間,上手Docker

成功了!一開始定下的三個夢想也都完成了!

提示

甚至,我們已經可以通過內網來訪問“夢想清單”了。Linux 或 macOS 的同學可以在終端輸入 ifconfig 命令查詢本機內網 IP,Windows 的同學則是在 CMD 輸入 ipconfig 查詢本機內網 IP,一般是以 10172.16~172.31192.168 開頭。例如我的內網 IP 是 192.168.0.2,那麼在同一局域網下(一般是 WiFi),可以用其他設備(比如說你的手機)訪問 192.168.0.2:8080

回憶與昇華

關於鏡像標籤

在剛才的實戰中,你也許已經注意到在拉取和構建鏡像時,Docker 總是會為我們加上一個 :latest 標籤,這個 :latest 的含義便是“最新”的意思。和軟件的版本機制一樣,鏡像也可以通過標籤實現“版本化”。

注意

latest 字面上的意思的確是“最新的”,但也只是一個普通的標籤,並不能確保真的是“最新的”,更不會自動更新。更多討論請參考這篇文章

實際上,我們完全可以在拉取或構建鏡像時指定標籤(通常被認為是一種好的做法):

docker pull nginx:1.13
docker build -t dream-client:1.0.0

還可以給現有的鏡像打上標籤:

# 把默認的 latest 鏡像打上一個 newest 標籤
docker tag dream-client dream-client:newest
# 甚至可以同時修改鏡像的名稱和標籤
docker tag dream-client:1.0.0 dream-client2:latest

可以看到,標籤未必一定是版本,還可以是任何字符串(當然最好要有意義,否則過了一陣子你也不記得這個打了這個標籤的容器有什麼作用了)。

關於 Dockerfile

Dockerfile 實際上是默認名稱,我們當然可以取一個別的名字,例如 myDockerfile,然後在構建鏡像時指定 -f--file)參數即可:

docker build -f myDockerfile -t dream-client .

這裡舉兩個經典的使用場景:

  1. 例如在 Web 開發時,分別創建 Dockerfile.dev 用於構建開發鏡像,創建 Dockerfile.prod 構建生產環境下的鏡像;
  2. 在訓練 AI 模型時,創建 Dockerfile.cpu 用於構建用 CPU 訓練的鏡像,創建 Dockerfile.gpu 構建用 GPU 訓練的鏡像。

再次思考鏡像和容器的關係

經過剛才的容器化實戰,相信你對鏡像和容器的關係又有了新的理解。請看下面這張圖:

一杯茶的時間,上手Docker

在之前的“小試牛刀”環節中(用綠色箭頭標出),我們:

  1. 通過 docker pull 從 Docker 鏡像倉庫拉取鏡像到本地
  2. 通過 docker run 命令,根據鏡像創建並運行容器
  3. 通過 docker stop 等命令操作容器,使其發生各種狀態轉變

而在這一節的容器化實戰中(用紅色箭頭標出),我們:

  1. 通過 docker build 命令,根據一個 Dockerfile 文件構建鏡像
  2. 通過 docker tag 命令,給鏡像打上標籤,得到一個新鏡像
  3. (由於篇幅有限沒有講)通過 docker commit 命令,將一個現有的容器轉化為鏡像

俯瞰全景:Docker 架構圖

是時候拿出經典的 Docker 架構圖了:

一杯茶的時間,上手Docker

可以看到,Docker 遵循經典的客戶端-服務器架構(client-server),核心組成部分包括:

  • 服務器(也就是 Docker 守護進程),在 Linux 系統中也就是 dockerd 命令
  • 服務器暴露出的 REST API,提供了與守護進程通信和操作的接口
  • 客戶端(也就是我們一直在用的命令行程序 docker

至此,這篇 Docker 快速入門實戰教程也就結束啦,希望你已經對 Docker 的概念和使用有了初步的理解。後續我們還會發表 Docker 進階的內容(例如 Network 網絡、Volume 數據卷、Docker Compose 等等),手把手帶大家部署一個全棧應用(前後端和數據庫)到雲主機(或任何你能夠登錄的機器),敬請期待~

想要學習更多精彩的實戰技術教程?來圖雀社區逛逛吧。

一杯茶的時間,上手Docker

相關文章

【Python機器學習基礎】1.人工智能、機器學習、深度學習介紹

阿里面試官讓我講講Unicode,我講了3秒說沒了,面試官說你可真菜

網頁設計中最常用的5種配圖

[前端]實戰年終盤點