Docker筆記(一)- 鏡像與容器,Overlay2

Docker筆記(一)- 鏡像與容器,Overlay2

來自專欄 Cerulean的專欄7 人贊了文章

背景

本系列Docker筆記將以基於TensorFlow的模型的訓練與部署的具體場景為例,總結Docker的基本原理、安裝、鏡像製作、容器部署等。事實上,在學校實驗室和自己的開源項目推進Docker使用已經快1年了,這篇文章之所以現在才開始寫,純粹是拖太久。

安裝docker與nvidia-docker

首先,請以官網教程安裝docker:

How to install docker on Ubuntu 16.04

如果你打算製作或者使用涉及到cuda或者cuddn等與GPU相關的鏡像或容器,你需要安裝nvidia-docker,請按照repo里的步驟安裝nvidia-docker:

How to install nvidia-docker on Ubuntu 16.04

在完成兩步安裝後,需要更改docker守護進程默認的runtime參數,請將/etc/docker/daemon.json文件中鍵default-runtime對應的值修改為nvidia,然後通過sudo service docker restart重啟docker服務,這一步操作是為了避免之後運行與cuda或者cudnn庫相關的容器時每次都要指定runtime參數的情況。

請務必確認docker被正確安裝,接下來我們將從基本概念開始介紹docker,一直到模型部署。

基本概念

首先引用百度百科的定義:

Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後發布到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何介面。

其次是官網的定義:

Docker is a platform for developers and sysadmins to develop, deploy, and run applications with containers.

其實百度百科的總結已經足夠好了,更概括地,Docker是一個輕量級虛擬機製作、分發、部署工具。

Docker有兩個非常重要的概念,他們分別是鏡像(image)與容器(container),直觀地,我們可以將容器類比為虛擬機,這個虛擬機可能是正在運行的,也可能是已經停止的,而鏡像則是像配置文件一樣定義了這些虛擬機如何運行。

而事實上事情要比上面的例子複雜很多,鏡像和容器的本質一個文件系統:

在計算機中,文件系統(File System)是命名文件及放置文件的邏輯存儲和恢復的系統。

我們首先將以Overlay2為例,詳細介紹鏡像與容器的區別和聯繫。

Overlay2

接下來將簡要的介紹文件存儲驅動overlay2,以便於更好的理解容器與鏡像的關係。overlay2是Ubuntu上最新的Docker CE版本18.06.0上的默認存儲驅動。上段提到,本質上鏡像與容器都是文件系統,它們唯一的不同,就是鏡像是只讀的,而容器是可讀可寫的。

舉個例子,我們通過以下命令獲取Ubuntu的鏡像:

? overlay2 docker pull ubuntuUsing default tag: latestlatest: Pulling from library/ubuntuc64513b74145: Pull complete01b8b12bad90: Pull completec5d85cf7a05f: Pull completeb6b268720157: Pull completee12192999ff1: Pull completeDigest: sha256:3f119dc0737f57f704ebecac8a6d8477b0f6ca1ca0332c7ee1395ed2c6a82be7Status: Downloaded newer image for ubuntu:latest? overlay2 docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEubuntu latest 735f80812f90 2 weeks ago 83.5MB

可以看出,Ubuntu鏡像具有5層,這5層都是只讀的,我們可以在這個目錄看到他們:

? overlay2 pwd/var/lib/docker/overlay2? overlay2 lltotal 24Kdrwx------ 4 root root 4.0K 8月 13 19:52 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd148 5drwx------ 3 root root 4.0K 8月 13 19:52 225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7 6drwx------ 4 root root 4.0K 8月 13 19:53 40b4fd1a0dea16978cffe5f26deee9a5834c76752db8c3b2a86057037a12b5f 5drwx------ 4 root root 4.0K 8月 13 19:52 7bb93daf624b3fc798554d36b75940fced7713f0d165631131432e230718555 9drwx------ 4 root root 4.0K 8月 13 19:52 ea274fcefed09dd48b0c6baa45e66bcd00887d4abbddbee1ef804c9dc7cfba4 edrwxr-xr-x 2 root root 4.0K 8月 13 19:53 l

