Docker 特性與原理詳細介紹與解析
Docker 特性與原理
首先看看Docker提供了哪些特性:
- 交互式Shell:Docker可以分配一個虛擬終端并關聯到任何容器的標準輸入上,例如運行一個一次性交互shell
- 文件系統(tǒng)隔離:每個進程容器運行在完全獨立的根文件系統(tǒng)里
- 寫時復制:采用寫時復制方式創(chuàng)建根文件系統(tǒng),這讓部署變得極其快捷,并且節(jié)省內存和硬盤空間
- 資源隔離:可以使用cgroup為每個進程容器分配不同的系統(tǒng)資源
- 網絡隔離:每個進程容器運行在自己的網絡命名空間里,擁有自己的虛擬接口和IP地址
- 日志記錄:Docker將會收集和記錄每個進程容器的標準流(stdout/stderr/stdin),用于實時檢索或批量檢索
- 變更管理:容器文件系統(tǒng)的變更可以提交到新的映像中,并可重復使用以創(chuàng)建更多的容器。無需使用模板或手動配置
從以上特性分別看實現原理
1. 交互式Shell
首先我們允許一個交互式的容器
$docker run -i -t <image name> /bin/bash
這樣就建立了一個到容器內的交互式連接,看到的是如下的命令行:
root@df3880b17407:/#
這里我們啟動了一個容器,以bash作為其根進程.
root@df3880b17407:/# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.0 18164 2020 ? S 06:06 0:00 /bin/bash
可以看到,在這個容器中,bash 的 PID為 1,而實體機平常情況下,是這樣的:
root@ubuntu:~# ps aux USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 24716 2612 ? Ss Sep04 0:01 /sbin/init
大家都知道,所有進程的共同祖先都是 PID=1的進程
所以在容器中,所有以后創(chuàng)建的進程都是通過/bin/bash 創(chuàng)建的,PID=1的 bash是容器中所有進程的祖先理解了這點后,對容器的理解就很簡單了.
2. 文件系統(tǒng)隔離
對于一個正在運行的容器,其文件系統(tǒng)都是一個從根目錄開始的虛擬文件系統(tǒng),在容器中看到的是這樣的:
root@df3880b17407:/# ll / total 68 drwxr-xr-x 2 root root 4096 Jul 22 22:51 bin drwxr-xr-x 2 root root 4096 Apr 10 22:12 boot drwxr-xr-x 3 root root 4096 Jul 22 22:49 dev drwxr-xr-x 85 root root 4096 Sep 5 06:49 etc drwxr-xr-x 2 root root 4096 Apr 10 22:12 home drwxr-xr-x 16 root root 4096 Jul 22 22:50 lib drwxr-xr-x 2 root root 4096 Aug 12 03:30 lib64 drwxr-xr-x 2 root root 4096 Jul 22 22:48 media drwxr-xr-x 2 root root 4096 Apr 10 22:12 mnt drwxr-xr-x 2 root root 4096 Jul 22 22:48 opt dr-xr-xr-x 356 root root 0 Sep 5 06:06 proc drwx------ 2 root root 4096 Jul 22 22:51 root drwxr-xr-x 7 root root 4096 Sep 5 07:23 run drwxr-xr-x 2 root root 4096 Aug 12 03:30 sbin drwxr-xr-x 2 root root 4096 Jul 22 22:48 srv dr-xr-xr-x 13 root root 0 Sep 5 06:06 sys drwxrwxrwt 2 root root 4096 Sep 5 06:55 tmp drwxr-xr-x 20 root root 4096 Sep 5 06:11 usr drwxr-xr-x 19 root root 4096 Sep 5 06:11 var
其實真是情況是這樣的,容器中的文件系統(tǒng)都是掛載到了真是系統(tǒng)中的一個目錄下面.
/var/lib/docker/containers/<image-long-id>/rootfs
這個配置是怎么來的呢,其實所有容器的管理都是通過lxc來管理的,lxc的配置文件放在
/var/lib/docker/containers/<image-long-id>/config.lxc
文件中有字段表示容器掛載到哪個文件目錄, 比如我的是這樣的:
lxc.rootfs = /var/lib/docker/containers/df3880b17407575cd642a6b7da3c7e417a55fad5bbd63152f89921925626d2b6/rootfs
打開看一下,一目了然:
root@ubuntu:/var/lib/docker/containers/df3880b17407575cd642a6b7da3c7e417a55fad5bbd63152f89921925626d2b6/rootfs# ll total 84 drwxr-xr-x 53 root root 4096 Sep 5 00:23 ./ drwx------ 4 root root 4096 Sep 5 00:53 ../ drwxr-xr-x 2 root root 4096 Jul 22 15:51 bin/ drwxr-xr-x 2 root root 4096 Apr 10 15:12 boot/ drwxr-xr-x 3 root root 4096 Jul 22 15:49 dev/ drwxr-xr-x 85 root root 4096 Sep 4 23:49 etc/ drwxr-xr-x 2 root root 4096 Apr 10 15:12 home/ drwxr-xr-x 16 root root 4096 Jul 22 15:50 lib/ drwxr-xr-x 2 root root 4096 Aug 11 20:30 lib64/ drwxr-xr-x 2 root root 4096 Jul 22 15:48 media/ drwxr-xr-x 2 root root 4096 Apr 10 15:12 mnt/ drwxr-xr-x 2 root root 4096 Jul 22 15:48 opt/ drwxr-xr-x 2 root root 4096 Apr 10 15:12 proc/ drwx------ 2 root root 4096 Jul 22 15:51 root/ drwxr-xr-x 7 root root 4096 Sep 5 00:23 run/ drwxr-xr-x 2 root root 4096 Aug 11 20:30 sbin/ drwxr-xr-x 2 root root 4096 Jul 22 15:48 srv/ drwxr-xr-x 2 root root 4096 Mar 12 18:41 sys/ drwxrwxrwt 2 root root 4096 Sep 4 23:55 tmp/ drwxr-xr-x 20 root root 4096 Sep 4 23:11 usr/ drwxr-xr-x 19 root root 4096 Sep 4 23:11 var/
這些就是容器中的真實目錄了,容器中對于目錄的操作都是操作了這個host機器的真實目錄。
對于不同的容器,掛載點是不一樣的,而容器不能穿越根目錄上一級去訪問, 所以這里對每一個容器都做到了文件系統(tǒng)隔離。
3. 寫時復制
我們把每一個
/var/lib/docker/containers/<image-long-id>
看做是一個容器的配置目錄的話,可以看到在配置目錄下面有一個 rw/目錄,打開看有些什么
total 36 drwxr-xr-x 9 root root 4096 Sep 5 00:23 ./ drwx------ 4 root root 4096 Sep 5 00:53 ../ drwxr-xr-x 6 root root 4096 Sep 4 23:49 etc/ drwxr-xr-x 2 root root 4096 Sep 5 00:23 run/ drwxrwxrwt 2 root root 4096 Sep 4 23:55 tmp/ drwxr-xr-x 7 root root 4096 Sep 4 23:11 usr/ drwxr-xr-x 5 root root 4096 Sep 4 23:11 var/ -r--r--r-- 1 root root 0 Sep 4 23:06 .wh..wh.aufs drwx------ 2 root root 4096 Sep 4 23:06 .wh..wh.orph/ drwx------ 2 root root 4096 Sep 4 23:11 .wh..wh.plnk/
里面是一些不完整的根目錄,這不能說明什么,但是我們在container中寫入文件后,看看其中的變化
在容器中執(zhí)行以下命令
root@df3880b17407:/# touch /opt/x
在 /opt 下我們生成了一個文件
再看看
root@ubuntu:/var/lib/docker/containers/df3880b17407575cd642a6b7da3c7e417a55fad5bbd63152f89921925626d2b6/rw# ll total 40 drwxr-xr-x 10 root root 4096 Sep 5 01:00 ./ drwx------ 4 root root 4096 Sep 5 00:53 ../ drwxr-xr-x 6 root root 4096 Sep 4 23:49 etc/ drwxr-xr-x 2 root root 4096 Sep 5 01:00 opt/ drwxr-xr-x 2 root root 4096 Sep 5 00:23 run/ drwxrwxrwt 2 root root 4096 Sep 4 23:55 tmp/ drwxr-xr-x 7 root root 4096 Sep 4 23:11 usr/ drwxr-xr-x 5 root root 4096 Sep 4 23:11 var/ -r--r--r-- 1 root root 0 Sep 4 23:06 .wh..wh.aufs drwx------ 2 root root 4096 Sep 4 23:06 .wh..wh.orph/ drwx------ 2 root root 4096 Sep 4 23:11 .wh..wh.plnk/
是的,在host機器上新生成了 opt/目錄,這里做到了容器的寫時復制
4. 資源隔離
以系統(tǒng)的三大進程間通信的消息隊列來看
初始狀態(tài)在 ghost, ipcs -q 查看消息隊列
root@ubuntu:~/codes/msq# ipcs -q ------ Message Queues -------- key msqid owner perms used-bytes messages
在ghost 創(chuàng)建一個, 這里樓主自己寫的代碼創(chuàng)建的消息隊列:
root@ubuntu:~/codes/msq# ./main 1 Create msq with key id 65536root@ubuntu:~/codes/msq# ipcs -q ------ Message Queues -------- key msqid owner perms used-bytes messages 0x00000001 65536 root 666 0 0
然后在容器中查看 ipcs -q
root@df3880b17407:/# ipcs -q ------ Message Queues -------- key msqid owner perms used-bytes messages
可以看到系統(tǒng)資源是隔離的,這里只是說了一部分,其實還包括了可以通過cgoup對其做CPU和Memory的Quota管理.
默認情況下是使用了所有CPU和內存的,但是可以在config.lxc增加如下配置設置CPU等,具體可以參考lxc的文檔
lxc.cgroup.cpu.shares=512 lxc.cgroup.cpuset.cpus=1.2
資源隔離的原理就在于利用cgroup,將不同進程的使用隔離開,假設每個容器都是以bash啟動的,那么在容器內部,每個子進程都只能使用當前bash下面的資源,對于其他的系統(tǒng)資源是隔離的.子進程的訪問權限由父進程決定
5.網絡隔離
在安裝好docker后,會默認初始化一個 docker0的網橋
docker0 Link encap:Ethernet HWaddr ee:8c:1f:8b:d7:59 inet addr:172.17.42.1 Bcast:0.0.0.0 Mask:255.255.0.0 ...
在host機器上,會為每一個容器生成一個默認的網卡類似這樣的 vethdBVa1H veth*
這個網卡的一端連接在容器的eth0,一端連接到docker0.這樣就實現了每個容器有一個單獨的IP.
這里如果需要容器訪問外網,需要將eth0設置為混雜模式:
$ifconfig eth0 promisc
這樣看來,容器會從172.17.0.0/24 這個網段選擇一個IP作為eth0的IP,這樣,容器就可以和外部通過 docker0網橋通信了.
在容器內部監(jiān)聽一個端口 python -m SimpleHTTPServer 80 >> /tmp/log.log &
從ghost訪問 telnet 172.17.0.2 80
在容器中看到如下:
Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 2823/python tcp 0 0 172.17.0.2:80 172.17.42.1:46142 TIME_WAIT -
在host上看到
Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 172.17.42.1:46142 172.17.0.2:80 ESTABLISHED 10244/telnet
如果需要外部能夠訪問容器,需要做端口映射規(guī)則,和配置虛擬機一樣的道理, 只不過這里可以看到的是,80端口并沒有占用了本地端口,而是在容器內部做了監(jiān)聽,外部是通過docker0 橋接過去的,每個容器間也做到了端口和網絡隔離.
6.日志記錄
不多說,在 /var/lib/docker/containers/<image-long-id>.log 下
7.變更管理
Docker的變更管理看做是git的版本管理好了。
生成鏡像的時候,未做改動的部分就是上一個版本的鏡像的引用,如果做了改動,就是一個新的文件。
將剛才操作的容器做成鏡像
docker commit <image-id> <REPOSITORY>
此時的鏡像多出來的部分,比如我在這個鏡像中安裝了Python,那么多出來的部分作為新文件,其他部分任然是上一個版本的引用。
你可以搭建自己的鏡像服務器,push到自己的鏡像服務器,從其他機器拉下來后直接可以運行。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關文章
Docker鏡像拉取報錯:Error response from daemon: 
這篇文章主要介紹了Docker鏡像拉取報錯:Error response from daemon: Get https://registry-1.docker.io/v2/的問題解決,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-07-07Ubuntu?環(huán)境下安裝?Docker環(huán)境詳解
這篇文章主要介紹了Ubuntu?環(huán)境下安裝?Docker環(huán)境詳解的相關資料,需要的朋友可以參考下2022-10-10docker運行PostgreSQL數據庫維護執(zhí)行腳本備份數據庫與更新表結構的方法
這篇文章主要介紹了docker運行PostgreSQL數據庫維護,執(zhí)行腳本備份數據庫與更新表結構,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-05-05