Btrfs文件系統之subvolume與snapshot

對於大部分文件系統來講,在磁盤上建立好文件系統,而後再掛載到系統中去就完事了。但對於Btrfs來講,除了在格式化和掛載的時候指定不一樣的參數外,還支持不少其餘的功能,好比管理多塊硬盤,支持LVM和RAID等,具體的能夠參考它的官方文檔或者Linux下常見文件系統對比php

Btrfs是Linux下你們公認的將會替代ext4的下一代文件系統,功能很是強大。本篇不會介紹Btrfs的原理,也不會介紹Btrfs的全部功能,只是挑了其中的subvolume和snapshot這兩個特性來進行介紹linux

本篇全部例子都在ubuntu-server-x86_64 16.04下執行經過git

準備環境

先建立一個虛擬的硬盤,而後將它格式化成Btrfs,最後將它掛載到目錄/mnt/btrfsubuntu

#爲了簡單起見,這裏只使用一塊硬盤來作測試(Btrfs能夠管理多塊硬盤或分區)。
#新建一個文件,用來虛擬一塊硬盤
dev@ubuntu:~$ fallocate -l 512M /tmp/btrfs.img

#在上面建立Btrfs文件系統
dev@ubuntu:~$ mkfs.btrfs /tmp/btrfs.img
btrfs-progs v4.4
See http://btrfs.wiki.kernel.org for more information.

Label:              (null)
UUID:               fd5efcd3-adc2-406b-a684-e6c87dde99a1
Node size:          16384
Sector size:        4096
Filesystem size:    512.00MiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP              40.00MiB
  System:           DUP              12.00MiB
SSD detected:       no
Incompat features:  extref, skinny-metadata
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1   512.00MiB  /tmp/btrfs.img

#建立文件夾並掛載
dev@ubuntu:~$ sudo mkdir /mnt/btrfs
dev@ubuntu:~$ sudo mount /tmp/btrfs.img /mnt/btrfs

#修改權限,這樣後面的部分操做就再也不須要sudo
dev@ubuntu:~$ sudo chmod 777 /mnt/btrfs

subvolume

能夠把subvolume理解爲一個虛擬的設備,由Btrfs管理,建立好了以後就自動掛載到了Btrfs文件系統的一個目錄上,因此咱們在文件系統裏面看到的subvolume就是一個目錄,但它是一個特殊的目錄,具備掛載點的一些屬性。segmentfault

新建立的Btrfs文件系統會建立一個路徑爲「/」的默認subvolume,即root subvolume,其ID爲5(別名爲0),這是一個ID和目錄都預設好的subvolume。bash

#這裏從mount的參數「subvolid=5,subvol=/」就能夠看出來,
#默認的root subvolume的id爲5,路徑爲「/」
dev@debian:/mnt/btrfs$ mount|grep btrfs
/dev/loop1 on /mnt/btrfs type btrfs (rw,relatime,space_cache,subvolid=5,subvol=/)

建立subvolume

這裏咱們將會利用Btrfs提供的工具建立兩個新subvolume和兩個文件夾,來看看他們之間的差異工具

dev@ubuntu:~$ cd /mnt/btrfs
#btrfs命令是Btrfs提供的應用層工具,能夠用來管理Btrfs

#這裏依次建立兩個subvolume,建立完成以後會自動在當前目錄下生成兩個目錄
dev@ubuntu:/mnt/btrfs$ btrfs subvolume create sub1
Create subvolume './sub1'
dev@ubuntu:/mnt/btrfs$ btrfs subvolume create sub2
Create subvolume './sub2'
#建立兩個文件夾
dev@ubuntu:/mnt/btrfs$ mkdir dir1 dir2

#在sub一、sub2和dir1中分別建立一個文件
dev@ubuntu:/mnt/btrfs$ touch dir1/dir1-01.txt
dev@ubuntu:/mnt/btrfs$ touch sub1/sub1-01.txt
dev@ubuntu:/mnt/btrfs$ touch sub2/sub2-01.txt

#最後看看目錄結構,是否是看起來sub1和dir1沒什麼區別?
dev@ubuntu:/mnt/btrfs$ tree
.
├── dir1
│   └── dir1-01.txt
├── dir2
├── sub1
│   └── sub1-01.txt
└── sub2
    └── sub2-01.txt

不過因爲每一個subvolume都是一個單獨的虛擬設備,因此沒法跨subvolume創建硬連接oop

#雖然sub1和sub2屬於相同的Btrfs文件系統,而且在一塊物理硬盤上
#但因爲他們屬於不一樣的subvolume,因此在它們之間創建硬連接失敗
dev@ubuntu:/mnt/btrfs$ ln ./sub1/sub1-01.txt ./sub2/
ln: failed to create hard link './sub2/sub1-01.txt' => './sub1/sub1-01.txt': Invalid cross-device link

刪除subvolume

subvolume不能用rm來刪除,只能經過btrfs命令來刪除測試

