Docker 是 Golang 編寫的, 自 2013 年推出以來,受到越來越多的開發者的關註。如今Docker無處不在,這是不爭的事實。開發人員都很喜歡它,運維工程師也需要它。他們都需要深入瞭解如何在關鍵業務環境中構建和維護符合生產級別要求的容器化應用,那麼什麼是Docker?運維和開發視角有什麼不同?本文將告訴你答案。
本文摘自《深入淺出Docker》,就是那本美亞作業系統排名第一的Docker入門書,被業內譽為:“高中生也能讀懂的 Docker入門教程”。
本文從下麵兩部分內容講解Docker。
-
運維(Ops)視角。
-
開發(Dev)視角。
在運維視角中,主要包括下載映象、執行新的容器、登入新容器、在容器內執行命令,以及銷毀容器。
在開發視角中,更多關註與應用相關的內容。《深入淺出Docker內》會從GitHub拉取一些應用程式碼,解釋其中的Dockerfile,將應用容器化,併在容器中執行它們。
透過上面兩部分內容,你可以從整體上理解Docker究竟是什麼,以及主要元件之間是如何相互配合的。推薦讀者對開發和運維兩部分內容都要閱讀。
1.1 運維視角
當讀者安裝Docker的時候,會涉及兩個主要元件:Docker客戶端和Docker daemon(有時也被稱為“服務端”或者“引擎”)。
daemon實現了Docker引擎的API。
使用Linux預設安裝時,客戶端與daemon之間的通訊是透過本地IPC/UNIX Socket完成的(/var/run/docker.sock
);在Windows上是透過名為npipe:////./pipe/docker_engine
的管道(pipe)完成的。讀者可以使用docker version
命令來檢測客戶端和服務端是否都已經成功執行,並且可以互相通訊。
1> docker version
2Client:
3 Version: 18.01.0-ce
4 API version: 1.35
5 Go version: go1.9.2
6 Git commit: 03596f5
7 Built: Wed Jan 10 20:11:05 2018
8 OS/Arch: linux/amd64
9 Experimental: false
10 Orchestrator: swarm
11
12Server:
13 Engine:
14 Version: 18.01.0-ce
15 API version: 1.35 (minimum version 1.12)
16 Go version: go1.9.2
17 Git commit: 03596f5
18 Built: Wed Jan 10 20:09:37 2018
19 OS/Arch: linux/amd64
20 Experimental: false
如果讀者能成功獲取來自客戶端和服務端的響應,那麼可以繼續後面的操作。如果讀者正在使用 Linux,並且服務端傳回了異常響應,則可嘗試在命令的前面加上 sudo
——sudo docker version
。如果加上sudo
之後命令正常執行,那麼讀者需要將當前使用者加入到docker
使用者組,或者給本書後面的命令都加上sudo
字首。
1.1.1 映象
將Docker映象理解為一個包含了OS檔案系統和應用的物件會很有幫助。如果讀者實際操作過,就會認為與虛擬機器模板類似。虛擬機器模板本質上是處於關機狀態的虛擬機器。在Docker世界中,映象實際上等價於未執行的容器。如果讀者是一名開發者,可以將映象比作類(Class)。
在Docker主機上執行docker image ls
命令。
1$ docker image ls
2REPOSITORY TAG IMAGE ID CREATED SIZE
如果讀者執行命令環境是剛完成Docker安裝的主機,或者是Play With Docker,那麼Docker主機中應當沒有任何映象,命令輸出內容會如上所示。
在Docker主機上獲取映象的操作被稱為拉取(pulling)。如果使用Linux,那麼會拉取ubuntu:latest
映象;如果使用Windows,則會拉取microsoft/powershell:nanoserver
映象。
1latest: Pulling from library/ubuntu
250aff78429b1: Pull complete
3f6d82e297bce: Pull complete
4275abb2c8a6f: Pull complete
59f15a39356d6: Pull complete
6fc0342a94c89: Pull complete
7Digest: sha256:fbaf303...c0ea5d1212
8Status: Downloaded newer image for ubuntu:latest
再次執行docker image ls
命令來檢視剛剛拉取的映象。
1$ docker images
2REPOSITORY TAG IMAGE ID CREATED SIZE
3ubuntu latest 00fd29ccc6f1 3 weeks ago 111MB
關於映象的儲存位置以及映象內部構成,本書會在後續的章節中詳細介紹。現在,讀者只需知道映象包含了基礎作業系統,以及應用程式執行所需的程式碼和依賴包。剛才拉取的ubuntu
映象有一個精簡版的Ubuntu Linux檔案系統,其中包含部分Ubuntu常用工具。而Windows示例中拉取的microsoft/powershell
映象,則包含了帶有PowerShell的Windows Nano Server作業系統。
如果拉取瞭如nginx
或者microsoft/iis
這樣的應用容器,則讀者會得到一個包含作業系統的映象,並且在映象中還包括了執行Nginx或IIS所需的程式碼。
重要的是,Docker的每個映象都有自己的唯一ID。使用者可以透過取用映象的ID
或名稱來使用映象。如果使用者選擇使用映象ID,通常只需要輸入ID開頭的幾個字元即可——因為ID是唯一的,Docker知道使用者想取用的具體映象是哪個。
1.1.2 容器
到目前為止,讀者已經擁有一個拉取到本地的映象,可以使用docker container run
命令從映象來啟動容器。
在Linux中啟動容器的命令如下。
1$ docker container run -it ubuntu:latest /bin/bash
2root@6dc20d508db0:/#
在Windows中啟動容器的命令如下。
1> docker container run -it microsoft/powershell:nanoserver pwsh.exe
2
3Windows PowerShell
4Copyright (C) 2016 Microsoft Corporation. All rights reserved.
5PS C:\>
仔細觀察上面命令的輸出內容,會註意到每個實體中的提示符都發生了變化。這是因為-it
引數會將Shell切換到容器終端——現在已經位於容器內部了!
接下來分析一下docker container run
命令。docker container run
告訴Docker daemon啟動新的容器。其中-it
引數告訴Docker開啟容器的互動樣式並將讀者當前的Shell連線到容器終端(在容器章節中會詳細介紹)。接下來,命令告訴Docker,使用者想基於ubuntu:latest
映象啟動容器(如果使用者使用Windows,則是基於microsoft/powershell:nanoserver
映象)。最後,命令告訴Docker,使用者想要在容器內部執行哪個行程。對於Linux示例來說是執行Bash Shell,對於Windows示例來說則是執行PowerShell。
在容器內部執行ps
命令檢視當前正在執行的全部行程。
Linux示例如下。
1root@6dc20d508db0:/# ps -elf
2F S UID PID PPID NI ADDR SZ WCHAN STIME TTY TIME CMD
34 S root 1 0 0 - 4560 wait 13:38 ? 00:00:00 /bin/bash
40 R root 9 1 0 - 8606 - 13:38 ? 00:00:00 ps -elf
Windows示例如下。
1PS C:\> ps
2
3Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
4------- ------ ----- ----- ------ -- -- -----------
5 0 5 964 1292 0.00 4716 4 CExecSvc
6 0 5 592 956 0.00 4524 4 csrss
7 0 0 0 4 0 0 Idle
8 0 18 3984 8624 0.13 700 4 lsass
9 0 52 26624 19400 1.64 2100 4 powershell
10 0 38 28324 49616 1.69 4464 4 powershell
11 0 8 1488 3032 0.06 2488 4 services
12 0 2 288 504 0.00 4508 0 smss
13 0 8 1600 3004 0.03 908 4 svchost
14 0 12 1492 3504 0.06 4572 4 svchost
15 0 15 20284 23428 5.64 4628 4 svchost
16 0 15 3704 7536 0.09 4688 4 svchost
17 0 28 5708 6588 0.45 4712 4 svchost
18 0 10 2028 4736 0.03 4840 4 svchost
19 0 11 5364 4824 0.08 4928 4 svchost
20 0 0 128 136 37.02 4 0 System
21 0 7 920 1832 0.02 3752 4 wininit
22 0 8 5472 11124 0.77 5568 4 WmiPrvSE
Linux容器中僅包含兩個行程。
-
PID 1:代表
/bin/bash
行程,該行程是透過docker container run
命令來通知容器執行的。 -
PID 9:代表
ps -elf
行程,檢視當前執行中行程所使用的命令/程式。
命令輸出中展示的ps -elf
行程存在一定的誤導,因為這個程式在ps
命令退出後就結束了。這意味著容器內長期執行的行程其實只有/bin/bash
。
Windows 容器執行中的行程會更多,這是由 Windows 作業系統工作方式決定的。雖然Windows容器中的行程比Linux容器要多,但與常見的Windows伺服器相比,其行程數量卻是明顯偏少的。
按Ctrl-PQ組合鍵
,可以在退出容器的同時還保持容器執行。這樣Shell就會傳回到Docker主機終端。可以透過檢視Shell提示符來確認。
現在讀者已經傳回到Docker主機的Shell提示符,再次執行ps
命令。
Linux示例如下。
1$ ps -elf
2F S UID PID PPID NI ADDR SZ WCHAN TIME CMD
34 S root 1 0 0 - 9407 - 00:00:03 /sbin/init
41 S root 2 0 0 - 0 - 00:00:00 [kthreadd]
51 S root 3 2 0 - 0 - 00:00:00 [ksoftirqd/0]
61 S root 5 2 -20 0 - 00:00:00 [kworker/0:0H]
71 S root 7 2 -0 - 0 - 00:00:00 [rcu_sched]
8
90 R ubuntu 22783 22475 0 - 9021 - 00:00:00 ps -elf
Windows示例如下。
1> ps
2Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName
3------- ------ ----- ----- ------ -- -- -----------
4 220 11 7396 7872 0.33 1732 0 amazon-ssm-agen
5 84 5 908 2096 0.00 2428 3 CExecSvc
6 87 5 936 1336 0.00 4716 4 CExecSvc
7 203 13 3600 13132 2.53 3192 2 conhost
8 210 13 3768 22948 0.08 5260 2 conhost
9 257 11 1808 992 0.64 524 0 csrss
10 116 8 1348 580 0.08 592 1 csrss
11 85 5 532 1136 0.23 2440 3 csrss
12 242 11 1848 952 0.42 2708 2 csrss
13 95 5 592 980 0.00 4524 4 csrss
14 137 9 7784 6776 0.05 5080 2 docker
15 401 17 22744 14016 28.59 1748 0 dockerd
16 307 18 13344 1628 0.17 936 1 dwm
17 <SNIP>
18 1888 0 128 136 37.17 4 0 System
19 272 15 3372 2452 0.23 3340 2 TabTip
20 72 7 1184 8 0.00 3400 2 TabTip32
21 244 16 2676 3148 0.06 1880 2 taskhostw
22 142 7 6172 6680 0.78 4952 3 WmiPrvSE
23 148 8 5620 11028 0.77 5568 4 WmiPrvSE
可以看到與容器相比,Docker主機中執行的行程數要多很多。Windows容器中執行的行程要遠少於Windows主機,Linux容器中的行程數也遠少於Linux主機。
在之前的步驟當中,是使用Ctrl-PQ組合鍵
來退出容器的。在容器內部使用該操作可以退出當前容器,但不會殺死容器行程。讀者可以透過docker container ls
命令檢視系統內全部處於執行狀態的容器。
1$ docker container ls
2CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
3e2b69eeb55cb ubuntu:latest "/bin/bash" 7 mins Up 7 min vigilant_borg
上述的輸出顯示只有一個執行中的容器。這就是前面示例中建立的那個容器。輸出中有該容器,證明瞭容器在退出後依然是執行的。讀者可以看到這個行程是7min之前建立的,並且一直在執行。
1.1.3 連線到執行中的容器
執行docker container exec
命令,可以將Shell連線到一個執行中的容器終端。因為之前示例中的容器仍在執行,所以下麵的示例會建立到該容器的新連線。
Linux示例如下。
1$ docker container exec -it vigilant_borg bash
2root@e2b69eeb55cb:/#
示例中的容器名為“vigilant_brog”。讀者環境中的容器名稱會不同,所以請記得將“vigilant_brog”替換為自己Docker主機上執行中的容器名稱或者ID。
Windows示例如下。
1> docker container exec -it pensive_hamilton pwsh.exe
2
3Windows PowerShell
4Copyright (C) 2016 Microsoft Corporation. All rights reserved.
5PS C:\>
本例中使用的容器為“pensive_hamilton”。同樣,讀者環境中的容器名稱會不同,所以請記得將“pensive_hamilton”替換為自己Docker主機上執行中的容器名稱或者ID。
註意,Shell提示符又發生了變化。此時已登入到了容器內部。
docker container exec
命令的格式是docker container exec
。在示例中,將本地Shell連線到容器是透過-it
引數實現的。本例中使用名稱取用容器,並且告訴Docker執行Bash Shell(在Windows示例中是PowerShell)。使用十六進位制ID的方式也可以很容易地取用具體容器。
再次使用Ctrl-PQ組合鍵
退出容器。
Shell提示符應當退回到Docker主機中。
再次執行docker container ls
命令來確認容器仍處於執行狀態。
1$ docker container ls
2CONTAINER ID IMAGE COMMAND CREATED STATUS NAMES
3e2b69eeb55cb ubuntu:latest "/bin/bash" 9 mins Up 9 min vigilant_borg
透過docker container stop
和docker container rm
命令來停止並殺死容器。切記需要將示例中的名稱/ID替換為讀者自己的容器對應的名稱和ID。
1$ docker container stop vigilant_borg
2vigilant_borg
3
4$ docker container rm vigilant_borg
5vigilant_borg
透過執行docker container ls
命令,並指定-a
引數來確認容器已經被成功刪除。新增-a
的作用是讓Docker列出所有容器,甚至包括那些處於停止狀態的。
1$ docker container ls -a
2CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1.2 開發視角
容器即應用!
在本節中,會分析一份應用程式碼中的Dockerfile並將其容器化,最終以容器的方式執行。相關程式碼可從本書配套資源或我的Github主頁中獲取。
本節接下來的內容會基於 Linux 示例進行演示。但其實兩個示例中都容器化了相同的Web 應用程式碼,所以步驟也是一樣的。
進入到倉庫檔案目錄之下,檢視其內容。
1$ cd psweb
2$ ls -l
3total 28
4-rw-rw-r-- 1 ubuntu ubuntu 341 Sep 29 12:15 app.js
5-rw-rw-r-- 1 ubuntu ubuntu 216 Sep 29 12:15 circle.yml
6-rw-rw-r-- 1 ubuntu ubuntu 338 Sep 29 12:15 Dockerfile
7-rw-rw-r-- 1 ubuntu ubuntu 421 Sep 29 12:15 package.json
8-rw-rw-r-- 1 ubuntu ubuntu 370 Sep 29 12:15 README.md
9drwxrwxr-x 2 ubuntu ubuntu 4096 Sep 29 12:15 test
10drwxrwxr-x 2 ubuntu ubuntu 4096 Sep 29 12:15 views
對於Windows示例,讀者需要cd
到dotnet-docker-samples\aspnetapp
目錄當中。
Linux的示例是一個簡單的Node.js Web應用。Windows示例是一個簡單的ASP.NET Web應用。
每個倉庫中都包含一個名為Dockerfile
的檔案。Dockerfile是一個純文字檔案,其中描述瞭如何將應用構建到Docker映象當中。
檢視Dockerfile的全部內容。
1$ cat Dockerfile
2
3FROM alpine
4LABEL maintainer="nigelpoulton@hotmail.com"
5RUN apk add --update nodejs nodejs-npm
6COPY . /src
7WORKDIR /src
8RUN npm install
9EXPOSE 8080
10ENTRYPOINT ["node", "./app.js"]
Windows示例中的Dockerfile內容會有所不同。但是,這些區別在現階段並不重要。關於Dockerfile的更多細節本書會在接下來的章節中進行詳細介紹。現在,只需要知道Dockerfile的每一行都代表一個用於構建映象的指令即可。
使用docker image build
命令,根據Dockerfile中的指令來建立新的映象。示例中新建的Docker映象名為test:latest
。
一定要在包含應用程式碼和Dockerfile的目錄下執行這些命令。
1$ docker image build -t test:latest .
2
3Sending build context to Docker daemon 74.75kB
4Step 1/8 : FROM alpine
5latest: Pulling from library/alpine
688286f41530e: Pull complete
7Digest: sha256:f006ecbb824...0c103f4820a417d
8Status: Downloaded newer image for alpine:latest
9 ---> 76da55c8019d
10
11Successfully built f154cb3ddbd4
12Successfully tagged test:latest
{註:}
Windows示例構建可能花費比較長的時間。構建時間長短是由構建過程中要拉取的映象大小和複雜度決定的。
一旦構建完成,就可以確認主機上是否存在test:latest
映象。
1$ docker image ls
2REPO TAG IMAGE ID CREATED SIZE
3Test latest f154cb3ddbd4 1 minute ago 55.6MB
4...
讀者現在已經擁有一個新的Docker映象,其中包含了應用程式。
從映象啟動容器,並測試應用。
Linux程式碼如下。
1$ docker container run -d \
2 --name web1 \
3 --publish 8080:8080 \
4 test:latest
開啟Web瀏覽器,在位址列中輸入容器執行所在的Docker主機的DNS名稱或者IP地址,併在後面加上埠號8080
。然後就能看到圖4.1的Web頁面。
如果讀者使用的是Windows示例或者Mac版Docker,則需要將地址替換為localhost:8080
或者127.0.0.1:8080
;如果讀者使用的是Play with Docker,需要單擊終端介面上的8080
超連結。
圖1.1 Linux系統測試應用Web介面
Windows程式碼如下。
1> docker container run -d \
2 --name web1 \
3 --publish 8080:80 \
4 test:latest
開啟Web瀏覽器,在位址列中輸入容器執行所在的Docker主機的DNS名稱或者IP地址,併在後面加上埠號8080
,然後就能看到圖4.2的Web頁面。
圖1.2 Windows系統測試應用Web介面
如果讀者使用的是Windows示例或者Mac版Docker,則可參考上面的規則。
讀者已經成功將應用程式碼構建到了Docker映象當中,然後以容器的方式啟動該映象,這個過程叫作“應用容器化”。
1.3 本文小結
在運維部分,我們下載了Docker映象,啟動容器並且登入到容器內部執行相應的命令,最後停止容器並刪除。
在開發部分,我們完成了簡單應用的容器化過程:從GitHub拉取應用原始碼,並且透過Dockerfile中的指令,將應用程式碼構建到映象之中。接著運行了該容器化應用。
深入淺出Docker
【英】Nigel Poulton(奈吉爾 波爾頓)
掃碼一鍵購
在美國亞馬遜,有一本書的影響力超高的Docker入門書,在作業系統分類中排行第一,超越了眾多實力派Docker書,眾多五星好評。也許你有所耳聞,這本書就是《深入淺出Docker》。
這是一本關於Docker的圖書。這本書的宗旨是從零開始學習Docker,因此你無須任何前置知識儲備。如果你對Docker感興趣,希望瞭解Docker工作原理以及如何正確使用Docker,則本書適合你。同時本書也可作為Docker認證工程師考試的參考圖書。
歡迎留下您對本文的討論,以及在使用.NET容器化過程中的感悟。評論獲點贊最多的前10位讀者將獲贈《深入淺出Docker》圖書一本(包郵)。活動截止時間3.31 23點。