Btrfs

Aus DebianforumWiki

Wechseln zu: Navigation, Suche

Überblick

Btrfs (B-tree FS, gelegentlich „Butterface“ ausgesprochen) ist ein noch relativ junges Copy-on-Write-Dateisystem, welches integriertes RAID, Volume-Management, Quotas und Prüfsummenberechnung bietet.

Inhaltsverzeichnis


Btrfs bietet einige Anwendungsfälle, die sich auch mit Logical Volume Management oder ZFS darstellen lassen. Dieser Artikel soll nicht die verschiedenen Möglichkeiten vergleichen, sondern das Vorgehen mit btrfs zeigen.

GRUB kann von btrfs booten; es kann daher als Wurzeldateisystem verwendet werden. Es unterstützt den TRIM-Befehl und ist daher für SSDs geeignet.

Hinweis.png Hinweis: Btrfs ist noch in der Entwicklung und nicht alle Funktionen sind ausreichend stabil für Produktivsysteme. Dieser Artikel stellt den Stand unter Debian Stretch dar, der nicht dem aktuellen, auf der Statusseite des Projekts dokumentierten Entwicklungsstand entspricht.

Getestet.png Getestet: Dieser Hinweis soll dir dabei helfen, zu entscheiden, ob dieser Artikel auf deinem System funktionieren wird oder nicht. Solltest du feststellen, dass dieser Artikel bei einer ungetestet Version funktioniert, kannst du das gerne hier korrigieren oder im Forum anmerken.


Review.png Review: Dieser Artikel ist für das Review freigegeben.


Grundlagen

Grundlegende Konzepte

Dateisystem und Geräte

Bei btrfs gibt es keine 1:1-Beziehung zwischen physikalischem Gerät (d.h. einer Festplattenpartition) und Dateisystem. Da btrfs ein integriertes Volume Management ähnlich Debianpackage.png mdadm mit sich bringt, muss man sich vorstellen, dass ein btrfs-Dateisystem immer auf einem „Pool“ von physikalischen Geräten aufsetzt. Bei der Nutzung des Dateisystems ist es für den Anwender weitestgehend irrelevant, wie das Dateisystem die einzelnen Geräte anspricht.

Copy-on-Write

Copy-on-Write bedeutet, dass beim Kopieren von Daten diese erst in dem Moment „echt“ kopiert werden, wenn diese verändert werden. Vorher „zeigen“ sowohl Original als auch Kopie auf die gleichen Speicherbereiche.

Dieses Verfahren wird für Snapshots genutzt: ein Snapshot ist „sofort“ angelegt, da dafür keine wesentlichen Datenmengen kopiert werden müssen, und belegt keinen zusätzlichen Speicherplatz. Änderungen am Original werden in neue Speicherbereiche geschrieben.

Chunks

Die Daten werden in Chunks (auch Block Groups) genannt abgelegt. Ein Chunk ist ein Speicherbereich fester Größe. Es gibt drei unterschiedliche Chunk Types:

  • Data: diese Chunks speichern ausschließlich Dateiinhalte. Ein Data Chunk ist in der Regel 1 GiB groß.
  • Metadata: diese Chunks speichern dateisysteminterne Metadaten in B-Bäumen sowie kleine Dateien, wenn sie in den Chunk hineinpassen. Ein Metadata Chunk ist abhängig von der Dateisystemgröße 256 MiB oder 1 GiB groß.
  • System: diese Chunks speichern Informationen über die Geräte, auf denen das Dateisystem liegt. System Chunks sind ein paar MiB groß.

Profile

Für die drei Chunk Types können Chunk Profiles ausgewählt werden. Die Profile entscheiden, wie die Chunks auf die physikalischen Geräte verteilt werden. In der Manpage von mkfs.btrfs findet sich eine Übersicht:

       ┌────────┬────────────────────────────────────┬────────────┐
       │        │                                    │            │
       │Profile │ Redundancy                         │  Min/max   │
       │        ├──────────────┬────────┬────────────┤  devices   │
       │        │              │        │            │            │
       │        │    Copies    │ Parity │  Striping  │            │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │single  │      1       │        │            │   1/any    │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │  DUP   │ 2 / 1 device │        │            │ 1/any (see │
       │        │              │        │            │ note 1)    │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │ RAID0  │              │        │   1 to N   │   2/any    │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │ RAID1  │      2       │        │            │   2/any    │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │RAID10  │      2       │        │   1 to N   │   4/any    │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │ RAID5  │      1       │   1    │ 2 to N - 1 │ 2/any (see │
       │        │              │        │            │ note 2)    │
       ├────────┼──────────────┼────────┼────────────┼────────────┤
       │        │              │        │            │            │
       │ RAID6  │      1       │   2    │ 3 to N - 2 │ 3/any (see │
       │        │              │        │            │ note 3)    │
       └────────┴──────────────┴────────┴────────────┴────────────┘

Bei der Erzeugung eines btrfs-Dateisystems auf einer rotierenden Festplatte wird mkfs.btrfs für Metadata- und System-Chunks das Profil DUP wählen, auf einer SSD dagegen Single. Durch die redundante Speicherung wird die Ausfallsicherheit erhöht. Da viele SSD-Controller intern Deduplizierung betreiben, lässt sich dort die Sicherheit so nicht erhöhen, weshalb mkfs.btrfs dann Single wählt.

Subvolumes

