詳細(xì)的Docker入門總結(jié),看這一篇就夠了
在計算機(jī)技術(shù)日新月異的今天, Docker 在國內(nèi)發(fā)展的如火如荼。特別是在一線互聯(lián)網(wǎng)公司 Docker 的使用是十分普遍的,甚至成為了一些企業(yè)面試的加分項(xiàng),不信的話看看下面這張圖。
這是我在某招聘網(wǎng)站上看到的招聘 Java 開發(fā)工程師的招聘要求,其中有一條熟悉 Docker 成為了你快速入職的加分項(xiàng),由此可見熟悉 Docker 在互聯(lián)網(wǎng)公司的地位之重要。
市面上已經(jīng)有很多優(yōu)秀的 Docker 教程,但是很多原理性的東西,筆者認(rèn)為那些教程對初學(xué)者而言還是很難理解,感覺沒有說清楚(筆者自己都覺得挺懵逼的)。為了讓初學(xué)者少走彎路,我將以我的學(xué)習(xí)經(jīng)歷以及作為一個 CTF 的角度,編寫此套教程,來帶大家去了解并熟練運(yùn)用 Docker 。
Docker 是什么?
說了這么多, Docker 到底是個什么東西呢?我們在理解 Docker 之前,首先得先區(qū)分清楚兩個概念,容器和虛擬機(jī)。
可能很多讀者朋友都用過虛擬機(jī),而對容器這個概念比較的陌生。我們用的傳統(tǒng)虛擬機(jī)如 VMware , VisualBox 之類的需要模擬整臺機(jī)器包括硬件。
每臺虛擬機(jī)都需要有自己的操作系統(tǒng),虛擬機(jī)一旦被開啟,預(yù)分配給它的資源將全部被占用。
每一臺虛擬機(jī)包括應(yīng)用,必要的二進(jìn)制和庫,以及一個完整的用戶操作系統(tǒng)。
而容器技術(shù)是和我們的宿主機(jī)共享硬件資源及操作系統(tǒng),可以實(shí)現(xiàn)資源的動態(tài)分配。
容器包含應(yīng)用和其所有的依賴包,但是與其他容器共享內(nèi)核。容器在宿主機(jī)操作系統(tǒng)中,在用戶空間以分離的進(jìn)程運(yùn)行。
容器技術(shù)是實(shí)現(xiàn)操作系統(tǒng)虛擬化的一種途徑,可以讓您在資源受到隔離的進(jìn)程中運(yùn)行應(yīng)用程序及其依賴關(guān)系。
通過使用容器,我們可以輕松打包應(yīng)用程序的代碼、配置和依賴關(guān)系,將其變成容易使用的構(gòu)建塊,從而實(shí)現(xiàn)環(huán)境一致性、運(yùn)營效率、開發(fā)人員生產(chǎn)力和版本控制等諸多目標(biāo)。
容器可以幫助保證應(yīng)用程序快速、可靠、一致地部署,其間不受部署環(huán)境的影響。
容器還賦予我們對資源更多的精細(xì)化控制能力,讓我們的基礎(chǔ)設(shè)施效率更高。
通過下面這幅圖,我們可以很直觀的反映出這兩者的區(qū)別所在:
Docker 屬于 Linux 容器的一種封裝,提供簡單易用的容器使用接口。它是目前最流行的 Linux 容器解決方案。
而 Linux 容器是 Linux 發(fā)展出的另一種虛擬化技術(shù),簡單來講, Linux 容器不是模擬一個完整的操作系統(tǒng),而是對進(jìn)程進(jìn)行隔離,相當(dāng)于是在正常進(jìn)程的外面套了一個保護(hù)層。
對于容器里面的進(jìn)程來說,它接觸到的各種資源都是虛擬的,從而實(shí)現(xiàn)與底層系統(tǒng)的隔離。
Docker 將應(yīng)用程序與該程序的依賴,打包在一個文件里面。運(yùn)行這個文件,就會生成一個虛擬容器。
程序在這個虛擬容器里運(yùn)行,就好像在真實(shí)的物理機(jī)上運(yùn)行一樣。有了 Docker ,就不用擔(dān)心環(huán)境問題。
總體來說,Docker 的接口相當(dāng)簡單,用戶可以方便地創(chuàng)建和使用容器,把自己的應(yīng)用放入容器。容器還可以進(jìn)行版本管理、復(fù)制、分享、修改,就像管理普通的代碼一樣。
Docker 的優(yōu)勢
Docker 相比于傳統(tǒng)虛擬化方式具有更多的優(yōu)勢:
- Docker 啟動快速屬于秒級別。虛擬機(jī)通常需要幾分鐘去啟動。
- Docker 需要的資源更少。Docker 在操作系統(tǒng)級別進(jìn)行虛擬化,Docker 容器和內(nèi)核交互,幾乎沒有性能損耗,性能優(yōu)于通過 Hypervisor 層與內(nèi)核層的虛擬化。
- Docker 更輕量。Docker 的架構(gòu)可以共用一個內(nèi)核與共享應(yīng)用程序庫,所占內(nèi)存極小。同樣的硬件環(huán)境,Docker 運(yùn)行的鏡像數(shù)遠(yuǎn)多于虛擬機(jī)數(shù)量,對系統(tǒng)的利用率非常高。
- 與虛擬機(jī)相比,Docker 隔離性更弱。Docker 屬于進(jìn)程之間的隔離,虛擬機(jī)可實(shí)現(xiàn)系統(tǒng)級別隔離。
- 安全性。Docker 的安全性也更弱,Docker 的租戶 Root 和宿主機(jī) Root 等同,一旦容器內(nèi)的用戶從普通用戶權(quán)限提升為 Root 權(quán)限,它就直接具備了宿主機(jī)的 Root 權(quán)限,進(jìn)而可進(jìn)行無限制的操作。
- 虛擬機(jī)租戶 Root 權(quán)限和宿主機(jī)的 Root 虛擬機(jī)權(quán)限是分離的,并且虛擬機(jī)利用如 Intel 的 VT-d 和 VT-x 的 ring-1 硬件隔離技術(shù)。
- 這種隔離技術(shù)可以防止虛擬機(jī)突破和彼此交互,而容器至今還沒有任何形式的硬件隔離,這使得容器容易受到攻擊。
- 可管理性。Docker 的集中化管理工具還不算成熟。各種虛擬化技術(shù)都有成熟的管理工具,例如 VMware vCenter 提供完備的虛擬機(jī)管理能力。
- 高可用和可恢復(fù)性。Docker 對業(yè)務(wù)的高可用支持是通過快速重新部署實(shí)現(xiàn)的。
- 虛擬化具備負(fù)載均衡,高可用,容錯,遷移和數(shù)據(jù)保護(hù)等經(jīng)過生產(chǎn)實(shí)踐檢驗(yàn)的成熟保障機(jī)制, VMware 可承諾虛擬機(jī) 99.999% 高可用,保證業(yè)務(wù)連續(xù)性。
- 快速創(chuàng)建、刪除。虛擬化創(chuàng)建是分鐘級別的,Docker 容器創(chuàng)建是秒級別的,Docker 的快速迭代性,決定了無論是開發(fā)、測試、部署都可以節(jié)約大量時間
- 交付、部署。虛擬機(jī)可以通過鏡像實(shí)現(xiàn)環(huán)境交付的一致性,但鏡像分發(fā)無法體系化。Docker 在 Dockerfile 中記錄了容器構(gòu)建過程,可在集群中實(shí)現(xiàn)快速分發(fā)和快速部署。
我們可以從下面這張表格很清楚地看到容器相比于傳統(tǒng)虛擬機(jī)的特性的優(yōu)勢所在:
Docker 的三個基本概念
從上圖我們可以看到,Docker 中包括三個基本的概念:
- Image(鏡像)
- Container(容器)
- Repository(倉庫)
鏡像是 Docker 運(yùn)行容器的前提,倉庫是存放鏡像的場所,可見鏡像更是 Docker 的核心。
Image(鏡像)
那么鏡像到底是什么呢?Docker 鏡像可以看作是一個特殊的文件系統(tǒng),除了提供容器運(yùn)行時所需的程序、庫、資源、配置等文件外,還包含了一些為運(yùn)行時準(zhǔn)備的一些配置參數(shù)(如匿名卷、環(huán)境變量、用戶等)。
鏡像不包含任何動態(tài)數(shù)據(jù),其內(nèi)容在構(gòu)建之后也不會被改變。鏡像(Image)就是一堆只讀層(read-only layer)的統(tǒng)一視角,也許這個定義有些難以理解,下面的這張圖能夠幫助讀者理解鏡像的定義:
從左邊我們看到了多個只讀層,它們重疊在一起。除了最下面一層,其他層都會有一個指針指向下一層。這些層是 Docker 內(nèi)部的實(shí)現(xiàn)細(xì)節(jié),并且能夠在主機(jī)的文件系統(tǒng)上訪問到。
統(tǒng)一文件系統(tǒng)(Union File System)技術(shù)能夠?qū)⒉煌膶诱铣梢粋€文件系統(tǒng),為這些層提供了一個統(tǒng)一的視角。
這樣就隱藏了多層的存在,在用戶的角度看來,只存在一個文件系統(tǒng)。我們可以在圖片的右邊看到這個視角的形式。
Container(容器)
容器(Container)的定義和鏡像(Image)幾乎一模一樣,也是一堆層的統(tǒng)一視角,唯一區(qū)別在于容器的最上面那一層是可讀可寫的。
由于容器的定義并沒有提及是否要運(yùn)行容器,所以實(shí)際上,容器 = 鏡像 + 讀寫層。
Repository(倉庫)
Docker 倉庫是集中存放鏡像文件的場所。鏡像構(gòu)建完成后,可以很容易的在當(dāng)前宿主上運(yùn)行。
但是, 如果需要在其他服務(wù)器上使用這個鏡像,我們就需要一個集中的存儲、分發(fā)鏡像的服務(wù),Docker Registry(倉庫注冊服務(wù)器)就是這樣的服務(wù)。
有時候會把倉庫(Repository)和倉庫注冊服務(wù)器(Registry)混為一談,并不嚴(yán)格區(qū)分。
Docker 倉庫的概念跟 Git 類似,注冊服務(wù)器可以理解為 GitHub 這樣的托管服務(wù)。
實(shí)際上,一個 Docker Registry 中可以包含多個倉庫(Repository),每個倉庫可以包含多個標(biāo)簽(Tag),每個標(biāo)簽對應(yīng)著一個鏡像。
所以說,鏡像倉庫是 Docker 用來集中存放鏡像文件的地方,類似于我們之前常用的代碼倉庫。
通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標(biāo)簽就常用于對應(yīng)該軟件的各個版本 。
我們可以通過<倉庫名>:<標(biāo)簽>的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標(biāo)簽,將以 Latest 作為默認(rèn)標(biāo)簽。
倉庫又可以分為兩種形式:
- Public(公有倉庫)
- Private(私有倉庫)
Docker Registry 公有倉庫是開放給用戶使用、允許用戶管理鏡像的 Registry 服務(wù)。
一般這類公開服務(wù)允許用戶免費(fèi)上傳、下載公開的鏡像,并可能提供收費(fèi)服務(wù)供用戶管理私有鏡像。
除了使用公開服務(wù)外,用戶還可以在本地搭建私有 Docker Registry。Docker 官方提供了 Docker Registry 鏡像,可以直接使用做為私有 Registry 服務(wù)。
當(dāng)用戶創(chuàng)建了自己的鏡像之后就可以使用 Push 命令將它上傳到公有或者私有倉庫,這樣下次在另外一臺機(jī)器上使用這個鏡像時候,只需要從倉庫上 Pull 下來就可以了。
我們主要把 Docker 的一些常見概念如 Image,Container,Repository 做了詳細(xì)的闡述,也從傳統(tǒng)虛擬化方式的角度闡述了 Docker 的優(yōu)勢。
我們從下圖可以直觀地看到 Docker 的架構(gòu):
Docker 使用 C/S 結(jié)構(gòu),即客戶端/服務(wù)器體系結(jié)構(gòu)。Docker 客戶端與 Docker 服務(wù)器進(jìn)行交互,Docker服務(wù)端負(fù)責(zé)構(gòu)建、運(yùn)行和分發(fā) Docker 鏡像。
Docker 客戶端和服務(wù)端可以運(yùn)行在一臺機(jī)器上,也可以通過 RESTful 、 Stock 或網(wǎng)絡(luò)接口與遠(yuǎn)程 Docker 服務(wù)端進(jìn)行通信。
這張圖展示了 Docker 客戶端、服務(wù)端和 Docker 倉庫(即 Docker Hub 和 Docker Cloud ),默認(rèn)情況下 Docker 會在 Docker 中央倉庫尋找鏡像文件。
這種利用倉庫管理鏡像的設(shè)計理念類似于 Git ,當(dāng)然這個倉庫是可以通過修改配置來指定的,甚至我們可以創(chuàng)建我們自己的私有倉庫。
Docker 的安裝和使用
Docker 的安裝和使用有一些前提條件,主要體現(xiàn)在體系架構(gòu)和內(nèi)核的支持上。
對于體系架構(gòu),除了 Docker 一開始就支持的 X86-64 ,其他體系架構(gòu)的支持則一直在不斷地完善和推進(jìn)中。
Docker 分為 CE 和 EE 兩大版本。CE 即社區(qū)版,免費(fèi)支持周期 7 個月;EE 即企業(yè)版,強(qiáng)調(diào)安全,付費(fèi)使用,支持周期 24 個月。
我們在安裝前可以參看官方文檔獲取最新的 Docker 支持情況,官方文檔在這里:https://docs.docker.com/install/。
Docker 對于內(nèi)核支持的功能,即內(nèi)核的配置選項(xiàng)也有一定的要求(比如必須開啟 Cgroup 和 Namespace 相關(guān)選項(xiàng),以及其他的網(wǎng)絡(luò)和存儲驅(qū)動等)。
Docker 源碼中提供了一個檢測腳本來檢測和指導(dǎo)內(nèi)核的配置,腳本鏈接在這里:https://raw.githubusercontent. ... ig.sh。
在滿足前提條件后,安裝就變得非常的簡單了。
Docker CE 的安裝請參考官方文檔:
- MacOS:https://docs.docker.com/docker-for-mac/install/
- Windows:https://docs.docker.com/docker ... tall/
- Ubuntu:https://docs.docker.com/instal ... untu/
- Debian:https://docs.docker.com/instal ... bian/
- CentOS:https://docs.docker.com/instal ... ntos/
- Fedora:https://docs.docker.com/instal ... dora/
其他 Linux 發(fā)行版:https://docs.docker.com/instal ... ries/
這里我們以 CentOS 7 作為演示。
環(huán)境準(zhǔn)備:
- 阿里云服務(wù)器(1 核 2G,1M 帶寬)
- CentOS 7.4 64 位
由于 Docker-CE 支持 64 位版本的 CentOS 7 ,并且要求內(nèi)核版本不低于 3.10,首先我們需要卸載掉舊版本的 Docker:
- $ sudo yum remove docker \
- docker-client \
- docker-client-latest \
- docker-common \
- docker-latest \
- docker-latest-logrotate \
- docker-logrotate \
- docker-selinux \
- docker-engine-selinux \
- docker-engine
我們執(zhí)行以下安裝命令去安裝依賴包:
- $ sudo yum install -y yum-utils \
- device-mapper-persistent-data \
- lvm2
這里我事先已經(jīng)安裝過了,所以提示我已經(jīng)安裝了最新版本:
安裝 Docker
Docker 軟件包已經(jīng)包括在默認(rèn)的 CentOS-Extras 軟件源里。因此想要安裝 Docker,只需要運(yùn)行下面的 yum 命令:
- $ sudo yum install docker
當(dāng)然在測試或開發(fā)環(huán)境中 Docker 官方為了簡化安裝流程,提供了一套便捷的安裝腳本,CentOS 系統(tǒng)上可以使用這套腳本安裝:
- curl -fsSL get.docker.com -o get-docker.sh
- sh get-docker.sh
具體可以參看 docker-install 的腳本:https://github.com/docker/docker-install。
執(zhí)行這個命令后,腳本就會自動的將一切準(zhǔn)備工作做好,并且把 Docker CE 的 Edge 版本安裝在系統(tǒng)中。
安裝完成后,運(yùn)行下面的命令,驗(yàn)證是否安裝成功:
- docker versionordocker info
返回 Docker 的版本相關(guān)信息,證明 Docker 安裝成功:
啟動 Docker-CE:
- $ sudo systemctl enable docker$ sudo systemctl start docker
Docker 的簡單運(yùn)用 Hello World
由于服務(wù)器日常崩潰了, Docker 出了點(diǎn)問題,所以以下案例的演示是基于 Kali Linux 環(huán)境下進(jìn)行的。
我們通過最簡單的 Image 文件 Hello World,感受一下 Docker 的魅力吧!
我們直接運(yùn)行下面的命令,將名為 hello-world 的 image 文件從倉庫抓取到本地:
- docker pull library/hello-world
docker pull images 是抓取 image 文件,library/hello-world 是 image 文件在倉庫里面的位置,其中 library 是 image 文件所在的組,hello-world 是 image 文件的名字。
抓取成功以后,就可以在本機(jī)看到這個 image 文件了:
- docker images
我們可以看到如下結(jié)果:
現(xiàn)在,我們可以運(yùn)行 hello-world 這個 image 文件:
- docker run hello-world
我們可以看到如下結(jié)果:
輸出這段提示以后,hello world 就會停止運(yùn)行,容器自動終止。有些容器不會自動終止,因?yàn)樘峁┑氖欠?wù),比如 MySQL 鏡像等。
是不是很 Easy 呢?我們從上面可以看出,Docker 的功能是十分強(qiáng)大的,除此之外,我們還可以拉取一些 Ubuntu,Apache 等鏡像,在未來的教程中我們將會一一提到。
Docker 提供了一套簡單實(shí)用的命令來創(chuàng)建和更新鏡像,我們可以通過網(wǎng)絡(luò)直接下載一個已經(jīng)創(chuàng)建好了的應(yīng)用鏡像,并通過 Docker RUN 命令就可以直接使用。
當(dāng)鏡像通過 RUN 命令運(yùn)行成功后,這個運(yùn)行的鏡像就是一個 Docker 容器啦。
容器可以理解為一個輕量級的沙箱,Docker 利用容器來運(yùn)行和隔離應(yīng)用,容器是可以被啟動、停止、刪除的,這并不會影響 Docker 鏡像。
我們可以看看下面這幅圖:
Docker 客戶端是 Docker 用戶與 Docker 交互的主要方式。當(dāng)您使用 Docker 命令行運(yùn)行命令時,Docker 客戶端將這些命令發(fā)送給服務(wù)器端,服務(wù)端將執(zhí)行這些命令。
Docker 命令使用 Docker API 。Docker 客戶端可以與多個服務(wù)端進(jìn)行通信。
我們將剖析一下 Docker 容器是如何工作的,學(xué)習(xí)好 Docker 容器工作的原理,我們就可以自己去管理我們的容器了。
Docker 架構(gòu)
在上面的學(xué)習(xí)中,我們簡單地講解了 Docker 的基本架構(gòu)。了解到了 Docker 使用的是 C/S 結(jié)構(gòu),即客戶端/服務(wù)器體系結(jié)構(gòu)。
明白了 Docker 客戶端與 Docker 服務(wù)器進(jìn)行交互時,Docker 服務(wù)端負(fù)責(zé)構(gòu)建、運(yùn)行和分發(fā) Docker 鏡像。
知道了 Docker 客戶端和服務(wù)端可以運(yùn)行在一臺機(jī)器上,我們可以通過 RESTful 、Stock 或網(wǎng)絡(luò)接口與遠(yuǎn)程 Docker 服務(wù)端進(jìn)行通信。
我們從下圖可以很直觀的了解到 Docker 的架構(gòu):
Docker 的核心組件包括:
- Docker Client
- Docker Daemon
- Docker Image
- Docker Registry
- Docker Container
Docker 采用的是 Client/Server 架構(gòu)??蛻舳讼蚍?wù)器發(fā)送請求,服務(wù)器負(fù)責(zé)構(gòu)建、運(yùn)行和分發(fā)容器。
客戶端和服務(wù)器可以運(yùn)行在同一個 Host 上,客戶端也可以通過 Socket 或 REST API 與遠(yuǎn)程的服務(wù)器通信。
可能很多朋友暫時不太理解一些東西,比如 REST API 是什么東西等,不過沒關(guān)系,在后面的文章中會一一給大家講解清楚。
Docker Client
Docker Client ,也稱 Docker 客戶端。它其實(shí)就是 Docker 提供命令行界面(CLI)工具,是許多 Docker 用戶與 Docker 進(jìn)行交互的主要方式。
客戶端可以構(gòu)建,運(yùn)行和停止應(yīng)用程序,還可以遠(yuǎn)程與 Docker_Host 進(jìn)行交互。
最常用的 Docker 客戶端就是 Docker 命令,我們可以通過 Docker 命令很方便地在 Host 上構(gòu)建和運(yùn)行 Docker 容器。
Docker Daemon
Docker Daemon 是服務(wù)器組件,以 Linux 后臺服務(wù)的方式運(yùn)行,是 Docker 最核心的后臺進(jìn)程,我們也把它稱為守護(hù)進(jìn)程。
它負(fù)責(zé)響應(yīng)來自 Docker Client 的請求,然后將這些請求翻譯成系統(tǒng)調(diào)用完成容器管理操作。
該進(jìn)程會在后臺啟動一個 API Server ,負(fù)責(zé)接收由 Docker Client 發(fā)送的請求,接收到的請求將通過 Docker Daemon 內(nèi)部的一個路由分發(fā)調(diào)度,由具體的函數(shù)來執(zhí)行請求。
我們大致可以將其分為以下三部分:
- Docker Server
- Engine
- Job
Docker Daemon 的架構(gòu)如下所示:
Docker Daemon 可以認(rèn)為是通過 Docker Server 模塊接受 Docker Client 的請求,并在 Engine 中處理請求,然后根據(jù)請求類型,創(chuàng)建出指定的 Job 并運(yùn)行。
Docker Daemon 運(yùn)行在 Docker Host 上,負(fù)責(zé)創(chuàng)建、運(yùn)行、監(jiān)控容器,構(gòu)建、存儲鏡像。
運(yùn)行過程的作用有以下幾種可能:
- 向 Docker Registry 獲取鏡像。
- 通過 GraphDriver 執(zhí)行容器鏡像的本地化操作。
- 通過 NetworkDriver 執(zhí)行容器網(wǎng)絡(luò)環(huán)境的配置。
- 通過 ExecDriver 執(zhí)行容器內(nèi)部運(yùn)行的執(zhí)行工作。
由于 Docker Daemon 和 Docker Client 的啟動都是通過可執(zhí)行文件 Docker 來完成的,因此兩者的啟動流程非常相似。
Docker 可執(zhí)行文件運(yùn)行時,運(yùn)行代碼通過不同的命令行 Flag 參數(shù),區(qū)分兩者,并最終運(yùn)行兩者各自相應(yīng)的部分。
啟動 Docker Daemon 時,一般可以使用以下命令來完成:
- docker --daemon = truedocker –d
- docker –d = true
再由 Docker 的 main() 函數(shù)來解析以上命令的相應(yīng) Flag 參數(shù),并最終完成 Docker Daemon 的啟動。
下圖可以很直觀地看到 Docker Daemon 的啟動流程:
默認(rèn)配置下,Docker Daemon 只能響應(yīng)來自本地 Host 的客戶端請求。如果要允許遠(yuǎn)程客戶端請求,需要在配置文件中打開 TCP 監(jiān)聽。
我們可以照著如下步驟進(jìn)行配置:
1、編輯配置文件/etc/systemd/system/multi-user.target.wants/docker.service,在環(huán)境變量 ExecStart 后面添加 -H tcp://0.0.0.0,允許來自任意 IP 的客戶端連接。
2、重啟 Docker Daemon:
- systemctl daemon-reload
- systemctl restart docker.service
3、我們通過以下命令即可實(shí)現(xiàn)與遠(yuǎn)程服務(wù)器通信:
- docker -H 服務(wù)器IP地址 info
-H 是用來指定服務(wù)器主機(jī),info 子命令用于查看 Docker 服務(wù)器的信息。
Docker Image
Docker 鏡像可以看作是一個特殊的文件系統(tǒng),除了提供容器運(yùn)行時所需的程序、庫、資源、配置等文件外,還包含了一些為運(yùn)行時準(zhǔn)備的一些配置參數(shù)(如匿名卷、環(huán)境變量、用戶等)。
鏡像不包含任何動態(tài)數(shù)據(jù),其內(nèi)容在構(gòu)建之后也不會被改變。我們可將 Docker 鏡像看成只讀模板,通過它可以創(chuàng)建 Docker 容器。
鏡像有多種生成方法:
- 從無到有開始創(chuàng)建鏡像
- 下載并使用別人創(chuàng)建好的現(xiàn)成的鏡像
- 在現(xiàn)有鏡像上創(chuàng)建新的鏡像
我們可以將鏡像的內(nèi)容和創(chuàng)建步驟描述在一個文本文件中,這個文件被稱作 Dockerfile ,通過執(zhí)行 docker build 命令可以構(gòu)建出 Docker 鏡像。
Docker Registry
Docker Registry 是存儲 Docker Image 的倉庫,它在 Docker 生態(tài)環(huán)境中的位置如下圖所示:
運(yùn)行 docker push、docker pull、docker search 時,實(shí)際上是通過 Docker Daemon 與 Docker Registry 通信。
Docker Container
Docker 容器就是 Docker 鏡像的運(yùn)行實(shí)例,是真正運(yùn)行項(xiàng)目程序、消耗系統(tǒng)資源、提供服務(wù)的地方。
Docker Container 提供了系統(tǒng)硬件環(huán)境,我們可以使用 Docker Images 這些制作好的系統(tǒng)盤,再加上我們所編寫好的項(xiàng)目代碼,Run 一下就可以提供服務(wù)啦。
Docker 組件是如何協(xié)作運(yùn)行容器
看到這里,我相信各位讀者朋友們應(yīng)該已經(jīng)對 Docker 基礎(chǔ)架構(gòu)熟悉的差不多了,我們還記得運(yùn)行的第一個容器嗎?
現(xiàn)在我們再通過 hello-world 這個例子來體會一下 Docker 各個組件是如何協(xié)作的。
容器啟動過程如下:
- Docker 客戶端執(zhí)行 docker run 命令。
- Docker Daemon 發(fā)現(xiàn)本地沒有 hello-world 鏡像。
- Daemon 從 Docker Hub 下載鏡像。
- 下載完成,鏡像 hello-world 被保存到本地。
- Docker Daemon 啟動容器。
具體過程可以看如下這幅演示圖:
我們可以通過 Docker Images 可以查看到 hello-world 已經(jīng)下載到本地:
我們可以通過 Docker Ps 或者 Docker Container ls 顯示正在運(yùn)行的容器,我們可以看到,hello-world 在輸出提示信息以后就會停止運(yùn)行,容器自動終止,所以我們在查看的時候沒有發(fā)現(xiàn)有容器在運(yùn)行。
我們把 Docker 容器的工作流程剖析的十分清楚了,我們大體可以知道 Docker 組件協(xié)作運(yùn)行容器可以分為以下幾個過程:
- Docker 客戶端執(zhí)行 docker run 命令。
- Docker Daemon 發(fā)現(xiàn)本地沒有我們需要的鏡像。
- Daemon 從 Docker Hub 下載鏡像。
- 下載完成后,鏡像被保存到本地。
- Docker Daemon 啟動容器。
了解了這些過程以后,我們再來理解這些命令就不會覺得很突兀了,下面我來給大家講講 Docker 常用的一些命令操作吧。
Docker 常用命令
我們可以通過 docker -h 去查看命令的詳細(xì)的幫助文檔。在這里我只會講一些日常我們可能會用的比較多的一些命令。
例如,我們需要拉取一個 Docker 鏡像,我們可以用如下命令:
- docker pull image_name
image_name 為鏡像的名稱,而如果我們想從 Docker Hub 上去下載某個鏡像,我們可以使用以下命令:
- docker pull centos:latest
cento:lastest 是鏡像的名稱,Docker Daemon 發(fā)現(xiàn)本地沒有我們需要的鏡像,會自動去 Docker Hub 上去下載鏡像,下載完成后,該鏡像被默認(rèn)保存到 /var/lib/docker 目錄下。
接著我們?nèi)绻氩榭粗鳈C(jī)下存在多少鏡像,我們可以用如下命令:
- docker images
我們要想知道當(dāng)前有哪些容器在運(yùn)行,我們可以用如下命令:
- docker ps -a
-a 是查看當(dāng)前所有的容器,包括未運(yùn)行的。我們該如何去對一個容器進(jìn)行啟動,重啟和停止呢?
我們可以用如下命令:
- docker start container_name/container_id
- docker restart container_name/container_id
- docker stop container_name/container_id
這個時候我們?nèi)绻脒M(jìn)入到這個容器中,我們可以使用 attach 命令:
- docker attach container_name/container_id
那如果我們想運(yùn)行這個容器中的鏡像的話,并且調(diào)用鏡像里面的 bash ,我們可以使用如下命令:
- docker run -t -i container_name/container_id /bin/bash
那如果這個時候,我們想刪除指定鏡像的話,由于 Image 被某個 Container 引用(拿來運(yùn)行),如果不將這個引用的 Container 銷毀(刪除),那 Image 肯定是不能被刪除。
我們首先得先去停止這個容器:
- docker ps
- docker stop container_name/container_id
然后我們用如下命令去刪除這個容器:
- docker rm container_name/container_id
然后這個時候我們再去刪除這個鏡像:
- docker rmi image_name
此時,常用的 Docker 相關(guān)的命令就講到這里為止了,我們在后續(xù)的文章中還會反復(fù)地提到這些命令。
Dockerfile 是什么
前面我們已經(jīng)提到了 Docker 的一些基本概念。以 CTF 的角度來看,我們可以去使用 Dockerfile 定義鏡像,依賴鏡像來運(yùn)行容器,可以去模擬出一個真實(shí)的漏洞場景。
因此毫無疑問的說, Dockerfile 是鏡像和容器的關(guān)鍵,并且 Dockerfile 還可以很輕易的去定義鏡像內(nèi)容,說了這么多,那么 Dockerfile 到底是個什么東西呢?
Dockerfile 是自動構(gòu)建 Docker 鏡像的配置文件,用戶可以使用 Dockerfile 快速創(chuàng)建自定義的鏡像。Dockerfile 中的命令非常類似于 Linux 下的 Shell 命令。
我們可以通過下面這幅圖來直觀地感受下 Docker 鏡像、容器和 Dockerfile 三者之間的關(guān)系:
我們從上圖中可以看到,Dockerfile 可以自定義鏡像,通過 Docker 命令去運(yùn)行鏡像,從而達(dá)到啟動容器的目的。Dockerfile 是由一行行命令語句組成,并且支持已 # 開頭的注釋行。
一般來說,我們可以將 Dockerfile 分為四個部分:
- 基礎(chǔ)鏡像(父鏡像)信息指令 FROM。
- 維護(hù)者信息指令 MAINTAINER。
- 鏡像操作指令 RUN 、EVN 、ADD 和 WORKDIR 等。
- 容器啟動指令 CMD 、ENTRYPOINT 和 USER 等。
下面是一段簡單的 Dockerfile 的例子:
- FROM python:2.7MAINTAINER Angel_Kitty <angelkitty6698@gmail.com>COPY . /app
- WORKDIR /app
- RUN pip install -r requirements.txt
- EXPOSE 5000ENTRYPOINT ["python"]CMD ["app.py"]
我們可以分析一下上面這個過程:
- 從 Docker Hub 上 Pull 下 Python 2.7 的基礎(chǔ)鏡像。
- 顯示維護(hù)者的信息。
- Copy 當(dāng)前目錄到容器中的 /App 目錄下 復(fù)制本地主機(jī)的 ( Dockerfile 所在目錄的相對路徑)到容器里 。
- 指定工作路徑為 /App。
- 安裝依賴包。
- 暴露 5000 端口。
- 啟動 App。
這個例子是啟動一個 Python Flask App 的 Dockerfile(Flask 是 Python 的一個輕量的 Web 框架),相信大家從這個例子中能夠稍微理解了 Dockfile 的組成以及指令的編寫過程。
Dockerfile 常用的指令
根據(jù)上面的例子,我們已經(jīng)差不多知道了 Dockerfile 的組成以及指令的編寫過程,我們再來理解一下這些常用命令就會得心應(yīng)手了。
由于 Dockerfile 中所有的命令都是以下格式:INSTRUCTION argument ,指令(INSTRUCTION)不分大小寫,但是推薦大寫和 SQL 語句是不是很相似呢?下面我們正式來講解一下這些指令集吧。
FROM
FROM 是用于指定基礎(chǔ)的 images ,一般格式為 FROM<image>or FROM <image>:<tag>
所有的 Dockerfile 都應(yīng)該以 FROM 開頭,F(xiàn)ROM 命令指明 Dockerfile 所創(chuàng)建的鏡像文件以什么鏡像為基礎(chǔ),F(xiàn)ROM 以后的所有指令都會在 FROM 的基礎(chǔ)上進(jìn)行創(chuàng)建鏡像。
可以在同一個 Dockerfile 中多次使用 FROM 命令用于創(chuàng)建多個鏡像。比如我們要指定 Python 2.7 的基礎(chǔ)鏡像,我們可以像如下寫法一樣:
- FROM python:2.7
MAINTAINER
MAINTAINER 是用于指定鏡像創(chuàng)建者和聯(lián)系方式,一般格式為 MAINTAINER 。
這里我設(shè)置成我的 ID 和郵箱:
- MAINTAINER Angel_Kitty
COPY
COPY 是用于復(fù)制本地主機(jī)的 (為 Dockerfile 所在目錄的相對路徑)到容器中的 。
當(dāng)使用本地目錄為源目錄時,推薦使用 COPY 。一般格式為 COPY 。
例如我們要拷貝當(dāng)前目錄到容器中的 /app 目錄下,我們可以這樣操作:
- COPY . /app
WORKDIR
WORKDIR 用于配合 RUN,CMD,ENTRYPOINT 命令設(shè)置當(dāng)前工作路徑。
可以設(shè)置多次,如果是相對路徑,則相對前一個 WORKDIR 命令。默認(rèn)路徑為/。一般格式為 WORKDIR /path/to/work/dir。
例如我們設(shè)置 /app 路徑,我們可以進(jìn)行如下操作:
- WORKDIR /app
RUN
RUN 用于容器內(nèi)部執(zhí)行命令。每個 RUN 命令相當(dāng)于在原有的鏡像基礎(chǔ)上添加了一個改動層,原有的鏡像不會有變化。一般格式為 RUN 。
例如我們要安裝 Python 依賴包,我們做法如下:
- RUN pip install -r requirements.txt
EXPOSE
EXPOSE 命令用來指定對外開放的端口。一般格式為 EXPOSE [...]。
例如上面那個例子,開放5000端口:
- EXPOSE 5000
ENTRYPOINT
ENTRYPOINT 可以讓你的容器表現(xiàn)得像一個可執(zhí)行程序一樣。一個 Dockerfile 中只能有一個 ENTRYPOINT,如果有多個,則最后一個生效。
ENTRYPOINT 命令也有兩種格式:
- ENTRYPOINT ["executable", "param1", "param2"] :推薦使用的 Exec 形式。
- ENTRYPOINT command param1 param2 :Shell 形式。
例如下面這個,我們要將 Python 鏡像變成可執(zhí)行的程序,我們可以這樣去做:
- ENTRYPOINT ["python"]
CMD
CMD 命令用于啟動容器時默認(rèn)執(zhí)行的命令,CMD 命令可以包含可執(zhí)行文件,也可以不包含可執(zhí)行文件。
不包含可執(zhí)行文件的情況下就要用 ENTRYPOINT 指定一個,然后 CMD 命令的參數(shù)就會作為 ENTRYPOINT 的參數(shù)。
CMD 命令有三種格式:
- CMD ["executable","param1","param2"]:推薦使用的 exec 形式。
- CMD ["param1","param2"]:無可執(zhí)行程序形式。
- CMD command param1 param2:Shell 形式。
一個 Dockerfile 中只能有一個 CMD,如果有多個,則最后一個生效。而 CMD 的 Shell 形式默認(rèn)調(diào)用 /bin/sh -c 執(zhí)行命令。
CMD 命令會被 Docker 命令行傳入的參數(shù)覆蓋:docker run busybox /bin/echo Hello Docker 會把 CMD 里的命令覆蓋。
例如我們要啟動 /app ,我們可以用如下命令實(shí)現(xiàn):
- CMD ["app.py"]
當(dāng)然還有一些其他的命令,我們在用到的時候再去一一講解一下。
構(gòu)建 Dockerfile
我們大體已經(jīng)把 Dockerfile 的寫法講述完畢,我們可以自己動手寫一個例子:
- mkdir static_web
- cd static_web
- touch Dockerfile
然后 vi Dockerfile 開始編輯該文件,輸入 i 開始編輯。以下是我們構(gòu)建的 Dockerfile 內(nèi)容:
- FROM nginx
- MAINTAINER Angel_Kitty <angelkitty6698@gmail.com>
- RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
編輯完后按 esc 退出編輯,然后 :wq寫入,退出。
我們在 Dockerfile 文件所在目錄執(zhí)行:
- docker build -t angelkitty/nginx_web:v1 .
我們解釋一下:
- -t 是為新鏡像設(shè)置倉庫和名稱
- angelkitty 為倉庫名
- nginx_web 為鏡像名
- :v1 為標(biāo)簽(不添加為默認(rèn) latest )
我們構(gòu)建完成之后,使用 Docker Images 命令查看所有鏡像,如果存在 REPOSITORY 為 Nginx 和 TAG 是 v1 的信息,就表示構(gòu)建成功。
接下來使用 docker run 命令來啟動容器:
- docker run --name nginx_web -d -p 8080:80 angelkitty/nginx_web:v1
這條命令會用 Nginx 鏡像啟動一個容器,命名為 nginx_web ,并且映射了 8080 端口。
這樣我們可以用瀏覽器去訪問這個 Nginx 服務(wù)器:http://localhost:8080/ 或者 http://本機(jī)的 IP 地址:8080/,頁面返回信息: