本文從下面兩部份內容講解Docker。node
在運維視角中,主要包括下載鏡像、運行新的容器、登陸新容器、在容器內運行命令,以及銷燬容器。linux
在開發視角中,更多關注與應用相關的內容。《深刻淺出Docker內》會從GitHub拉取一些應用代碼,解釋其中的Dockerfile,將應用容器化,並在容器中運行它們。nginx
經過上面兩部份內容,你能夠從總體上理解Docker到底是什麼,以及主要組件之間是如何相互配合的。推薦讀者對開發和運維兩部份內容都要閱讀。web
當讀者安裝Docker的時候,會涉及兩個主要組件:Docker客戶端和Docker daemon(有時也被稱爲「服務端」或者「引擎」)。docker
daemon實現了Docker引擎的API。shell
使用Linux默認安裝時,客戶端與daemon之間的通訊是經過本地IPC/UNIX Socket完成的(/var/run/docker.sock);在Windows上是經過名爲npipe:////./pipe/docker_engine的管道(pipe)完成的。讀者可使用docker version命令來檢測客戶端和服務端是否都已經成功運行,而且能夠互相通訊。npm
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前綴。json
1.1.1 鏡像ubuntu
將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容器中僅包含兩個進程。
命令輸出中展現的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<Snip> 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 <options><container-name or container-id> <command/app>。在示例中,將本地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
容器即應用!
在本節中,會分析一份應用代碼中的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示例是一個簡單的[http://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<Snip> 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鏡像當中,而後以容器的方式啓動該鏡像,這個過程叫做「應用容器化」。
在運維部分,咱們下載了Docker鏡像,啓動容器而且登陸到容器內部執行相應的命令,最後中止容器並刪除。
在開發部分,咱們完成了簡單應用的容器化過程:從GitHub拉取應用源代碼,而且經過Dockerfile中的指令,將應用代碼構建到鏡像之中。接着運行了該容器化應用。