2017年2月4日 星期六

在 zfs 上面的 lxc 裡面跑 docker

zfs + lxc + docker 我在安裝 proxmox 4.2 時, 檔案系統選用 zfs, 裝好之後想要在 ubuntu lxc 容器裡面跑 docker。 沒想到這是一個棘手的組合, 在過去這幾個月裡, 燒掉了我好幾個週末跟半個年假 (所以我貼文的頻率很低)。 這篇文章是給 linux 熟手看的 -- 筆記的成份多於教學的成份; 只在 proxmox host 底下測試, 並且省略很多解釋及較單純的細節。

一、 快放棄前的心得

灌在 zfs 上面的 host 可以跑 docker [ 官網 問答] 也可以跑 lxc (proxmox 4.* 就是這樣); 在 lxc 裡面也可以跑 docker 。 (詳第三節)

但是當這三件事情疊在一起時... 撞牆碰壁很多次之後, 終於發現: 若要在 zfs 的 host 裡面的 lxc 裡面跑 docker, 就必須把一個完整的 file system 類型的 zfs dataset 掛在 lxc 的 /var/lib/docker 。 所以必須想辦法把 host 上的某一個 zfs file system 分享給 lxc。 如果是分享普通的目錄, 設定 lxc.mount.entry 就可以了: 問題是我們要分享的東西, 不是普通的目錄, 而是一個 zfs file system。 到最後看到 這一串討論 終於死心了: 因為 zfs 是整體管理的, 所以不可能只把 zfs pool 裡面的一個 dataset 切給容器去管理。 Bye Bye, 玩完了!

其實為了做正事, 早在一兩個月前我就用 kvm 另外架了一部 archlinux, 在上面直接跑 docker, 早就不打算再跟它纆鬥了。 只是不時會手癢再試、 再失敗。

都已經開始寫失敗文, 就在絕望放棄之際, 在一個失眠的夜裡突然想到: 在 (proxmox host 的) zfs pool 裡面建一個 volume, 把它切割、 格式化成 ext4, 再把它分享給 lxc, 那麼 docker 看到的就是單純的 ext4 了, 這樣可以嗎? 總之在這個三角關係裡面, 就是要有一方被矇在鼓裡、 不知道事實的真相, 這樣才玩得下去啊 :-)

二、 底層環境設定

  1. 我在安裝 proxmox 4.2-2/725d76f0 時選用 zfs。
  2. 創建 container 時, 採用的 template 是 ubuntu-16.04-standard_16.04-1_amd64.tar.gz 。 以下假設這部 lxc 的名稱/代號為 $LXC。

建議暫時先不急著在 $LXC 裡面安裝 docker。

三、 脫掉脫掉, 安全防護通通脫掉

想要在 lxc 裡面跑 docker, 就必須把 host 的一些權限開放給 lxc。 如果 「能動」 的考量勝過 「安全防護」, 那麼 這個 github issue 是我所找到最簡單清楚可行的經驗分享。 [更正過!] 在 $LXC 的設定檔 (proxmox: /etc/pve/lxc/$LXC.conf ; 其他一般狀況: /var/lib/lxc/$HOST/config ) 的最後面加上這幾句:

lxc.aa_profile = unconfined
lxc.cgroup.devices.allow = a
lxc.cap.drop =

lxc.hook.mount =
lxc.hook.post-stop =

其中後面兩句的目的是要取消 /usr/share/lxc/config/common.conf.d/00-lxcfs.conf 的效果。

如何檢查確認: 待補。

對, 叫 apparmor 大門大開不是個好主意; 不過 如何除錯 apparmor 太囉嗦了, 先求能動; 安全等以後再來傷腦筋吧。 反正我的 IP 不夠用, 我的這一部 $LXC 躲在 NAT 後面, 所以暫時先隨便沒關係啦 :-)

如果不涉及 zfs, 而是在一般 (例如採用 ext4) 的 host 上, 據說這樣也就夠了。

四、 建立一個 ext4

在 zfs 裡面建一個 volume 類型的 dataset、 切割、 格式化成 ext4、 寫入 fstab、 掛載起來:

zfs create -V 8G rpool/data/docker-payload
# 更正: 建議不要切分割,
# 直接在整個 volume 上面製作檔案系統,
# 這樣將來若有必要才能夠直接 resize 擴大容量
mkfs -t ext4 /dev/rpool/data/docker-payload
mkdir /mnt/docker-payload
echo '/dev/rpool/data/docker-payload /mnt/docker-payload ext4 defaults 0 0' >> /etc/fstab
mount -a

