理解鏡像,容器,存儲引擎

爲了很好的使用存儲引擎,必須明白Dcoker是如何建立和存儲images的。
而後要明白這些images是如何讓containers使用的。最後簡短介紹一些images和containers操做。
 
Images and layers

 
每一個Docker image都會參照只讀列表裏的layers,這些layers表明不一樣的文件系統。
layers被堆放在頂端,以造成容器根文件系統的基礎。
下圖,Ubuntu15.04 image包含了4 個堆image layers.

 

Docker 存儲引擎負責堆放這些layers,而後提供一個統一的展現。
 
當建立一個新的容器,將底層堆的最上層設置成可寫,一般這層叫作「containers layer」
對正在運行的容器作全部的修改,好比建立新文件,修改已存在的文件,刪除文件。這些操做都是在可寫的容器層。
下圖,展現了以Ubuntu15.04爲基礎的容器。
 

 

Containers and layers

 
容器和鏡像主要的不一樣就是,頂層可寫層。全部的修改操做都是存儲在這個可寫層的。
當容器被刪除的時候這個可寫層也就被刪除了。而底層的image沒有改變。
 
由於每一個容器有本身的可寫容器層,全部的更改都存儲在這容器層中,這意味着多個容器能夠同時使用同一個底層image。
下圖顯示多個容器共用Ubuntu15.04image:

 

docker存儲引擎負責開啓和管理 image 層 和可寫的container 層。
存儲引擎的兩個關鍵技術:堆棧image層和copy-on-write.
 
Copy-on-write 策略

 
共享是提高資源利用率的很好途徑。
copy-on-write是個很想共享和複製的策略。
  • 須要相同數據的系統進程共享該數據的同一實例,而不是擁有本身的副本。
  • 若是一個進程須要修改和寫數據,操做系統會給這個進程複製須要的數據給它使用。
    • 只有須要寫數據的進程纔有權訪問複製的數據。
  • 其餘全部的進程繼續使用原共享數據
 
docker在image和containers中都是用了copy-on-write技術。CoW策略優化了image 磁盤空間使用以及 容器的啓動時間性能。
 
共享減小images空間

 
全部image和containers 層存在與Dcoker本地的存儲空間中,都被存儲引擎管理。
通常在/var/lib/docker/. 目錄中。
zane@zane-V:~$ docker pull ubuntu:15.04
15.04: Pulling from library/ubuntu
 
9502adfba7f1: Pull complete
4332ffb06e4b: Pull complete
2f937cc07b5f: Pull complete
a3ed95caeb02: Pull complete
Digest: sha256:2fb27e433b3ecccea2a14e794875b086711f5d49953ef173d8a03e8707f1510f
Status: Downloaded newer image for ubuntu:15.04
 
從上面的輸出來看,實際上pull 了4個image layer.這四個image layers 組成了ubuntu:15.04 這個Dcokerimage。
這4個image layer 分別存在本身目錄中,固然這些目錄在Dcoker 本地的存儲空間中
 
在docker1.10以前,存儲每一個layer都是以image layer ID爲目錄名稱存儲的。docker1.10以後就不是這樣了。
儘管顯示的方式不一樣,可是1.10先後都是能夠共享image層的。
 
作些實驗來講明共享image:
1.在空目錄中寫Dockerfile,以15.04爲基礎
FROM ubuntu:15.04
 
2.在image中 /tmp目錄增長newfile,內容爲"Hello world"
FROM ubuntu:15.04
 
RUN echo "Hello world" > /tmp/newfile
 
3.保存並退出文件
 
4.在Dockerfile文件的目錄下啓動命令行,並建立新image
zane@zane-V:~/mydockerbuild$ docker build -t changed-ubuntu .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM ubuntu:15.04
 ---> d1b55fd07600
Step 2 : RUN echo "Hello world" > /tmp/newfile
 ---> Running in 914cfa4e2724
 ---> 3a3a082982a2
Removing intermediate container 914cfa4e2724
Successfully built 3a3a082982a2
 
上面的輸出顯示新的image被建立ID:3a3a082982a2
 