Ein Subvolume ist eine zusätzliche interne Dateisystemwurzel, die separat eingehängt werden kann. Aus Anwendungssicht erscheinen Subvolumes wie Verzeichnisse, sie lassen sich jedoch nicht über rmdir löschen. Auch ein rm -rf auf ein Verzeichnis schlägt fehl, wenn darin Subvolumes enthalten sind.

Subvolumes sind die Grundlage für Snapshots und Quotas.

Befehle

Für den Umgang mit btrfs-Dateisystemen müssen die Debianpackage.png btrfs-progs installiert werden. Das grundlegende Userspace-Tool heißt btrfs; die wichtigsten zugehörigen Befehle sind

  • btrfs balance: Ausbalancieren eines Dateisystem, d.h. die Neuanordnung der Chunks, in denen die Daten abgelegt sind
  • btrfs check: diverse Offline-Überprüfungen eines btrfs-Dateisystems
  • btrfs device: Verwaltung der einem btrfs-Dateisystem zugeordneten Geräte
  • btrfs filesystem: Verwaltung eines btrfs-Dateisystems an sich
  • btrfs qgroups: Verwalten von Quota Groups
  • btrfs quota: Verwalten von Quotas
  • btrfs replace: Austauschen von Geräten bei RAID
  • btrfs rescue: Reparieren beschädigter Dateisysteme
  • btrfs scrub: Online-Überprüfungen eines btrfs-Dateisystems
  • btrfs subvolume: Verwaltung von btrfs-Subvolumes und Snapshots
Für jeden Befehl gibt eine eigene Manpage. Die für balance ruft man bspw. über
user@debian:~$ man btrfs-balance
auf.

btrfs-Dateisysteme erzeugen

Ein btrfs-Dateisystem wird mit mkfs.btrfs angelegt.

root@demo:~# mkfs -t btrfs /dev/sdb1
btrfs-progs v4.7.3
See http://btrfs.wiki.kernel.org for more information.

Label:              (null)
UUID:               
Node size:          16384
Sector size:        4096
Filesystem size:    8.00GiB
Block group profiles:
  Data:             single            8.00MiB
  Metadata:         DUP             409.50MiB
  System:           DUP               8.00MiB
SSD detected:       no
Incompat features:  extref, skinny-metadata
Number of devices:  1
Devices:
   ID        SIZE  PATH
    1     8.00GiB  /dev/sdb1

Kompression

btrfs bietet eine transparente Datenkompression, die sich mit der mount-Option -o compress aktivieren lässt. Neu geschriebene oder veränderte Daten werden dann automatisch komprimiert. Das Dateisystem erkennt anhand eines Algorithmus’, welche Daten sich komprimieren lassen. Dadurch können Daten auch unkomprimiert gespeichert werden. Weitere Informationen gibt im btrfs-Wiki. (Bitte beachten: das Wiki spiegelt immer den aktuellen Entwicklungsstand von btrfs wieder, der nicht mit dem in Debian übereinstimmen wird.)

Verschlüsselung

Eine integrierte Verschlüsselung ist nicht vorhanden. Will man btrfs verschlüsseln, so kann man Debianpackage.png dm-crypt verwenden. Da btrfs auf mehreren Geräten aufsetzen kann, muss man darauf achten, jedes Gerät zu verschlüsseln. Weitere Informationen gibt im btrfs-Wiki. (Bitte beachten: das Wiki spiegelt immer den aktuellen Entwicklungsstand von btrfs wieder, der nicht mit dem in Debian übereinstimmen wird.)

Freien Speicherplatz anzeigen

btrfs speichert die Daten intern in sog. Chunks, die Datenbereiche bestimmter Größe darstellen. Innerhalb eines Chunks können Daten unterschiedlicher Dateien abgelegt werden. Für das Vorhandensein freien Speicherplatzes auf einem btrfs-Dateisystem ist das Vorhandensein freier Chunks entscheidend. Anders ausgedrückt: bei btrfs muss man zwischen freiem Platz auf dem Dateisystem und freiem Platz auf den Geräten unterscheiden.

Da btrfs mit Copy-on-Write arbeitet und die Daten in Chunks allokiert, sind die Ausgaben von df und du mitunter irreführend. Bei Vorhandensein von Snapshots kann du u.U. mehr belegten Platz ausweisen, als das Dateisystem groß ist.

root@proliant:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1       105G     35G   69G   34% /
root@proliant:~# btrfs filesystem show /
Label: none  uuid: 2a485f0a-0fcc-4198-a92e-e0a9f854ac46
        Total devices 1 FS bytes used 33.98GiB
        devid    1 size 104.31GiB used 61.03GiB path /dev/sda1

Wie interpretiert man diese Ausgabe?

  • Total devices 1: das Dateisystem liegt ist einem Gerät zugeordnet
  • FS bytes used 33.98GiB: auf dem Dateisystem sind 33,98 GiB belegt
  • devid 1: es folgen Angaben zum ersten zugeordneten Gerät
    • size 104.31GiB: dieses Gerät ist 104,31 GiB groß
    • used 61.03GiB: es sind Chunks im Volumen von 61,03 GiB in Verwendung
    • path /dev/sda1: das Gerät ist /dev/sda1

