天天看點

Docker之- 使用Docker 鏡像和倉庫

使用Docker 鏡像和倉庫

目錄

  • 使用Docker 鏡像和倉庫
    • 什麼是 Docker 鏡像
    • 列出 Docker 鏡像
      • tag 标簽
      • Docker Hub
    • 拉取鏡像
    • 查找鏡像
    • 建構鏡像
      • 建立Docker Hub 賬号
      • 使用 Docker 的commit 指令建立鏡像
      • 使用 Dockerfile 建構鏡像
        • 我們的第一個 Dockerfile
      • 基于 Dockerfile 建構新鏡像
      • 指令失敗時呢?
      • Dockerfile 和建構緩存
      • 基于建構緩存的 Dockerfile 模版
      • 檢視新鏡像
      • 從新鏡像啟動容器
      • Dockerfile 指令
    • 将鏡像推送至 Docker Hub
    • 删除鏡像
    • 總結

上一篇文章中,我們學習了包括 docker run 在内的許多對容器進行操作的基本指令,那麼在本節中,我們主要探讨 Docker 鏡像的一些概念,比如什麼是鏡像,如何對鏡像進行管理,如何修改鏡像,如何建立、存儲、共享自己建立的鏡像等,那麼就開始我們的學習

什麼是 Docker 鏡像

Docker 鏡像是由檔案系統疊加而成,最底端是一個引導檔案系統,也就是

bootfs

,這很像典型的 Linux/Unix 的引導檔案系統。Docker 使用者永遠不會和引導檔案系統有什麼互動。實際上,一個容器啟動後,它就會被移入内容,而引導檔案系統則會被解除安裝,進而留出更多的空間。(感覺有點像古代的餐館招待?負責引導顧客進入餐館,自己的工作就算是完成了)

傳統的Linux 引導過程中,root檔案系統最先以隻讀的方式加載,當引導結束後,會切換為讀寫模式。但是在Docker 中,root檔案系統永遠隻是隻讀狀态,并且使用聯合加載的技術一次同時加載多個檔案系統。聯合加載會将各層系統檔案疊加在一起,最終的檔案系統包含底層的檔案和目錄。

聯合加載:聯合加載指的是一次同時加載多個檔案系統,但是外面看起來隻有一個檔案系統。

Docker 将這樣的檔案系統成為鏡像。一個鏡像可以放到另一個鏡像的頂部。位于下面的鏡像稱為父鏡像,一次類推,知道鏡像棧的最底部,最底部的鏡像稱為基礎鏡像。最後,當一個鏡像啟動容器時,Docker會在鏡像的最頂層加載一個檔案系統。我們想在 Docker 中運作的程式就是在這個讀寫層中執行的。

用一幅圖來表示一下:

Docker之- 使用Docker 鏡像和倉庫

列出 Docker 鏡像

我們先從如何列出系統中的 Docker 鏡像來開始,可以使用 docker images 指令來實作,如下

Docker之- 使用Docker 鏡像和倉庫

可以看到,我們已經擷取了一個鏡像清單。那麼,這些鏡像是從哪來的呢?我們執行 docker run 指令時,同時進行了鏡像下載下傳

鏡像從倉庫下載下傳下來。鏡像儲存在倉庫中,而倉庫存在于 Registry 中。預設的 Registry 是由 Docker 公司運作的公共 Registry 服務,即 Docker Hub。需要進行ID的注冊

Docker之- 使用Docker 鏡像和倉庫
Docker Registry 的代碼是開源的,你也可以擁有自己的Registry。

在 Docker Hub (或者是你自己營運的 Docker Registry)中,鏡像是儲存在倉庫中的,可以将鏡像倉庫想象成類似于Git 倉庫的東西。它包括鏡像、層、以及包括鏡像的中繼資料。

倉庫可以包含很多鏡像,你可以使用

docker pull

來拉取倉庫中的鏡像,如下

Docker之- 使用Docker 鏡像和倉庫
Git 拉取代碼的指令是 git pull ,這樣就很相似了。

再來使用 docker images 看一下現在有哪些鏡像

Docker之- 使用Docker 鏡像和倉庫

因為我的 Docker Hub 倉庫中隻有一個 ubuntu 的鏡像,是以圖中标紅的這個鏡像是我們剛從 Docker Hub 上下載下傳下來的。

tag 标簽