5.運行docker image命令驗證新changed-ubuntu image 已經在docker本地存儲空間
zane@zane-V:~/mydockerbuild$ docker images
REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
changed-ubuntu          latest              3a3a082982a2        3 minutes ago       131.3 MB
zane/aiapple            007                 d2f5f84bec87        12 days ago         275.1 MB
zane/aiapple            009                 d2f5f84bec87        12 days ago         275.1 MB
ziapple                 latest              d2f5f84bec87        12 days ago         275.1 MB
 
6.運行docker history 命令來查看建立changed-ubuntu image 時使用了哪些image 層。
zane@zane-V:~/mydockerbuild$ docker history changed-ubuntu
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
3a3a082982a2        6 minutes ago       /bin/sh -c echo "Hello world" > /tmp/newfile    12 B               
d1b55fd07600        11 months ago       /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B                 
<missing>           11 months ago       /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.879 kB           
<missing>           11 months ago       /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   701 B               
<missing>           11 months ago       /bin/sh -c #(nop) ADD file:3f4708cf445dc1b537   131.3 MB
 
能夠看到只有3a3a082982a2 是剛建立的,且執行了咱們預期的命令。而其他4個都是ubuntu.15.04 本來就有的。
能夠看到他們的建立時間都是好久以前的。
 
注意:changed-ubuntu image並無本身獨享的每個image層。從下圖中能夠看到,新image 與ubuntu15.04是共享底層的4個image的。
圖片是官方例子,其實本地的3a3a082982a2,就是官方例子中的94e6b7d2c720.

 

能夠看到docker history 還能夠看到每一層的大小,咱們剛剛建立的3a3a082982a2 只有12B,也就是說,咱們剛剛建立了changed-ubuntu只額外佔用了12B 的空間,其他都是共享的。
 
那麼若是再以changed-ubuntu爲基礎image 再建立新的image呢?
重寫Dcokerfile:
zane@zane-V:~/mydockerbuild$ cat Dockerfile
FROM changed-ubuntu
RUN echo "Based on changed-ubuntu" > /tmp/new.t
 
建立新的image:
zane@zane-V:~/mydockerbuild$ docker build -t bashed_on_changed-ubuntu .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM changed-ubuntu
 ---> 3a3a082982a2
Step 2 : RUN echo "Based on changed-ubuntu" > /tmp/new.t
 ---> Running in 0132c351fa33
 ---> 93a447865aa6
Removing intermediate container 0132c351fa33
Successfully built 93a447865aa6
 
查看新image的history
zane@zane-V:~/mydockerbuild$ docker history bashed_on_changed-ubuntu
IMAGE               CREATED             CREATED BY                                      SIZE                COMMENT
93a447865aa6        29 seconds ago      /bin/sh -c echo "Based on changed-ubuntu" > /   24 B               
3a3a082982a2        31 minutes ago      /bin/sh -c echo "Hello world" > /tmp/newfile    12 B               
d1b55fd07600        11 months ago       /bin/sh -c #(nop) CMD ["/bin/bash"]             0 B                 
<missing>           11 months ago       /bin/sh -c sed -i 's/^#\s*\(deb.*universe\)$/   1.879 kB           
<missing>           11 months ago       /bin/sh -c echo '#!/bin/sh' > /usr/sbin/polic   701 B               
<missing>           11 months ago       /bin/sh -c #(nop) ADD file:3f4708cf445dc1b537   131.3 MB
能夠看到bashed_on_changed-ubuntu,共享了changed-ubuntu的全部image,在從新建立了93a447865aa6;而且只佔用了額外的24B。
 
 
複製使容器更效率

只有容器層是可寫的,共享image 層都是隻讀的。這樣保證了,多個容器層底層共享image的數據安全。
每一個容器有它本身的可寫層。

 

當在容器中修改一個已存在的文件,Dcoker使用存儲引擎執行copy-on-write 操做。
操做的細節取決於不一樣的存儲引擎,對於AUFS 和 OverlayFS 存儲引擎來講copy-on-write操做很是完美:
  • 經過image 層 搜索須要更改的文件。
    • 該過程從頂部,最新層開始,並向下工做到基本層一次一層。
  • 在找到的文件第一次複製的時候執行copy-up操做。
    • copy-up 複製這個文件到容器的本身的可寫層。
  • 在容器本身的可寫成修改剛剛複製過來的文件。
 
