1. 程式人生 > >Docker實踐之02-使用鏡像及定制

Docker實踐之02-使用鏡像及定制

world 工作原理 占用 最終 new 名稱 ignore strong 運行時

目錄

  • 一.獲取鏡像
  • 二.使用鏡像啟動容器實例
  • 三.列出鏡像
  • 四.刪除本地鏡像
  • 五.定制鏡像
    • 通過commit命令定制鏡像
    • 通過Dockerfile定制鏡像
      • docker build的工作原理
      • docker build的用法

一.獲取鏡像

Docker運行容器前需要本地存在對應的鏡像,如果本地不存在該鏡像,Docker會從鏡像倉庫下載該鏡像。
從Docker鏡像倉庫獲取鏡像的命令是docker pull,其命令格式為:

docker pull [選項] [Docker Registry 地址[:端口號]/]倉庫名[:標簽]

如:

$ docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
297061f60c36: Pull complete 
e9ccef17b516: Pull complete 
dbc33716854d: Pull complete 
8fe36b178d25: Pull complete 
686596545a94: Pull complete 
Digest: sha256:1dfb94f13f5c181756b2ed7f174825029aca902c78d0490590b1aaa203abc052
Status: Downloaded newer image for ubuntu:16.04

上面的命令中沒有給出Docker鏡像倉庫地址,因此將會從Docker Hub獲取鏡像(如果配置鏡像加速器,則從鏡像加速器上拉取鏡像)。而鏡像名稱是ubuntu:16.04,因此將會獲取官方鏡像library/ubuntu倉庫中標簽為16.04的鏡像。
從下載過程中可以看到我們之前提及的分層存儲的概念,鏡像是由多層存儲所構成。下載也是一層層的去下載,並非單一文件。下載過程中給出了每一層的ID前12位,並且下載結束後,給出該鏡像完整的sha256的摘要,以確保下載一致性。

二.使用鏡像啟動容器實例

使用命令:docker run從鏡像啟動容器實例。

$ docker run -it --rm ubuntu:16.04 bash

docker run就是運行容器的命令,簡要的說明一下上面用到的參數。
-it:這是2個參數,一個是-i:交互式操作,一個-t:終端。
--rm:這個參數說明容器退出之後隨之將其刪除。默認情況,不使用--rm參數啟動的容器在退出之後不會立即刪除,除非使用命令明確刪除:docker rm
ubuntu:16.04:指定使用ubuntu:16.04這個鏡像為基礎啟動容器
bash:放在鏡像名之後的是命令,即:啟動容器之後在容器中執行的命令

使用exit命令退出容器。

三.列出鏡像

使用docker image ls命令列出當前已經下載到本地的鏡像。

$ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
ubuntu              16.04               0b1edfbffd27        4 days ago          113MB
hello-world         latest              e38bc07ac18e        2 weeks ago         1.85kB

輸出列表中包含了倉庫名,標簽,鏡像ID,創建時間和所占用的空間。
鏡像ID則是鏡像的唯一標識,一個鏡像可以對應多個標簽(不同標簽下的同一個鏡像的ID值相同)。

四.刪除本地鏡像

$ docker image rm <鏡像id>

五.定制鏡像

鏡像的定制實際上就是定制每一層所添加的配置和文件,定制鏡像有2種方式:commit,Dockerfile。

通過commit命令定制鏡像

使用鏡像啟動容器實例之後,在容器內部做的所有修改,都可以使用commit命令將容器存儲層保存為鏡像。如:

$ docker commit --author "[email protected]" --message "modify default index page content" webserver nginx:v2

--author指定修改的作者,而--message則是記錄本次修改的內容,webserver為容器名稱,nginx:v2為需要保存為的鏡像倉庫名和標簽。

慎用docker commit命令保存鏡像!制作鏡像應該使用Dockerfile實現。使用docker commit命令保存鏡像存在一些缺陷:
首先,在對容器進行配置時,會涉及多個文件的修改或添加,但其實有些文件是不需要保存為鏡像的,如果不進行小心清理,會導致最終保存的鏡像文件過於臃腫。
其次,使用命令方式意味著所有對鏡像的操作都是黑箱的,不利於重現鏡像制作的步驟,以及後期的維護。

通過Dockerfile定制鏡像

通過Dockerfile定制鏡像就是把每一層修改、安裝、構建、操作的命令都寫入一個腳本,用這個腳本來構建、定制鏡像,那麽之前提及的無法重復的問題、鏡像構建透明性的問題、體積的問題就都會解決。
Dockerfile是一個文本文件,其內包含了一條條的指令,每一條指令構建一層,因此每一條指令的內容,就是描述該層應當如何構建。

使用了docker build命令進行鏡像構建,其格式為:

docker build [選項] <上下文路徑/URL/->

例如: $ docker build -t nginx:v3 .

docker build的工作原理

