在本專欄往期的 Flux7 系列教程 裏,咱們已經簡單地探討了 Docker 的基本操做。而在那篇教程中,咱們一直是簡單地將容器當成是「正在運行的鏡像」,並無深刻地區分鏡像和容器究竟是什麼、有什麼區別。所以本次翻譯 深刻 Docker:容器和鏡像 這篇文章,經過一些實例向你們介紹 Docker 容器和鏡像的具體區別。html
Docker 是一個很是有趣的項目。它本身宣稱能夠減輕部署服務器的難度,固然我相信裏面有炒做的成分。可是實際使用後,我以爲 Docker 的表現仍是可圈可點的。本篇文章將會從頭開始進行操做鏡像,同時試圖經過實例和文檔來解答實際操做 Docker 中遇到的問題。mysql
本文僅僅是試圖使用深刻講解 Docker 鏡像和容器的基礎知識,而不是像 瞭解 Docker:一種更好的虛擬化方式 同樣試圖總結出 Docker 的全部操做方法。git
若是你打算按照本文操做的話,那麼你首先有檯安裝好 Docker 的 Linux 主機。使用 Docker Machine 安裝 Docker 很簡單,可是同時我也推薦使用 Digital Ocean 中已經安裝好 Docker 的雲主機直接操做。本篇文章所使用的 Docker 版本爲 1.6.0,同時文中全部的命令都須要 root 權限。sql
一樣,譯者在正文開始提供一個操做 Docker 的小知識。下文中也會屢次使用到這一點。docker
Docker 會爲全部已經運行(包括已經中止)的容器隨機分配一個惟一的名字和一個惟一的 ID,docker
命令能夠識別 ID,也能夠識別這個名字。shell
如圖所示,第一行的容器的 ID 是 43de70a54ec1
,名字是 admiring_ardinghelli
想刪除第一行對應的容器,咱們只須要 docker rm 43de70a54ec1
,或者簡寫成 docker rm 43de
,或者 docker rm admiring_ardinghelli
。ubuntu
剛開始使用 Docker 的用戶會發現,運行完一個容器,再次運行這個容器,原來的容器內的內容已經消失了,例如:segmentfault
如今咱們使用 -i
(交互式)和 -t
(臨時終端)參數運行一個容器,而後輸入一些交互命令:安全
shell(HOST) # docker run -it ubuntu /bin/bash (CONTAINER) root@1f608dc4e5b4:/# echo hello docker > /message.txt (CONTAINER) root@1f608dc4e5b4:/# cat /message.txt hello docker (CONTAINER) root@1f608dc4e5b4:/# exit
在上面那個容器內,咱們建立了 /message.txt
文件,如今咱們嘗試從新讀取這個文件:bash
shell(HOST) # docker run -it ubuntu cat /message.txt cat: /message.txt: No such file or directory
剛剛咱們明明新建了這個文件,如今怎麼沒了?
同時,運行 docker ps
列出容器,剛剛那個 1f608dc4e5b4
容器到哪裏去了?
好吧,那麼咱們用 docker ps -a
命令列出全部容器,而後仔細觀察一下:
(HOST) # docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b3d8c3ef31a0 ubuntu:latest "cat /message.txt" 33 minutes ago Exited (1) 33 minutes ago admiring_lalande 1f608dc4e5b4 ubuntu:latest "/bin/bash" 34 minutes ago Exited (0) 15 minutes ago insane_wright
如今你們應該能夠發現問題了:竟然有兩個不一樣的容器,一個執行了 /bin/bash
,一個執行了 cat /message.txt
。
在本專欄以往的文章中,屢次提到:Docker 使用一個叫作 UnionFS 的層級文件系統進行鏡像操做。容器對鏡像文件的全部操做均是在虛擬出的「改動層」上進行的。固然對容器而言,UnionFS 和普通的文件系統並沒有差異,也沒法看到任何「改動層」。
每次運行 docker run
命令的時候,Docker 都會指定新建容器,而且爲容器本身的改動層。因此咱們兩次都運行了 ubuntu 鏡像,那麼咱們也將會有兩個新的、不一樣的容器,每一個容器也都會有本身獨立的的「改動層」。所以,在第一個容器內建立的 /message.txt
文件在第二個容器內是沒法訪問的。固然,要是能互相訪問,那咱們就得爲 Docker 的安全性擔憂了。
剛剛的問題是:「容器去哪兒了?」
如今的問題是:「容器到底去哪兒了?我須要裏面的 message.txt 文件,怎麼才能取出來?」
已經中止的容器中的數據並不會消失,而是被存儲在相應的「改動層」中。咱們能夠經過 docker ps -a
進行查找容器。在這個例子中,咱們要找的容器即是執行了 /bin/bash
命令的容器,ID是 1f608dc4e5b4
,名稱是 insane_wright
。
對比起 ID,容器的名稱更加易讀。所以在本篇文章後面的例子中,我都將用名稱來進行操做和識別容器。固然,你也能夠在 docker run
的時候用 --name
參數指定容器的名稱。
那麼如今咱們如今運行上面例子中已經中止的 insane_wright
容器:
(HOST) # docker start -ai insane_wright (CONTAINER) root@1f608dc4e5b4:/# cat /message.txt hello docker (CONTAINER) root@1f608dc4e5b4:/# exit
使用 -a
參數將容器的輸出導出到終端,同時使用 -i
參數進行交互式的操做。這條命令可讓咱們繼續運行容器 insane_wright
,如今你應該能看獲得咱們剛剛建立的 /message.txt
文件了。
如今咱們已經運行了本身的容器,容器內也有獨立的數據,咱們也知道如何獲取容器內的數據,那麼問題來了:咱們能不能把這個狀態下的容器給保存起來?
本專欄前面的文章提到過得益於 UnionFS,對於 Docker 來講,其實容器和鏡像的差異並不大。容器能夠認爲是已經運行過的或正在運行的鏡像,只不過是鏡像上面添加了幾個改動層。固然,大部分鏡像也是如此。例如某些 mysql 鏡像,便僅僅是在官方 ubuntu 鏡像的基礎上增長了一個 mysql 改動層。
對於上面例子中的容器,咱們能夠用下面這條命令將其打包成鏡像:
(HOST) $ docker commit -a "Jordan Bach" -m "saved my message" insane_wright jbgo/message:v0.0.1 1c9195e4c24c894a274f857a60b46fb828ee70ff0e78d18017dfb79c5bf68409
本條命令將容器 insange_wright
的改動層的屬性變爲「只讀」,而且指定存儲爲 jbgo/message
鏡像。鏡像基於原 ubuntu 鏡像,增長了一個改動層,標籤爲 v0.0.1
。標籤能夠爲任意,固然通常用來記錄版本信息。
固然,若是你打算本身嘗試的話,記得將個人名字 jbgo
替換爲你本身在 Docker Hub 上的用戶名。即使你沒有 Docker Hub 帳戶,也要養成給鏡像作簽名的習慣。
能夠用以下命令檢查當前的鏡像:
(HOST) # docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE jbgo/message v0.0.1 1c9195e4c24c 18 seconds ago 188.3 MB ubuntu latest b7cf8f0d9e82 4 days ago 188.3 MB
如今咱們有兩個鏡像了:一個是原有的 ubuntu 鏡像,鏡像是咱們在剛開始進行 docker run
的時候自動從 Docker Hub
上面下載下來的;另外一個即是咱們剛剛保存的,有一個 /message.txt
文件的鏡像。
不信?
那我運行給你看。
(HOST) # docker run -it jbgo/message:v0.0.1 cat /message.txt hello docker
這裏咱們並無運行剛剛的 insane_wright
容器,而是運行了咱們剛剛保存的鏡像。
咱們確實把剛剛的容器保存爲一個新的鏡像了。
還不信?
那我檢查給你看。
(HOST) # docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 01bd4c098203 jbgo/message:v0.0.1 "cat /message.txt" 10 seconds ago Exited (0) 9 seconds ago hopeful_lovelace b3d8c3ef31a0 ubuntu:latest "cat /message.txt" 33 minutes ago Exited (1) 33 minutes ago admiring_lalande 1f608dc4e5b4 ubuntu:latest "/bin/bash" 34 minutes ago Exited (0) 15 minutes ago insane_wright
容器運行記錄裏面多了一個叫作 hopeful_lovelace
的容器,是運行 jdgo/message:v0.0.1
鏡像而建立的這個容器。
上面咱們建立了帶有一個
/message.txt
文件的鏡像,也用它運行了容器,那麼若是 Docker 宿主機崩潰了或者文件消失了怎麼辦?如何保證咱們建立的鏡像在系統崩潰後仍然不丟失?
咱們須要 Docker Registry,能夠存儲和下載鏡像的地方。Docker Hub 官方提供了 Docker Hub Registry 讓咱們來存儲鏡像,固然咱們也能夠本身搭建 Docker Registry。本篇文章將會使用 Docker Hub Registry,若是你想按照本文中繼續操做的話,記得將個人名字 jbgo
換成你本身在 Docker Hub 上的用戶名。
使用以下操做將鏡像上傳到 Docker Registry 上:
(HOST) # docker push jbgo/message The push refers to a repository [jbgo/message] (len: 1) 1c9195e4c24c: Image push failed Please login prior to push: Username: jbgo Password: Email: jordanbach@gmail.com WARNING: login credentials saved in /root/.dockercfg. Login Succeeded The push refers to a repository [jbgo/message] (len: 1) 1c9195e4c24c: Image already exists b7cf8f0d9e82: Image successfully pushed 2c014f14d3d9: Image successfully pushed a62a42e77c9c: Image successfully pushed 706766fe1019: Image successfully pushed Digest: sha256:b2a98b19e06a4df91150f8a2cd47a6e440cbc13b39f1fc235d46f97f631ad117
由於我是第一次在本機執行 docker push
操做,因此 Docker Registry 須要驗證個人身份。
本次上傳的鏡像被保存在這裏。
你可使用 docker pull jbgo/message
來下載個人鏡像。
固然,若是你按照上面的來操做的話,可能會出現一點小問題:
(HOST) # docker pull jbgo/message Pulling repository jbgo/message FATA[0007] Tag latest not found in repository jbgo/message
下面我來解釋一下到底出了什麼問題:
當你使用 docker push jbgo/message
命令的時候,默認上傳標籤爲 latest 的鏡像。若是沒有便會報錯。你能夠採用以下命令解決: docker tag jbgo/message:v0.0.1 jbgo/message:latest
。
此次再嘗試推送:
(HOST) # docker push jbgo/message:latest The push refers to a repository [jbgo/message] (len: 1) 1c9195e4c24c: Image already exists b7cf8f0d9e82: Image already exists 2c014f14d3d9: Image already exists a62a42e77c9c: Image already exists 706766fe1019: Image already exists Digest: sha256:cc2fbbb2029c6402cea639b2454da08ef05672da81176ae97f57d4f51be19fc3
此次上傳會快得多,由於服務器上已經有了這個鏡像,咱們上傳的僅僅是相同鏡像的不一樣標籤而已。
你不信?
那我驗證給你看:
(HOST) # docker pull jbgo/message latest: Pulling from jbgo/message 1c9195e4c24c: Already exists 706766fe1019: Already exists a62a42e77c9c: Already exists 2c014f14d3d9: Already exists b7cf8f0d9e82: Already exists Digest: sha256:cc2fbbb2029c6402cea639b2454da08ef05672da81176ae97f57d4f51be19fc3 Status: Downloaded newer image for jbgo/message:latest
固然,別臺服務器同樣可使用這個命令下載個人鏡像。
固然,也可使用以下命令上傳特定標籤的鏡像:docker push jbgo/message:v0.0.1
。
建議使用版本標籤標記鏡像,並推送特定版本標籤的鏡像和 latest 標籤的鏡像。
上面只是介紹了容器如何理解容器和鏡像,若是有不少歷史容器被保存在硬盤上,想要釋放掉這些空間,咱們應該怎麼作?
使用以下命令刪除多個容器:
(HOST) # docker rm hopeful_lovelace insane_wright admiring_lalande hopeful_lovelace insane_wright admiring_lalande
如今再檢查一下還有沒有這些容器:
(HOST) # docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
咱們運行的全部容器和全部的歷史容器都會被保存在硬盤上,除非你手動 docker rm
它們。手動刪除的容器不能恢復。
有心人注意到,剛剛咱們第一次上傳 jbgo/message
鏡像的時候,Docker 上傳了不止一個鏡像。
(HOST) # docker push jbgo/message:v0.0.1 ... 1c9195e4c24c: Image already exists b7cf8f0d9e82: Image successfully pushed 2c014f14d3d9: Image successfully pushed a62a42e77c9c: Image successfully pushed 706766fe1019: Image successfully pushed
到底發生了什麼?
首先使用 docker history
檢查一下鏡像改動歷史:
(HOST) # docker history jbgo/message:v0.0.1 IMAGE CREATED CREATED BY SIZE 1c9195e4c24c 33 minutes ago /bin/bash 108 B b7cf8f0d9e82 4 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B 2c014f14d3d9 4 days ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.895 kB a62a42e77c9c 4 days ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB 706766fe1019 4 days ago /bin/sh -c #(nop) ADD file:777fad733fc954c0c1 188.1 MB
會發現,其實咱們建立了不止一個鏡像,也就是有不止一個改動層。
使用 docker inspect
命令仔細觀察第一個鏡像(第一層)進行過的改動:
(HOST) # docker inspect 1c9195e4c24c [{ "Architecture": "amd64", "Author": "Jordan Bach", "Comment": "saved my message", "Config": {
這個確定就是咱們的 /message.txt
改動層了,那麼其餘的那麼可能是什麼?
jbgo/message
是基於 ubuntu 鏡像的,咱們來檢查一下 ubuntu 鏡像的改動歷史:
(HOST) # docker history ubuntu IMAGE CREATED CREATED BY SIZE b7cf8f0d9e82 4 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B 2c014f14d3d9 4 days ago /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/ 1.895 kB a62a42e77c9c 4 days ago /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic 194.5 kB 706766fe1019 4 days ago /bin/sh -c #(nop) ADD file:777fad733fc954c0c1 188.1 MB
那麼這樣一切就很明確了:那些是 ubuntu 鏡像的改動歷史。爲了確保本地和上傳到 Docker Hub 的鏡像一致,咱們上傳到 Docker Hub 中的鏡像即是包含着五層改動層的新鏡像。
前面提到過,咱們刪除容器,那麼咱們本機上仍然保存着鏡像,如何刪掉它們?
(HOST) # docker rmi jbgo/message Error response from daemon: Conflict, cannot delete 1c9195e4c24c because the container 2ea39e64a130 is using it, use -f to force FATA[0000] Error: failed to remove one or more images
提示出錯了,根據錯誤信息能夠看出,還有容器在用這一個鏡像。
可是咱們沒有正在運行着的容器啊,到底哪兒出錯了?
檢查全部容器,發現容器 2ea39e64a130
。
(HOST) # docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2ea39e64a130 jbgo/message:latest "cat /message.txt" 2 minutes ago Exited (0) 2 minutes ago lonely_jones
固然,這是 Docker 的機制:若是你仍然有容器的歷史記錄,那麼爲了確保你能再次啓動這個容器,這些鏡像是不能刪除的。
首先刪除掉這個容器:docker rm lonely_jones
。
而後刪除鏡像:
(HOST) # docker rmi jbgo/message Untagged: jbgo/message:latest Deleted: 1c9195e4c24c894a274f857a60b46fb828ee70ff0e78d18017dfb79c5bf68409 Deleted: b7cf8f0d9e82c9d96bd7afd22c600bfdb86b8d66c50d29164e5ad2fb02f7187b Deleted: 2c014f14d3d95811df672ddae2af376f9557f6b8f5623e3e3f8c5ca3f6ff42e6 Deleted: a62a42e77c9c3626118dc411092d23cf658220261acdafe21a7589a8eeba627e Deleted: 706766fe101906a1a6628173c2677173a5f8c6c469075083f3cf3a8f5e5eb367
此次咱們能夠很輕鬆地刪除掉 jbgo/message
鏡像了。
確認一下:
(HOST) # docker images REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE 確實沒了。
本篇文章介紹了容器和鏡像的操做實例,固然 Docker 還有更多新奇的功能和只是等待你們去發現。 本專欄仍在繼續組織翻譯更多的系列文章,但願你們多多關注。