這可能是最為詳細的Docker入門吐血總結
Docker是什麼?
在計算機技術日新月異的今天,Docker
在國內發展的如火如荼,特別是在一線網際網路公司,Docker
的使用是十分普遍的,甚至成為了一些企業面試的加分項,不信的話看看下面這張圖。
這是我在某招聘網站上看到的招聘Java開發工程師的招聘要求,其中有一條熟悉docker
成為了你快速入職的加分項,由此可見熟悉docker
在網際網路公司的地位之重要。
當然對於我們CTF選手而言,熟悉docker
可以快速搭建CTF環境
,完美地還原比賽真實漏洞的場景,幫助我們快速提升自己。
市面上已經有很多優秀的教程,但是很多原理性的東西,筆者認為那些教程對初學者而言還是很難理解,感覺沒有說清楚(筆者自己都覺得挺懵逼的),為了讓初學者少走彎路,我將以我的學習經歷以及作為一個CTF選手
docker
,祝願各位讀者朋友們學完此套教程後,在未來企業面試中能夠多一項加分的籌碼,能夠幫助到大家,我覺得就很值了。
既然說了這麼多,docker
到底是個什麼東西呢?
我們在理解docker
之前,首先我們得先區分清楚兩個概念,容器和虛擬機器。
可能很多讀者朋友都用過虛擬機器,而對容器這個概念比較的陌生。
我們用的傳統虛擬機器如VMware
,VisualBox
之類的需要模擬整臺機器包括硬體,每臺虛擬機器都需要有自己的作業系統,虛擬機器一旦被開啟,預分配給它的資源將全部被佔用。每一臺虛擬機器包括應用,必要的二進位制和庫,以及一個完整的使用者作業系統。
而容器技術是和我們的宿主機共享硬體資源及作業系統,可以實現資源的動態分配。容器包含應用和其所有的依賴包,但是與其他容器共享核心。容器在宿主機作業系統中,在使用者空間以分離的程序執行。
容器技術是實現作業系統虛擬化的一種途徑,可以讓您在資源受到隔離的程序中執行應用程式及其依賴關係。通過使用容器,我們可以輕鬆打包應用程式的程式碼、配置和依賴關係,將其變成容易使用的構建塊,從而實現環境一致性、運營效率、開發人員生產力和版本控制等諸多目標。容器可以幫助保證應用程式快速、可靠、一致地部署,其間不受部署環境的影響。容器還賦予我們對資源更多的精細化控制能力,讓我們的基礎設施效率更高。通過下面這幅圖我們可以很直觀的反映出這兩者的區別所在。
Docker 屬於 Linux 容器的一種封裝,提供簡單易用的容器使用介面。它是目前最流行的Linux
容器解決方案。
而Linux
容器是Linux
發展出了另一種虛擬化技術,簡單來講,Linux
容器不是模擬一個完整的作業系統,而是對程序進行隔離,相當於是在正常程序的外面套了一個保護層。對於容器裡面的程序來說,它接觸到的各種資源都是虛擬的,從而實現與底層系統的隔離。
Docker
將應用程式與該程式的依賴,打包在一個檔案裡面。執行這個檔案,就會生成一個虛擬容器。程式在這個虛擬容器裡執行,就好像在真實的物理機上執行一樣。有了Docker
,就不用擔心環境問題。
總體來說,Docker
的介面相當簡單,使用者可以方便地建立和使用容器,把自己的應用放入容器。容器還可以進行版本管理、複製、分享、修改,就像管理普通的程式碼一樣。
Docker的優勢
Docker相比於傳統虛擬化方式具有更多的優勢:
docker
啟動快速屬於秒級別。虛擬機器通常需要幾分鐘去啟動docker
需要的資源更少,docker
在作業系統級別進行虛擬化,docker
容器和核心互動,幾乎沒有效能損耗,效能優於通過Hypervisor
層與核心層的虛擬化docker
更輕量,docker
的架構可以共用一個核心與共享應用程式庫,所佔記憶體極小。同樣的硬體環境,Docker
執行的映象數遠多於虛擬機器數量,對系統的利用率非常高- 與虛擬機器相比,
docker
隔離性更弱,docker
屬於程序之間的隔離,虛擬機器可實現系統級別隔離 - 安全性:
docker
的安全性也更弱。Docker
的租戶root
和宿主機root
等同,一旦容器內的使用者從普通使用者許可權提升為root許可權,它就直接具備了宿主機的root許可權,進而可進行無限制的操作。虛擬機器租戶root
許可權和宿主機的root
虛擬機器許可權是分離的,並且虛擬機器利用如Intel
的VT-d
和VT-x
的ring-1
硬體隔離技術,這種隔離技術可以防止虛擬機器突破和彼此互動,而容器至今還沒有任何形式的硬體隔離,這使得容器容易受到攻擊 - 可管理性:
docker
的集中化管理工具還不算成熟。各種虛擬化技術都有成熟的管理工具,例如VMware vCenter
提供完備的虛擬機器管理能力 - 高可用和可恢復性:
docker
對業務的高可用支援是通過快速重新部署實現的。虛擬化具備負載均衡,高可用,容錯,遷移和資料保護等經過生產實踐檢驗的成熟保障機制,VMware
可承諾虛擬機器99.999%
高可用,保證業務連續性 - 快速建立、刪除:虛擬化建立是分鐘級別的,
Docker
容器建立是秒級別的,Docker
的快速迭代性,決定了無論是開發、測試、部署都可以節約大量時間 - 交付、部署:虛擬機器可以通過映象實現環境交付的一致性,但映象分發無法體系化。
Docker
在Dockerfile
中記錄了容器構建過程,可在叢集中實現快速分發和快速部署
我們可以從下面這張表格很清楚地看到容器相比於傳統虛擬機器的特性的優勢所在:
特性 | 容器 | 虛擬機器 |
---|---|---|
啟動 | 秒級 | 分鐘級 |
硬碟使用 | 一般為MB | 一般為GB |
效能 | 接近原生 | 弱於 |
系統支援量 | 單機支援上千個容器 | 一般是幾十個 |
Docker的三個基本概念
從上圖我們可以看到,Docker
中包括三個基本的概念:
Image
(映象)Container
(容器)Repository
(倉庫)
映象是Docker
執行容器的前提,倉庫是存放映象的場所,可見映象更是Docker
的核心。
Image (映象)
那麼映象到底是什麼呢?
Docker
映象可以看作是一個特殊的檔案系統,除了提供容器執行時所需的程式、庫、資源、配置等檔案外,還包含了一些為執行時準備的一些配置引數(如匿名卷、環境變數、使用者等)。映象不包含任何動態資料,其內容在構建之後也不會被改變。
映象(Image)
就是一堆只讀層(read-only layer)
的統一視角,也許這個定義有些難以理解,下面的這張圖能夠幫助讀者理解映象的定義。
從左邊我們看到了多個只讀層,它們重疊在一起。除了最下面一層,其它層都會有一個指標指向下一層。這些層是Docker
內部的實現細節,並且能夠在主機的檔案系統上訪問到。統一檔案系統(union file system)
技術能夠將不同的層整合成一個檔案系統,為這些層提供了一個統一的視角,這樣就隱藏了多層的存在,在使用者的角度看來,只存在一個檔案系統。我們可以在圖片的右邊看到這個視角的形式。
Container (容器)
容器(container)
的定義和映象(image)
幾乎一模一樣,也是一堆層的統一視角,唯一區別在於容器的最上面那一層是可讀可寫的。
由於容器的定義並沒有提及是否要執行容器,所以實際上,容器 = 映象 + 讀寫層。
Repository (倉庫)
Docker
倉庫是集中存放映象檔案的場所。映象構建完成後,可以很容易的在當前宿主上執行,但是, 如果需要在其它伺服器上使用這個映象,我們就需要一個集中的儲存、分發映象的服務,Docker Registry
(倉庫註冊伺服器)就是這樣的服務。有時候會把倉庫(Repository)
和倉庫註冊伺服器(Registry)
混為一談,並不嚴格區分。Docker
倉庫的概念跟Git
類似,註冊伺服器可以理解為GitHub
這樣的託管服務。實際上,一個Docker Registry
中可以包含多個倉庫(Repository)
,每個倉庫可以包含多個標籤(Tag)
,每個標籤對應著一個映象。所以說,映象倉庫是Docker
用來集中存放映象檔案的地方類似於我們之前常用的程式碼倉庫。
通常,一個倉庫會包含同一個軟體不同版本的映象,而標籤就常用於對應該軟體的各個版本。我們可以通過<倉庫名>:<標籤>
的格式來指定具體是這個軟體哪個版本的映象。如果不給出標籤,將以latest
作為預設標籤.。
倉庫又可以分為兩種形式:
public
(公有倉庫)private
(私有倉庫)
Docker Registry
公有倉庫是開放給使用者使用、允許使用者管理映象的Registry
服務。一般這類公開服務允許使用者免費上傳、下載公開的映象,並可能提供收費服務供使用者管理私有映象。
除了使用公開服務外,使用者還可以在本地搭建私有Docker Registry
。Docker
官方提供了Docker Registry
映象,可以直接使用做為私有Registry
服務。當用戶建立了自己的映象之後就可以使用push
命令將它上傳到公有或者私有倉庫,這樣下次在另外一臺機器上使用這個映象時候,只需要從倉庫上pull
下來就可以了。
我們主要把Docker
的一些常見概念如Image
,Container
,Repository
做了詳細的闡述,也從傳統虛擬化方式的角度闡述了docker
的優勢,我們從下圖可以直觀地看到Docker
的架構:
Docker
使用C/S
結構,即客戶端/伺服器體系結構。Docker
客戶端與Docker
伺服器進行互動,Docker服務端負責構建、執行和分發Docker
映象。Docker
客戶端和服務端可以執行在一臺機器上,也可以通過RESTful
、stock
或網路介面與遠端Docker
服務端進行通訊。
這張圖展示了Docker
客戶端、服務端和Docker
倉庫(即Docker Hub
和Docker Cloud
),預設情況下Docker
會在Docker
中央倉庫尋找映象檔案,這種利用倉庫管理映象的設計理念類似於Git
,當然這個倉庫是可以通過修改配置來指定的,甚至我們可以建立我們自己的私有倉庫。
Docker的安裝和使用
Docker
的安裝和使用有一些前提條件,主要體現在體系架構和核心的支援上。對於體系架構,除了Docker
一開始就支援的X86-64
,其他體系架構的支援則一直在不斷地完善和推進中。
Docker
分為CE
和EE
兩大版本。CE
即社群版(免費,支援週期7
個月),EE
即企業版,強調安全,付費使用,支援週期24
個月。
我們在安裝前可以參看官方文件獲取最新的Docker
支援情況,官方文件在這裡:
https://docs.docker.com/install/
Docker
對於核心支援的功能,即核心的配置選項也有一定的要求(比如必須開啟Cgroup
和Namespace
相關選項,以及其他的網路和儲存驅動等),Docker
原始碼中提供了一個檢測指令碼來檢測和指導核心的配置,指令碼連結在這裡:
https://raw.githubusercontent.com/docker/docker/master/contrib/check-config.sh
在滿足前提條件後,安裝就變得非常的簡單了。
Docker CE
的安裝請參考官方文件:
MacOS
:https://docs.docker.com/docker-for-mac/install/Windows
:https://docs.docker.com/docker-for-windows/install/Ubuntu
:https://docs.docker.com/install/linux/docker-ce/ubuntu/Debian
:https://docs.docker.com/install/linux/docker-ce/debian/CentOS
:https://docs.docker.com/install/linux/docker-ce/centos/Fedora
:https://docs.docker.com/install/linux/docker-ce/fedora/- 其他
Linux
發行版:https://docs.docker.com/install/linux/docker-ce/binaries/
這裡我們以CentOS7
作為本文的演示。
環境準備
- 阿里雲伺服器(1核2G,1M頻寬)
- CentOS 7.4 64位
由於Docker-CE
支援64
位版本的CentOS7
,並且要求核心版本不低於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
我們執行以下安裝命令去安裝依賴包:
- $ sudo yum install -y yum-utils \
- device-mapper-persistent-data \
- lvm2
這裡我事先已經安裝過了,所以提示我已經安裝了最新版本
安裝Docker
Docker
軟體包已經包括在預設的CentOS-Extras
軟體源裡。因此想要安裝docker
,只需要執行下面的yum
命令
$ sudo yum install docker
當然在測試或開發環境中Docker
官方為了簡化安裝流程,提供了一套便捷的安裝指令碼,CentOS
系統上可以使用這套指令碼安裝:
- curl -fsSL get.docker.com -o get-docker.sh
- sh get-docker.sh
具體可以參看docker-install
的指令碼:
https://github.com/docker/docker-install
執行這個命令後,指令碼就會自動的將一切準備工作做好,並且把Docker CE
的Edge
版本安裝在系統中。
安裝完成後,執行下面的命令,驗證是否安裝成功:
- docker version
- or
- docker info
返回docker的版本相關資訊,證明docker
安裝成功
啟動Docker-CE
- $ sudo systemctl enable docker
- $ sudo systemctl start docker
Docker的簡單運用---Hello World
由於伺服器日常崩潰了,docker
出了點問題,所以以下案例的演示是基於Kali Linux
環境下進行的。
我們通過最簡單的image
檔案hello world
,感受一下Docker
的魅力吧!
我們直接執行下面的命令,將名為hello-world
的image
檔案從倉庫抓取到本地。
docker pull library/hello-world
docker pull images
是抓取image
檔案,library/hello-world
是image
檔案在倉庫裡面的位置,其中library
是image
檔案所在的組,hello-world
是image
檔案的名字。
抓取成功以後,就可以在本機看到這個image
檔案了。
docker images
我們可以看到如下結果:
現在,我們可以執行hello-world
這個image
檔案
docker run hello-world
我們可以看到如下結果:
輸出這段提示以後,hello world
就會停止執行,容器自動終止。有些容器不會自動終止,因為提供的是服務,比如Mysql映象等。
是不是很easy
呢?我們從上面可以看出,docker
的功能是十分強大的,除此之外,我們還可以拉去一些Ubuntu
,Apache
等映象,在未來的教程中我們將會一一提到。
Docker
提供了一套簡單實用的命令來建立和更新映象,我們可以通過網路直接下載一個已經建立好了的應用映象,並通過Docker RUN
命令就可以直接使用。當映象通過RUN
命令執行成功後,這個執行的映象就是一個Docker
容器啦,容器可以理解為一個輕量級的沙箱,Docker
利用容器來執行和隔離應用,容器是可以被啟動、停止、刪除的,這並不會影響Docker
映象。
我們可以看看下面這幅圖:
Docker
客戶端是Docker
使用者與Docker
互動的主要方式。當您使用docker
命令列執行命令時,Docker
客戶端將這些命令傳送給伺服器端,服務端將執行這些命令。docker
命令使用docker API
。Docker
客戶端可以與多個服務端進行通訊。
我們將剖析一下Docker
容器是如何工作的,學習好Docker容器工作的原理,我們就可以自己去管理我們的容器了。
Docker架構
在上面的學習中,我們簡單地講解了Docker的基本架構。瞭解到了Docker
使用的是C/S
結構,即客戶端/伺服器體系結構。明白了Docker
客戶端與Docker
伺服器進行互動時,Docker
服務端負責構建、執行和分發Docker
映象。 也知道了Docker
客戶端和服務端可以執行在一臺機器上,可以通過RESTful
、stock
或網路介面與遠端Docker
服務端進行通訊。
我們從下圖可以很直觀的瞭解到Docker的架構:
Docker
的核心元件包括:
- Docker Client
- Docker daemon
- Docker Image
- Docker Registry
- Docker Container
Docker
採用的是Client/Server
架構。客戶端向伺服器傳送請求,伺服器負責構建、執行和分發容器。客戶端和伺服器可以執行在同一個Host
上,客戶端也可以通過socket
或REST API
與遠端的伺服器通訊。可能很多朋友暫時不太理解一些東西,比如REST API
是什麼東西等,不過沒關係,在後面的文章中會一一給大家講解清楚。
Docker Client
Docker Client
,也稱Docker
客戶端。它其實就是Docker
提供命令列介面(CLI)
工具,是許多Docker
使用者與Docker
進行互動的主要方式。客戶端可以構建,執行和停止應用程式,還可以遠端與Docker_Host
進行互動。最常用的Docker
客戶端就是docker
命令,我們可以通過docker
命令很方便地在host
上構建和執行docker
容器。
Docker daemon
Docker daemon
是伺服器元件,以Linux
後臺服務的方式執行,是Docker
最核心的後臺程序,我們也把它稱為守護程序。它負責響應來自Docker Client
的請求,然後將這些請求翻譯成系統呼叫完成容器管理操作。該程序會在後臺啟動一個API Server
,負責接收由Docker Client
傳送的請求,接收到的請求將通過Docker daemon
內部的一個路由分發排程,由具體的函式來執行請求。
我們大致可以將其分為以下三部分:
- Docker Server
- Engine
- Job
Docker Daemon的架構如下所示:
Docker Daemon
可以認為是通過Docker Server
模組接受Docker Client
的請求,並在Engine
中處理請求,然後根據請求型別,創建出指定的Job
並執行。Docker Daemon
執行在Docker host
上,負責建立、執行、監控容器,構建、儲存映象。
執行過程的作用有以下幾種可能:
- 向
Docker Registry
獲取映象 - 通過
graphdriver
執行容器映象的本地化操作 - 通過
networkdriver
執行容器網路環境的配置 - 通過
execdriver
執行容器內部執行的執行工作
由於Docker Daemon
和Docker Client
的啟動都是通過可執行檔案docker
來完成的,因此兩者的啟動流程非常相似。Docker
可執行檔案執行時,執行程式碼通過不同的命令列flag
引數,區分兩者,並最終執行兩者各自相應的部分。
啟動Docker Daemon
時,一般可以使用以下命令來完成
- docker --daemon = true
- docker –d
- docker –d = true
再由docker
的main()
函式來解析以上命令的相應flag
引數,並最終完成Docker Daemon
的啟動。
下圖可以很直觀地看到Docker Daemon
的啟動流程:
預設配置下,Docker daemon
只能響應來自本地Host
的客戶端請求。如果要允許遠端客戶端請求,需要在配置檔案中開啟TCP
監聽。我們可以照著如下步驟進行配置:
1、編輯配置檔案/etc/systemd/system/multi-user.target.wants/docker.service
,在環境變數ExecStart
後面新增-H tcp://0.0.0.0
,允許來自任意 IP 的客戶端連線。
2、重啟Docker daemon
- systemctl daemon-reload
- systemctl restart docker.service
3、我們通過以下命令即可實現與遠端伺服器通訊
docker -H 伺服器IP地址 info
-H
是用來指定伺服器主機,info
子命令用於檢視Docker
伺服器的資訊
Docker Image
Docker
映象可以看作是一個特殊的檔案系統,除了提供容器執行時所需的程式、庫、資源、配置等檔案外,還包含了一些為執行時準備的一些配置引數(如匿名卷、環境變數、使用者等)。映象不包含任何動態資料,其內容在構建之後也不會被改變。我們可將Docker
映象看成只讀模板,通過它可以建立Docker
容器。
映象有多種生成方法:
- 從無到有開始建立映象
- 下載並使用別人建立好的現成的映象
- 在現有映象上建立新的映象
我們可以將映象的內容和建立步驟描述在一個文字檔案中,這個檔案被稱作Dockerfile
,通過執行docker build <docker-file>
命令可以構建出 Docker 映象,在後續的教程中,我們會用一篇專門討論這個問題。
Docker Registry
Docker registry
是儲存docker image
的倉庫,它在docker
生態環境中的位置如下圖所示:
執行docker push
、docker pull
、docker search
時,實際上是通過docker daemon
與docker registry
通訊。
Docker Container
Docker
容器就是Docker
映象的執行例項,是真正執行專案程式、消耗系統資源、提供服務的地方。Docker Container
提供了系統硬體環境,我們可以使用Docker Images
這些製作好的系統盤,再加上我們所編寫好的專案程式碼,run
一下就可以提供服務啦。
Docker元件是如何協作執行容器
看到這裡,我相信各位讀者朋友們應該已經對Docker基礎架構已經熟悉的差不多了,我們還記得執行的第一個容器嗎?現在我們再通過hello-world這個例子來體會一下Docker
各個元件是如何協作的。
容器啟動過程如下:
Docker
客戶端執行docker run
命令Docker daemon
發現本地沒有hello-world
映象daemon
從Docker Hub
下載映象- 下載完成,映象
hello-world
被儲存到本地 Docker daemon
啟動容器
具體過程可以看如下這幅演示圖:
我們可以通過docker images
可以檢視到hello-world
已經下載到本地
我們可以通過docker ps
或者docker container ls
顯示正在執行的容器,我們可以看到,hello-world
在輸出提示資訊以後就會停止執行,容器自動終止,所以我們在檢視的時候沒有發現有容器在執行。
我們把Docker
容器的工作流程剖析的十分清楚了,我們大體可以知道Docker
元件協作執行容器可以分為以下幾個過程:
Docker
客戶端執行docker run
命令Docker daemon
發現本地沒有我們需要的映象daemon
從Docker Hub
下載映象- 下載完成後,映象被儲存到本地
Docker daemon
啟動容器
瞭解了這些過程以後,我們再來理解這些命令就不會覺得很突兀了,下面我來給大家講講Docker
常用的一些命令操作吧。
Docker常用命令
我們可以通過docker -h
去檢視命令的詳細的幫助文件。在這裡我只會講一些平常日常比賽或者生活中我們可能會用的比較多的一些命令。
例如,我們需要拉取一個docker
映象,我們可以用如下命令:
docker pull image_name
image_name
為映象的名稱,而如果我們想從Docker Hub
上去下載某個映象,我們可以使用以下命令:
docker pull centos:latest
centos:lastest
是映象的名稱,Docker daemon
發現本地沒有我們需要的映象,會自動去Docker Hub
上去下載映象,下載完成後,該映象被預設儲存到/var/lib/docker
目錄下。
接著我們如果想檢視下主機下存在多少映象,我們可以用如下命令:
docker images
我們要想知道當前有哪些容器在執行,我們可以用如下命令:
docker ps -a
-a
是檢視當前所有的容器,包括未執行的
我們該如何去對一個容器進行啟動,重啟和停止呢?我們可以用如下命令:
- docker start container_name/container_id
- docker restart container_name/container_id
- docker stop container_name/container_id
這個時候我們如果想進入到這個容器中,我們可以使用attach
命令:
docker attach container_name/container_id
那如果我們想執行這個容器中的映象的話,並且呼叫映象裡面的bash
,我們可以使用如下命令:
docker run -t -i container_name/container_id /bin/bash
那如果這個時候,我們想刪除指定映象的話,由於image
被某個container
引用(拿來執行),如果不將這個引用的container
銷燬(刪除),那image
肯定是不能被刪除。我們首先得先去停止這個容器:
- docker ps
- docker stop container_name/container_id
然後我們用如下命令去刪除這個容器:
docker rm container_name/container_id
然後這個時候我們再去刪除這個映象:
docker rmi image_name
此時,常用的Docker
相關的命令就講到這裡為止了,我們在後續的文章中還會反覆地提到這些命令。
Dockerfile是什麼
前面我們已經提到了Docker
的一些基本概念。以CTF
選手的角度來看,我們可以去使用Dockerfile
定義映象,依賴映象來執行容器,可以去模擬出一個真實的漏洞場景。因此毫無疑問的說,Dockerfile
是映象和容器的關鍵,並且Dockerfile
還可以很輕易的去定義映象內容,說了這麼多,那麼Dockerfile
到底是個什麼東西呢?
Dockerfile
是自動構建docker
映象的配置檔案, 使用者可以使用Dockerfile
快速建立自定義的映象。Dockerfile
中的命令非常類似於linux
下的shell
命令。
我們可以通過下面這幅圖來直觀地感受下 Docker 映象、容器和 Dockerfile 三者之間的關係。
我們從上圖中可以看到,Dockerfile
可以自定義映象,通過Docker
命令去執行映象,從而達到啟動容器的目的。
Dockerfile
是由一行行命令語句組成,並且支援已#
開頭的註釋行。
一般來說,我們可以將Dockerfile
分為四個部分:
- 基礎映象(父映象)資訊指令
FROM
- 維護者資訊指令
MAINTAINER
- 映象操作指令
RUN
、EVN
、ADD
和WORKDIR
等 - 容器啟動指令
CMD
、ENTRYPOINT
和USER
等
下面是一段簡單的Dockerfile的例子:
- FROM python:2.7
- MAINTAINER Angel_Kitty <angelkitty6698@gmail.com>
- COPY . /app
- WORKDIR /app
- RUN pip install -r requirements.txt
- EXPOSE 5000
- ENTRYPOINT ["python"]
- CMD ["app.py"]
我們可以分析一下上面這個過程:
- 1、從
Docker Hub
上pull
下python 2.7
的基礎映象 - 2、顯示維護者的資訊
- 3、
copy
當前目錄到容器中的/app
目錄下 複製本地主機的<src>
(Dockerfile
所在目錄的相對路徑)到容器裡<dest>
- 4、指定工作路徑為
/app
- 5、安裝依賴包
- 6、暴露
5000
埠 - 7、啟動
app
這個例子是啟動一個python flask app
的Dockerfile
(flask
是python
的一個輕量的web
框架),相信大家從這個例子中能夠稍微理解了Dockfile的組成以及指令的編寫過程。
Dockerfile常用的指令
根據上面的例子,我們已經差不多知道了Dockerfile的組成以及指令的編寫過程,我們再來理解一下這些常用命令就會得心應手了。
由於Dockerfile
中所有的命令都是以下格式:INSTRUCTION argument
,指令(INSTRUCTION)
不分大小寫,但是推薦大寫,和sql語句是不是很相似呢?下面我們正式來講解一下這些指令集吧。
FROM
FROM
是用於指定基礎的images
,一般格式為FROM <image>
orFORM <image>:<tag>
,所有的Dockerfile
都用該以FROM
開頭,FROM
命令指明Dockerfile
所建立的映象檔案以什麼映象為基礎,FROM
以後的所有指令都會在FROM
的基礎上進行建立映象。可以在同一個Dockerfile
中多次使用FROM
命令用於建立多個映象。比如我們要指定python 2.7
的基礎映象,我們可以像如下寫法一樣:
FROM python:2.7
MAINTAINER
MAINTAINER 是用於指定映象建立者和聯絡方式,一般格式為MAINTAINER <name>
。這裡我設定成我的ID
和郵箱:
MAINTAINER Angel_Kitty <angelkitty6698@gmail.com>
COPY
COPY
是用於複製本地主機的<src>
(為 Dockerfile 所在目錄的相對路徑)到容器中的<dest>
。
當使用本地目錄為源目錄時,推薦使用COPY
。一般格式為COPY <src><dest>
。例如我們要拷貝當前目錄到容器中的/app
目錄下,我們可以這樣操作:
COPY . /app
WORKDIR
WORKDIR
用於配合RUN
,CMD
,ENTRYPOINT
命令設定當前工作路徑。可以設定多次,如果是相對路徑,則相對前一個WORKDIR
命令。預設路徑為/
。一般格式為WORKDIR /path/to/work/dir
。例如我們設定/app
路徑,我們可以進行如下操作:
WORKDIR /app
RUN
RUN
用於容器內部執行命令。每個RUN
命令相當於在原有的映象基礎上添加了一個改動層,原有的映象不會有變化。一般格式為RUN <command>
。例如我們要安裝python
依賴包,我們做法如下:
RUN pip install -r requirements.txt
EXPOSE
EXPOSE
命令用來指定對外開放的埠。一般格式為EXPOSE <port> [<port>...]
例如上面那個例子,開放5000埠:
EXPOSE 5000
ENTRYPOINT
ENTRYPOINT
可以讓你的容器表現得像一個可執行程式一樣。一個Dockerfile
中只能有一個ENTRYPOINT
,如果有多個,則最後一個生效。
ENTRYPOINT
命令也有兩種格式:
ENTRYPOINT ["executable", "param1", "param2"]
:推薦使用的exec
形式ENTRYPOINT command param1 param2
:shell
形式
例如下面這個,我們要將python
映象變成可執行的程式,我們可以這樣去做:
ENTRYPOINT ["python"]
CMD
CMD
命令用於啟動容器時預設執行的命令,CMD
命令可以包含可執行檔案,也可以不包含可執行檔案。不包含可執行檔案的情況下就要用ENTRYPOINT
指定一個,然後CMD
命令的引數就會作為ENTRYPOINT
的引數。
CMD
命令有三種格式:
CMD ["executable","param1","param2"]
:推薦使用的exec
形式。CMD ["param1","param2"]
:無可執行程式形式CMD command param1 param2
:shell 形式。
一個Dockerfile
中只能有一個CMD
,如果有多個,則最後一個生效。而CMD
的shell
形式預設呼叫/bin/sh -c
執行命令。
CMD
命令會被Docker
命令列傳入的引數覆蓋:docker run busybox /bin/echo Hello
Docker
會把CMD
裡的命令覆蓋。
例如我們要啟動/app
,我們可以用如下命令實現:
CMD ["app.py"]
當然還有一些其他的命令,我們在用到的時候再去一一講解一下。
構建Dockerfile
我們大體已經把Dockerfile的寫法講述完畢,我們可以自己動手寫一個例子:
- mkdir static_web
- cd static_web
- touch Dockerfile
- 然後 vi Dockerfile 開始編輯該檔案
- 輸入 i 開始編輯
- 以下是我們構建的Dockerfile內容
- ``````````
- FROM nginx
- MAINTAINER Angel_Kitty <[email protected]>
- RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
- ``````````
- 編輯完後 按 esc 退出編輯
- 然後 :wq 寫入 退出
我們在Dockerfile
檔案所在目錄執行:
docker build -t angelkitty/nginx_web:v1 .
我們解釋一下,-t
是為新映象設定倉庫和名稱,其中angelkitty
為倉庫名,nginx_web
為映象名,:v1
為標籤(不新增為預設latest
)
我們構建完成之後,使用docker images
命令檢視所有映象,如果存在REPOSITORY
為nginx
和TAG
是v1
的資訊,就表示構建成功。
接下來使用docker run
命令來啟動容器
docker run --name nginx_web -d -p 8080:80 angelkitty/nginx_web:v1
這條命令會用 nginx 映象啟動一個容器,命名為nginx_web
,並且映射了 8080 埠,這樣我們可以用瀏覽器去訪問這個nginx
伺服器:http://localhost:8080/
或者 http://本機的IP地址:8080/,頁面返回資訊:
這樣一個簡單使用Dockerfile
構建映象,執行容器的示例就完成了!