首先我們要理解docker build的工作原理。Docker在運行時分為Docker引擎(也就是服務端守護進程)和客戶端工具。Docker的引擎提供了一組REST API,被稱為Docker Remote API,而如docker命令這樣的客戶端工具,則是通過這組API與Docker引擎交互,從而完成各種功能。因此,雖然表面上我們好像是在本機執行各種Docker功能,但實際上,一切都是使用的遠程調用形式在服務端(Docker 引擎)完成的。也因為這種C/S設計,讓我們操作遠程服務器的Docker引擎變得輕而易舉。
當我們進行鏡像構建的時候,並非所有定制都會通過RUN指令完成,經常會需要將一些本地文件復制進鏡像,比如通過COPY指令,ADD指令等。而docker build命令構建鏡像,其實並非在本地構建,而是在服務端,也就是Docker引擎中構建的。那麽在這種C/S架構中,如何才能讓服務端獲得本地文件呢?這就引入了上下文的概念。當構建的時候,用戶會指定構建鏡像上下文的路徑,docker build命令得知這個路徑後,會將路徑下的所有內容打包,然後上傳給Docker引擎。這樣Docker引擎收到這個上下文包後,展開就會獲得構建鏡像所需的一切文件。如果在Dockerfile中這麽寫:“COPY ./package.json /app/”,這並不是要復制執行docker build命令所在的目錄下的package.json ,也不是復制Dockerfile所在目錄下的 package.json ,而是復制上下文(context)目錄下的package.json。因此,COPY這類指令中的源文件的路徑都是相對路徑。這也是初學者經常會問的為什麽“COPY ../package.json /app”或者“COPY /opt/xxxx /app”無法工作的原因,因為這些路徑已經超出了上下文的範圍,Docker引擎無法獲得這些位置的文件。如果真的需要那些文件,應該將它們復制到上下文目錄中去。
現在就可以理解剛才的命令docker build -t nginx:v3 .中的這個.實際上是在指定上下文的目錄,docker build命令會將該目錄下的內容打包交給Docker引擎以幫助構建鏡像。
如果觀察docker build輸出,我們其實已經看到了這個發送上下文的過程:

$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...

理解構建上下文對於鏡像構建是很重要的,避免犯一些不應該的錯誤。比如有些初學者在發現COPY /opt/xxxx /app不工作後,於是幹脆將Dockerfile放到了硬盤根目錄去構建,結果發現docker build執行後,在發送一個幾十GB的東西,極為緩慢而且很容易構建失敗。那是因為這種做法是在讓docker build打包整個硬盤,這顯然是使用錯誤。
一般來說,應該將Dockerfile置於一個空目錄下,或者項目根目錄下。如果該目錄下沒有所需文件,那麽應該把所需文件復制一份過來。如果目錄下有些東西確實不希望構建時傳給Docker引擎,那麽可以用.gitignore一樣的語法寫一個.dockerignore ,該文件是用於剔除不需要作為上下文傳遞給Docker引擎的。那麽為什麽會有人誤以為“.”是指定Dockerfile所在目錄呢?這是因為在默認情況下,如果不額外指定Dockerfile的話,會將上下文目錄下的名為“Dockerfile”的文件作為構建鏡像需要的Dockerfile。這只是默認行為,實際上Dockerfile的文件名並不要求必須為Dockerfile,而且並不要求必須位於上下文目錄中,比如可以用“-f ../Dockerfile.php”參數指定某個文件作為Dockerfile 。
當然,一般大家習慣性的會使用默認的文件名Dockerfile,以及會將其置於鏡像構建上下文目錄中。

docker build的用法

docker build的基本語法為:docker build [選項] <上下文路徑/URL/->,從語法中可以很明顯地看出該命令支持多種構建方式.

1.從本地文件系統構建

$ docker build -t nginx:v3 .

上述構建操作包含2個參數:
"-t"指定鏡像標簽名稱
"."指定構建上下文路徑

2.從Git倉庫構建

$ docker build https://github.com/nuccch/docker_test#:8.14

這行命令指定了構建所需的Git倉庫,並且指定默認的master分支,構建目錄為/8.14/,然後Docker就會自己去 git clone這個項目、切換到指定分支、並進入到指定目錄後開始構建。
說明: 從Git倉庫中構建Docker鏡像是在實際項目中使用得比較多的方式,開發者只需要將代碼提交到指定Git倉庫的指定分支,測試只需要從該倉庫的分支上構建Docker鏡像即可.

3.用給定的tar壓縮包構建

$ docker build http://server/context.tar.gz

如果所給出的URL不是個Git倉庫,而是個tar壓縮包,那麽Docker引擎會下載這個包,並自動解壓縮,以其作為上下文,開始構建。

4.從標準輸入中讀取Dockerfile進行構建

$ docker build - < Dockerfile

或者

$ cat Dockerfile | docker build -

如果標準輸入傳入的是文本文件,則將其視為Dockerfile ,並開始構建。
註意: 這種形式由於直接從標準輸入中讀取Dockerfile的內容,它沒有上下文,因此不可以存在像其他方法那樣可以將本地文件COPY進鏡像之類的事情。

5.從標準輸入中讀取上下文壓縮包進行構建

$ docker build - < context.tar.gz

如果發現標準輸入的文件格式是gzip,bzip2以及xz的話,將會使其為上下文壓縮包,直接將其展開,將裏面視為上下文,並開始構建。

【參考】
http://blog.daocloud.io/principle-of-docker-image/ 深入分析 Docker 鏡像原理
https://yq.aliyun.com/articles/68477 Docker鏡像原理和最佳實踐

Docker實踐之02-使用鏡像及定制