Die entscheidende Angabe ist die unterhalb der Devices: sie gibt an, in welchem Umfang Chunks belegt sind. Wenn keine freien Chunks mehr vorhanden sind, ist das Dateisystem voll. Bei der Angabe der belegten Chunks werden diese immer voll gezählt unabhängig davon, in welchem Umfang sie belegt sind.

Im Beispiel oben sind also nicht die von df ausgegebenen 69 GB frei. Das Dateisystem wird weitaus früher melden, dass kein Speicherplatz mehr vorhanden ist.

Wird innerhalb eines Chunks Speicherplatz durch Löschen einer Datei frei, so wird der Chunk immer noch als voll belegt angezeigt. Durch Neubalancieren des Dateisystems können Daten zwischen Chunks verschoben und so freier Speicherplatz gewonnen werden. Chunks, die keine Daten mehr enthalten, werden freigegeben.

Wie sich die belegten Daten über unterschiedliche Chunks verteilen, lässt sich über

root@proliant:~# btrfs filesystem df /
Data, single: total=59.00GiB, used=33.33GiB
System, single: total=32.00MiB, used=16.00KiB
Metadata, single: total=2.00GiB, used=668.80MiB
GlobalReserve, single: total=81.98MiB, used=0.00B

detaillierter anzeigen. Die Angabe „single“ zeigt an, dass es sich hier um Chunks handelt, die nur auf einem Gerät existieren.

Aber wieviel Platz ist belegt? Die Ausgabe von du ist genau wie die von df irreführend:

root@proliant:~# du -h --max-depth=0 /var/lib/lxc/
83G     /var/lib/lxc/

du meldet, dass unterhalb eines Verzeichnisses 83 GB belegt sind. Dies rührt daher, dass btrfs mit Copy-on-Write arbeitet, mehrere identische Dateien also nur einmal Speicherplatz belegen, du diesen aber mehrfach zählt. Im Beispiel oben existieren unter /var/lib/lxc mehrere Snapshots, die aber nur in soweit Speicherplatz belegen, wie sich das per Snapshot gesicherte Subvolume verändert hat. Auch das kann man sich ausgeben lassen:

root@proliant:~# btrfs filesystem du -s /var/lib/lxc                            
     Total   Exclusive  Set shared  Filename
  69.35GiB     2.30GiB     8.54GiB  /var/lib/lxc

Tatsächlich sind nur knapp 11 GiB belegt, von denen 8,54 GiB von mehreren Snapshots und den Originalen gemeinsam verwendet werden. Nur 2,30 GiB entfallen auf sich unterscheidende Daten.

Balancieren

Unter Balancieren oder Balancing wird die Neuanordnung der Daten in den Chunks verstanden. Dies ist im Regelfall in zwei Fällen erforderlich:

  • Das Chunk-Profil soll gewechselt werden. Beispiele dazu finden sich weiter unten unter Multidisk zu finden.
  • Der Speicherplatz geht zur Neige, weil nur noch wenige freie Chunks vorhanden sind. Per Balancieren kann man btrfs dazu bewegen, Daten aus wenig belegten Chunks in andere Chunks zu verlagern und anschließend nicht mehr verwendete Chunks freizugeben.

Das Ergebnis sieht man hier in einer (gekürzten) Ausgabe von btrfs-balance, eines Tools von btrfsmaintenance:

/etc/cron.monthly/btrfs-balance:
Before balance of /
Data, single: total=60.00GiB, used=29.93GiB
System, single: total=64.00MiB, used=16.00KiB
Metadata, single: total=2.00GiB, used=582.12MiB
GlobalReserve, single: total=74.83MiB, used=0.00B
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1       112G     33G   78G   30% /
Done, had to relocate 0 out of 64 chunks
Dumping filters: flags 0x1, state 0x0, force is off
  DATA (flags 0x2): balancing, usage=1
Done, had to relocate 0 out of 64 chunks
…
Dumping filters: flags 0x6, state 0x0, force is off
  METADATA (flags 0x2): balancing, usage=30
  SYSTEM (flags 0x2): balancing, usage=30
Done, had to relocate 2 out of 39 chunks
After balance of /
Data, single: total=36.00GiB, used=29.92GiB
System, single: total=32.00MiB, used=16.00KiB
Metadata, single: total=1.00GiB, used=577.44MiB
GlobalReserve, single: total=70.14MiB, used=0.00B

Vorher waren Data Chunks im Umfang von 60 GiB vorhanden, die nur knapp 30 GiB Daten enthielten. Danach waren Data Chunks im Umfang von 36 GiB vorhanden. De facto sind 24 GiB Speicherplatz freigeworden. (Das darunterliegende Gerät ist übrigens 104,31 GiB groß.)

Das Balancieren zum Neuordnen von Chunks wird über den Filter usage gesteuert. Möchte man bspw. alle Data Chunks, die zu weniger als 40% belegt sind, umgliedern, so geschieht dies über btrfs balance -dusage=40 /. Den Prozess muss man möglicherweise mehrfach wiederholen und dabei steigende Prozentwerte verwenden. Das oben gezeigte btrfsmaintenance führt wiederholtes Balancieren mit steigenden Prozentwerten bis 30% durch.

Scrubbing

Beim Scrubbing werden alle Daten mit den gespeicherten Prüfsummen verglichen. Bei redundant gespeicherten Chunks (Profile DUP, RAID1/5/6) werden Fehler automatisch korrigiert. Es wird über btrfs scrub ausgelöst.