#普通的目錄經過rm就能夠被刪除
dev@ubuntu:/mnt/btrfs$ rm -r dir2

#經過rm命令刪除subvolume失敗
dev@ubuntu:/mnt/btrfs$ sudo rm -r sub2
rm: cannot remove 'sub2': Operation not permitted

#須要經過btrfs命令才能刪除
#刪除sub2成功(就算subvolume裏面有文件也能被刪除)
dev@ubuntu:/mnt/btrfs$ sudo btrfs subvolume del sub2
Delete subvolume (no-commit): '/mnt/btrfs/sub2'
dev@ubuntu:/mnt/btrfs$ tree
.
├── dir1
│   └── dir1-01.txt
└── sub1
    └── sub1-01.txt

上面刪除的時候能夠看到這樣的提示: Delete subvolume (no-commit),表示subvolume被刪除了,但沒有提交,意思是在內存裏面生效了,但磁盤上的內容還沒刪,意味着若是這個時候系統crash掉,這個subvolume有可能還會回來。btrfs這樣作的好處是刪除速度很快,不會影響使用,缺點是有可能在後臺commit的過程當中系統掛掉,致使commit失敗。spa

爲了確保subvolume裏的數據被真正的從磁盤上移除掉,能夠在刪除subvolume的時候指定-c參數,這樣btrfs命令會等提交完成以後再返回

dev@ubuntu:/mnt/btrfs$ sudo btrfs subvolume del -c sub2
Delete subvolume (commit): '/mnt/btrfs/sub2'

掛載subvolume

subvolume能夠直接經過mount命令掛載,和掛載其它設備沒什麼區別,具體的掛載參數請參考文檔

#建立一個用於掛載點的目錄
dev@ubuntu:/mnt/btrfs$ sudo mkdir /mnt/sub1

#先查看待掛載的subvolume的id
dev@debian:/mnt/btrfs$ sudo btrfs subvolume list /mnt/btrfs/
ID 256 gen 9 top level 5 path sub1

#經過-o參數來指定要掛載的subvolume的ID
#經過路徑來掛載也是同樣的效果:sudo mount -o subvol=/sub1 /tmp/btrfs.img /mnt/sub1/
dev@debian:/mnt/btrfs$ sudo mount -o subvolid=256 /tmp/btrfs.img /mnt/sub1/
dev@debian:/mnt/btrfs$ tree /mnt/sub1/
/mnt/sub1/
└── sub1-01.txt

設置subvolume只讀

subvolume能夠被設置成只讀狀態

#經過btrfs property能夠查看和修改subvolume的只讀狀態
#默認狀況下,subvolume的只讀屬性爲false,即容許寫
dev@ubuntu:/mnt/btrfs$ btrfs property get -ts ./sub1/
ro=false

#將sub1的只讀屬性設置成true
dev@ubuntu:/mnt/btrfs$ btrfs property set -ts ./sub1/ ro true
dev@ubuntu:/mnt/btrfs$ btrfs property get -ts ./sub1
ro=true

#寫文件失敗,提示文件系統只讀
dev@ubuntu:/mnt/btrfs$ touch ./sub1/sub1-02.txt
touch: cannot touch './sub1/sub1-02.txt': Read-only file system

#將sub1的狀態改回去,以避免影響後續測試
dev@ubuntu:/mnt/btrfs$ btrfs property set -ts ./sub1/ ro false

snapshot

能夠在subvolume的基礎上製做快照,幾點須要注意:

  • 默認狀況下subvolume的快照是可寫的

  • 快照是特殊的subvolume,具備subvolume的屬性。因此快照也能夠經過mount掛載,也能夠經過btrfs property命令設置只讀屬性

  • 因爲快照的本質就是一個subvolume,因此能夠在快照上面再作快照

在subvolume上作了快照後,subvolume和快照就會共享全部的文件,只有當文件更新的時候,纔會觸發COW(copy on write),因此建立快照很快,基本不花時間,而且Btrfs的COW機制很高效,就算多個快照共享一個文件,更新這個文件也和更新一個普通文件差很少的速度。

若是用過git的話,就能很容易理解Btrfs裏的快照,能夠把subvolume理解爲git裏面的master分支,而快照就是從master checkout出來的新分支,因而快照跟git裏的分支有相似的特色:

  • 建立快照幾乎沒有開銷

  • 能夠在快照的基礎上再建立快照

  • 當前快照裏面的修改不會影響其它快照

  • 快照能夠被刪除

固然subvolume也能夠像git裏的master同樣被刪除。

建立快照

#在root subvolume的基礎上建立一個快照
#默認狀況下快照是可寫的,若是要建立只讀快照,須要加上-r參數
dev@debian:/mnt/btrfs$ sudo btrfs subvolume snapshot ./ ./snap-root
Create a snapshot of './' in './snap-root'

#建立完成後,能夠看到咱們已經有了兩個subvolume
dev@debian:/mnt/btrfs$ sudo btrfs subvolume list ./
ID 256 gen 11 top level 5 path sub1
ID 257 gen 13 top level 5 path snap-root

