(第3章)Docker核心原理解讀(續)

1.Docker數據卷

  • 類似於mount操作,用戶將一個文件夾作爲volume掛載到容器上,可以將數據添加到容器中,以供其中的進程使用。
    多個容器可以共享同一個volume
  • 創建volume,掛載volume,以及使用Dockerfile添加volume,共享volume,珊瑚volume,備份恢復或遷移volume
1)創建volume
創建了一個指定名字的volume
docker volume creat --name vol_simle

掛載到容器的/data目錄下
docker run -d -v /data ubuntu /bin/bash

創建一個指定名字的volume,並掛載到容器中的/data目錄
docker run -d -v vol_simple: /data ubuntu /bin/bash

獲取該volume在宿主機中該文件夾的位置等信息
docker volume inspect vol_simple

(2)掛載volume
創建一個指定名字的volume,並掛載到容器中的/data目錄
docker run -d -v vol_simple: /data ubuntu /bin/bash

創建一個隨機ID的volume,並將其掛載到/data下
docker run -d -v  /data ubuntu /bin/bash

將宿主機上的目錄掛載到容器中
在/host/dir文件夾中的所有文件或文件夾都可以在容器的/container/dir文件夾下被訪問,若鏡像中的/container/dir文件夾下
有內容,將會被隱藏
docker run -v /host/dir:/container/dir ubuntu /bin/bash

將單個文件作爲volume掛載到容器中
文件必須使用絕對路徑
docker run -it --name vol_file -v /host/file:/container/file ubuntu /bin/bash

爲容器添加多個volume
docker run -it --name vol_mult -v /data1 -v /data2 -v /host/dir:/container/dir ubuntu /bin/bash

(2)使用Dockerfile添加volume
若鏡像中存在/data文件夾,該文件夾中的內容將全部被複制到宿主機中對應的文件夾中,並且依據容器中的文件
來設置合適的權限和所有者
VOLUME /data


Dockerfile中除了FROM指令的每一行都是基於上一行生成的臨時鏡像,運行一個容器,
執行一條指令並執行類似docker commit的命令得到一個新的鏡像,docker commit的命令不會對掛載的volume進行保存
FROM ubuntu
RUN useradd foo
RUN mkdir /data && touch /data/file
RUN chown -R foo:foo /data
VOLUME /data

(3)共享volume
新創建的容器vol_use與之前創建的容器vol_simple共享volume,這個volume目的目錄也是/data;
若被共享的容器有多個volume(eg:上面的vol_mult),新容器也將有多個volume,並且其掛載的目的目錄也與vol_mult
docker run --rm -it --name vol_use --volumes-from vol_simple ubuntu /bin/bash

容器與多個已有容器共享volume
docker run --rm -it --name vol_use_mult --volumes-from vol_1 --volumes-from vol_2 ubuntu /bin/bash


創建一個掛載了volume的數據容器vol_data,該容器僅僅輸出了一條提示後就停止運行以避免浪費資源。
接下來的兩個容器vol_share1和vol_share2與這個數據容器共享了這個volume
docker run --name vol_data -v /data ubuntu echo "This is a data-only container"
docker run -it --name vol_share1 --volumes-from vol_data ubuntu /bin/bash
docker run -it --name vol_share2 --volumes-from vol_data ubuntu /bin/bash


(4)刪除volume
只要在創建容器時掛載了volume,在/var/lib/docker/volumes下就會生成與volume對應的目錄,
使用docker rm刪除容器並不會刪除與volume對應的目錄。


只有當沒有任何容器使用該volume時,該volume才能成功刪除
docker volume rm <volume_name> 刪除volume

下面的2種方法:只會對掛載在該容器上的未命名的volume進行刪除,而對用戶指定名字的volume進行保留
docker rm -v <container_name> 刪除容器
docker run --rm ,--rm標籤會在容器停止時,刪除容器以及容器所掛載的volume

(5)備份恢復或遷移volume
備份volume方法:
方法1:利用docker inspect命令查找到/data在宿主機上對於的文件夾位置,複製其內容,利用tar打包
方法2:
vol_simple容器中包含了需要備份的volume
該命令啓動了一個臨時容器,該容器掛載了2個volume,第一個volume來自vol_simple容器的共享,即需要備份的volume
第二個volume將宿主機的當前目錄掛載到容器的/backup下。
容器運行後,將要備份的內容(/data文件夾)備份到/backup/data.tar,
然後刪除容器,備份後data.tar就留在了當前的目錄
docker run --rm --volumes-from vol_simple -v $(pwd):/backup ubuntu tar cvf /backup/data.tar /data