Das Ergebnis sieht man hier in einer Ausgabe von btrfs-scrub, eines Tools von btrfsmaintenance:

/etc/cron.monthly/btrfs-scrub:
Running scrub on /
scrub device /dev/sda1 (id 1) done
        scrub started at Fri Dec  1 07:06:06 2017 and finished after 00:01:26
        total bytes scrubbed: 30.49GiB with 0 errors

Scrubbing belastet die Festplatte und den Prozessor enorm. Der Vorgang kann bei großen Dateisystemen mehrere Stunden dauern.

Debian auf btrfs installieren

Der Debian-Installer installiert Debian auf btrfs, wenn dieses während der Installation explizit ausgewählt wird. Die Debianpackage.png btrfs-progs werden dann automatisch installiert.

Subvolumes

Grundlagen

Ein Subvolume ist eine zusätzliche interne Dateisystemwurzel, die separat eingehängt werden kann. Aus Anwendungssicht erscheinen Subvolumes wie Verzeichnisse. Alle unterhalb eines eingehängten Subvolumes vorhandenen Subvolumes erscheinen gleichfalls als eingehängt. Bei der Nutzung von Subvolumes sind grundsätzlich zwei Vorgehensweisen denkbar: ein „flaches“ (flat) und ein „verschachteltes“ (nested) Layout.

Subvolumes sind die Grundlage für Snapshots und Quotas.

Flaches Layout

Beim flachen Layout wird die Dateisystemwurzel an sich nicht eingehängt, dafür aber die einzelnen Subvolumes. Es hat sich als Namenskonvention herausgebildet, die Namen solcher Subvolumes mit einem Klammeraffen beginnen zu lassen.

toplevel        (Dateisystemwurzel, wird nicht eingehängt)
 +-- @root      (Subvolume, das als Wurzelverzeichnis / eingehängt wird)
 +-- @home      (Subvolume, das als /home eingehängt wird)
 +-- @topsecret (Subvolume, das nicht eingehängt wird)
 +-- var        (normales Verzeichnis)
      +-- @lib  (Subvolume, das unter /var/lib eingehängt wird)
      +-- @www  (Subvolume, das unter /var/www eingehängt wird)

Im Beispiel wird die Verzeichnisstruktur näherungsweise durch Einfügen des Verzeichnisses /var nachgebildet. Technisch ist dies nicht erforderlich.

In der /etc/fstab wird der Verzeichnisbaum folgendermaßen zusammengebaut:

UUID=…  /         btrfs  defaults,subvol=@root      0  0
UUID=…  /home     btrfs  defaults,subvol=@home      0  0
UUID=…  /var/lib  btrfs  defaults,subvol=/var/@lib  0  0
UUID=…  /var/www  btrfs  defaults,subvol=/var/@www  0  0

Alles, was sich außerhalb/oberhalb der eingehängten Subvolumes befindet, ist aus Anwendersicht nicht vorhanden. Da das Subvolume @topsecret nicht explizit eingehängt wird und die Dateisystemwurzel auch nicht, kann auf Daten in @topsecret nicht zugegriffen werden.

Anstelle des über subvol angegebenen Pfads des Subvolumes kann man auch dessen ID über subvolid angeben. Die ID eines Subvolume kann über btrfs subvolume list oder btrfs subvolume show ermittelt werden. Die Angabe der ID ist kompakter und unempfindlich gegen das Verschieben des Subvolumes im Dateisystem, dafür ist die fstab weniger „sprechend“.

Verschachteltes Layout

Beim verschachtelten Layout wird die Dateisystemwurzel eingehängt. Damit werden auch automatisch alle Subvolumes eingehängt und erscheinen als Verzeichnisse.

toplevel        (Dateisystemwurzel, wird als / eingehängt)
 +-- home       (Subvolume, wird nicht explizit eingehängt, erscheint automatisch als Verzeichnis /home)
 +-- topsecret  (Subvolume, wird nicht explizit eingehängt, erscheint automatisch als Verzeichnis /topsecret)
 +-- var        (normales Verzeichnis)
      +-- lib   (Subvolume, wird nicht explizit eingehängt, erscheint automatisch als Verzeichnis /var/lib)
      +-- www   (Subvolume, wird nicht explizit eingehängt, erscheint automatisch als Verzeichnis /var/www)

Die fstab umfasst nur eine Zeile:

UUID=…  /         btrfs  defaults                   0  0

Wenn kein Subvolume angegeben wird, wird das als Default festgelegte Volume gemountet. Das ist im Regelfall die Dateisystemwurzel (das „top-level subvolume“); über btrfs subvolume set-default kann dieses geändert werden.

Subvolumes verwalten

Subvolumes lassen sich über btrfs subvolume verwalten. Die wichtigsten Befehle sind

  • btrfs subvolume create: erzeuge ein Subvolume am angegebenen Ort.
  • btrfs subvolume delete: lösche das angegebene Subvolume.
  • btrfs subvolume list: zeige alle Subvolumes unterhalb des angegebenen Ortes.
  • btrfs subvolume show: zeige Details des angegebenen Subvolumes.
  • btrfs subvolume snapshot: erzeuge einen Snapshot des angegebenen Subvolumes.
root@demo:~# btrfs subvolume create subvol1
Create subvolume './subvol1'
root@demo:~# btrfs subvolume create subvol2
Create subvolume './subvol2'
root@demo:~# btrfs subvolume list .
ID 263 gen 3526 top level 5 path root/subvol1
ID 264 gen 3527 top level 5 path root/subvol2

