- 關於base鏡像
- 關於存儲結構(About storage drivers)
- 先來建立一個本身的鏡像
- docker鏡像的分層結構
- 容器的大小
- 修改時複製策略 copy-on-write (CoW)
- Copying makes containers efficient
base 鏡像有兩層含義:nginx
因此,能稱做 base 鏡像的一般都是各類 Linux 發行版的 Docker 鏡像,好比 Ubuntu, Debian, CentOS 等。git
base 鏡像提供的是最小安裝的 Linux 發行版。github
咱們大部分鏡像都將是基於base鏡像構建的。因此,一般使用的是官方發佈的base鏡像。能夠在docker hub裏找到。好比centos: hub.docker.com/_/centossql
點擊版本能夠看到github裏的Dockerfiledocker
FROM scratch
ADD centos-7-docker.tar.xz /
LABEL org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20181205"
CMD ["/bin/bash"]
複製代碼
ADD命令將本地的centos7的tar包添加到鏡像,並解壓到根目錄/下。生成/dev,/proc/,/bin等。vim
咱們能夠本身構建docker base鏡像,也能夠直接使用已有的base鏡像。好比centos。咱們能夠直接從docker hub上拉取。centos
拉取安全
docker pull centos
複製代碼
查看bash
# docker images centos
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 1e1148e4cc2c 2 months ago 202MB
複製代碼
能夠看到最新的centos鏡像只有200mb,是否是以爲過小了?這是由於docker鏡像在運行的時候直接使用docker宿主機器的kernel。服務器
Linux操做系統由內核空間和用戶空間組成。
內核空間是kernel,用戶空間是rootfs, 不一樣Linux發行版的區別主要是rootfs.好比 Ubuntu 14.04 使用 upstart 管理服務,apt 管理軟件包;而 CentOS 7 使用 systemd 和 yum。這些都是用戶空間上的區別,Linux kernel 差異不大。
因此 Docker 能夠同時支持多種 Linux 鏡像,模擬出多種操做系統環境。
須要注意的是:
上文裏展現瞭如何下載一個base鏡像。咱們一般是基於這份base鏡像來構建咱們本身的鏡像。好比,在centos裏添加一個nginx負載均衡。首先,得須要瞭解鏡像的結構是什麼。
官方文檔: docs.docker.com/storage/sto…
首先,base鏡像是基於docker宿主機器kernel之上的Linux發行版。
如今,咱們給這臺機器安裝一個vim,一個httpd. 基於Dockerfile來建立一個新的鏡像。
咱們的Dockerfile
FROM centos:7
RUN yum install -y vim
RUN yum install -y httpd
CMD ["/bin/bash"]
複製代碼
含義:
在當前目錄下新建一個文件Dockerfile, 填充上述內容。而後執行
# docker build -t ryan/httpd:v1.0 .
Sending build context to Docker daemon 6.144kB
Step 1/4 : FROM centos:7
---> 1e1148e4cc2c
Step 2/4 : RUN yum install -y vim
---> Using cache
---> 74bdbea98f73
Step 3/4 : RUN yum install -y httpd
---> Using cache
---> 17d8c4095dc4
Step 4/4 : CMD /bin/bash
---> Using cache
---> f2b58b1192de
Successfully built f2b58b1192de
Successfully tagged ryan/httpd:latest
複製代碼
/id:version
的方式標記Dockerfile
所在的路徑.
, 表示當前目錄而後咱們添加一個tag latest
docker tag ryan/httpd:v1.0 ryan/httpd:latest
複製代碼
ryan/httpd:v1.0
標記爲ryan/httpd:latest
構建完成以後,查看
# docker images | grep -E '(ryan|centos)'
ryan/httpd latest f2b58b1192de About an hour ago 444MB
ryan/httpd v1.0 f2b58b1192de About an hour ago 444MB
centos 7 1e1148e4cc2c 2 months ago 202MB
centos latest 1e1148e4cc2c 2 months ago 202MB
複製代碼
能夠運行咱們建立的鏡像:
# docker run -d --privileged=true -it ryan/httpd:v1.0 /usr/sbin/init
48a4a128cd7b6924149cd97670919d4e2af6cb96c73c901af60d05fe4478225a
# docker ps | grep ryan
48a4a128cd7b ryan/httpd:v1.0 "/usr/sbin/init" 8 seconds ago Up 8 seconds
複製代碼
如今咱們的基於原生base centos7的httpd服務器已經啓動了。能夠經過docker exec -it zealous_kirch /bin/bash來進入容器內部,查看啓動httpd。
咱們能夠查看鏡像的歷史,用上一步的鏡像id f2b58b1192de
# docker history f2b58b1192de
IMAGE CREATED CREATED BY SIZE COMMENT
f2b58b1192de About an hour ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
17d8c4095dc4 About an hour ago /bin/sh -c yum install -y httpd 110MB
74bdbea98f73 About an hour ago /bin/sh -c yum install -y vim 133MB
1e1148e4cc2c 2 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 2 months ago /bin/sh -c #(nop) LABEL org.label-schema.... 0B
<missing> 2 months ago /bin/sh -c #(nop) ADD file:6f877549795f479... 202MB
複製代碼
啓動鏡像的時候,一個新的可寫層會加載到鏡像的頂部。這一層一般稱爲「容器層」, 之下是「鏡像層」。
容器層能夠讀寫,容器全部發生文件變動寫都發生在這一層。鏡像層read-only,只容許讀取。
(上圖來自官方文檔,和本次實驗內容略有不一樣,但原理同樣)
第一列是imageid, 最上面的id就是咱們新建立ryan/httpd:latest. 下面幾行都是咱們dockerfile裏定義的步驟堆棧。由此能夠看出,每一個步驟都將建立一個imgid, 一直追溯到1e1148e4cc2c
正好是咱們的base鏡像的id。關於的部分,則不在本機上。
最後一列是每一層的大小。最後一層只是啓動bash
,因此沒有文件變動,大小是0. 咱們建立的鏡像是在base鏡像之上的,並非徹底複製一份base,而後修改,而是共享base的內容。這時候,若是咱們新建一個新的鏡像,一樣也是共享base鏡像。
那修改了base鏡像,會不會致使咱們建立的鏡像也被修改呢? 不會!由於不容許修改歷史鏡像,只容許修改容器,而容器只能夠在最上面的容器層進行寫和變動。
建立鏡像的時候,分層可讓docker只保存咱們添加和修改的部份內容。其餘內容基於base鏡像,不須要存儲,讀取base鏡像便可。如此,當咱們建立多個鏡像的時候,全部的鏡像共享base部分。節省了磁盤空間。
對於啓動的容器,查看所須要的磁盤空間能夠經過docker ps -s
# docker run -d -it centos
4b0df4bc3e705c540144d545441930689124ade087961d01f56c2ac55bfd986d
# docker ps -s | grep -E '(ryan|centos)'
4b0df4bc3e70 centos "/bin/bash" 23 seconds ago Up 23 seconds vigorous_elion 0B (virtual 202MB)
b36421d05005 ryan/httpd:v1.0 "/usr/sbin/init" 32 minutes ago Up 32 minutes gracious_swirles 61.6kB (virtual 444MB)
複製代碼
docker經過一個叫作copy-on-write (CoW) 的策略來保證base鏡像的安全性,以及更高的性能和空間利用率。
Copy-on-write is a strategy of sharing and copying files for maximum efficiency. If a file or directory exists in a lower layer within the image, and another layer (including the writable layer) needs read access to it, it just uses the existing file. The first time another layer needs to modify the file (when building the image or running the container), the file is copied into that layer and modified. This minimizes I/O and the size of each of the subsequent layers. These advantages are explained in more depth below.
Copying makes containers efficient
When you start a container, a thin writable container layer is added on top of the other layers. Any changes the container makes to the filesystem are stored here. Any files the container does not change do not get copied to this writable layer. This means that the writable layer is as small as possible.
When an existing file in a container is modified, the storage driver performs a copy-on-write operation. The specifics steps involved depend on the specific storage driver. For the aufs, overlay, and overlay2 drivers, the copy-on-write operation follows this rough sequence:
Search through the image layers for the file to update. The process starts at the newest layer and works down to the base layer one layer at a time. When results are found, they are added to a cache to speed future operations.
Perform a copy_up operation on the first copy of the file that is found, to copy the file to the container’s writable layer.
Any modifications are made to this copy of the file, and the container cannot see the read-only copy of the file that exists in the lower layer.
Btrfs, ZFS, and other drivers handle the copy-on-write differently. You can read more about the methods of these drivers later in their detailed descriptions.
Containers that write a lot of data consume more space than containers that do not. This is because most write operations consume new space in the container’s thin writable top layer.
簡單的說,啓動容器的時候,最上層容器層是可寫層,之下的都是鏡像層,只讀層。
當容器須要讀取文件的時候
從最上層鏡像開始查找,往下找,找到文件後讀取並放入內存,若已經在內存中了,直接使用。(即,同一臺機器上運行的docker容器共享運行時相同的文件)。
當容器須要添加文件的時候
直接在最上面的容器層可寫層添加文件,不會影響鏡像層。
當容器須要修改文件的時候
從上往下層尋找文件,找到後,複製到容器可寫層,而後,對容器來講,能夠看到的是容器層的這個文件,看不到鏡像層裏的文件。容器在容器層修改這個文件。
當容器須要刪除文件的時候
從上往下層尋找文件,找到後在容器中記錄刪除。即,並不會真正的刪除文件,而是軟刪除。這將致使鏡像體積只會增長,不會減小。
綜上,Docker鏡像經過分層實現了資源共享,經過copy-on-write實現了文件隔離。
對於文件只增長不減小問題,咱們應當在同一層作增刪操做,從而減小鏡像體積。好比,以下測試。
Dockerfile.A: 分層刪除文件
FROM centos:7
RUN yum install -y vim
RUN yum install -y httpd
WORKDIR /home
RUN dd if=/dev/zero of=50M.file bs=1M count=50
#建立大小爲50M的測試文件
RUN rm -rf 50M.file
CMD ["/bin/bash"]
複製代碼
構建
docker build -t test:a -f Dockerfile.A .
複製代碼
Dockerfile.B: 同層刪除
FROM centos:7
RUN yum install -y vim
RUN yum install -y httpd
WORKDIR /home
RUN dd if=/dev/zero of=50M.file bs=1M count=50 && rm -rf 50M.file
複製代碼
構建
docker build -t test:b -f Dockerfile.B .
複製代碼
比較兩者大小
[root@sh-k8s-001 tmp]# docker images | grep test
test a ae673aa7db48 9 minutes ago 497MB
test b 21b2bc49f0bd 12 minutes ago 444MB
複製代碼
顯然,分層刪除操做並無真正刪除掉文件。
最後,歡迎作Java的工程師朋友們加入Java高級架構進階Qqun:963944895
羣內有技術大咖指點難題,還提供免費的Java架構學習資料(裏面有高可用、高併發、高性能及分佈式、Jvm性能調優、Spring源碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)
比你優秀的對手在學習,你的仇人在磨刀,你的閨蜜在減肥,隔壁老王在練腰, 咱們必須不斷學習,不然咱們將被學習者超越!
趁年輕,使勁拼,給將來的本身一個交代!