FreeBSD-Installation aus einem Rettungssystem
Einige Anbieter dedizierter Server bieten auch ein BSD-basiertes Rettungssystem an. In dieser meist per PXE gebooteten Umgebung kann man problemlos an Platten und Partitionen herumschrauben; es fehlen jedoch häufig einige elementare Dinge, um tatsächlich eine Installation von FreeBSD durchführen zu können. Diese Anleitung beschäftigt sich damit, aus einem Rettungssystem heraus einen dedizierten Server mit FreeBSD zu installieren. Durchgespielt wurde das ganze Procedere von mir mit einem Server von OVH.
Vorbereitungen
Im Rettungssystem meines Providers stehen leider die FreeBSD-Installationsmedien nicht zur Verfügung. Da das System auf einem via NFS gemounteten (und schreibgeschützten) Root-Dateisystem basiert, bleibt einem nichts anderes übrig, als alle benötigten Daten zeitweise im RAM abzulegen (davon hat der Server glücklicherweise genug). Dazu wird eine Memory Disk benötigt, auf der die ISO-Images abgelegt werden können. Von dort aus lassen sie sich dann problemlos mounten:
mdconfig -a -t malloc -o reserve -s 2g -u 4
newfs /dev/md4
mount /dev/md4 /mnt
cd /mnt && mkdir disk1 disk2
fetch ftp://ftp7.de.freebsd.org/pub/FreeBSD/releases/amd64/ISO-IMAGES/7.2/7.2-RELEASE-amd64-disc1.iso
mdconfig -a -t vnode -f 7.2-RELEASE-amd64-disc1.iso -u 5
mount_cd9660 /dev/md5 /mnt/disk1
Festplatten präparieren
Dies ist der wahrscheinlich mühseligste und fehleranfälligste Schritt im gesamten Installationsprozess. Ziel dabei ist es, auf den beiden vorhandenen Festplatten (identisches Modell) mit gmirror ein Software-RAID1 für die erste Slice einzurichten, den übrigen Plattenplatz aber „roh“ zu belassen – auf diesem soll später ein ZFS RAIDZ Pool residieren.
Die Bezeichnung der Festplatten hängt vom Bustyp und vom Kanal ab, an den sie angeschlossen sind. Der Einfachheit halber werde ich immer die Bezeichnungen ad0 und ad1 verwenden.
RAID auflösen und Platten reinigen
Zunächst einmal mussten bei mir die beiden Festplatten aus einem bereits vom Provider angelegten gmirror-Verbund „befreit“ werden:
gmirror forget gm0
gmirror remove gm0 ad1
gmirror remove gmo ad0
gmirror dump ad0
gmirror dump ad1
Mit dem dump-Befehl wird überprüft, ob die gmirror-Metadaten tatsächlich aus dem letzten Sektor gelöscht wurden. Anschließend werden die Festplatten gründlich gereinigt, um Konflikte mit alten gmirror-Metadaten und Partitionsinformationen vorzubeugen:
dd if=/dev/zero of=/dev/ad0 bs=1m
dd if=/dev/zero of=/dev/ad1 bs=1m
Slices anlegen
Nach Abschluss dieser doch recht langwierigen Prozedur geht es jetzt ans Eingemachte: Die Platten müssen in Slices aufgeteilt werden. Ich verwende dazu die klassischen FreeBSD-Werkzeuge fdisk und bsdlabel. Gerade fdisk ist in seiner Anwendung etwas gewöhnungsbedürftig, und ich kann nur jedem empfehlen, spätestens jetzt einen Taschenrechner (oder noch besser: eine Tabellenkalkulationsanwendung) bereit zu halten.
Ich persönlich bevorzuge es, die „Drecksarbeit“ weitgehend fdisk zu überlassen und hinterher nur die Partitionstabelle meinen Wünschen anzupassen. Die einmal mühsam zusammengebastelte Konfiguration der ersten Platte wird dann einfach gnadenlos geklont und auf die zweite Platte übertragen; das spart Zeit und Mühe – und reduziert das Risiko von Zahlendrehern…
fdisk -BIv /dev/ad0
cd ~ && fdisk -p /dev/ad0 > fdisk.txt
Mit dieser Vorgehensweise wurde eine saubere Partitionstabelle auf ad0 angelegt; außerdem wurde der Bootcode in den MBR geschrieben. Allerdings wurde die Platte auch mit nur einer Slice initialisiert, die den gesamten Plattenplatz für sich beansprucht (ich mache diesen Zwischenschritt, um mir etwas Tipparbeit zu sparen und eine bearbeitbare Partitionstabelle zu erhalten, die auch schon die korrekten Geometriedaten der Festplatte enthält). Mit dem zweiten Befehl wurde diese in die Datei fdisk.txt gespeichert. Diese sieht nun in etwa folgendermaßen aus:
# /dev/ad0
g c1453521 h16 s63
p 1 0xa5 63 1465149105
a 1
Jetzt kommt der Taschenrechner oder die Tabellenkalkulation zum Einsatz: In meinem Fall ist die Partition 1.465.149.105 Sektoren groß geraten. Diesen Betrag gilt es nun, sinnvoll auf drei Partitionen aufzuteilen. Als grober Daumenwert sollte die erste Slice etwa zwischen 15 und 25 GB groß sein, die zweite so groß wie der Arbeitsspeicher des Systems, und die dritte bekommt den restlichen Speicherplatz zugewiesen.
Zur Erklärung: die erste Slice wird /, /var, /tmp und /usr beherbergen. Während / und /tmp nicht allzu groß sein müssen (je 1 GB sollte mehr als ausreichend sein), empfiehlt es sich, für /var und /usr mindestens je 8 GB zu reservieren (auf /var liegen neben den Syslog-Dateien auch die Accounting- und Audit-Daten, und /usr sollte für den Source Tree und den Ports Tree etwas Luft nach oben haben).
Die zweite Slice ist für den Auslagerungsspeicher (Swap) des Systems vorgesehen. Da der FreeBSD-Kernel ohnehin versucht, mehrere vorhandene Auslagerungsspeicher optimiert zu nutzen, wäre ist nicht sehr sinnvoll, den Swap-Bereich mit in den gmirror-Spiegel einzubeziehen – das würde den Zugriff auf ausgelagerte Speicherbereiche nur unnötig ausbremsen.
Nach Bearbeitung sieht die Datei fdisk.txt bei mir so aus:
# /dev/ad0
g c1453521 h16 s63
p 1 0xa5 63 37748736
p 2 0xa5 37748799 16777216
p 3 0xbf 54526015 1410623153
a 1
Nun kommt der Moment der Wahrheit, in dem sich zeigen wird, ob die angestellten Berechnungen einigermaßen korrekt waren:
fdisk -if ~/fdisk.txt /dev/ad0
fdisk /dev/ad0
Wenn die Ausgabe des letzten Befehls einigermaßen zufriedenstellend aussieht, kann die Aufteilung von ad0 auf ad1 übertragen werden:
fdisk -BIv /dev/ad1
fdisk -p /dev/ad0 | fdisk -if - /dev/ad1
Damit verfügen nun beide Platten über eine absolut identische Slice-Aufteilung.
gmirror-Array erstellen
Nun ist es an der Zeit, aus den ersten beiden Slices ein gmirror-RAID-Array zu erstellen. Bei der Wahl der Balance-Algorithmen scheint mir split am vernünftigsten zu sein (zumal beide Platten bei mir an unterschiedlichen Kanälen hängen, so dass echte parallele reads möglich sind). Um die Namenskonvention ein wenig hochzuhalten, bezeichne ich mein mirror-Device als gm0s1 – damit ist (zumindest für mich) kenntlich gemacht, dass das Array aus zwei Slices besteht (OK, es besteht natürlich ein gewisses Verwechslungsrisiko mit einem Array aus zwei Disks, auf dem dann mit fdisk eine Slice angelegt wurde, aber da so etwas bei mir nicht existiert, ist das Risiko für mich dann doch eher gering
)
Und so wird das Array erzeugt:
gmirror label -v -b split -s 4096 gm0s1 ad0s1 ad1s1
gmirror list
Mit dem list-Kommando kontrollieren wir wie üblich den Erfolg unserer Mühen. Hier sollte eine Ausgabe ähnlich der folgenden ausgegeben werden:
Geom name: gm0s1
State: COMPLETE
Components: 2
Balance: split
Slice: 4096
Flags: NONE
GenID: 0
SyncID: 1
ID: 919049269
Providers:
1. Name: mirror/gm0s1
Mediasize: 19327246336 (18G)
Sectorsize: 512
Mode: r0w0e0
Consumers:
1. Name: ad0s1
Mediasize: 19327246848 (18G)
Sectorsize: 512
Mode: r1w1e1
State: ACTIVE
Priority: 0
Flags: NONE
GenID: 0
SyncID: 1
ID: 4258031912
2. Name: ad1s1
Mediasize: 19327246848 (18G)
Sectorsize: 512
Mode: r1w1e1
State: ACTIVE
Priority: 1
Flags: NONE
GenID: 0
SyncID: 1
ID: 238864099
Label erstellen
Spätestens jetzt sollte man sich überlegt haben, wie denn die Plattenaufteilung für das Basissystem aussehen soll. Hier mein Partitionierungsvorschlag:
| Device | Größe | Mount Point |
|---|---|---|
| /dev/mirror/gm0s1a | 1 GB | / |
| /dev/mirror/gm0s1d | 8 GB | /usr |
| /dev/mirror/gm0s1e | 8 GB | /var |
| /dev/mirror/gm0s1f | 1 GB | /tmp |
Die Umsetzung erfolgt mit dem Werkzeug bsdlabel:
setenv EDITOR vi
bsdlabel -Bw /dev/mirror/gm0s1
bsdlabel -e /dev/mirror/gm0s1
Der letzte Befehl startet einen Editor, um die Disklabel-Tabelle zu bearbeiten (ich habe mich auf vi festgelegt, wer etwas anderes haben möchte, muss halt die Umgebungsvariable $EDITOR entsprechend setzen…). Nach Aufruf des Editors sollte die Label-Tabelle in etwa wie folgt aussehen:
# /dev/mirror/gm0s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 37748512 16 unused 0 0
c: 37748528 0 unused 0 0 # "raw" part, don't edit
Nun gilt es, diese Tabelle zu bearbeiten, bis sie beispielsweise so aussieht:
# /dev/mirror/gm0s1:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 1G 16 4.2BSD
c: 37748528 0 unused 0 0 # "raw" part, don't edit
d: 8G * 4.2BSD
e: 8G * 4.2BSD
f: * * 4.2BSD
Nun die Datei speichern und den Editor verlassen (bei vi: [ESC] :wq) und kontrollieren, ob alles nach Plan gelaufen ist:
bsdlabel /dev/mirror/gm0s1
sollte etwa folgende Ausgabe zu Tage fördern:
8 partitions:
# size offset fstype [fsize bsize bps/cpg]
a: 2097152 16 4.2BSD 0 0 0
c: 37748528 0 unused 0 0 # "raw" part, don't edit
d: 16777216 2097168 4.2BSD 0 0 0
e: 16777216 18874384 4.2BSD 0 0 0
f: 2096928 35651600 4.2BSD 0 0 0
Dateisysteme erzeugen
Jetzt können die Dateisysteme erzeugt werden:
newfs -O2 -L root /dev/mirror/gm0s1a
newfs -O2 -U -L usr /dev/mirror/gm0s1d
newfs -O2 -U -L var /dev/mirror/gm0s1e
newfs -O2 -U -L tmp /dev/mirror/gm0s1f
Damit ist die Vorbereitung der Festplatten nun abgeschlossen, und die eigentliche Installation kann beginnen.
FreeBSD installieren
Dateisysteme mounten
Zunächst müssen die eben erzeugten Dateisysteme gemounted werden, damit die Installation erfolgen kann:
mkdir /mnt/inst
mount /dev/ufs/root /mnt/inst
mkdir /mnt/inst/usr /mnt/inst/var /mnt/inst/tmp
mount /dev/ufs/usr /mnt/inst/usr
mount /dev/ufs/var /mnt/inst/var
mount /dev/ufs/tmp /mnt/inst/tmp
Minimalsystem installieren
Jetzt kann endlich das FreeBSD-System installiert werden. Ich installiere an dieser Stelle nur ein absolutes Minimalsystem; den Rest installiere ich später nach:
setenv DESTDIR /mnt/inst
cd /mnt/disk1/7.2-RELEASE/base
./install.sh
# Für den Bau eines eigenen Kernels werden noch ein paar Kleinigkeiten benötigt:
cd /mnt/disk1/7.2-RELEASE/src
./install.sh sys base
Kernel erstellen
Der GENERIC-Kernel schleppt mir zu viel Ballast mit sich herum; außerdem wäre es sinnvoll, GEOM_MIRROR direkt in den Kernel einzukompilieren – das vermeidet später Ärger beim booten…
Dazu muss man in das eben installierte FreeBSD-System „einsteigen“:
mount -t devfs devfs /mnt/inst/dev
chroot /mnt/inst
Nun kann man sich seine eigene Kernel-Konfiguration erstellen:
cd /usr/src/sys/amd64/conf/
mkdir /root/kernel
cp GENERIC /root/kernel/CUSTOM
ln -s /root/kernel/CUSTOM
cat > /etc/make.conf << EOF
# Global settings
CPUTYPE?= nocona
CFLAGS= -O2 -pipe
# Kernel
KERNCONF= CUSTOM
MODULES_OVERRIDE= opensolaris zfs
EOF
vi CUSTOM
Den Inhalt der Datei /etc/make.conf bitte den eigenen Bedürfnissen anpassen!
Eine für OVH-Server recht brauchbare Kernel-Konfiguration könnte etwa wie folgt aussehen (funktioniert nur für den „SuperPlan Max“, die kleineren Server haben ein RealTek NIC verbaut und benötigen daher mii und re):
cpu HAMMER
ident CUSTOM
options SCHED_ULE # ULE scheduler
options PREEMPTION # Enable kernel thread preemption
options INET # InterNETworking
options INET6 # IPv6 communications protocols
options FFS # Berkeley Fast Filesystem
options SOFTUPDATES # Enable FFS soft updates support
options UFS_ACL # Support for access control lists
options UFS_DIRHASH # Improve performance on big directories
options UFS_GJOURNAL # Enable gjournal-based UFS journaling
options MD_ROOT # MD is a potential root device
options PROCFS # Process filesystem (requires PSEUDOFS)
options PSEUDOFS # Pseudo-filesystem framework
options GEOM_PART_GPT # GUID Partition Tables.
options GEOM_LABEL # Provides labelization
options GEOM_MIRROR
options GEOM_ELI
device crypto
options COMPAT_43TTY # BSD 4.3 TTY compat [KEEP THIS!]
options COMPAT_IA32 # Compatible with i386 binaries
options SCSI_DELAY=5000 # Delay (in ms) before probing SCSI
options KTRACE # ktrace(1) support
options STACK # stack(9) support
options SYSVSHM # SYSV-style shared memory
options SYSVMSG # SYSV-style message queues
options SYSVSEM # SYSV-style semaphores
options _KPOSIX_PRIORITY_SCHEDULING # POSIX P1003_1B real-time extensions
options KBD_INSTALL_CDEV # install a CDEV entry in /dev
options ADAPTIVE_GIANT # Giant mutex is adaptive.
options STOP_NMI # Stop CPUS using NMI instead of IPI
options AUDIT # Security event auditing
options SMP # Symmetric MultiProcessor Kernel
device cpufreq
device acpi
device pci
device fdc
device ata
device atadisk # ATA disk drives
options ATA_STATIC_ID # Static device numbering
device scbus # SCSI bus (required for SCSI)
device ch # SCSI media changers
device da # Direct Access (disks)
device pass # Passthrough device (direct SCSI access)
device ses # SCSI Environmental Services (and SAF-TE)
device atkbdc # AT keyboard controller
device atkbd # AT keyboard
device psm # PS/2 mouse
device kbdmux # keyboard multiplexer
device vga # VGA video card driver
device splash # Splash screen and screen saver support
device sc
device agp # support several AGP chipsets
device sio # 8250, 16[45]50 based serial ports
device uart # Generic UART driver
device ppc
device ppbus # Parallel port bus (required)
device ppi # Parallel port interface device
device em # Intel PRO/1000 Gigabit Ethernet Family
device loop # Network loopback
device random # Entropy device
device ether # Ethernet support
device pty # Pseudo-ttys (telnet etc)
device md # Memory "disks"
device gif # IPv6 and IPv4 tunneling
device faith # IPv6-to-IPv4 relaying (translation)
device bpf # Berkeley packet filter
device pf
device pflog
options ALTQ
options ALTQ_CBQ
options ALTQ_RED
options ALTQ_RIO
options ALTQ_HFSC
options ALTQ_PRIQ
options ALTQ_NOPCC
Nun muss der Kernel nur noch übersetzt und installiert werden:
setenv DESTDIR /
cd /usr/src
make buildkernel
make installkernel
Abschließende Konfigurationsarbeiten
/etc/fstab erstellen
Noch im chroot:
cat > /etc/fstab << EOF
# Device Mountpoint FSType Options Dump Pass
/dev/ufs/root / ufs rw 1 1
/dev/ad0s2.eli none swap sw 0 0
/dev/ad1s2.eli none swap sw 0 0
/dev/ufs/usr /usr ufs rw,noatime 2 2
/dev/ufs/var /var ufs rw,noatime 2 2
/dev/ufs/tmp /tmp ufs rw,noatime,noexec,nosuid 2 2
EOF
Auf die Verschlüsselung der Swap-Bereiche mit GELI kann man auch verzichten – dazu den Suffix .eli einfach weglassen (in dem Fall kann man auch im Kernel auf GEOM_ELI und das Crypto-Device verzichten).
/etc/rc.conf erstellen
cat > /etc/rc.conf << EOF
# rc.conf
# version 2009-07-01
# ----- Ensure availability at boot time -----
fsck_y_enable="YES"
# ----- Security settings -----
syslogd_flags="-ss"
clear_tmp_enable="YES"
accounting_enable="YES"
auditd_enable="YES"
# ----- Network setup -----
defaultrouter="12.34.56.1"
hostname="myhost.mydomain.tld"
ifconfig_em0="inet 12.34.56.78 netmask 255.255.255.0"
# ----- Miscellaneous settings -----
keymap="german.iso"
# ----- Service Startups -----
sshd_enable="YES"
pf_enable="YES"
pflog_enable="YES"
gateway_enable="YES"
EOF
Netzwerk-Einstellungen und Hostname an die eigenen Gegebenheiten anpassen!
Netzwerk konfigurieren
cat > /etc/resolv.conf << EOF
nameserver 22.33.44.55
EOF
echo -e "12.34.56.78\tmyhost.mydomain.tld\myhost" >> /etc/hosts
Installation beenden
Soweit müsste es das jetzt gewesen sein. Die chroot-Umgebung können Sie jetzt mit exit verlassen. Anschließend müssen noch alle Datenträger wieder ausgehängt werden, bevor das System neu gestartet werden kann:
cd ~
umount /mnt/inst/dev
umount /mnt/inst/tmp
umount /mnt/inst/var
umount /mnt/inst/usr
umount /mnt/inst
shutdown -r now

Content is subject to the conditions of the