恢復volume方法:
首先運行一個新容器作爲數據恢復的目標
第二個指令:掛載了兩個volume,第一個volume與要恢復的volume共享,第二個volume將宿主機的當前目錄掛載到容器的/backup下
容器啓動後,將這個存檔文件中的/data恢復到根目錄下,然後刪除容器,恢復後的數據就在vol_bck的volume中
docker run -it --name vol_bck -v /data ubuntu /bin/bash
docker run -rm --volumes-from vol_bck -v $(pwd):/backup ubunttu tar xvf /backup/data.tar -C /

  • Docker的volume的本質是容器中的一個特殊的目錄
    Docker會將宿主機上的指定目錄(一個volume ID爲名稱的目錄,或指定的宿主機的目錄)掛載到容器中指定的目錄上,掛載完成胡的宿主機目錄和容器內的目錄表現一致。
docker run -v /data busybox /bin/bash
指定容器裏的/data目錄爲一個volume
將宿主機上的volume_id目錄綁定掛載到rootfs中指定的掛載點/data上
mount("/var/lib/docker/volumes/volume_id/_data","rootfd/data","none",MS_BIND,NULL)

docker run -v /var/log:/data busybox /bin/bash
將宿主機上的/var/log目錄綁定掛載到rootfs中指定的掛載點/data上
mount("/var/log","rootfs/data","none",MS_BIND,NULL)

容器內部進行只能看見以rootfs爲跟的文件內容以及被mount到rootfs之下的目錄
下面的data目錄就是生成出來的volume掛載點
root@0a4b299bb8e8:/# ls
bin  boot  dev  etc  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  test.txt  tmp  usr  var
  • 創建volume原理
    (1)容器創建階段
    (2)容器的啓動階段,libcontainer使用組裝好的掛載點列表進行mount操作,完成volume的創建

  • volume相關的配置文件:該容器所使用的volumeID以及他們的可寫情況
    位置:每個容器在/var/lib/docker/containers/容器ID/config.json

2.Docker網絡基礎

  • Docker公司在libnetwork中使用了CNM(Container Network Model),CNM定義了構建容器虛擬化網絡的模型,還提供了可以用於開發多種網絡驅動的標準化接口和組件
    (1)CNM的3個核心組件:
    沙盒:包含一個容器網絡棧的信息,對容器的接口,路由和DNS設置進行管理
    端點:veth pair,Open vSwitch內部的端口或者相似設備
    網絡:Linux bridge,VLAN等
    (2)CNM的5種內置驅動:bridge驅動,host驅動,overlay驅動,remote驅動,null驅動
    在這裏插入圖片描述
  • 在單一主機上使用Docker默認的bridge驅動進行演示
    在這裏插入圖片描述
上圖中:backend network爲後端網絡,frontend network則爲前端網絡

(1)創建backend和frontend網絡
docker network creat backend
docker network creat frontend

查看主機上的所有Docker網絡;
docker network ls
。。。  bridge   bridge
。。。   none    null
。。。   host    host
上面3個是Docker內置的網絡,無法使用docker network rm進行刪除

(2)將container1和container2的容器加入到backend網絡
將container3的容器加入到frontend網絡
docker run -it -name container1 --net backend busybox
docker run -it -name container2 --net backend busybox
docker run -it -name container3 --net frontend busybox

(3)將container2加入到frontend網絡
socker network connect frontend container2

(4)在container2中使用ifconfig可以發現:有兩塊網卡eth0和eth1,
兩個ip分別在不同的ip段
  • bridge驅動實現機制分析:(1)Docker0網橋
(1)在宿主機上使用ifconfig命令:可以看到多了一塊名爲docker0的網卡,
其IP默認爲172.17.0.1/16,之後創建的Docker容器都會在docker0子網
的範圍內選取一個未佔用的ip來使用,並連接到docker0網橋上。
在主機上輸入:route -n
...
172.17.0.0   0.0.0.0     255.255.0.0     U     0   0   0 docker0
表示:所有目的IP地址的爲172.17.0.0/16的數據包從docker0網卡發出

使用docker run創建一個容器名爲con1的容器,在con1容器中可以看到他有兩塊
網卡:
網卡lo:容器的迴環網卡;
網卡eth0:容器與外界通信的網卡,eth0的IP爲172.17.0.2/16,和宿主機上的
網橋docker0在同一個網段;

(2)查看容器con1的路由表,發現con1的默認網關是宿主機的docker0網卡。
con1的eth0網卡與宿主機的docker0網卡是互相連通的。

(3)由於veth pair總是成對出現的,veth pair通常用來連接兩個network namespace
查看宿主機的網絡設備發現會有一塊以veth開頭的網卡,
另一個是Docker容器con1中的eth0.
所以docker0就是一個網橋