以及這個文件夾:

? l pwd/var/lib/docker/overlay2/l? l lltotal 20Klrwxrwxrwx 1 root root 72 8月 13 19:52 IBZJKDU6Z76YR2ZDYWHDVUVHEZ -> ../0ba50fa3b79a5dc66ebb8f2939e77128 b0ab7c3989fc776bd4268af366bd1485/difflrwxrwxrwx 1 root root 72 8月 13 19:52 NRJEUKQPNXJQSQBLGCULIHRT77 -> ../7bb93daf624b3fc798554d36b75940fc ed7713f0d165631131432e2307185559/difflrwxrwxrwx 1 root root 72 8月 13 19:52 UXY6233J7FGSMGWJ2KJKU4Z6U3 -> ../225c757add2a395c0cfc47e1bc4472bf 8fccf9dedd42f76f99b21c7637cb2a76/difflrwxrwxrwx 1 root root 72 8月 13 19:52 VYQ3FKAYCIQNRXABOBPQ3ACEEH -> ../ea274fcefed09dd48b0c6baa45e66bcd 00887d4abbddbee1ef804c9dc7cfba4e/difflrwxrwxrwx 1 root root 72 8月 13 19:53 ZFT2GFUH6ZW3BMC3A4VY7S6HZV -> ../40b4fd1a0dea16978cffe5f26deee9a5 834c76752db8c3b2a86057037a12b5f5/diff

可以發現,全部都是到各層diff之間的軟鏈接,以IBZJKDU6Z76YR2ZDYWHDVUVHEZ為例,我們觀察一下這個鏈接目錄:

? l cd IBZJKDU6Z76YR2ZDYWHDVUVHEZ? IBZJKDU6Z76YR2ZDYWHDVUVHEZ lltotal 4.0Kdrwxr-xr-x 3 root root 4.0K 7月 27 06:20 etc

發現除了etc文件夾之外空空如也,再以NRJEUKQPNXJQSQBLGCULIHRT77為例,觀察一下這個符號鏈接目錄的內容:

? NRJEUKQPNXJQSQBLGCULIHRT77 lltotal 16Kdrwxr-xr-x 4 root root 4.0K 7月 27 06:20 etcdrwxr-xr-x 2 root root 4.0K 7月 27 06:20 sbindrwxr-xr-x 3 root root 4.0K 7月 25 04:51 usrdrwxr-xr-x 3 root root 4.0K 7月 25 04:53 var

可以發現以上內容。事實上,每層的diff即是文件系統在統一掛載時的掛載點,我們可以再進一步地觀察下一層,UXY6233J7FGSMGWJ2KJKU4Z6U3的內容:

? UXY6233J7FGSMGWJ2KJKU4Z6U3 lltotal 76Kdrwxr-xr-x 2 root root 4.0K 7月 25 04:53 bindrwxr-xr-x 2 root root 4.0K 4月 24 16:34 bootdrwxr-xr-x 4 root root 4.0K 7月 25 04:51 devdrwxr-xr-x 29 root root 4.0K 7月 25 04:53 etcdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 homedrwxr-xr-x 8 root root 4.0K 7月 25 04:51 libdrwxr-xr-x 2 root root 4.0K 7月 25 04:52 lib64drwxr-xr-x 2 root root 4.0K 7月 25 04:51 mediadrwxr-xr-x 2 root root 4.0K 7月 25 04:51 mntdrwxr-xr-x 2 root root 4.0K 7月 25 04:51 optdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 procdrwx------ 2 root root 4.0K 7月 25 04:53 rootdrwxr-xr-x 4 root root 4.0K 7月 25 04:51 rundrwxr-xr-x 2 root root 4.0K 7月 25 04:53 sbindrwxr-xr-x 2 root root 4.0K 7月 25 04:51 srvdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 sysdrwxrwxrwt 2 root root 4.0K 7月 25 04:53 tmpdrwxr-xr-x 10 root root 4.0K 7月 25 04:51 usrdrwxr-xr-x 11 root root 4.0K 7月 25 04:53 var

