Docker實踐之02-使用鏡像及定制
目錄
- 一.獲取鏡像
- 二.使用鏡像啟動容器實例
- 三.列出鏡像
- 四.刪除本地鏡像
- 五.定制鏡像
- 通過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-使用鏡像及定制