五、 把這個額外的 ext4 檔案系統也配給 $LXC

照著 12 設定, 也就是在 $LXC 的設定檔最後面加上這兩句:

lxc.mount.entry = /mnt/docker-payload var/lib/docker none bind 0 0
lxc.logfile = /root/lxc-start.log

改完後, 要在 proxmox host 底下事先手動掛載 /mnt/docker-payload、 在 $LXC 的目錄裡面建一個 var/lib/docker 子目錄, 然後重新啟動 $LXC、 進入 $LXC, 檢查有沒有多一個 file system 可用。 注意:

  1. var/lib/docker 要用相對路徑!
  2. 這裡我卡了好久; 如果出錯, 一定要 設定 lxc.logfile 才看得到錯誤訊息。
  3. 如果看到你設定的路徑出現在錯誤訊息裡這一串 /usr/lib/x86_64-linux-gnu/lxc/rootfs/ 的後面, 而不是在你的 lxc 容器的 rootfs 底下, 不必困惑, 那只是因為 這是 pivot 之前的路徑
  4. 在 $LXC 裡面看到的應該是類似 /dev/zd64 這樣的裝置才對; 如果看到類似 rpool/ROOT/pve-1 (你的 zfs 的主要 pool!) 那就錯了 -- 可能在啟動 $LXC 之前, 在 host 裡忘記掛載 /mnt/docker-payload?

六、 安裝 docker

  1. 在 proxmox host 上, 如果 lsmod | grep aufs 沒有東西, 就要先 modprobe aufs 並且在 echo aufs >> /etc/modules
  2. 在 $LXC 裡面安裝 docker 之前, 預先建立 設定檔echo '{ "storage-driver": "aufs" }' > /etc/docker/daemon.json 因為平常 docker 會自動偵測; 但現在我們的 $LXC 的 root fs 類型是 zfs, 而 /var/lib/docker 卻是 ext4, 所以如果沒告訴它, 它會猜錯, 然後失敗。
  3. 在 $LXC 裡面 安裝 docker。 我不敢去動 host 的 kernel, 所以省略 linux-image-extra-*。 反正 proxmox 應該早就已經幫我們把虛擬環境需要的東西都準備好了吧? 後面我照著 Set up the repository 的 Docker CE 做。

因為 ubuntu 遇到服務類的套件會一直做到啟動成功才算安裝成功; 但是整個預備設定過程想要一步到位恐怕不太容易。 一旦 (或是像我一樣...不斷) 出錯, docker 套件就進入卡卡無法安裝無法移除的狀態。 我最近才學會這個用法: 可以用 apt-get purge docker.io 把它砍掉, 然後就可以重裝一次、 再改設定、 一試再試試不成, 再試一下...

望著 docker run hello-world 完美的輸出, 有一種打敗魔王的快感...

3 則留言:

  1. 老師假如用ZFS切成volume再格式化成ext4這樣在效能是會不會有影響?當然ZFS上的snapshot真的好好用喔!!我之前用ZFS切出volume給DRBD當區塊裝置又在上面用LVM 好像最後的速度好像會受限於LVM??

    回覆刪除
  2. 哇 你的 zfs 應該玩得比我多很多吧。 有你自己寫的或別人寫的教學文可以分享一下嗎? 對啊, 越多層在做相同的事, 效率自然就越低囉。 這裡是為了避免讓 docker 看到 zfs 不得不多加一層 ext4。 至於你的狀況, 如果沒有要用 drbd 那就連 lvm 也可以省下來了。 所以換作我的話, 會搜尋 zfs high availability 看能不能只補上原始 zfs 欠缺的功能就好。

    回覆刪除
  3. http://acidrop.biz/wp/?p=559 我是proxmox 3.4剛出來就馬上用上它的cluster架構!現在到出到4.4了!我原本有效能上的問題,後來改程式去了!效能問題也就解了~只是我有試單純zfs pool效能真的有比較快!DRBD我還不想放棄可是9以後可能要收費了!打算用ZFS Remote Replication去取代!FB上也有滿多人用 PVE的https://www.facebook.com/groups/pve.tw/

    回覆刪除