為了區分同一個倉庫中的不同鏡像,Docker 為我們提供了 tag 這個标簽,每個鏡像在列出來的時候都帶有一個标簽,如12.10、 12.04等,這種标簽機制使得一個倉庫中允許存儲多個鏡像。

我們可以在倉庫後面加一個

冒号:标簽名

的方式來指定該倉庫中的某一個鏡像,例如 docker run -t -i --name new_container ubuntu:12.04 /bin/bash

Docker之- 使用Docker 鏡像和倉庫

Docker 會自動幫我們切換到 Ubuntu 的環境下,當然,這種方式建立了一個互動式任務。

在建構容器時指定倉庫的标簽也是一個好習慣,這樣便可以準确的指定容器來源于哪裡。

Docker Hub

Docker Hub 有兩種倉庫,一種是使用者倉庫,一種是頂層倉庫。使用者倉庫是由開發人員自己建立的,頂層倉庫是由Docker Hub 内部人員管理。

使用者倉庫的命名由兩部分構成,如 cxuan/ubuntu

  • 使用者名 例如 : cxuan
  • 倉庫名 例如 : ubuntu

相對的,頂層倉庫的命名就比較嚴謹,如 ubuntu 倉庫。頂層倉庫由 Docker 公司和標明的優質基礎鏡像廠商來管理,使用者可以基于這些鏡像建構自己的鏡像。

使用者鏡像都是由愛好者社群自己提供的,沒有經過 Docker 公司的認證,是以需要自己承擔相應的風險。

拉取鏡像

還記得docker run 的啟動過程嗎?再來一下這張圖回顧一下

Docker之- 使用Docker 鏡像和倉庫

其實也可以通過 docker pull 指令先預先拉取鏡像到本地,使用 docker pull 指令可以節省從一個新鏡像啟動一個容器所需要的時間。下面就來領取一下

fedora

基礎鏡像( fedora 是 Fedora 優質廠商提供的基礎鏡像 )

Docker之- 使用Docker 鏡像和倉庫

可以使用 docker images 檢視新鏡像是否拉取到本地,不過我們這次隻希望看到 fedora 的鏡像,那麼你可以使用這個指令:

docker images fedora

Docker之- 使用Docker 鏡像和倉庫

可以看到我們已經把 fedora 鏡像拉取到了本地

查找鏡像

我們可以通過 docker search 指令來查找所有 Docker Hub 上公共可用的鏡像,如下

Docker之- 使用Docker 鏡像和倉庫

下面還有很多鏡像,我們主要看一下每條鏡像都傳回了哪些内容

  • 倉庫名稱
  • 鏡像描述
  • 使用者評價 --- 反應一個鏡像的受歡迎程度
  • 是否官方 --- 是否由Docker 公司及其指定廠商開發的鏡像
  • 是否自動建構 --- 表示這個鏡像是由 Docker Hub 自動建構的

從上面查詢的結果中選擇一個鏡像進行拉取,

docker pull jamtur01/puppetmaster

這條指令将會下載下傳 jamtur01/puppetmaster鏡像到本地。

接下來就可以使用這個鏡像來建構一個容器,下面就用 docker run 指令建構一個容器。

Docker之- 使用Docker 鏡像和倉庫

...

檢視版本号

Docker之- 使用Docker 鏡像和倉庫

建構鏡像

上面我們看到如何拉取并且建構好帶有定制内容的 Docker 鏡像,那麼我們如何修改自己的鏡像,并且管理和更新這些鏡像呢?

  • 使用 docker commit 指令
  • 使用 docker build 指令和 Dockerfile 檔案

現在我們不推薦使用 docker commit 指令,相反應該使用更靈活更強大的 Dockerfile 來建構鏡像。不過,為了對 Docker 又一個更深的了解,我們還是會先介紹一下 docker commit 建構鏡像。之後,我們重點介紹Docker 所推薦的建構方法:編寫 Dockerfile 之後使用

docker build

指令。

建立Docker Hub 賬号

建構鏡像中很重要的一環就是如何共享和釋出鏡像。可以将鏡像推送到 Docker Hub中或者自己的私有 Registry 中。為了完成這項工作,需要在 Docker Hub上建立一個賬号

如果你還沒有Docker 通行證,在 hub.docker.com 注冊一個,記下你的使用者名,登入本地計算機上的Docker公共系統資料庫。使用

docker login

,輸入使用者名和密碼進行登入

Docker之- 使用Docker 鏡像和倉庫
你的個人資訊會儲存在 $HOME/.dockercfg 檔案中