若是以咱們剛剛建立的changed-ubuntu 爲基礎運行了5個容器會發生什麼?
 
1.在命令行運行5次docker run
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
9635de83c668a61be7077ad309798a686fbfe29abf5f152dc84d641dd4b84a7b
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
f92f01d11ff0d93f09ca6b94ebda3b699dc959297cf90565acf7f337d1c8af03
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
a341fec4b3604a21dda733839cfea56005712335daa8344ece0a22c4df53076a
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
54b836a993084953972507015515ad4a9c8c779508ad1a581a7b50fa0a496f42
zane@zane-V:~$ docker run  -dit changed-ubuntu bash
7f3ea62f1430b20ab7948281d88ef96140a688cf7bd08454c40ce1ad3d057620
 
以changed-ubuntu image爲基礎啓動了5個容器。當每一個容器被建立後,Dcoker 會分別增長一個可寫層,並分配隨機的UUID。
 
2.運行docker ps 命令驗證5個容器正常運行中
zane@zane-V:~$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
7f3ea62f1430        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            berserk_borg
54b836a99308        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            condescending_mccarthy
a341fec4b360        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            ecstatic_saha
f92f01d11ff0        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes                            hungry_mestorf
9635de83c668        changed-ubuntu      "bash"              3 minutes ago       Up 3 minutes
上面5個運行的容器,都共享changed-ubuntu鏡像。CONTAINER ID 是由 UUID所派生的。
 
3.列出本地存儲的內容
$ sudo ls /var/lib/docker/containers
 
copy-on-write
  • 不只僅減小了容器使用的空間,
  • 並且減小了容器啓動須要的時間。
由於在啓動的時候,Dcoker僅須要爲每一個容器建立一個可寫層。
 
數據卷和存儲引擎

當刪除容器時,任何沒有存到數據卷裏的容器修改內容,也會被刪除。
 
數據卷是docker 本地主機的文件系統裏的一個目錄或者文件,這個目錄或文件是直接掛載到容器裏的。
數據卷是不被docker的存儲引擎所控制的。讀寫數據卷是繞過存儲引擎的,並以本機主機速度運行。
 
能夠掛載多個數據捲到容器。多個容器也能夠共享一個或者多個數據卷。
數據卷駐留在Docker主機上的本地存儲區域以外,進一步加強了它們與存儲驅動程序控制的獨立性。
當容器被刪除時,任何存儲到數據卷的數據都會持久化在docker 主機上。
 
總結

  • 新建容器,將底層堆的最上層設置爲可寫,叫作「containers layer」。對運行容器的修改,都是保存在這一層,而不會對底層image(不可寫)作任何改變
  • docker存儲引擎
    • 任務
      • 開啓和管理image層和container層
    • 關鍵技術
      • 堆棧image層
      • copy-on-write
  • copy-on-write
    • 共享是提高資源利用率的很好途徑
    • copy-on-write是很好的共享和複製的策略
      • 系統進程共享同一實例,不是擁有本身的副本
      • 須要修改時,給這進程複製須要的數據
    • 優勢
      • 優化了image磁盤空間使用
        • 由於共享了公用數據
      • 容器的啓動時間性能
        • 啓動時,僅須要爲每一個容器建立一個可寫層便可
  • 當容器須要修改文件,docker存儲引擎執行copy-on-write操做
    • 不一樣存儲引擎操做細節不一樣,AUFS,OverlayFS 執行過程
      • 經過image 層 搜索須要更改的文件
        • 該過程從頂部,最新層開始,並向下工做到基本層一次一層
      • 在找到的文件第一次複製的時候執行copy-up操做
        • copy-up 複製這個文件到容器的本身的可寫層
      • 在容器本身的可寫成修改剛剛複製過來的文件
  • 數據卷
    • 刪除容器時,任何存到數據卷的數據都會持久化到docker主機上
    • 數據卷是本地主機的一個目錄或文件,可直接掛載到容器裏
    • 數據卷是不被docker存儲引擎控制。
      • 讀寫數據卷是繞過存儲引擎的,並以本地主機速度運行
    • 容器與數據卷
      • 可掛載多個數據捲到一個容器
      • 也可多個容器共享一個數據卷
相關文章
相關標籤/搜索