可以發現這一層彷彿就是一個Ubuntu了。到這裡我們可以知道,鏡像是由多個層組織並定義的,這些層本質上是文件,這些文件是只讀的,每層具體的文件存放在層標識符下的diff目錄下。接下來我們將介紹他們是如何被組織起來的。

回過頭來,我們繼續觀察層標識符目錄:

? overlay2 pwd/var/lib/docker/overlay2? overlay2 lltotal 24Kdrwx------ 4 root root 4.0K 8月 13 19:52 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd148 5drwx------ 3 root root 4.0K 8月 13 19:52 225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7 6drwx------ 4 root root 4.0K 8月 13 19:53 40b4fd1a0dea16978cffe5f26deee9a5834c76752db8c3b2a86057037a12b5f 5drwx------ 4 root root 4.0K 8月 13 19:52 7bb93daf624b3fc798554d36b75940fced7713f0d165631131432e230718555 9drwx------ 4 root root 4.0K 8月 13 19:52 ea274fcefed09dd48b0c6baa45e66bcd00887d4abbddbee1ef804c9dc7cfba4 edrwxr-xr-x 2 root root 4.0K 8月 13 19:53 l

接著我們進入225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7這個目錄,觀察一下目錄結構:

? 225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76 tree . -L 2.├── diff│ ├── bin│ ├── boot│ ├── dev│ ├── etc│ ├── home│ ├── lib│ ├── lib64│ ├── media│ ├── mnt│ ├── opt│ ├── proc│ ├── root│ ├── run│ ├── sbin│ ├── srv│ ├── sys│ ├── tmp│ ├── usr│ └── var└── link20 directories, 1 file

好像沒什麼特別的,接著我們進入0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd148這個目錄,觀察一下目錄結構:

? overlay2 cd 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485? 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 tree ..├── diff│ └── etc│ └── apt│ └── sources.list├── link├── lower└── work4 directories, 3 files? 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 cat lowerl/VYQ3FKAYCIQNRXABOBPQ3ACEEH:l/NRJEUKQPNXJQSQBLGCULIHRT77:l/UXY6233J7FGSMGWJ2KJKU4Z6U3#? 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 cat linkIBZJKDU6Z76YR2ZDYWHDVUVHEZ#

可以看出,link文件描述了該層標識符的精簡版,而lower文件描述了層序的組織關係。接著我們通過以下命令啟動一個容器:

? 0ba50fa3b79a5dc66ebb8f2939e77128b0ab7c3989fc776bd4268af366bd1485 docker run -it ubuntu

然後通過以下命令觀察overlay2聯合掛載情況:

root@7d01751deb92:/# mount | grep overlayoverlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/FSK5KQSBSQH67GQ5IEWQKL4YPF:/var/lib/docker/overlay2/l/ZFT2GFUH6ZW3BMC3A4VY7S6HZV:/var/lib/docker/overlay2/l/IBZJKDU6Z76YR2ZDYWHDVUVHEZ:/var/lib/docker/overlay2/l/VYQ3FKAYCIQNRXABOBPQ3ACEEH:/var/lib/docker/overlay2/l/NRJEUKQPNXJQSQBLGCULIHRT77:/var/lib/docker/overlay2/l/UXY6233J7FGSMGWJ2KJKU4Z6U3,upperdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/diff,workdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/work)root@7d01751deb92:/#

我們可以觀察到一些關鍵信息,例如lowerdir,可以看到是這些層標識符:

FSK5KQSBSQH67GQ5IEWQKL4YPFZFT2GFUH6ZW3BMC3A4VY7S6HZVIBZJKDU6Z76YR2ZDYWHDVUVHEZVYQ3FKAYCIQNRXABOBPQ3ACEEHNRJEUKQPNXJQSQBLGCULIHRT77UXY6233J7FGSMGWJ2KJKU4Z6U3