(4)下圖3-18是:創建了docker0網橋,並以veth pair連接各容器的網絡,容器
中的數據通過docker0網橋轉發給eth0的網卡上.
這裏的網橋相當於是交換機,工作在二層,所以不需要配置ip地址。

(5)Docker0作爲二層設備,爲什麼要配置IP地址?
docker0是普通的Linux網橋,可認爲其內部有一個可以用於配置IP信息的網卡接口。
docker0的IP地址可以作爲連於之上的容器的默認網管地址存在!

(6)創建網橋可以使用--bridge+BRIDGE參數指定,也可以
添加一個名爲br0的網橋,並且爲其配置ip
brctl addbr br0
ifconfig br0 188.18.0.1

查看本機的Linux網橋及其端口
brctl show

在這裏插入圖片描述

  • bridge驅動實現機制分析:(2)iptables規則
(1)iptables規則主要用於Docker容器和容器之間以及和外界的通信
在宿主機上查看:
iptables-save
。。。
-A POSTROUTING -s 172.17.0.0/16 !-o docker0 -j MASQUERAED
。。。
上面的這條規則表示:Docker容器與外界通信時,將源地址爲172.17.0.0/16的
數據包(從Docker容器發出的數據),當不是從docker0網卡發出時做SNAT
(源地址轉換,將IP包的源地址替換爲相應網卡的地址)
這樣,玩不就感覺不到Docker容器的存在。

(2)eg:目的地址轉換DNAT
docker run -d -p 5000:5000 training/webapp python app.py
在主機上輸入:iptables-save
...
*nat
-A DOCKER | -i docker0 -p tcp -m tcp --dport 5000 -j DNAT --to-destination 172.17.0.4:5000
...
*filter
-A DOCKER -d 172.17.0.0.4/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 5000 -j ACCEPT

上述的規則將訪問宿主機5000端口的流量轉發到172.1.0.45000端口上
(真正提供服務的Docker容器的IP端口),所以外界訪問Docker容器都是
通過iptables做DNAT

(3)Docker容器之間的通信也受到iptables規則限制。同一臺宿主機上的Docker容器默認都連在docker0網橋上,它們屬於一個子網。

在Docker容器和外界通信的過程中,還涉及到docker0網卡到宿主機eth0的
轉發,還需內核將ip-forward功能打開。
  • bridge驅動實現機制分析:(3)Docker容器的DNS和主機名
1)同一個Docker鏡像可以啓動很多個Docker容器,通過查看,發現他們
的主機名不一樣,即:主機名並非是被寫入鏡像中的。

(2/etc/hostname,/etc/hosts,/etc/resolv.conf這三個文件,
實際會被容器啓動後的虛擬文件覆蓋掉的。
在主機輸入mount,也可以看到:
...
/dev/disk............on /etc/hostname type ext4
/dev/disk............on /etc/hosts type ext4
/dev/disk............on /etc/resolv.conf type ext4
...

  • Docker網絡現在是一個單獨的庫:libnetwork

  • 傳統link:若Docker使用默認的bridge模式網絡,則會默認使用傳統的link系統
    Link特點:Link是一種比端口映射更親密的Docker容器間的通信方式,提供了更安全,高效的服務,通過環境變量和/etc/hosts文件的設置,提供了別名到具體通信地址的發現,適合於一些需要各組件間通信的應用。

1)新建一個含有數據庫服務的Docker容器,取名爲db。
新建一個包含web應用的Docker容器,取名爲web,並將web連接到db
web容器稱之爲接收容器,db容器稱之爲源容器
docker run -d --name db training/postgres
docker run -d -P --name web --link db:webdb training/webapp python app.py

Docker將連接信息以下面(2)(3)的兩種方式保存在接收容器中。

(2)設置接收容器的環境變量
當兩個容器通過--link建立了連接後,會在接收容器中額外設置一些環境變量,以保存源容器的一些信息

(3)更新接收容器的/etc/hosts文件
link操作除了將link信息保存在接收容器中,還在/etc/hosts中添加了一項————
源容器的IP和別名(--link),以用來解析源容器,以用來解析源容器的IP地址。

當源容器重啓後,會自動更新接收容器的/etc/hosts文件,需要注意的是:這裏用
的是別名,而不是源容器的主機名(實際上,主機名對外界是不可見的)。

(4)建立iptables規則(防火牆規則)進行通信
在接收容器上設置了環境變量和更改了/etc/hosts文件之後,接收容器僅僅是得到
了源容器的相關信息(環境變量,IP地址),但是不代表源容器和接收容器在網絡上
可以互相通信。

