OpenIndiana: Zonen im privaten Netzwerk

Ziel ist es unter OpenIndiana (Oi_151a7) zwei Zonen zu konfigurieren, die Teil eines virtuellen privaten Netzwerkes (192.168.127.0/24) sind und jeweils ein eigenes (delegiertes) ZFS-Dateisystem besitzen.

Die Zonen werden über den virtuellen Switch stub127 miteinander verbunden sein:

# dladm create-etherstub stub127

Jede Zone erhält ein eigenes (exklusives) virtuelles Netzwerkinterface (vnic):

# dladm create-vnic -l stub127 vnic11
# dladm create-vnic -l stub127 vnic12
# dladm show-vnic
LINK OVER SPEED MACADDRESS MACADDRTYPE VID
vnic11 stub127 0 2:8:20:9a:cf:f9 random 0
vnic12 stub127 0 2:8:20:d7:18:55 random 0

Die Zonen werden im ZFS rpool/zones laufen und die delegierten Dateisysteme liegen unter rpool/zonefs. Zur Sicherheit bekommt rpool/zonefs noch ein Quota von 5GB:

# zfs create rpool/zones
# zfs create rpool/zonefs
# zfs set quota=5G rpool/zonefs
# zfs create rpool/zonefs/zone1
# zfs create rpool/zonefs/zone2
# zfs list -r rpool/zonefs
NAME         USED AVAIL REFER MOUNTPOINT
rpool/zonefs       94K 5,00G 32K /rpool/zonefs
rpool/zonefs/zone1 31K 5,00G 31K /rpool/zonefs/zone1
rpool/zonefs/zone2 31K 5,00G 31K /rpool/zonefs/zone2

Die Zonenkonfigurationen werden in Textdateien gespeichert …

vi zone1.cfg
create -b
set zonepath=/rpool/zones/zone1
set brand=ipkg
set autoboot=true
set ip-type=exclusive
add net
set physical=vnic11
end
add dataset
set name=rpool/zonefs/zone1
end
vi zone2.cfg
create -b
set zonepath=/rpool/zones/zone2
set brand=ipkg
set autoboot=true
set ip-type=exclusive
add net
set physical=vnic12
end
add dataset
set name=rpool/zonefs/zone2
end

… und zoneadm beim Anlegen der Zonen als Parameter übergeben:

# zonecfg -z zone1 -f zone1.cfg
# zonecfg -z zone2 -f zone2.cfg

Die Installation der Zonen dauert, abhängig von der Internetanbindung, einige Minuten:

# zoneadm -z zone1 install
A ZFS file system has been created for this zone.
 Publisher: Using openindiana.org (http://pkg.openindiana.org/dev/ ).
 Publisher: Using sfe (http://pkg.openindiana.org/sfe/).
 Publisher: Using sfe-encumbered (http://pkg.openindiana.org/sfe-encumbered/).
 Image: Preparing at /rpool/zones/zone1/root.
 Cache: Using /var/pkg/publisher.
Sanity Check: Looking for 'entire' incorporation.
 Installing: Packages (output follows)
 Packages to install: 132
 Create boot environment: No
Create backup boot environment: No
 Services to change: 4

DOWNLOAD PKGS FILES XFER (MB)
Completed 132/132 28252/28252 150.7/150.7

PHASE ACTIONS
Install Phase 40855/40855

PHASE ITEMS
Package State Update Phase 132/132 
Image State Update Phase 2/2

Note: Man pages can be obtained by installing pkg:/system/manual
 Postinstall: Copying SMF seed repository ... done.
 Postinstall: Applying workarounds.
 Done: Installation completed in 226,349 seconds.

Next Steps: Boot the zone, then log into the zone console (zlogin -C)
 to complete the configuration process.
# zoneadm -z zone2 install
[...]

Jetzt stehen zwei installierte, noch nicht konfigurierte Zonen zur Verfügung:

# zoneadm list -vi
ID NAME       STATUS    PATH               BRAND IP 
 0 global     running   /                  ipkg shared
 - zone1      installed /rpool/zones/zone1 ipkg excl 
 - zone2      installed /rpool/zones/zone2 ipkg excl

Die Konfiguration der Zonen wird während des ersten Bootvorganges erledigt. Der Aufruf zlogin -C stellt die hierzu nötig Verbindung zur Konsole der Zone her.

# zoneadm -z zone1 boot
# zlogin -C zone1
# zoneadm -z zone2 boot
# zlogin -C zone2

Um ausserdem vom Hostsystem aus auf das private Zonennetzwerk zugreifen zu können, muss ein weiteres virtuelles Netzwerkinterface erstellt werden und mit dem virtuellen Switch verbunden werden. vnic1 benötigt ausserdem eine IP-Adresse:

# dladm create-vnic -l stub127 vnic1
# ipadm create-addr -T static -a 192.168.127.1/24 vnic1/v4address
# dladm show-vnic
LINK OVER SPEED MACADDRESS MACADDRTYPE VID
vnic11 stub127 0 2:8:20:9a:cf:f9 random 0
vnic12 stub127 0 2:8:20:d7:18:55 random 0
vnic1  stub127 0 2:8:20:80:e2:ff random 0

Fertig sind die Zonen:

# zoneadm list -vi
  ID NAME             STATUS     PATH                           BRAND    IP    
   0 global           running    /                              ipkg     shared
   1 zone1            running    /rpool/zones/zone1             ipkg     excl 
   2 zone2            running    /rpool/zones/zone2             ipkg     excl

Um nicht ständig IP-Adressen tippen zu müssen, kann man die Netzwerknamen dem Hostsystem bekanntmachen …

# vi /etc/hosts
[...]
192.168.127.1   global
192.168.127.11  zone1
192.168.127.12  zone2

… und nötigenfalls in die Zonen kopieren.

# cp /etc/hosts /rpool/zones/zone1/root/etc/
# cp /etc/hosts /rpool/zones/zone2/root/etc/

Wenn das virtuelle Netzwerkinterface im Hostsystem auch nach einem Neustart existieren soll, muss man folgende Datei anlegen:

# vi /etc/hostname.vnic1
192.168.128.1/24

ZFS: Automatische Replikation via ssh

Ich habe das ZYNK-Script von Ben Rockwood dahingehend angepasst, dass es einen ZFS-Dataset vollständig (d. h. rekursiv, inklusive aller abhängigen Dateisysteme und Snapshots) auf einen Backup-Server repliziert. Das Script wird einmal am Tag via crontab aufgerufen, so dass die Daten der letzten Nacht auf dem Backup-Server zur Verfügung stehen. Es kann durchaus häufiger aufgerufen werden (jede Stunde oder auch mehrmals pro Stunde). Hier erst einmal das Skript:

#!/bin/bash
## ZYNK: The Zuper Zimple ZFS Sync (Replication) Tool
## Form: zynk local/dataset root@remote.host destination/dataset

# Please note: The reason this is so simple is because there is no error checking, reporting, or cleanup.
#               In the event that something goes wonkey, you'll manually need to fix the snapshots and
#               modify or remote the /var/run/zynk datafile which contains the most recent snapshot name.
# Furthermore, this absolutely relies on the GNU version of 'date' in order to get epoch time
# Before using, make sure you've distributed your SSH key to the remote host and can ssh without password.

if [ ! $3 ] 
then
        echo "Usage: zynk local/dataset root@remote.host destination/dataset"
        echo "WARNING: The destination is the full path for the remote dataset, not the prefix dataset stub."
        exit
fi

DATE=`date +%s`
if [ $DATE == "%s" ]
then
        echo "Must use GNU Date, please install and modify script."
        exit
fi

if [ -e /var/run/zynk ] 
then
        # Datafile is found, creating incr.
        echo "Incremental started at `date`"
        zfs snapshot -r ${1}@zynk-${DATE}
        zfs send -RI  ${1}@zynk-`cat /var/run/zynk` ${1}@zynk-${DATE} | ssh ${2} zfs recv -Fd ${3}
        zfs destroy -r ${1}@zynk-`cat /var/run/zynk`
        ssh ${2} zfs destroy -r ${3}@zynk-`cat /var/run/zynk`
        echo ${DATE} > /var/run/zynk
        echo "Incremental complete at `date`"
else 
        # Datafile not found, creating full.
        echo "Full started at `date`"
        zfs snapshot -r ${1}@zynk-${DATE}
        zfs send -R     ${1}@zynk-${DATE} | ssh ${2} zfs recv -Fd ${3}
        echo ${DATE} > /var/run/zynk
        echo "Full completed at `date`"
fi

In diesem Szenario soll tank/export vom Server (rubin) nach rpool/export auf dem Backup-Server (onyx) repliziert werden. Beide Systeme laufen mit OpenIndiana build 151a7. Das ZYNK-Skript liegt auf rubin unter /usr/bin/zynk. Der initiale Aufruf sieht wie folgt aus und dauerte hier mehrere Stunden (für 406 GB):

root@rubin:~# rm /var/run/zynk
root@rubin:~# zync tank/export onyx rpool
Full started at 20. November 2012 15:21:09 CET
Full completed at 21. November 2012 04:35:42 CET

Nach diesem ‚Full Backup‘ sind die inkrementellen Sicherungen schnell erledigt, da nur noch die Dateisystemänderungen (lies: Snapshots) gesendet werden, die seit dem letzten ZYNK-Aufruf angefallen sind:

root@rubin:~# zynk tank/export onyx rpool
Incremental started at 21. November 2012 13:39:24 CET
Incremental complete at 21. November 2012 13:48:11 CET

Um ZYNK jeden Tag um 2:00 Uhr automatisch aufzurufen, muss schließlich folgende Zeile in die root-crontab (crontab -e) eingetragen werden:

0  2 * * * /usr/bin/zynk tank/export onyx rpool

Das Skript setzt voraus, dass ein passwortloses (ssh-key-basiertes) root-Login möglich ist. Es empfiehlt sich allerdings einen dediziert zynk-Account mit entsprechenden Attributen zu benutzen. Wie schon dem Originalskript von Ben Rockwood, fehlt auch dieser Version jegliche Fehlerprüfung oder -behandlung.

Postfix und zusätzliche Domains

Immer wenn ein Mailserver ausfällt und ein anderer dessen Aufgaben mit übernehmen soll, sitze ich vor dieser doofen Posftfix-Konfigurationsdatei und frage mich, an welchen Stellen ich den zusätzlichen Hostnamen noch eintragen soll. Jetzt schreib‘ ich es einmal auf:  ($IP ist die Adresse des ausgefallenen Servers, $NAME ist sein fully qualified domain name (fqdn). Ich habe es mir zu Angewohnheit gemacht, die IP-Adresse des aufgefallenen Servers auf den Backup-Server zu übertragen, bis dieser wieder einsatzbereit ist.)

  1. IP an Interface anhängen
    # ip addr add $IP dev eth0
  2. /etc/postfix/main.cf editieren
    [...]
    inet_interfaces = $myhostname, localhost, $NAME
    [...]
    mydestination = $myhostname, localhost.$mydomain, localhost, $NAME
    [...]
  3. /etc/init.d/postfix stop
  4. /etc/init.d/postfix start

Das stopstart-Prozedere ist wichtig. Ein einfaches reload reicht nicht aus, restart sollte aber auch gehen.

Shadow-Passwords erzeugen

Verschlüsselte Passwörter, wie sie in /etc/shadow gespeichert werden, kann man mit der folgenden Zeile für das Passwort password erzeugen:

# echo "password" | openssl passwd -1 -stdin $1$YFi.APLv$VZiopcJ9udPYifg/4E7vo/ 

Die Option -1 steht für den zu benutzenden MD5-Algorithmus. Die Ausgabe kann via Copy-Paste für den Benutzer USERNAME in die /etc/shadow eingefügt werden.

USERNAME:$1$YFi.APLv$VZiopcJ9udPYifg/4E7vo/:15132::::::

Diese Vorgehensweise ist dann nützlich, wenn man nicht auf Tools wie adduser oder useradd zurückgreifen kann oder will, weil beispielsweise separate passwd/shadow files für einen NIS-Server gepflegt werden. Zum Anlegen eines neuen Benutzers USERNAME muss noch eine Zeile wie die folgende zu /etc/passwd hinzugefügt werden:

USERNAME:x:541:100:Vorname Nachname (Kommentar):/home/USERNAME:/bin/bash

wobei 514 die userID und 100 die groupID des Benutzer sind.

Automatischer Neustart eines Jobs bei Absturz

Manchmal hat man Jobs die einfach nur laufen müssen (z. b. NIS Server, Name Server). Und wenn sie einmal abstürzen, interessiert mich erst einmal gar nicht das Warum. Vielmehr möchte ich das der Jobs einfach wieder los läuft – Am besten von allein. Die näheren Umstände des Absturzes kann ich ja später noch anhand des Logfiles untersuchen. Unter Solaris gibt es hierzu die Service Management Facility (SMF), die sicher stellt, dass Systemdienste laufen und nötigenfalls auch einen (oder mehrere) Neustart(s) des Dienstes versucht, falls dieser abstürzen sollte. Unter Linux habe ich noch kein Äquivalent dazu gefunden. Eine brutale Methode einen Job bei gelegentlichen Abstürzen automatisch erneut zu starten, ist, ihn in einer Endlosschleife aufzurufen:

while true; do ./jobs.sh; done

Diese Methode geht allerdings nach hinten los, falls jobs.sh ein ernsthaftes Problem hat und sofort nach dem Start wieder terminiert. Zur Sicherheit sollte man also die Neustartorgie durch einen Zähler begrenzen, z. B. auf 1000 Versuche:

for i in {1..1000}; do ./jobs.sh; done

Das ist nicht elegant, hilft aber gelegentlich abstürzende Programme am Laufen zu halten.  Ist der kritische Zählerstand erreicht, wird nicht weiter versucht den Job neu zu starten. Man kann sich hierüber z. B. per Mail an root an  informieren lassen:

for i in {1..1000}; do ./jobs.sh; done ; echo "Message" | mail -s "Job died." root