使用 Docker 的commit 指令建立鏡像

建立 Docker鏡像的第一種方式是使用 docker commit 指令。可以将此想象為我們是在版本控制系統裡面送出變更,畢竟這和 git commit 指令真是太像了。

我們先從建立一個容器開始,這個容器基于我們前面見過的 ubuntu 鏡像。如下

Docker之- 使用Docker 鏡像和倉庫

接下來,我們在 ubuntu 中安裝 apache 伺服器,使用

apt-get -yqq update

apt-get -y install apache2

指令。

Docker之- 使用Docker 鏡像和倉庫

我們啟動了一個容器,并安裝了 Apache 伺服器,我們會将這個伺服器作為 Web 伺服器運作,是以我們想把它目前狀态儲存起來。這樣下次啟動就不用重新安裝了。為了完成這項工作,需要先使用 exit 從 ubuntu 中退出,之後再運作 docker commit 指令。如下

Docker之- 使用Docker 鏡像和倉庫

我們看到,在上圖所示的 docker commit 指令中,指定了要送出修改過的容器ID(可以通過 docker ps -l -q 指令得到剛剛建立的容器 ID),以及一個鏡像倉庫和鏡像名,這裡是 jamtur01/puppetmaster

可以使用

docker images jamtur01/puppetmaster

指令檢視剛剛建立的鏡像。

Docker之- 使用Docker 鏡像和倉庫

可以在送出時指定更多的資料,就和 git 的指令是一樣的,使用

docker commit -m

指令

Docker之- 使用Docker 鏡像和倉庫

這條指令中我們使用 -m(message) 指定送出資訊,同時指定了 --authro 選項,列出鏡像作者資訊。接着列出了想要送出的容器ID, 最後指定了 jamtur01/puppetmaster ,并為其打上了 webserver 的tag 标簽。

可以使用

docker inspect

指令來檢視新建立的鏡像的詳細資訊。

Docker之- 使用Docker 鏡像和倉庫

使用 Dockerfile 建構鏡像

我們并不推薦使用 docker commit 方法來建構鏡像。相反,我們推薦使用

Dockerfile

docker build

的指令來建構鏡像。Dockerfile 使用基于 DSL 文法的指令來建構一個 Docker 鏡像,之後使用 docker build 指令基于 Dockerfile 中的指令建構一個新的鏡像。

我們的第一個 Dockerfile

下面我們建立一個目錄并初始化 Dockerfile,我們建立一個包含簡單web伺服器的Docker鏡像

Docker之- 使用Docker 鏡像和倉庫

如上圖所示,我們在 /usr/local/docker 目錄下建立了一個

static_web

的目錄,再建立了一個 Dockerfile 檔案。那麼這個 static_web 目錄就是我們的建構環境。Docker 稱此環境為

上下文(context)

或者

建構上下文(build context)

,Docker 會在建構鏡像時将建構上下文和該上下文中的檔案和目錄上傳到 Docker 守護程序。這樣 Docker 守護程序就可以直接通路你鏡像中的 代碼、檔案和資料。

下面是一個通過 Dockerfile 來建構 web 伺服器的 Docker 鏡像

# Version: 0.0.1
FROM ubuntu:14.04
MAINTAINER James Turnbull "[email protected]"
RUN apt-get update
RUN apt-get install -y nginx
RUN echo \'Hi, I am in your container\' \
        >/usr/share/nginx/html/index.html
EXPOSE 80
           

該 Dockerfile 由一系列指令和參數組成。每條指令,如FROM,都必須為大寫字母,而且後面要跟随一個參數: FROM ubuntu:14.04。Dockerfile 中的指令會按照順序

由上向下

執行,是以編寫 Dockerfile 時,請注意它的順序。

如果不能使用

:wq

來進行儲存的話,請首先使用

sudo su

切換到管理者模式,然後就可以儲存啦。

每條指令都會建立一個新的鏡像層并對鏡像進行送出。Docker 大體按照如下流程執行 Dockerfile 指令

  • Docker 從基礎鏡像運作一個容器
  • 執行一條指令,對容器作出修改
  • 執行類似docker commit 操作,送出一個新的鏡像層
  • Docker 再基于剛送出的鏡像運作一個新容器
  • 執行 Dockerfile 中的下一條指令,直到所有指令都執行完畢

