寫在前面
鏡像是Docker三大核心概念中最重要的一部分,而Docker運行容器前需要本地存在對應的鏡像,如果鏡像不存在,Docker會嘗試從默認的鏡像倉庫中下載(默認使用Docker Hub公共注冊服務器的倉庫),用戶也可以通過配置來使用自定義的鏡像倉庫,筆者在前面就自定義了鏡像倉庫。
既然鏡像這么重要,那么本篇就圍繞鏡像這一核心概念來具體介紹相關操作:(1)如何使用pull命令從Docker Hub倉庫中下載鏡像到本地;(2)如何查看本地已有的鏡像信息和管理鏡像標簽;(3)如何在遠端倉庫中使用search命令進行搜索和過濾;(4)如何刪除鏡像標簽和鏡像文件;(5)如何創建用戶定制的鏡像并且保存為外部文件;(6)如何往Docker Hub倉庫中推送自己的鏡像。
獲取鏡像
鏡像是運行容器的前提,因此首先是需要獲取鏡像,開發者可以使用docker [image] pull命令直接從官方的Docker Hub網站上進行獲取,該命令的格式為docker [image] pull NAME[:TAG],其中NAME是鏡像倉庫名稱,用來區分鏡像,而TAG則是鏡像的標簽,一般用來表示版本信息,因此通常情況下描述一個鏡像需要使用"名稱+標簽"這一格式。
舉個例子,獲取一個Ubuntu18.04系統的基礎鏡像可以使用如下命令:
[envythink@localhost ~]$ docker pull ubuntu:18.04
運行結果如下所示:
對于Docker鏡像來說,如果后面不顯式指定TAG,則默認會選擇latest標簽,也就是會下載倉庫中最新版本的鏡像。
此時上面的命令可以修改為:
[envythink@localhost ~]$ docker pull ubuntu
其實也就相當于執行docker pull ubuntu:latest命令。請注意,鏡像的latest標簽是最新版的,因此可能是不穩定的,所以在生產環境中一定不能使用默認的latest標簽。
細心的你可能發現了在下載過程中,鏡像文件是由若干層(layer)組成,像171857c49d0f這樣的字符串其實就是該層的唯一id(完整的id包括256比特,64個十六進制字符組成)。使用docker pull命令下載鏡像的時候,里面會輸出鏡像各層的信息,當不同的鏡像包含相同的層時,本地僅存儲層的一份內容,這無疑可以減少存儲空間。
現在有一個問題,就是在不同的鏡像服務器的情況下,可能會出現鏡像重名的情況。也就是說,其實鏡像的倉庫名稱中還應該添加倉庫地址(register,注冊服務器)作為前綴,如果你之前使用的是默認的官方DockerHub地址,就可以忽略該前綴。
舉個例子,使用docker pull ubuntu:18.04命令其實就相當于執行docker pull register.hub.docker.com/ubuntu:18.04命令,即從默認的注冊服務器DockerHub Register中的ubuntu倉庫來下載標記為18.04的鏡像。
由于官方鏡像倉庫在國外,因此訪問速度是非常慢的,通常是從非官方倉庫下載,此時需要在倉庫名稱前指定完整的倉庫地址。舉個例子,假設從網易蜂巢的鏡像源來下載ubuntu:18.04的鏡像,此時可以使用的命令如下:
docker pull hub.c.163.com/public/ubuntu:18.04
當然這個pull命令支持添加一些選項參數,如-a或者--all-tags它的值可以是true或者false,表示是否獲取倉庫中的所有鏡像,默認肯定是否。--disable-content-trust參數表示取消鏡像的內容校驗,默認是真。
前面也說過官方鏡像倉庫在國外,所以訪問速度是非常慢的,同時為了避免每次執行命令前都添加register地址,因此可以使用鏡像代理服務來加速Docker鏡像的獲取過程。centos系統只需在/etc/docker/daemon.json文件中添加一行配置即可,如下所示:
{"registry-mirrors": ["http://f1361db2.m.daocloud.io"]}
當然也可以在啟動配置參數中添加--registry-mirror=proxy_URL來指定鏡像代理服務器地址。
為了后續演示的需要,這里先使用docker pull ubuntu:18.04命令來下載一個18.04版本的ubuntu系統鏡像,然后就使用該鏡像創建一個容器,并在其中運行bash命令,執行輸出“Hello World”的命令,如下所示:
[envythink@localhost ~]$ docker run -it ubuntu bash
root@cae6035fd1b2:/# echo "Hello World"
Hello World
root@cae6035fd1b2:/# exit
查看鏡像信息
使用images命令列出所有鏡像
開發者可以使用docker images或者docker image ls命令來列出本地主機上已有的所有鏡像的基本信息,如下所示:
[envythink@localhost ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 9140108b62dc 8 hours ago 72.9MB
haproxy latest 4e531c2cb889 5 months ago 92.4MB
pxc latest a6a51beefff1 9 months ago 494MB
percona/percona-xtradb-cluster latest a6a51beefff1 9 months ago 494MB
接下來詳細解釋一下上述列出的信息:
- REPOSITORY:表示來自哪個倉庫。這里的ubuntu則表示ubuntu系列的基礎鏡像。
- TAG:表示鏡像的標簽。這里的latest表示最新的版本信息,請注意標簽只是標記,并不能標識鏡像內容。
- IMAGE ID:表示鏡像的ID,注意這是鏡像的唯一標識ID。如果兩個鏡像的ID相同,說明它們指向了同一個鏡像,只是具有不同的標簽名稱而已。
- CREATED:表示創建時間,也就是鏡像最后的更新時間。
- SIZE:表示鏡像大小,一般來說比較優秀的鏡像其體積就越小。
由于鏡像ID非常重要,它唯一標識了鏡像,因此在使用鏡像ID的時候,可以使用該ID的前若干個字符組成的可區分串來代替完整的ID。TAG信息只是用來標記來自同一倉庫的不同鏡像,如ubuntu倉庫的多個鏡像,則使用TAG消息來區分不同的發行版本,如18.04、20.04等。SIZE信息只是表示該鏡像的邏輯體積大小,實際上由于相同的鏡像層本地只會存儲一份,因此物理上占用的存儲空間會小于各鏡像的邏輯體積之和。
當然images也支持多種子命令,如-a或者--all=true|false表示列出所有(包含臨時文件)鏡像大小,默認是fasle。-f或者--filter=[]參數,它表示過濾列出的鏡像,如dangling=true等,它只顯示沒有被使用的鏡像,也可指定帶有特定標注的鏡像等。更多的子命令可以使用man docker-images命令來進行查看。
使用tag命令來添加鏡像標簽
一般來說,為了后續工作中使用特定鏡像,會使用docker tag命令來為本地鏡像任意添加新的標簽。舉個例子,給之前下載的ubuntu鏡像添加一個新的envyubuntu:latest標簽,如下所示:
[envythink@localhost ~]$ docker tag ubuntu:latest envyubuntu:latest
然后再次使用docker images命令來列舉出本地主機上的鏡像信息,可以看到多了一個envyubuntu:latest標簽的鏡像,如下所示:
[envythink@localhost ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
envyubuntu latest 9140108b62dc 8 hours ago 72.9MB
ubuntu latest 9140108b62dc 8 hours ago 72.9MB
haproxy latest 4e531c2cb889 5 months ago 92.4MB
pxc latest a6a51beefff1 9 months ago 494MB
percona/percona-xtradb-cluster latest a6a51beefff1 9 months ago 494MB
這樣后續就可以直接使用envyubuntu:latest來表示這個鏡像,細心的你可能發現這個envyubuntu:latest鏡像和之前的ubuntu:latest鏡像兩者的鏡像ID一樣,因此可以知道docker tag只是給原來的鏡像添加了一個新的快捷訪問方式,也就是CentOS中的鏈接。
使用inspect來查看詳細信息
如果我們需要查看某個鏡像的詳細信息,可以使用docker inspect [image]命令來進行查看:
可以發現上面返回的是一個JSON格式的對象,但是顯示的內容太多,如果只想其中某一項內容時,可以使用-f參數來進行過濾,如只想獲取鏡像的Architecture信息:
[envythink@localhost ~]$ docker inspect envyubuntu:latest -f {{".Architecture"}}
amd64
使用history命令查看鏡像歷史
前面也說過鏡像文件由多個層組成,現在問題來了,如何知道每個層的具體內容?可以使用history命令來列出各層的創建信息。
舉個例子,可以使用如下命令來查看envyubuntu:latest鏡像的創建過程:
[envythink@localhost ~]$ docker history envyubuntu:latest
IMAGE CREATED CREATED BY SIZE COMMENT
9140108b62dc 8 hours ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 8 hours ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B
<missing> 8 hours ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 0B
<missing> 8 hours ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 811B
<missing> 8 hours ago /bin/sh -c #(nop) ADD file:da80f59399481ffc3… 72.9MB
可以發現上面一些過長的命令被自動截斷了,如果想查看完整的輸出命令,可以使用--no-trunc選項參數來設置。
搜索鏡像
開發者可以使用docker search [option] keyword命令來搜索Docker Hub官方倉庫中的鏡像。該命令支持的選項參數如下:(1)-f或者--filter filter表示過濾輸出內容;(2)--format string表示格式化輸出內容;(3)--limit int表示限制輸出結果的個數,默認為25個;(4)--no-trunc表示不截斷輸出結果。
這些選項參數都不用記憶,用的使用時候使用man docker-search命令查看一下即可。舉個例子,搜索官方提供的鏡像中包含Nginx關鍵字的鏡像,如下所示:
[envythink@localhost ~]$ docker search --filter=is-official=true nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 13785 [OK]
再舉個例子,搜索官方提供的鏡像中所有收藏數超過8的包含tensorflow關鍵字的鏡像:
[envythink@localhost ~]$ docker search --filter=stars=8 tensorflow
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
tensorflow/tensorflow Official Docker images for the machine learn… 1766
jupyter/tensorflow-notebook Jupyter Notebook Scientific Python Stack w/ … 236
tensorflow/serving Official images for TensorFlow Serving (http… 97
xblaster/tensorflow-jupyter Dockerized Jupyter with tensorflow 54 [OK]
rocm/tensorflow Tensorflow with ROCm backend support 54
floydhub/tensorflow tensorflow 25 [OK]
bitnami/tensorflow-serving Bitnami Docker Image for TensorFlow Serving 14 [OK]
opensciencegrid/tensorflow-gpu TensorFlow GPU set up for OSG 12
從返回結果中可以看到有關鏡像的基本信息,如鏡像名稱、描述、收藏數(受歡迎程度)、是否官方創建、是否自動創建等,注意默認的輸出結果是按照星級評價來進行排序的。
其實這些在man docker-search命令中都有介紹:
刪除和清理鏡像
使用標簽刪除鏡像
開發者可以使用docker rmi或者docker image rm命令來刪除鏡像,相應的命令格式為docker rmi IMAGE[IMAGE...],其中IMAGE可以是標簽或者ID。
該命令支持的選項參數如下:(1)-f或者--force表示強制刪除鏡像,即使有容器依賴它;(2)-no-prune表示不清理未帶標簽的父鏡像。
同樣這些選項參數都不用記憶,用的時候使用man docker-image-rm命令查看一下即可。
舉個例子,將之前創建的envyubuntu:latest鏡像給刪除,相應的命令如下:
[envythink@localhost ~]$ docker image rm envyubuntu:latest
Untagged: envyubuntu:latest
請注意我們刪除上述envyubuntu:latest鏡像的時候,本地的ubuntu:latest鏡像是不受到任何影響的。當某一個鏡像擁有多個標簽的時候,此時執行docker image rm或者是docker rmi命令的時候,只是刪除了該鏡像多個標簽中的指定標簽而已,并不影響鏡像文件,也就是僅僅刪除了這個鏡像的一個標簽副本而已。
此時開發者可以確認一下,本地的ubuntu:latest鏡像依舊也是存在的:
[envythink@localhost ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu latest 9140108b62dc 9 hours ago 72.9MB
haproxy latest 4e531c2cb889 5 months ago 92.4MB
pxc latest a6a51beefff1 9 months ago 494MB
percona/percona-xtradb-cluster latest a6a51beefff1 9 months ago 494MB
但是需要注意,如果這個鏡像只剩下一個標簽,那么此時執行docker image rm或者是docker rmi命令的時候就會刪除這個鏡像文件的所有文件層。
使用鏡像ID來刪除鏡像
前面說過當使用docker image rm或者是docker rmi命令的時候,其后面也是可以接鏡像的ID(也可以是能進行區分的部分ID串前綴),此時會先嘗試刪除所有指向該鏡像的標簽,然后刪除該鏡像文件本身。因為多個標簽指向的都是同一個鏡像ID,所以最終肯定會刪除該鏡像本身。
但是需要注意,當有該鏡像創建的容器存在時,鏡像文件默認是無法被刪除的。
舉個例子,可以使用之前的ubuntu:latest鏡像來創建一個簡單的容器,并輸出一句話:
[envythink@localhost ~]$ docker run ubuntu:latest echo "hello,I am envy"
hello,I am envy
接著使用docker ps -a命令來查看本機上存在的所有容器:
[envythink@localhost ~]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd56a307081b ubuntu:latest "echo 'hello,I am en…" 24 seconds ago Exited (0) 23 seconds ago nostalgic_lewin
可以看到這個容器就是基于前面的ubuntu:latest鏡像而創建出來的,不過它的狀態是退出而已。但是開發者是無法刪除該ubuntu:latest鏡像的,Docker會提示有容器正在運行,無法刪除,如下所示:
[envythink@localhost ~]$ docker image rm ubuntu:latest
Error response from daemon: conflict: unable to remove repository reference "ubuntu:latest" (must force) - container cae6035fd1b2 is using its referenced image 9140108b62dc
且它告訴我們,如果想強行刪除鏡像,可以使用-f參數:
[envythink@localhost ~]$ docker image rm -f ubuntu:latest
Untagged: ubuntu:latest
Untagged: ubuntu@sha256:bc2f7250f69267c9c6b66d7b6a81a54d3878bb85f1ebb5f951c896d13e6ba537
但是筆者并不建議使用-f參數來強制刪除一個存在容器依賴的鏡像,正確的做法是先刪除依賴該鏡像的所有容器,再刪除鏡像。
第一步,查看本地已經存在的容器,如下所示:
[envythink@localhost ~]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd56a307081b ubuntu:latest "echo 'hello,I am en…" 13 minutes ago Exited (0) 13 minutes ago nostalgic_lewin
第二步,刪除容器ID為fd56a307081b的容器,注意刪除容器使用的命令是docker rm:
[envythink@localhost ~]$ docker rm fd56a307081b
fd56a307081b
第三步,查看當前本地主機上的所有鏡像信息:
[envythink@localhost ~]$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
envyubuntu latest 9140108b62dc 9 hours ago 72.9MB
ubuntu latest 9140108b62dc 9 hours ago 72.9MB
第四步,使用鏡像ID來刪除鏡像,此時會正常打印輸出各層的信息:
[envythink@localhost ~]$ docker image rm ubuntu:latest
Untagged: ubuntu:latest
Deleted: sha256:bc2f7250f69267c9c6b66d7b6a81a54d3878bb85f1ebb5f951c896d13e6ba567
Deleted: sha256:bc937250f69267c9c6b66d7b6a81a54d38726785f1ebb5f951c896d13e6b0978