Hier werden die eindeutigen numerischen IDs der Subvolumes angezeigt. Das „top-level subvolume“, also die Wurzel eines btrfs-Dateisystems, hat immer die ID 5.

root@demo:~# btrfs subvolume delete subvol2
Delete subvolume (no-commit): '/root/subvol2'
root@demo:~# btrfs subvolume list .
ID 263 gen 3526 top level 5 path root/subvol1

Jetzt hängen wir das verbliebene Subvolume ein und wieder aus:

root@demo:~# mkdir /mnt/subvol1
root@demo:~# mount -t btrfs -o subvol=/root/subvol1 /dev/sda1 /mnt/subvol1/
root@demo:~# touch subvol1/test
root@demo:~# ls /mnt/subvol1/
test
root@demo:~# umount /mnt/subvol1/
root@demo:~# rmdir /mnt/subvol1/
root@demo:~# ls subvol1/
test

Und nun löschen wir das verbliebene Subvolume:

root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
root@demo:~# btrfs subvolume list .
root@demo:~# ls subvol1/
ls: Zugriff auf 'subvol1/' nicht möglich: Datei oder Verzeichnis nicht gefunden

Verzeichnisse in Subvolume umwandeln

Es gibt keinen Befehl, um ein Verzeichnis in ein Subvolume umzuwandeln. Man muss von Hand

  1. das Verzeichnis umbenennen,
  2. ein Subvolume mit dem ursprünglichen Namen des Verzeichnisses neu erzeugen,
  3. den Inhalt des Verzeichnisses in das Subvolume verschieben und
  4. das Verzeichnis löschen.

Snapshots

Grundlagen

Snapshots sind – technisch gesehen – Subvolumes, die aus einem anderen Subvolume erzeugt werden. Wegen Copy-on-Write geschieht das Erzeugen eines Snapshots augenblicklich und es wird kein zusätzlicher Speicherplatz verbraucht. Zusätzlicher Speicherplatz wird erst in dem Moment belegt, in dem der Snapshot oder das Original verändert werden.

Technisch besteht zwischen dem Original und dem Snapshot keine Beziehung, die beschreibt, wer Original und wer Snapshot ist.

Snapshots können nur von Subvolumes erzeugt werden. Wenn man einen Snapshot eines normalen Verzeichnisses erstellen möchte, muss dieses zuerst durch ein Subvolume ersetzt werden.

Snapshots erzeugen und löschen

Snapshots werden über btrfs subvolume snapshot erzeugt. Gelöscht werden sie, da sie nichts anderes als Subvolumes sind, über btrfs subvolume delete.

root@demo:~# btrfs subvolume create subvol1
Create subvolume './subvol1'
root@demo:~# echo Original > subvol1/test.txt
root@demo:~# cat subvol1/test.txt 
Original
root@demo:~# btrfs subvolume snapshot subvol1/ subvol1-snapshot
Create a snapshot of 'subvol1/' in './subvol1-snapshot'
root@demo:~# btrfs subvolume list .
ID 265 gen 3537 top level 5 path root/subvol1
ID 266 gen 3537 top level 5 path root/subvol1-snapshot
root@demo:~# cat subvol1-snapshot/test.txt 
Original

Jetzt verändern wir die Textdatei im Original:

root@demo:~# echo verändert >> subvol1/test.txt
root@demo:~# cat subvol1/test.txt 
Original
verändert
root@demo:~# cat subvol1-snapshot/test.txt 
Original
root@demo:~# 

Nun löschen wir beide Subvolumes:

root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
root@demo:~# cat subvol1-snapshot/test.txt 
Original
root@demo:~# btrfs subvolume delete subvol1-snapshot
Delete subvolume (no-commit): '/root/subvol1-snapshot'

Original aus Snapshot wiederherstellen

Es gibt keine Beziehung zwischen Original und Snapshot, die sie als Original und Snapshot kennzeichnet. Daher gibt es auch keine Funktion, die ein verändertes Original auf den Stand eines Snapshots zurücksetzt. Statt dessen

  1. löscht man das Original und
  2. erzeugt einen neuen Snapshot des Snapshots mit dem Namen des Originals.

Zunächst bauen wir uns ein Subvolume auf und erzeugen einen Snapshot:

root@demo:~# btrfs subvolume create subvol1
Create subvolume './subvol1'
root@demo:~# echo Original > subvol1/test.txt
root@demo:~# btrfs subvolume snapshot subvol1/ subvol1-snapshot
Create a snapshot of 'subvol1/' in './subvol1-snapshot'
root@demo:~# echo verändert >> subvol1/test.txt
root@demo:~# cat subvol1/test.txt 
Original
verändert
root@demo:~# cat subvol1-snapshot/test.txt 
Original

Jetzt stellen wir das Original aus dem Snapshot wieder her:

root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
root@demo:~# btrfs subvolume snapshot subvol1-snapshot/ subvol1
Create a snapshot of 'subvol1-snapshot/' in './subvol1'
root@demo:~# cat subvol1/test.txt 
Original

In diesem Beispiel bleibt der Snapshot bestehen:

root@demo:~# btrfs subvolume list .
ID 268 gen 3543 top level 5 path root/subvol1-snapshot
ID 269 gen 3543 top level 5 path root/subvol1