從上面可以看出,如果你的Dockerfile 由于某些原因(例如指令失敗了)沒有正常結束,那麼你将得到了一個可以使用的鏡像。這對調試很有幫助: 可以基于鏡像運作一個具備互動功能的容器,使用最後建立的鏡像對你最後失敗的指令做出調試

Dockerfile 也支援中文注釋,以 # 開頭的行都會被認為是注釋。Dockerfile 中的第一行就是注釋的例子

每個 Dockerfile 的第一行指令都應該是 FROM。FROM指令指定一個已經存在的鏡像,後續指令都将基于該鏡像進行,這個鏡像被稱為基礎鏡像(base image)。

  • 在上面的示例中,我們使用了

    ubuntu:14.04

    作為新鏡像基礎鏡像。基于這個 Dockerfile 建構的新鏡像以 Ubuntu 14.04 作業系統為基礎。再運作一個容器時,必須要指明基于哪個基礎鏡像進行建構。
  • 接着指定了

    MAINTAINER

    指令,這條指令會告訴 Docker 該鏡像的作者是誰,以及作者的電子郵件位址,這有助于标示鏡像的所有者以及聯系方式。
  • 在這些指令之後,我們指定了三條

    RUN

    指令。RUN指令會在目前的鏡像中運作指定的指令。在這個例子中,我們通過 RUN 指令更新了已經安裝的 APT 倉庫,安裝了 nginx 包,之後建立了 /usr/share/nginx/html/index.html 檔案,該檔案由一些簡單的示例文本。像前面說的那樣,每條RUN指令都會建立一個新的鏡像層,如果該指令執行成功,就會将此鏡像送出,繼續執行下一條指令。
    預設情況下,RUN指令會在shell裡使用指令包裝器 /bin/sh -c 來執行。如果是在一個不支援 shell 的平台上運作或者不希望在 shell 中運作,也可以使用 exec 格式的 RUN 指令

    如下 : RUN["apt-get", "install", "-y", "nginx"]

    在這種方式中,我們使用數組的方式來指定要運作的指令和要傳遞的參數。

  • 接着設定了

    EXPOSE

    指令,這條執行告訴 Docker 容器内的應用程式将會使用容器的指定接口。這并不意味着可以自動通路任意容器運作中的服務端口,可以指定多個 EXPOSE 指令向外公開多個端口。

基于 Dockerfile 建構新鏡像

執行

docker build

指令時,Dockerfile 中的所有指令都會被執行并且送出,并且在指令成功結束後傳回一個新鏡像,下面就來看看如何建構一個新鏡像。

Docker之- 使用Docker 鏡像和倉庫

一定不要忘記最後面有個空格 和

.

,也可以在建構鏡像的過程中為鏡像設定一個标簽: 使用方法為“鏡像名 : 标簽”,如下所示

指令失敗時呢?

之前大緻介紹了一下指令失敗時的執行過程,下面來看一個例子: 假設我們在上面的 Dockerfile 中把 nginx 拼成了 ngnx ,再來建構一遍

Docker之- 使用Docker 鏡像和倉庫

我們可以看到,程式出錯了,這個時候我們希望去調試一下這次失敗。我們使用 docker run 指令來基于這次建構到目前為止已經成功的最後一步建立一個容器,這裡它的ID 是

dee85a65a396

,我們可以使用如下指令

docker run -t -i dee85a65a396 /bin/bash

,來恢複到出錯之前的鏡像,然後重新運作出錯的指令

apt-get install -y ngnx

,可以看到哪裡出錯了

Docker之- 使用Docker 鏡像和倉庫

但是感覺這個步驟是多餘了一些,如果 Dockerfile 中出現了錯誤,那麼 Docker 就會給你提示,用不着重新運作指令來找出問題原因。

Dockerfile 和建構緩存

由于每一步的結果都會作為下一步的基礎鏡像,是以Docker 建構鏡像的過程非常聰明,它會将之前的鏡像層作為緩存。

正如上面 Dockerfile 來舉例,比如,在我們調試過程中,不需要在第一步和第三步之間做任何修改,是以 Docker 會将之前建構時建立的鏡像當作緩存并作為新的開始點。再次建構時,Docker 會直接從第四步開始。當之前的建構步驟沒有變化時,這會節省大量的時間。如果第一步到第三步之間有什麼變化,則回到第一步開始。

然而,有的時候不希望有緩存的功能,這個時候你需要使用

apt-get update

,那麼 Docker 将不會重新整理 APT 包的緩存,要想略過緩存,可以使用

docker build

