目錄
Docker 容器互聯
1 基於Docker Volum的容器互聯
1.1 Docker的文件存儲
docker的文件系統是copy on write方式的, 文件是一層層往上疊加的, 最下的一層是隻讀的, 要修改的時候會複製這層只讀的覆蓋在只讀的上面作爲一層可寫的, 這個時候原來的只讀文件依然是存在的這是看不到了
docker 中鏡像的存儲就是使用的上述方式,分層結構如下
上圖Docker的容器就是通過只讀的鏡像上面覆蓋的可寫層, 要想把這個可寫層持久化, 就要用到之前的講的 docker commit 了
但是鏡像的層次又可以共享:
鏡像是分層疊加的, 頂層的鏡像依賴於底層的鏡像, 容器是通過鏡像 Copy On Write 一個可寫層, 這個可寫層 Commit 就完成了持久化又成了一個新的鏡像.
Docker鏡像的存儲位置:
docker的鏡像是分層的,一個鏡像往往是多個鏡像分層疊加出來的, 鏡像的分層信息是存儲在 Docker Graph 裏面
這裏記錄了鏡像有些那些層, 每一層的父層和大小等信息
其中 GrapDB 存儲的是分層結構 , Repository 裏面存儲是鏡像的信息, 兩者結合得到鏡像的分層信息
但是這些裏面存儲都是鏡像的信息, 並不是鏡像的實際存儲位置,
鏡像的二進制文件存儲位置是. /var/lib/docker/<storage-driver>
<storage-driver> 是 Docker Graph Driver , 不同的 Docker Graph Driver 會存儲在不同的目錄, 比如本機採用的是 overlay2
Docker Graph Driver 是Docker 使用來管理存儲鏡像每層內容及可讀寫的容器的驅動, 目前主要有 DeviceMapper、AUFS、Overlay、Overlay2、Btrfs、ZFS 等,不同的存儲驅動實現方式有差異,鏡像組織形式可能也稍有不同,但都採用棧式存儲,並採用 Copy-on-Write策略。且存儲驅動採用熱插拔架構,可動態調整。
Graph Driver 就類似於Java連接數據庫的 JDBC, 只不過這個驅動是用於 docker Deamon 來對鏡像個容器進行讀寫.
選擇策略:
- 若內核支持多種存儲驅動,且沒有顯式配置,Docker 會根據它內部設置的優先級來選擇。優先級爲 AUFS > Btrfs/ZFS > Overlay2 > Overlay > DeviceMapper。若使用 DeviceMapper 的話,在生產環境,一定要選擇 direct-lvm, loopback-lvm 性能非常差。
- 選擇會受限於 Docker 版本、操作系統、系統版本等。例如,AUFS 只能用於 Ubuntu 或 Debian 系統,Btrfs 只能用於 SLES (SUSE Linux Enterprise Server, 僅 Docker EE 支持)。
- 有些存儲驅動依賴於後端的文件系統。例如,Btrfs 只能運行於後端文件系統 Btrfs 上。
- 不同的存儲驅動在不同的應用場景下性能不同。例如,AUFS、Overlay、Overlay2 操作在文件級別,內存使用相對更高效,但大文件讀寫時,容器層會變得很大;DeviceMapper、Btrfs、ZFS 操作在塊級別,適合工作在寫負載高的場景;容器層數多,且寫小文件頻繁時,Overlay 效率比 Overlay2 更高;Btrfs、ZFS 更耗內存。
1.2Docker Volume
Docker Copy on Write
和剛剛說的一樣, Docker 的寫操作主要分爲兩種, 一個是 塊級別的, 一個是文件級別的, 塊級別的在修改文件的時候會把修改部分所在的塊 copy 到 write層用於寫, 文件級別則是直接複製真個文件到 write 層, 所以塊級別能夠節省空間, 但是塊級別 copy的操作次數多, 文件級別直接copy 文件就會是的容器變大, 但是直接copy 文件就會大大減少copy操作的次數.
以上操作對容器中中需要頻繁讀寫的大文件就很不利, 塊級別的 COW 操作太頻繁,效率低, 文件級別的又會使得容器變得很大, 比如mysql數據庫的數據庫存儲文件.
這個時候就引入了 Volume, Volme 可以將宿主機的文件映射到容器直接操作, 不必經過 COW, 常見的高頻寫文件有日誌系統和數據存儲文件.
Vloume使用是通過docker run 的 -v 參數來實現的
[docker@VM_121_116_centos ~]$ docker run -it -p 6379:6379 -v ~/redis/date:/data redis /bin/bash
root@b385ca73cad3:/data# ls
appendonly.aof root tomcat
root@b385ca73cad3:/data# mkdir testdir
root@b385ca73cad3:/data# exit
exit
[docker@VM_121_116_centos ~]$ ll
total 580364
drwxrwxr-x 2 docker docker 4096 Mar 6 22:00 dockerFile
-rw-rw-r-- 1 docker docker 594276824 Mar 6 17:31 ideaIU-191.6014.8.exe
drwxr-xr-x 3 root root 4096 Mar 19 21:26 redis
[docker@VM_121_116_centos ~]$ cd redis/date/
[docker@VM_121_116_centos date]$ ll
total 16
-rw-r--r-- 1 polkitd ssh_keys 443 Mar 19 21:27 appendonly.aof
-rw-r--r-- 1 polkitd ssh_keys 249 Mar 19 21:27 root
drwxr-xr-x 2 root root 4096 Mar 19 21:32 testdir
-rw-r--r-- 1 polkitd ssh_keys 259 Mar 19 21:27 tomcat
[docker@VM_121_116_centos date]$
如命令所示 使用 -v 把宿主機的 ~/redis/date 掛載到 容器的 /data , 之後在容器的 /data 建立一個 testdir 文件夾. 之後在宿主機的 ~/redis/data 裏面看見了剛剛建的文件夾. 如不指定宿主機目錄, 那麼docker就會在docker得volume目錄中創建一個目錄掛載到容器內, 當容器刪除的時候, 這個目錄也就隨之刪除了.
這樣容器的 /data 目錄就能避免大量的 COW 操作了, 直接進行讀寫.
在次看看容器信息 使用 docker inspect
[docker@VM_121_116_centos date]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b385ca73cad3 redis "docker-entrypoint..." 8 minutes ago Exited (0) 7 minutes ago sad_murdock
e8abf8c09a26 mysql:5.6 "docker-entrypoint..." 3 weeks ago Exited (0) 2 weeks ago mysql5.6
[docker@VM_121_116_centos date]$ docker inspect b385ca73cad3
[
{
"Id": "b385ca73cad3607f78fc87214dadf23435eebeaf5ee5551a17f160a432b08264",
"Created": "2019-03-19T13:31:55.437267785Z",
"Path": "docker-entrypoint.sh",
"Args": [
"/bin/bash"
],
"State": {
"Status": "exited",
"Running": false,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 0,
"ExitCode": 0,
"Error": "",
"StartedAt": "2019-03-19T13:31:55.783433554Z",
"FinishedAt": "2019-03-19T13:32:42.591792526Z"
},
====================省略==================================
"GraphDriver": {
"Name": "overlay2",
"Data": {
"LowerDir": "/var/lib/docker/overlay2/f2806e52d83beca84bd0bb5a137a4a70974fcecdede03e5bc1b3f5c3711619c9-init/diff:/var/lib/docker/overlay2/61d911e09963bc0e77b7020b7bdbf798a5c35dbd90514c810f3aebb6875610f7/diff:/var/lib/docker/overlay2/fdddcb2b85492183f28660470d17c554e5bdb3a01cc8089e9c2112da7eefb4d5/diff:/var/lib/docker/overlay2/9476ee9fd3b0ccad81bdfa469c4188bda49230dff3598e9018cb763a0ba683c1/diff:/var/lib/docker/overlay2/65cfb70d00faeb7477439d635dc0a6f78f4f3d7bb6c5d28b29c6428744a58ee3/diff:/var/lib/docker/overlay2/55e18ec0a5caafed7907512ee5f2dd3e4db5cd701a1adfe5f4fe3bec2911e922/diff:/var/lib/docker/overlay2/ed7f62d6e611f876fb9eb55b06653b23198a915971fd1efdffbb61c33626f759/diff",
"MergedDir": "/var/lib/docker/overlay2/f2806e52d83beca84bd0bb5a137a4a70974fcecdede03e5bc1b3f5c3711619c9/merged",
"UpperDir": "/var/lib/docker/overlay2/f2806e52d83beca84bd0bb5a137a4a70974fcecdede03e5bc1b3f5c3711619c9/diff",
"WorkDir": "/var/lib/docker/overlay2/f2806e52d83beca84bd0bb5a137a4a70974fcecdede03e5bc1b3f5c3711619c9/work"
}
},
"Mounts": [
{
"Type": "bind",
"Source": "/home/docker/redis/date",
"Destination": "/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
=============================省略================================
}
}
}
]
[docker@VM_121_116_centos date]$
可以看到 將 /home/docker/redis/date 掛載到 /data
講了這麼多怎麼基於 Volume 互聯呢? 我們剛剛把宿主機一個指定的目錄掛載到了容器內部, 那麼要是我們把這個目錄掛載到多個容器有什麼效果呢?
我們再次創建一個容器還是掛在這個目錄
[docker@VM_121_116_centos date]$ docker run -it -p 26379:6379 -v ~/redis/date:/data --name redistwo redis /bin/bash
root@cf0ca7e833bb:/data# ls
appendonly.aof root testdir tomcat
root@cf0ca7e833bb:/data# mkdir testdirtwo
root@cf0ca7e833bb:/data# exit
exit
爲了區別剛剛那個, 我們加了 --name 參數, 進入容器發現有剛剛的建的 testdir, 我們再次建一個 testdirtwo, 再次回到上個容器看看
[docker@VM_121_116_centos date]$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
cf0ca7e833bb redis "docker-entrypoint..." 3 minutes ago Exited (0) 2 minutes ago redistwo
b385ca73cad3 redis "docker-entrypoint..." 26 minutes ago Exited (0) 25 minutes ago sad_murdock
e8abf8c09a26 mysql:5.6 "docker-entrypoint..." 3 weeks ago Exited (0) 2 weeks ago mysql5.6
[docker@VM_121_116_centos date]$ docker start b385ca73cad3
b385ca73cad3
[docker@VM_121_116_centos date]$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b385ca73cad3 redis "docker-entrypoint..." 28 minutes ago Up 7 seconds 0.0.0.0:6379->6379/tcp sad_murdock
[docker@VM_121_116_centos date]$ docker exec -it b385ca73cad3 /bin/bash
root@b385ca73cad3:/data# ls
appendonly.aof root testdir testdirtwo tomcat
root@b385ca73cad3:/data#
有第二個容器創建的文件夾, 這樣就容器之間的互聯
對於跨越宿主機的容器互聯, 可是在linux採取共享目錄等技術, 或者分佈式文件系統
有 iscsi nfs ceph 等
1.3 數據容器共享解決方案(volumes-form)
將本地目錄掛載到容器, 在 DockerFile 中是不推薦使用的, 因爲這樣的容器就不可移植了.
就要不指定宿主機的目錄, 使用容器存儲位置的目錄.
如下:
[docker@VM_121_116_centos date]$ docker run -it -p 6379:6379 -v /data --name oneredis redis /bin/bash
root@6636fb7d293c:/data# mkdir redis
root@6636fb7d293c:/data# ls
redis
root@6636fb7d293c:/data# exit
exit
[docker@VM_121_116_centos date]$ docker inspect oneredis
查看掛載信息結果: 將 /var/lib/docker/volumes/b4fca8f9587e906919b5c2110b5e33643315dcc73d24ca177c8122072b44ef0a/_data 掛載到了 /data
我們沒有實現約定好共享目錄,就不能體檢知道掛載的目錄, 其他容器怎麼從這裏讀取呢?
這個時候要用 --volumes-from
示例:
[docker@VM_121_116_centos date]$ docker run -it -p 26379:6379 --volumes-from=oneredis --name tworedis redis /bin/bash
root@bb1d9cd3a282:/data# ls
redis
root@bb1d9cd3a282:/data# exit
exit
能看到第一個容器創建的目錄
再看看容器掛載
和第一個一模一樣
這樣就解決的移植性的問題
一般的解決方案就是 建議一個 數據共享的管理容器, 其他業務容器 volumes-from 數據管理容器, 這樣實現業務容器互聯