Falls man den Snapshot von vornherein nicht mehr braucht, löscht man einfach das Original und benennt den Snapshot um.

root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
root@demo:~# mv subvol1-snapshot/ subvol1
root@demo:~# btrfs subvolume list .
ID 268 gen 3543 top level 5 path root/subvol1

Schreibgeschützte Snapshots

Snapshots lassen sich mit dem Parameter -r schreibgeschützt erzeugen:

root@demo:~# btrfs subvolume create subvol1
Create subvolume './subvol1'
root@demo:~# echo Original > subvol1/test.txt
root@demo:~# btrfs subvolume snapshot -r subvol1/ subvol1-snapshot
Create a readonly snapshot of 'subvol1/' in './subvol1-snapshot'
root@demo:~# echo verändert >> subvol1-snapshot/test.txt
bash: subvol1-snapshot/test.txt: Das Dateisystem ist nur lesbar

Will man das Original aus einem solchen Snapshot wiederherstellen, muss man zunächst das Original löschen und dann einen neuen Snapshot erzeugen.

root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
root@demo:~# btrfs subvolume snapshot subvol1-snapshot/ subvol1
Create a snapshot of 'subvol1-snapshot/' in './subvol1'
root@demo:~# echo verändert >> subvol1/test.txt
root@demo:~# echo verändert >> subvol1-snapshot/test.txt
bash: subvol1-snapshot/test.txt: Das Dateisystem ist nur lesbar

Die zweite Methode – löschen des Originals und Umbenennen des Snapshots – funktioniert nicht, da auch der umbenannte Snapshot schreibgeschützt ist.

Snapshots und verschachtelte Subvolumes

Wenn man einen Snapshot eines Subvolumes erzeugt, das wiederum Subvolumes enthält, dann landen die enthaltenen Subvolumes als normale Verzeichnisse im Snapshot.

root@demo:~# btrfs subvolume list .
ID 268 gen 3543 top level 5 path root/subvol1
root@demo:~# btrfs subvolume create subvol1/subvol2
Create subvolume 'subvol1/subvol2'
root@demo:~# btrfs subvolume list .
ID 268 gen 3545 top level 5 path root/subvol1
ID 270 gen 3545 top level 268 path root/subvol1/subvol2
root@demo:~# btrfs subvolume snapshot subvol1/ subvol1-snapshot
Create a snapshot of 'subvol1/' in './subvol1-snapshot'
root@demo:~# btrfs subvolume list .
ID 268 gen 3546 top level 5 path root/subvol1
ID 270 gen 3545 top level 268 path root/subvol1/subvol2
ID 271 gen 3546 top level 5 path root/subvol1-snapshot
root@demo:~# ls subvol1-snapshot/
subvol2  test.txt

Das Subvolume subvol1 lässt sich nicht löschen, da dort ein weiteres Subvolume enthalten ist:

root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
ERROR: cannot delete '/root/subvol1': Directory not empty

Wenn man nun subvol2 und subvol1 löscht, ist subvol2 als normales Verzeichnis weiterhin im Snapshot enthalten:

root@demo:~# btrfs subvolume delete subvol1/subvol2/
Delete subvolume (no-commit): '/root/subvol1/subvol2'
root@demo:~# btrfs subvolume delete subvol1
Delete subvolume (no-commit): '/root/subvol1'
root@demo:~# btrfs subvolume list .
ID 271 gen 3547 top level 5 path root/subvol1-snapshot
root@demo:~# ls subvol1-snapshot/
subvol2  test.txt

Quotas

Muss noch beschrieben werden. Ist unter Stretch ohnehin kaputt…

Multidisk

Grundlagen

Wie oben unter Dateisystem und Geräte erläutert, spricht ein btrfs-Dateisystem im Hintergrund immer einen Pool von Geräten an und verhält sich in dieser Hinsicht ähnlich Debianpackage.png mdadm. Die Strategie, wie Daten auf Geräte verteilt werden, wird über die Chunk Profiles festgelegt. Die Profile können zur Laufzeit und auch auf einem eingehängten Dateisystem gewechselt werden.

RAID0/1

Single mit zwei Geräten

In der Ausgangssituation liegt ein btrfs-Dateisystem auf /dev/sda1 vor, das als Wurzeldateisystem gemountet ist. Die Platte ist 8 GB groß; ein Teil der Kapazität wird für Swap auf /dev/sda2 verwendet.

root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1       7,0G    949M  5,5G   15% /
root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 1 FS bytes used 902.12MiB
	devid    1 size 7.00GiB used 2.13GiB path /dev/sda1

Da es sich um ein frisch installiertes Minimalsystem in einer virtuellen Maschine handelt, ist das Dateisystem größtenteils leer und nur wenige Chunks sind belegt.

root@demo:~# btrfs filesystem df /
Data, single: total=1.41GiB, used=872.05MiB
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=358.31MiB, used=30.06MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Nun „bauen“ wir eine weitere virtuelle Festplatte von 8 GB Größe ein und erzeugen eine Partition /dev/sdb1, die die komplette Platte in Beschlag nimmt, legen darauf aber kein Dateisystem an. Statt dessen fügen wir /dev/sdb1 zum Wurzeldateisystem hinzu.

root@demo:~# btrfs device add /dev/sdb1 /

Als Ergebnis erhalten wir ein um 8 GB vergrößertes btrfs-Dateisystem:

root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1        15G    949M   14G    7% /

Wir sehen, dass das neue Gerät dem Dateisystem zugehört, aber noch keine Daten abgelegt sind:

root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 2 FS bytes used 902.12MiB
	devid    1 size 7.00GiB used 2.13GiB path /dev/sda1
	devid    2 size 8.00GiB used 0.00B path /dev/sdb1

Die Chunks werden allerdings nicht als RAID0 abgelegt, sondern unverändert in den Profilen Single und DUP.

root@demo:~# btrfs filesystem df /
Data, single: total=1.41GiB, used=872.05MiB
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=358.31MiB, used=30.06MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Wechsel auf RAID0

Hinweis.png Hinweis: Die im folgenden gezeigten Profilwechsel sind weitestgehed willkürlich. Wer auf RAID1 umstellen möchte, kann das direkt tun.

Nun stellen wir die Data Chunks auf RAID0 um. Am Speicherplatz wird das nichts ändern, allerdings wird btrfs die Chunks dann intern anders allokieren und ein Striping (d.h. eine Verteilung auf beide physikalischen Geräte) anwenden.

root@demo:~# btrfs balance start -dconvert=raid0 /
Done, had to relocate 3 out of 5 chunks

btrfs meldet eine Umgliederung der Chunks. Der Speicherplatz ist erwartungsgemäß unverändert:

root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1        15G    949M   12G    8% /

Die umgegliederten Chunks tauchen auf dem neuen Gerät auf:

root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 2 FS bytes used 902.47MiB
	devid    1 size 7.00GiB used 2.96GiB path /dev/sda1
	devid    2 size 8.00GiB used 2.25GiB path /dev/sdb1 

Die Chunks sind nun in den Profilen RAID0 und DUP abgelegt:

root@demo:~# btrfs filesystem df /
Data, RAID0: total=4.50GiB, used=872.17MiB
System, DUP: total=8.00MiB, used=16.00KiB
Metadata, DUP: total=358.31MiB, used=30.28MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Wechsel auf RAID1

Jetzt stellen wir auf RAID1 um:

root@demo:~# btrfs balance start -dconvert=raid1 -mconvert=raid1 /
Done, had to relocate 5 out of 5 chunks

Die Größe des Dateisystems entspricht nun dem des kleineren Geräts:

root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1       7,5G    919M  5,9G   14% /

Beide Geräte sind identisch belegt, da Chunks nun identisch auf beide Geräte geschrieben werden:

root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 2 FS bytes used 902.26MiB
	devid    1 size 7.00GiB used 3.28GiB path /dev/sda1
	devid    2 size 8.00GiB used 3.28GiB path /dev/sdb1

Bis auf die Global Reserve werden nun alle Chunks im Profil RAID1 abgelegt:

root@demo:~# btrfs filesystem df /
Data, RAID1: total=3.00GiB, used=872.05MiB
System, RAID1: total=32.00MiB, used=16.00KiB
Metadata, RAID1: total=256.00MiB, used=30.19MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Umgekehrte Wechsel

Und nun drehen wir das Ganze zurück auf RAID0:

root@demo:~# btrfs balance start -dconvert=raid0 -mconvert=dup /
Done, had to relocate 5 out of 5 chunks
root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1        15G    949M   14G    7% /
root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 2 FS bytes used 901.82MiB
	devid    1 size 7.00GiB used 2.25GiB path /dev/sda1
	devid    2 size 8.00GiB used 2.56GiB path /dev/sdb1

root@demo:~# btrfs filesystem df /
Data, RAID0: total=4.50GiB, used=871.57MiB
System, DUP: total=32.00MiB, used=16.00KiB
Metadata, DUP: total=128.00MiB, used=30.23MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Noch ein Schritt zurück zu Single/DUP:

root@demo:~# btrfs balance start -dconvert=single /
Done, had to relocate 3 out of 5 chunks
root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1        15G    948M   14G    7% /
root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 2 FS bytes used 901.78MiB
	devid    1 size 7.00GiB used 1.00GiB path /dev/sda1
	devid    2 size 8.00GiB used 2.31GiB path /dev/sdb1

root@demo:~# btrfs filesystem df /
Data, single: total=3.00GiB, used=871.58MiB
System, DUP: total=32.00MiB, used=16.00KiB
Metadata, DUP: total=128.00MiB, used=30.19MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Und jetzt nehmen wir /dev/sdb1 wieder weg:

root@demo:~# btrfs device remove /dev/sdb1 /
root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1       7,0G    948M  5,9G   14% /
root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 1 FS bytes used 901.51MiB
	devid    1 size 7.00GiB used 1.72GiB path /dev/sda1

root@demo:~# btrfs filesystem df /
Data, single: total=1.41GiB, used=871.37MiB
System, DUP: total=32.00MiB, used=16.00KiB
Metadata, DUP: total=128.00MiB, used=30.12MiB
GlobalReserve, single: total=16.00MiB, used=0.00B
Warnung.png Warnung: Wenn btrfs mit RAID0 oder RAID1 betrieben wird, müssen zu jedem Zeitpunkt mindestens zwei Geräte vorhanden sein. Wenn man ein Gerät einfach wegnimmt und das Dateisystem beschreibt, werden Single-Chunks erzeugt. btrfs toleriert diese Mischung nicht. Weitere Infos weiter unten unter Dateisystem unwiderruflich nur lesbar.