的 --no-cache 标志。

基于建構緩存的 Dockerfile 模版

建構緩存的一個好處就是,我們可以實作簡單的 Dockerfile 模版,一般會在 Dockerfile 檔案頂部使用相同的指令集模版,比如對 ubuntu ,使用下面的模版

FROM ubuntu:14.04
MAINTAINER James Turnbull "[email protected]"
ENV REFRESHED_AT 2019-08-15
RUN apt-get -qq update
           

我們來分析一下這個新的 Dockerfile :

  • 首先,通過 FROM 指令為新鏡像設定了一個基礎鏡像 ubuntu:14.04。
  • 接着,使用 MAINTAINER 指令添加了自己的詳細資訊
  • 然後,通過 ENV 指令設定了一個名為 REFRESHED_AT 的環境變量,用來表示最後一次的更新時間
  • 最後,使用 RUN 指令運作 apt-get -qq update 指令,該指令會重新整理 APT 包的緩存,用來確定每個安裝的軟體包都在最新版本。

檢視新鏡像

現在來看一下新建構的鏡像,使用 docker images 指令來完成

Docker之- 使用Docker 鏡像和倉庫

如果想要了解鏡像是如何建構出來的,可以使用 docker history 指令,如下

Docker之- 使用Docker 鏡像和倉庫

從結果可以看出鏡像建構的每一層都是哪些指令構成的

從新鏡像啟動容器

我們可以基于新建構的鏡像啟動新容器,來檢查我們的建構工作是否正常

Docker之- 使用Docker 鏡像和倉庫

在這裡,我們使用 docker run 指令,啟動一個 static_web 的容器,

-d

表示的是以分離(detached) 的方式在背景運作。這種方式适合 nginx守護程序 這種需要長時間運作的程序。我們也指定了需要在 容器中運作的指令:

nginx -g "daemon off;"

,将以前台方式運作 nginx 作為我們的伺服器。

我們這裡也使用了一個新的 -p 标志,用來控制 Docker 再運作時應該給外部開放哪些端口

  • Docker 可以在主控端上随機選擇 49153 --- 65535 之間的一個比較大的端口映射到 80 端口上
  • 可以在 Docker 主控端指定一個具體的端口來映射到 80 端口上

使用

docker ps

檢視一下端口配置設定情況

Docker之- 使用Docker 鏡像和倉庫

Docker 把 32769 端口映射到了 80 端口上

也可以通過

docker port

檢視端口的映射情況

Docker之- 使用Docker 鏡像和倉庫

Dockerfile 指令

Dockerfile 指令比較多,這裡我們會對 Dockerfile 單獨列一個章節進行說明

将鏡像推送至 Docker Hub

鏡像建構完畢之後,我們也可以将它上傳到 Docker Hub 上面去,這樣其他人就能使用這個鏡像了。

Docker Hub 的私有倉庫是需要收費的

我們可以使用 docker push 指令将鏡像推送至 Docker Hub。指令如下

Docker之- 使用Docker 鏡像和倉庫

為什麼推送不上去?

網上搜尋了一下,大概是鏡像标簽的問題,重新為鏡像設定一個标簽

Docker之- 使用Docker 鏡像和倉庫

然後把這個标簽推送上去,相當于就是把鏡像推送上去

Docker之- 使用Docker 鏡像和倉庫

我們可以在 Docker Hub 上看到我們推送的鏡像了

Docker之- 使用Docker 鏡像和倉庫

删除鏡像

如果不再需要一個鏡像了,也可以将它删除,使用

docker rmi

指令來删除一個鏡像

Docker之- 使用Docker 鏡像和倉庫
該操作隻能删除本地鏡像,如果你已經推送至 Docker Hub 上,那麼你還需要在 Docker Hub 上将其删除

登入 Docker Hub ,直接點下面的連結删除

Docker之- 使用Docker 鏡像和倉庫

docker rmi 删除多個容器的方式直接在後面枚舉容器即可,中間用空格隔開

總結

本篇文章主要講述了 Docker 中的鏡像和倉庫的一些概念和基本用法,那麼你是否能回顧起來下面這些内容呢?

  • 什麼是鏡像
  • 如何列出Docker中的鏡像,tag标簽是幹什麼用的
  • 如何拉取遠端倉庫中的鏡像
  • 如何查找鏡像
  • 對于鏡像建構,你能想到哪些内容
  • 如何推送鏡像至 Docker Hub
  • 如何删除鏡像