Docker 環境 Storage Pool 用完解決方案:resize-device-mapper

問題引出

今日筆者docker下出現詭異問題,在容器中vim編輯文件,保存是,一直提示 「E667: Fsync failed」,並且在同一個宿主機上的容器均有此問題,遂懷疑環境故障,且與存儲相關,後偶然運行命令「docker info」,發現異常信息:git

Containers: 26
Images: 12
Storage Driver: devicemapper
 Pool Name: docker-8:3-6684808-pool
 Pool Blocksize: 65.54 kB
 Backing Filesystem: extfs
 Data file: 
 Metadata file: 
 Data Space Used: 107.4 GB
 Data Space Total: 107.4 GB
 Metadata Space Used: 65.48 MB
 Metadata Space Total: 2.147 GB
 Udev Sync Supported: true
 Library Version: 1.02.89-RHEL6 (2014-09-01)
Execution Driver: native-0.2
Kernel Version: 2.6.32-504.el6.x86_64
Operating System: <unknown>
CPUs: 8
Total Memory: 15.58 GiB
Name: lx-vm01.lianjia.com.vm
ID: T2YG:HFF3:DEZP:5SDU:4B2K:B76F:AUHD:YYVA:DXPS:AVZL:X4WQ:BQFZ
Username: liuyanglong
Registry: [https://index.docker.io/v1/]

其中可看到,Data Space Used 已和 Data Space Total同樣,storage Pool用完,呃。。。。
docker start默認分配的device mapper的storage pool就是100G,它會把你全部的容器存儲到一個 100G 的簡短文件中,而且限制每一個容器最大爲 10GBgithub

如下解決方案來自:http://jpetazzo.github.io/2014/01/29/docker-device-mapper-resize/docker

它的工做原理

要真正理解咱們要作的事情,首先來了解 Device Mapper 插件的工做原理。ubuntu

它是基於 Device Mapper 的「精簡目標」的特性。它其實是目標塊設備的快照,之因此被稱爲「精簡」是由於它容許精簡配置。精簡配置意味着你有一個(但願很大)可用存儲塊的池,接着你能夠從那個池中建立任意大小的塊設備(虛擬磁盤,若有須要);在你實際讀寫後,這些存儲塊將會被標記爲已使用(或者從池中拿走)。vim

這意味着你是能夠超額使用這個池,好比在一個 100GB 的池裏面建立幾千個 10GB 的卷,甚至多是一個 100TB 的卷在一個 1GB 的池裏面。只要你的實際讀寫的塊的容量不大於池的大小,你怎麼作都 OK 。app

除此以外,精簡目標的方式是能夠作快照的。這代表不管什麼時候,你均可以建立一個存在的卷的淺拷貝。在用戶看來,就像你有兩個同樣的卷,它們能夠獨立地各自修改。即便你作了一個完整的拷貝,除了在時間上它是瞬間發生的(即便是很大的卷),它們不會兩次重複使用存儲。額外的存儲只有當其中任何一卷有變化的時候纔會發生,而後精簡目標會從池裏面分配一個存儲快。性能

從本質上來看,「精簡目標」實際上使用了兩個存儲設備:一個(大)的是存儲塊池本身,還有一個小的存儲了一些元數據。這些元數據中包括了卷、快照、以及每一個卷的塊或者快照同存儲池中塊的映射信息。ui

當 Docker 使用 Device Mapper 存儲插件的時候,它會在 /var/lib/docker/devicemapper/devicemapper/data/var/lib/docker/devicemapper/devicemapper/metadata 下建立兩個文件(若是它們不存在)來存儲對應的存儲池和相關的元數據。這很是方便,你不須要作任何安裝部署的工做(你不須要額外的分區來存儲 Docker 容器,或者創建 LVM 或其餘相似的東西)。然而它也有兩個缺點:插件

  • 存儲池會有一個默認 100GB 的容量
  • 它將會被稀疏文件所支持。從磁盤的使用效率的觀點來看,這還不錯的(就像在精簡池中的卷,它一開始是小的,只有當實際須要寫的時候纔會使用磁盤的存儲塊)。可是從性能的角度來看就不那麼好了,由於 VFS 增長了一些額外的負擔,特別是"第一次寫的時候"。

在瞭解如何調整容器的大小以前,咱們來試試看如何給池增長更多空間。3d

咱們須要一個更大的池

警告:下面的操做會刪除你全部的容器和鏡像,確保你已經把以前的數據作了備份!

記住上面說過的,當數據和元類信息文件不存在的時候 Docker 會建立它們,因此解決方案很是簡單:在啓動它們以前,在 Docker 裏建立這些文件!

中止 Docker 守護進程,由於咱們將要從新設置咱們的存儲插件,若是咱們在運行的時候移除文件,那麼糟糕的事情就將發生。

  1. 擦去 /var/lib/docker。(警告:正如前面提到的,這個操做會把你全部的容器和鏡像都刪除掉。)

  2. 建立存儲目錄: mkdir -p /var/lib/docker/devicemapper/devicemapper

  3. 建立你的池: dd if=/dev/zero of=/var/lib/docker/devicemapper/devicemapper/data bs=1G count=0 seek=250 ,建立一個 250G 的稀疏文件。若是你指定 bs=1G count=250(不使用 seek 選項),那麼它會建立一個普通文件(而不是一個稀疏文件)。

  4. 重啓 Docker 守護進程。提示:在默認狀況下,若是你有 AUFS 的支持, Docker 會使用它;因此若是你要強制使用 Device Mapper 的插件,須要在啓動 Docker 的命令中增長 -s devicemapper 的選項。

  5. 使用 docker info 來檢查 Data Space Total 的值是否正確。

咱們須要一個更快的池

警告:下面的操做也會刪除你全部的容器和鏡像。確保把你重要的鏡像保存在 registry 中,保存你容器裏面的重要數據。

要得到一個更快速的池,最簡單的辦法就是使用一個真實的設備而不是一個基於文件的循環設備。過程幾乎同樣。假設你有一個徹底空的硬盤, /dev/sdb,你想把它徹底用於容器的存儲,你能夠這樣作:

  1. 中止 Docker 守護進程
  2. 移除 /var/lib/docker (似曾相識,對麼?)
  3. 建立一個存儲目錄: mkdir -p /var/lib/docker/devicemapper/devicemapper
  4. 在目錄下建立一個數據軟連接,指向設備:

    ln -s /dev/sdb /var/lib/docker/devicemapper/devicemapper/data
  5. 重啓 Docker
  6. 使用 docker info 來檢查 Data Space Total 的值是否正確

使用 RAID 和 LVM

若是你但願合併多塊類似的磁盤,可使用 RADID10 軟件,這個會經過連接到 /dev/md 而實現。另一個很是好的選擇是把你的磁盤(或者RAID磁盤陣列)放到 LVM 的物理卷中,而且建立兩個邏輯卷:一個是數據,一個是元數據。對於元數據池的最佳的大小我沒有什麼特別的建議,不過佔數據池的 1% 看起來不錯。

就像前面同樣,中止 Docker ,移除它的數據目錄,而後建立一個指向 /dev/mapper 設備的符號連接,而後重啓 Docker 。

ln -s /dev/mapper/vg_dockerdev-LogVol01 /var/lib/docker/devicemapper/devicemapper/data

以後重啓docker便可。

擴容容器

默認來講,若是你使用 Device Mapper 的存儲插件,全部的鏡像和容器是從一個初始 10G 的文件系統中建立的。讓咱們來看看如何從一個更大的文件系統中建立一個容器。

首先,咱們用 Ubuntu 的鏡像來建立咱們的容器。咱們不須要在這個容器裏運行任何東西,只須要這個文件(或者關聯的文件系統)存在。爲了演示,咱們會在這個容器裏運行 df ,來看一下根文件系統的大小。

$ docker run -d ubuntu df -h /
4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603

因爲須要修改 Device Mapper 管理中的一些卷的信息,咱們如今用 root 的身份來運行一些命令。全部以#開頭的命令都必須以 root 身份來執行。只要能訪問 Docker 的 Socket 服務,你也能夠用普通用戶的身份來執行其餘的命令(以$開頭)。

讓咱們看一下 /dev/mapper ,那裏應該有一個對應容器文件系統的符號連接,以 docker-X:Y-Z- 開頭:

ls -l /dev/mapper/docker-*-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603
lrwxrwxrwx 1 root root 7 Jan 31 21:04 /dev/mapper/docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603 -> ../dm-8

注意記住那個全名,咱們將來會用到。

首先讓咱們來看一下當前卷的信息表:

dmsetup table docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603
0 20971520 thin 254:0 7

第二個數字是設備的大小,表示有多少個 512-bytes 的扇區. 這個值略高於 10GB 的大小。

咱們來計算一下一個 42GB 的卷鬚要多少扇區,

$ echo $((42*1024*1024*1024/512))
88080384

精簡快照目標的一個神奇的特色是它不會限制卷的大小。當你建立它的時候,一個精簡的卷使用0個塊,當你開始往塊裏面寫入的時候,它們會從共用的塊池中進行分配。你能夠寫0個塊,或者是10億個塊,這個和精簡快照目標不要緊。文件系統的大小隻和 Device Mapper 表有關係。

以爲困惑?不要擔憂。咱們只是須要裝載一個新的表,這個徹底和以前的是同樣的,可是有更多的扇區。僅此而已。

舊錶是 0 20971520 thin 254:0 7 。咱們會改變第二個數字,要很是當心保持其餘的值不變。你的卷可能不是 7 ,因此要使用正確的值!

這樣操做:

# echo 0 88080384 thin 254:0 7 | dmsetup load docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603

如今若是咱們再次檢查表的信息,步驟和前面同樣。首先使用下面的命令激活新表:

# dmsetup resume docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603

執行完命令後,再次檢查一下表的信息,發現它會使用新的扇區數量。

咱們已經調整了塊設備的大小,可是咱們仍然須要調整文件系統的大小,咱們使用 resize2fs 來操做:

# resize2fs /dev/mapper/docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603
resize2fs 1.42.5 (29-Jul-2012)
Filesystem at /dev/mapper/docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603 is mounted on /var/lib/docker/devicemapper/mnt/4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603; on-line resizing required
old_desc_blocks = 1, new_desc_blocks = 3
The filesystem on /dev/mapper/docker-0:37-1471009-4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603 is now 11010048 blocks long

做爲一個可選步驟,咱們會重啓容器,檢查一下咱們的確有了正確大小的空閒空間:

$ docker start 4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603
$ docker logs 4ab0bdde0a0dd663d35993e401055ee0a66c63892ba960680b3386938bda3603
df: Warning: cannot read table of mounted file systems: No such file or directory
Filesystem      Size  Used Avail Use% Mounted on
-               9.8G  164M  9.1G   2% /
df: Warning: cannot read table of mounted file systems: No such file or directory
Filesystem      Size  Used Avail Use% Mounted on
-                42G  172M   40G   1% /

想把整個過程自動化起來?固然沒問題。

CID=$(docker run -d ubuntu df -h /)
DEV=$(basename $(echo /dev/mapper/docker-*-$CID))
dmsetup table $DEV | sed "s/0 [0-9]* thin/0 $((42*1024*1024*1024/512)) thin/" | dmsetup load $DEV
dmsetup resume $DEV
resize2fs /dev/mapper/$DEV
docker start $CID
docker logs $CID

擴容鏡像

不幸的是,當前版本的 Docker 不能讓咱們很方便地擴容鏡像。你能夠把鏡像對應的塊設備進行擴容,而後從它來建立一個容器,可是新的容器不會有正確的大小。

一樣,若是你提交了一個很大的容器,最後生成的鏡像也不會很大(這是由 Docker 爲鏡像準備文件系統的方法形成的)。

這意味着若是一個容器真的超過了 10GB ,在不使用一些其餘的小技巧的狀況下,你無法正確的把它提交爲一個鏡像。

總結

Docker 未來確定會提供一些更好的方法來擴容容器,所需的代碼變更是很小的。管理一個精簡的池和對應的元信息比較複雜(由於這個須要不少不一樣的操做流程,以及一個潛在的數據遷移。鑑於移除了全部的東西來構件新的池,也就沒有在本文說起),可是咱們今天提到的一些解決方案相信已經對你有所幫助。

相關文章
相關標籤/搜索