要爲連接的容器添加特定的iptables規則,才能保證兩個容器間的通信。
eg:接着上面的web和db容器例子
源容器db容器將tcp/5432端口暴露給外界來爲外界提供服務,web容器和db容器
需要在db容器的tcp/5432端口上進行通信。
假設:web容器的IP地址爲172.17.0.2/16,db容器的IP地址爲172.177.0.1/16
在主機上可以看到下面的iptables規則:
-A DOCKER -s 172.17.0.2/32 -d 172.17.0.1/32 -i docker0 ...... --dport 5432 -j ACCEPT
-A DOCKER -s 172.17.0.1/32 -d 172.17.0.2/32 -i docker0 ...... --dport 5432 -j ACCEPT

  • 新link:若用戶使用自定義的網絡(user-defined network),則會使用新的link系統
    (1)新舊link系統的一個重要區別是:新的link系統在創建一個link時,並不要求源容器已經創建或者啓動。
使用bridge驅動創建一個自定義網絡isolated_nw,再運行一個容器container1
加入到該網絡,並鏈接另一個容器contianer2(contianer2不存在)

docker network creat isolated_nm
docker run --net=isolated_nm -it --name=container1 --link container2:c2 busybox
docker run --net=isolated_nw -itd --name=container2 busybox

測試:
在container1容器內執行:
ping c2
cat /etc/hosts發現並沒有container2的相關信息。
實際上,Docker是通過DNS解析的方式提供名字和別名的解析,這很好地解決了在
傳統link系統中由於容器重啓造成注入的環境變量更新不及時的問題。

3.Docker與容器安全

  • Docker daemon安全
    (1)其默認是以Unix域套接字的方式來與客戶端進行通信
    (2)Docker提供了TLS(Transport Layer Security)傳輸層安全協議,其安全認證主要是在服務器端設置,客戶端可以對服務端進行驗證。

  • 內核安全
    (1)容器的本質是進程,cgroups的存在就是爲了限制宿主機上不同容器的資源使用量,避免單個容器耗盡宿主機資源而導致其它容器異常
    (2)namespace隔離容器,使容器與容器之間,容器與宿主機之間相互隔離。

  • 磁盤資源限制問題
    Docker通過鏡像層疊的方式來構建其文件系統。
    當需要改寫文件時,把改寫的文件複製到最頂層的讀寫層,其本質還是在宿主機的文件系統的某一目錄下存儲這些信息。
    若一個容器把宿主機上所有的磁盤容量耗盡,節時,其它容器將無法進行文件存儲的操作。

創建虛擬文件系統來對磁盤進行限額
首先創建虛擬文件系統,接着把Docker的rootfs構建於文件系統之上
(1)創建一個4G的文件
dd if=/dev/zeor of=/usr/dick-quota.ext3 count=4096 bs=1M

(2)在磁盤文件上創建文件系統
mkfs -t ext3 -F /usr/disk-quota.ext3

(3)掛載這個文件系統到指定的容器目錄
mount -o loop,rw,usrquota,grpquota /usr/disk-quota.ext3 /path/to/image/top/lever

將創建的虛擬文件系統作爲rootfs最頂層的layer
對於需要共享的數據,可以通過掛載volume方式進行磁盤數據存儲
  • 容器逃逸問題
    在全虛擬化和半虛擬化中,每一個租戶都獨立運行一個內核。
    操作熊虛擬化指的是共享內核,內存,CPU以及磁盤等,所以容器的安全問題特別突出。
    解決辦法:設置黑白名單

  • 容器DoSe攻擊與流量限制問題
    實際上所有容器在共用一張物理網卡,如果在同一宿主機中的某一個容器搶佔了大部分帶寬,將會影響其它容器的使用,例如:大流量的容器內下載程序會影響其它交互式應用的訪問。

  • 通過ulimit限制最大進程數目
    Docker無法使用ulimit來限制fork炸彈問題;
    fork炸彈:以極快的速度創建大量進程,並以此來消耗系統分配給進程的可用空間使得進程表包和,從而系統無法運行新程序



我們使用daemon用戶啓動4個容器,並設置運行的最大進程數爲3
docker run -d -u daemon --ilimit nproc=3 busybox top
docker run -d -u daemon --ilimit nproc=3 busybox top
docker run -d -u daemon --ilimit nproc=3 busybox top
docker run -d -u daemon --ilimit nproc=3 busybox top##失敗,資源不足

我們本想的是:在每個容器裏,用戶最多隻能創建3個進程。現在在使用daemon用戶
在啓動4個容器的時候就失敗了。
nproc調節的是屬於一個用戶UID的最大進程數之和。

<參考:Docker+容器與容器雲>

發佈了569 篇原創文章 · 獲贊 140 · 訪問量 17萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章