這時我們再觀察overlay2文件夾,發現在該文件夾和l文件夾都多出了2個標識符:

? overlay2 ls | grep 3893895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da-init? l ll | grep 389lrwxrwxrwx 1 root root 77 8月 13 20:26 FSK5KQSBSQH67GQ5IEWQKL4YPF -> ../3895f4ddbd45f65e509ed996d39536d1 737647bf1b70c2b9c82b6765b2e376da-init/difflrwxrwxrwx 1 root root 72 8月 13 20:26 O5NQ7PKEES3VHMHNZAZHE54M2C -> ../3895f4ddbd45f65e509ed996d39536d1 737647bf1b70c2b9c82b6765b2e376da/diff

這一層是動態生成的,觀察其目錄結構:

? 3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da-init tree . -L 4.├── diff│ ├── dev│ │ └── console│ └── etc│ ├── hostname│ ├── hosts│ ├── mtab -> /proc/mounts│ └── resolv.conf├── link├── lower└── work └── work5 directories, 7 files

可以看出,它主要是一些配置文件構成的層。而不帶init後綴的3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da的情況就比較特殊了:

? 3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da tree . -L 2.├── diff├── link├── lower├── merged│ ├── bin│ ├── boot│ ├── dev│ ├── etc│ ├── home│ ├── lib│ ├── lib64│ ├── media│ ├── mnt│ ├── opt│ ├── proc│ ├── root│ ├── run│ ├── sbin│ ├── srv│ ├── sys│ ├── tmp│ ├── usr│ └── var└── work └── work23 directories, 2 files? 3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da cat lowerl/FSK5KQSBSQH67GQ5IEWQKL4YPF:l/ZFT2GFUH6ZW3BMC3A4VY7S6HZV:l/IBZJKDU6Z76

回憶一下225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a7這個標識符:

? 225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76 tree . -L 2.├── diff│ ├── bin│ ├── boot│ ├── dev│ ├── etc│ ├── home│ ├── lib│ ├── lib64│ ├── media│ ├── mnt│ ├── opt│ ├── proc│ ├── root│ ├── run│ ├── sbin│ ├── srv│ ├── sys│ ├── tmp│ ├── usr│ └── var└── link20 directories, 1 file

可以看出,3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da這標識符多了一個文件夾merged,與225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76diff文件夾相似,它正是容器的可讀可寫層。講到這裡,我們在回頭來觀察overlay2聯合掛載情況:

root@7d01751deb92:/# mount | grep overlayoverlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/FSK5KQSBSQH67GQ5IEWQKL4YPF:/var/lib/docker/overlay2/l/ZFT2GFUH6ZW3BMC3A4VY7S6HZV:/var/lib/docker/overlay2/l/IBZJKDU6Z76YR2ZDYWHDVUVHEZ:/var/lib/docker/overlay2/l/VYQ3FKAYCIQNRXABOBPQ3ACEEH:/var/lib/docker/overlay2/l/NRJEUKQPNXJQSQBLGCULIHRT77:/var/lib/docker/overlay2/l/UXY6233J7FGSMGWJ2KJKU4Z6U3,upperdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/diff,workdir=/var/lib/docker/overlay2/3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da/work)root@7d01751deb92:/#

我們可以看出,overlay2將lowerdirupperdirworkdir聯合掛載,形成最終的merged掛載點,其中lowerdir是鏡像只讀層,upperdir是容器可讀可寫層,workdir是執行涉及修改lowerdir執行copy_up操作的中轉層(例如,upperdir中不存在,需要從lowerdir中進行複製,該過程暫未詳細了解,遇到了再分析),接著我們可以做一個實驗,我們在容器中通過以下命令創建一個文件:

root@7d01751deb92:/# touch test.txt

接下來我們觀察容器的可讀寫層,與鏡像的只讀層:

? 3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da ll difftotal 0-rw-r--r-- 1 root root 0 8月 13 20:54 test.txt? 3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da ll mergedtotal 76Kdrwxr-xr-x 2 root root 4.0K 7月 25 04:53 bindrwxr-xr-x 2 root root 4.0K 4月 24 16:34 bootdrwxr-xr-x 1 root root 4.0K 8月 13 20:26 devdrwxr-xr-x 1 root root 4.0K 8月 13 20:26 etcdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 homedrwxr-xr-x 8 root root 4.0K 7月 25 04:51 libdrwxr-xr-x 2 root root 4.0K 7月 25 04:52 lib64drwxr-xr-x 2 root root 4.0K 7月 25 04:51 mediadrwxr-xr-x 2 root root 4.0K 7月 25 04:51 mntdrwxr-xr-x 2 root root 4.0K 7月 25 04:51 optdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 procdrwx------ 2 root root 4.0K 7月 25 04:53 rootdrwxr-xr-x 1 root root 4.0K 7月 27 06:20 rundrwxr-xr-x 1 root root 4.0K 7月 27 06:20 sbindrwxr-xr-x 2 root root 4.0K 7月 25 04:51 srvdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 sys-rw-r--r-- 1 root root 0 8月 13 20:54 test.txtdrwxrwxrwt 2 root root 4.0K 7月 25 04:53 tmpdrwxr-xr-x 1 root root 4.0K 7月 25 04:51 usrdrwxr-xr-x 1 root root 4.0K 7月 25 04:53 var? 3895f4ddbd45f65e509ed996d39536d1737647bf1b70c2b9c82b6765b2e376da ll ../225c757add2a395c0cfc47e1bc4472bf8fccf9dedd42f76f99b21c7637cb2a76/difftotal 76Kdrwxr-xr-x 2 root root 4.0K 7月 25 04:53 bindrwxr-xr-x 2 root root 4.0K 4月 24 16:34 bootdrwxr-xr-x 4 root root 4.0K 7月 25 04:51 devdrwxr-xr-x 29 root root 4.0K 7月 25 04:53 etcdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 homedrwxr-xr-x 8 root root 4.0K 7月 25 04:51 libdrwxr-xr-x 2 root root 4.0K 7月 25 04:52 lib64drwxr-xr-x 2 root root 4.0K 7月 25 04:51 mediadrwxr-xr-x 2 root root 4.0K 7月 25 04:51 mntdrwxr-xr-x 2 root root 4.0K 7月 25 04:51 optdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 procdrwx------ 2 root root 4.0K 7月 25 04:53 rootdrwxr-xr-x 4 root root 4.0K 7月 25 04:51 rundrwxr-xr-x 2 root root 4.0K 7月 25 04:53 sbindrwxr-xr-x 2 root root 4.0K 7月 25 04:51 srvdrwxr-xr-x 2 root root 4.0K 4月 24 16:34 sysdrwxrwxrwt 2 root root 4.0K 7月 25 04:53 tmpdrwxr-xr-x 10 root root 4.0K 7月 25 04:51 usrdrwxr-xr-x 11 root root 4.0K 7月 25 04:53 var

可以發現,新創建的文件被存在了上述位置,而此時如果我們通過以下命令:

docker commit CONTAINER_ID

提交容器更改,則會將該容器的當前可讀可寫層轉化為只讀層,更新鏡像。總結一下,鏡像大體上,可以認為是多個只讀層通過某些特定的方式組織起來,而容器則是在其之上的一個可讀寫層,我們可以保存一個可讀寫層的更改,將它轉化為一個只讀層。

以上是對鏡像與容器,及其存儲驅動overlay2的一個簡要概述。

下一篇還沒想好是什麼。


推薦閱讀:

從無到有:利用Docker部署項目
記錄一次Linux Docker的部署過程
微服務那麼火,我也該用微服務嗎?
Docker 學習新手筆記:從入門到放棄
跟我一起學docker(五)--倉庫

TAG:Docker | 容器 | TensorFlow |