#咱們能夠經過指定-s參數來只列出快照
dev@debian:/mnt/btrfs$ sudo btrfs subvolume list -s ./
ID 257 gen 10 cgen 10 top level 5 otime 2017-03-05 21:46:03 path snap-root

#再來看看快照snap-root中的文件,能夠看到有dir1及下面的文件,
#但看不到sub1下的文件,那是由於sub1是一個subvolume,
#在作一個subvolume的快照的時候,不會將它裏面的subvolume也作快照
dev@debian:/mnt/btrfs$ tree ./snap-root
./snap-root
├── dir1
│   └── dir1-01.txt
└── sub1

#建立sub1的一個快照,能夠看到sub1裏面的文件出如今了快照裏面
dev@debian:/mnt/btrfs$ sudo btrfs subvolume snapshot ./sub1/ ./snap-sub1
Create a snapshot of './sub1/' in './snap-sub1'
#而後在sub1和它的快照snap-sub1下面各自建立一個文件,
#會發現它們之間不受影響
dev@debian:/mnt/btrfs$ touch snap-sub1/snap-sub1-01.txt
dev@debian:/mnt/btrfs$ touch sub1/sub1-02.txt
dev@debian:/mnt/btrfs$ tree
.
├── dir1
│   └── dir1-01.txt
├── snap-root
│   ├── dir1
│   │   └── dir1-01.txt
│   └── sub1
├── snap-sub1
│   ├── snap-sub1-01.txt
│   └── sub1-01.txt
└── sub1
    ├── sub1-01.txt
    └── sub1-02.txt

刪除快照

刪除快照和刪除subvolume是同樣的,沒有區別

dev@debian:/mnt/btrfs$ sudo btrfs subvolume del snap-root
Delete subvolume (no-commit): '/mnt/btrfs/snap-root'
dev@debian:/mnt/btrfs$ sudo btrfs subvolume del snap-sub1
Delete subvolume (no-commit): '/mnt/btrfs/snap-sub1'
dev@debian:/mnt/btrfs$ tree
.
├── dir1
│   └── dir1-01.txt
└── sub1
    ├── sub1-01.txt
    └── sub1-02.txt

default subvolume

能夠設置Btrfs分區的默認subvolume,即在掛載磁盤的時候,能夠只讓分區中的指定subvolume對用戶可見。看下面的例子:

#查看sub1的ID
dev@debian:/mnt/btrfs$ sudo btrfs subvolume list ./
ID 256 gen 14 top level 5 path sub1

#將sub1設置爲當前Btrfs文件系統的默認subvolume
dev@debian:/mnt/btrfs$ sudo btrfs subvolume set-default 256 /mnt/btrfs/

#從新將虛擬硬盤掛載到一個新目錄
dev@debian:/mnt/btrfs$ sudo mkdir /mnt/btrfs1
dev@debian:/mnt/btrfs$ sudo mount /tmp/btrfs.img /mnt/btrfs1/
#這裏將只能看到sub1下的文件
dev@debian:/mnt/btrfs$ tree /mnt/btrfs1
/mnt/btrfs1
├── sub1-01.txt
└── sub1-02.txt

#因爲Btrfs原來的默認subvolume是root subvolume,
#其ID是5(也能夠經過0來標識),
#因此咱們能夠經過一樣的命令將默認subvolume再改回去
dev@debian:/mnt/btrfs$ sudo btrfs subvolume set-default 0 /mnt/btrfs/

default subvolume有什麼用呢?

利用snapshot和default subvolume,能夠很方便的實現不一樣系統版本的切換,好比將系統安裝在一個subvolume下面,當要作什麼危險操做的時候,先在subvolume的基礎上作一個快照A,若是操做成功,那麼什麼都不用作(或者把A刪掉),繼續用原來的subvolume,A不被刪掉也不要緊,多一個快照在那裏也不佔空間,若是操做失敗,那麼能夠將A設置成default subvolume,並將原來的subvolume刪除,這樣就至關於系統回滾。

有了這樣的功能後,Linux的每次操做都能回滾,養成在修改操做前作snapshot的習慣,就不再用擔憂rm誤刪文件了。

如今有些發行版已經有了相似的功能,如ubuntu,將安裝工具(apt)和Btrfs結合,自動的在安裝軟件以前打一個snapshot,而後安裝軟件,若是成功,刪除新的snapshot,若是失敗,修改default subvolume爲新的snapshot,刪除掉原來的snapshot,這樣對系統沒有任何影響,而且全部操做對用戶是透明的。

隨着Btrfs的成熟和普及,相信會改變一些咱們使用Linux的習慣。

結束語

Btrfs的功能太多,須要在使用的過程當中去熟悉,本文只是粗略的介紹了一下subvolume和snapshot,關於subvolume的增量備份和磁盤限額都沒有涉及到,下次有時間再繼續這部份內容。

參考

相關文章
相關標籤/搜索