RAID5

Warnung.png Warnung: RAID 5 und 6 sollten mit Debian Stretch nicht für produktive Daten verwendet werden, da diese RAID-Level dort fehlerhaft sind und Datenverlust droht[1].

Single mit drei Geräten

Wir beginnen damit, dass wir dem Wurzeldateisystem zwei Partitionen hinzufügen. Die Profile der Chunks bleiben zunächst unverändert.

root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 1 FS bytes used 901.70MiB
	devid    1 size 7.00GiB used 1.72GiB path /dev/sda1
root@demo:~# btrfs device add /dev/sdb1 /dev/sdc1 /
root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1        23G    948M   22G    5% /
root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 3 FS bytes used 901.70MiB
	devid    1 size 7.00GiB used 1.72GiB path /dev/sda1
	devid    2 size 8.00GiB used 0.00B path /dev/sdb1
	devid    3 size 8.00GiB used 0.00B path /dev/sdc1

root@demo:~# btrfs filesystem df /
Data, single: total=1.41GiB, used=871.54MiB
System, DUP: total=32.00MiB, used=16.00KiB
Metadata, DUP: total=128.00MiB, used=30.14MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Umstellung auf RAID5

Nun stellen wir Data- und Metadata-Chunks auf RAID5 um. Die System-Chunks werden automatisch angepasst:

root@demo:~# btrfs balance start -dconvert=raid5 -mconvert=raid5 /
Done, had to relocate 4 out of 4 chunks

df zeigt unzuverlässige Ergebnisse an.

root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1        23G    919M   20G    5% /

Die Geräte sind nun alle belegt und die Chunk sind alle im Profil RAID5 gespeichert.

root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 3 FS bytes used 901.99MiB
	devid    1 size 7.00GiB used 2.16GiB path /dev/sda1
	devid    2 size 8.00GiB used 2.16GiB path /dev/sdb1
	devid    3 size 8.00GiB used 2.16GiB path /dev/sdc1

root@demo:~# btrfs filesystem df /
Data, RAID5: total=4.00GiB, used=871.79MiB
System, RAID5: total=64.00MiB, used=16.00KiB
Metadata, RAID5: total=256.00MiB, used=30.19MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Zurück auf RAID1

Da RAID5 kaputt ist und wir den Platz nicht brauchen, stellen wir auf RAID1 um und entnehmen eine Platte.

root@demo:~# btrfs balance start -dconvert=raid1 -mconvert=raid1 /
Done, had to relocate 4 out of 4 chunks
root@demo:~# btrfs device remove /dev/sdc1 /
root@demo:~# df -h /
Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf
/dev/sda1       7,5G    918M  5,9G   14% /
root@demo:~# btrfs filesystem show /
Label: none  uuid: c6325d8f-955b-455a-90b8-69ca095f6bf0
	Total devices 2 FS bytes used 901.70MiB
	devid    1 size 7.00GiB used 1.28GiB path /dev/sda1
	devid    2 size 8.00GiB used 1.28GiB path /dev/sdb1

root@demo:~# btrfs filesystem df /
Data, RAID1: total=1.00GiB, used=871.54MiB
System, RAID1: total=32.00MiB, used=16.00KiB
Metadata, RAID1: total=256.00MiB, used=30.14MiB
GlobalReserve, single: total=16.00MiB, used=0.00B

Dateisystem unwiderruflich nur lesbar

Der RAID-Code ist in der in Debian Stretch vorhandenen Version von btrfs fehlerhaft. Bei RAID5 kann Datenverlust eintreten, siehe die Warnung oben; RAID1 dagegen kann unwiderruflich nur lesbar werden, wobei aber keine Daten verloren gehen[2].

Hintergrund bei RAID1 ist, dass der Kernel ein solches Dateisystem mounten wird, auch wenn eines der Geräte fehlt. Werden dann Daten geschrieben, werden Chunks im Profil Single erzeugt. Beim nächsten Mounten stellt der Kernel fest, dass auf dem Dateisystem Chunks in den Profilen RAID1 und Single vorhanden sind, was als Fehler interpretiert wird. Das Dateisystem wird dann read only eingehängt. Die Daten sind noch lesbar; es hilft jedoch nur ein Backup, gefolgt von einer Neuanlage des Dateisystems.

In neueren btrfs-Versionen ist dieser Fehler behoben bzw. das Dateisystem kann repariert werden.

Wartung

btrfsmaintenance

btrfsmaintenance ist eine Sammlung von Skripten, mit denen per crontab u.a. ein Rebalancing und Scrubbing durchgeführt werden kann. Der Code wird auf GitHub gehostet und kann via
user@debian:~$ git clone https://github.com/kdave/btrfsmaintenance.git
heruntergeladen werden.

btrfs-snap

Mit btrfs-snap lassen sich Snapshots erzeugen, die wie bei rsnapshot automatisch rotiert werden. Der Code wird gleichfalls auf GitHub gehostet und kann via
user@debian:~$ git clone https://github.com/jf647/btrfs-snap.git
heruntergeladen werden.

Skript von scientific

Der Forenuser scientific hat ein Skript geschrieben, mit dem sich Snapshots und Backups erzeugen lassen.

Weblinks

  1. https://btrfs.wiki.kernel.org/index.php/RAID56
  2. https://www.spinics.net/lists/linux-btrfs/msg63370.html
Meine Werkzeuge