How to get rid of the plus signs in Bonnie++ output

I’m confident that many of you have seen this countless times when using the popular “standard” harddisk benchmarking tool, Bonnie++:

# bonnie++ -u root -d /mnt/1
Version 1.03d       ------Sequential Output------ --Sequential Input- --Random-
                    -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine        Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP  /sec %CP
grml             8G 51447  98 129399  39 56253  16 43505  97 136526  16 985.2   1
                    ------Sequential Create------ --------Random Create--------
                    -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                 16 +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++ +++++ +++


The manpage explains this as follows:

If a test completes in less than 500ms then the output will be displayed as “++++”. This is because such a test result can’t be calculated accurately due to rounding errors and I would rather display no result than a wrong result.

You will most likely see this for the lower “Create” section of Bonnie++. While the tests from the upper “Input/Output” section always make an educated guess and use twice the size of RAM, the “Create” tests have no way of guessing the proper number of files. Look for the word “files” near the left: Bonnie++ always defaults to this 16 x 1024 files for testing. Indeed, munging 16000 files in less than half a second sounds like no big task for a fast system.

Bonnie++ takes an option that instructs it to use more files for the “Create” tests, e.g. -n 128 tells it to test with 128 x 1024 files. In this second run, I’ll also disable the “Input/Output” tests by setting -s0:

# bonnie++ -u root -d /mnt/1 -s 0 -n 128
Version 1.03d       ------Sequential Create------ --------Random Create--------
grml                -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
              files  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP  /sec %CP
                128 46336  84 +++++ +++ 31277  50 32574  65 +++++ +++ 12803  23


We now get values for file creation and deletion, but reads are still excessively fast. Since Bonnie++ discarded the measurement because it finished within less than 500 ms, it may be possible to estimate that the system is capable of performing at least (128 x 1024 x 2 = 262144) read operations per second.

Further increasing the number may not be possible due to lack of inodes on your filesystem. Also, such an extremely high number of files and file operations may decrease overall file system performance.
The system used for testing was a dual AMD Opteron 285 with a hardware RAID 0 of 3 UW320 disks @ 15k RPM.

Wired, Juli 1997: The Long Boom

Beim Umziehen und Aufräumen fielen mir die Ordner mit archivierten Zeitschriften in die Hand. Zentnerweise Altpapier habe ich weggeschmissen. Ich wusste aber, dass sich irgendwo ein ganz besonderes Schätzchen verstecken müßte. Und tatsächlich:
thelongboom
Wir hatten 1997 ja nichts. Es gab kein Slashdot, kein Digg, keine Blogs, kein Twitter, keine Wikipedia. Na gut, wir hatten das Usenet. Google gab es übrigens auch noch nicht. Aber für den echt allerheißesten und visionären Shice gab es das Leitmedium WIRED. Erhältlich als Import am Bahnhofskiosk für 17 Deutsche Mark.

Im Juli 1997 werden in WIRED sensationelle technische Neuerungen beworben, wie z.B. der Apple Powermac 9600/200MP mit 2x200MHz. Auf vielen Werbebildern (und die gab es in WIRED wirklich im Überfluss) sieht man Röhrenmonitore, bei denen man sich fragen muss, wie die Statik gängiger Wohnungen das jemals ausgehalten haben kann.

WIRED stand auf jeden Fall an der Speerspitze so einer Art digitaler Revolution. Und WIRED sagte damals im Essay “The Long Boom” eine digitale Revolution voraus, die der Menschheit über Jahrzehnte Freiheit, Frieden und Wohlstand bringen würde.

Der Boom fällt aus. Das wissen wir hier in der “zivilisierten” Welt, und das wissen auch die ganzen armen Schweine in der dritten Welt, die heute so wenig zu beißen haben, wie vor 12 Jahren. Aber es kann ja auch sein, dass die ganzen Afrikaner eh nicht gemeint waren, als von “a better environment for the whole world” die Rede war. Davon, dass für jedes neue Gadget, das man sich kauft, ein Gorillababy abgemurkst wird, will ja auch keiner was wissen.

Auch WIRED wusste natürlich, dass möglicherweise doch nicht alles nach Plan laufen könnte und so wurden gleich “10 Scenario Spoilers” umrissen, die so genau den Nagel auf den Kopf treffen sollten, dass es heute schon fast erschreckend wirkt. Ich zähle mal nur die auf, die eingetreten sind:

2.
New technologies turn out to be a bust. They simply don’t bring the expected productivity increases or the big economic boosts.
3.
Russia devolves into a kleptocracy run by a mafia or retreats into quasicommunist nationalism that threatens europe.
6.
Major rise in crime and terrorism forces the world to pull back in fear. People who constantly feel they could be blown up or ripped off are not in the mood to reach out and open up.
8.
Energy prices go through the roof. Convulsions in the Middle East disrupt the oil supply, and alternative energy sources fail to materialize.
10.
A social and cultural backlash stops progress dead in its tracks. Human beings need to choose to move forward. They just may not…

Die Vorhersage über den “Long Boom” ist nicht eingetreten, aber die “Scenario Spoilers” haben den Nagel wirklich auf den Kopf getroffen. Herzlichen Glückwunsch dazu. 🙁

OpenSSH connection multiplexing

The Challenge
I was in touch with a developer the other day who used SSH to programmatically connect to a remote machine where he would start some kind of processing job. Unfortunately, he was in trouble when he wanted to kill the remote process. Killing the local SSH client would leave his job active. He claimed that there used to be some sort of signal forwarding feature in OpenSSH on the machine where he had developed his application in OpenSSH 3.x days, but this feature seems to have been removed by now.
I wasn’t able to confirm anything of this, but this gentleman’s problem got me curious. I started to wonder: Is there some kind of sideband connection that I might use in SSH to interact with a program that is running on a remote machine?
The first thing I thought of were port forwards. These might actually be used to maintain a control channel to a running process on the other side. On the other hand, sockets aren’t trivial to implement for a /bin/ksh type of guy, such as the one I was dealing with. Also, this approach just won’t scale. Coordination of local and remote ports is bound to turn into a bureaucratic nightmare.
I then started to skim the SSH man pages for anything that looked like a “sideband”, “session control” or “signaling” feature. What I found, were the options ControlMaster and ControlPath. These configure connection multiplexing in SSH.
Proof Of Concept
Manual one-shot multiplexing can be demonstrated using the -M and -S options:
1) The first connection to the remote machine is opened in Master mode (-M). A UNIX socket is specified using the -S option. This socket enables the connection to be shared with other SSH clients:

localhost$ ssh -M -S ~/.ssh/controlmaster.test.socket remotehost


2) A second SSH session is attached to the running session. The socket that was opened before is specified with the -S option. The remote shell opens without further authentication:

localhost$ ssh -S ~/.ssh/controlmaster.test.socket remotehost


The interesting thing about this is that we now have two login sessions running on the remote machine, who are children of the same sshd process:

remotehost$ pstree -p $PPID
sshd(4228)─┬─bash(4229)
           └─bash(4252)───pstree(4280)


What About The Original Challenge?
Well, he can start his transaction by connecting to the remote machine in Master mode. For simplicity’s sake, let’s say he starts top in one session and wants to be able to kill it from another session:

localhost$ ssh -t -M -S ~/.ssh/controlmaster.mytopsession.socket remotehost top


Now he can pick up the socket and find out the PIDs of all other processes running behind the same SSH connection:

localhost$ ssh -S ~/.ssh/controlmaster.mytopsession.socket remotehost 'ps --ppid=$PPID | grep -v $$'
  PID TTY          TIME CMD
 4390 pts/0    00:00:00 top


This, of course, leads to:

localhost$ ssh -S ~/.ssh/controlmaster.mytopsession.socket remotehost 'ps --no-headers -o pid --ppid=$PPID | grep -v $$ | xargs kill'


Then again, our shell jockey could just use PID or touch files. I think this is what he’s doing now anyway.
Going Fast And Flexible With Multiplexed Connections
With my new developer friend’s troubles out of the way, what else could be done with multiplexed connections? The SSH docs introduce “opportunistic session sharing”, which I believe might actually be quite useful for me.
It is possible to prime all SSH connections with a socket in ~/.ssh/config. If the socket is available, the actual connection attempt is bypassed and the ssh client hitches a ride on a multiplexed connection. In order for the socket to be unique per multiplexed connection, it should be assigned a unique name through the tokens %r (remote user), %h (remote host) and %p (destination port):

ControlPath ~/.ssh/controlmaster.socket.%r.%h.%p
# Will create socket as e.g.: ~/.ssh/controlmaster.socket.root.remotehost.example.com.22


If there is no socket available, SSH connects directly to the remote host. In this case, it is possible to automatically pull up a socket for subsequent connections using the following option in ~/.ssh/config:

ControlMaster auto


So Where’s The Actual Benefit?
I use a lot of complex proxied SSH connections who take ages to come up. However, connecting through an already established connection goes amazingly fast:

# Without multiplexing:
localhost$ time ssh remotehost /bin/true
real    0m1.376s
...
# With an already established shared connection:
localhost$ time ssh remotehost /bin/true
real    0m0.129s
...


I will definitely give this a try for a while, to see if it is usable for my daily tasks.
Update, 2009/05/04: No, it isn’t. Disconnecting slave sessions upon logout of the master session are too much of a nuisance for me.

Using the SSH agent from daemon processes

One of my more recent installations, the BackupPC server I wrote about earlier, needs full access as the user root to his clients in order to retrieve the backups. Here’s how I implemented authentication on this machine.
BackupPC runs as its own designated user, backuppc. All authentication procedures therefore happen in the context of this user.
The key component in ssh-agent operation is a Unix domain socket that the ssh client uses to communicate with the agent. The default naming scheme for this socket is /tmp/ssh-XXXXXXXXXX/agent.<ppid>. The name of the socket is stored in the environment variable SSH_AUTH_SOCK. The windowing environments on our local workstations usually run as child processes of ssh-agent. They inherit this environment variable from their parent process (the agent) and therefore the shells running inside our Xterms know how to communicate with it.
In the case of a background server using the agent, however, things are happening in parallel: On one hand, we have the daemon which is being started on bootup. On the other hand, we have the user which the daemon is running as, who needs to interactively add his SSH identity to the agent. Therefore, the concept of an automatically generated socket path is not applicable and it would be preferable to harmonize everything to a common path, such as ~/.ssh/agent.socket.
Fortunately, all components in the SSH authentication system allow for this kind of harmonization.
The option -a to the SSH agent allows us to set the path for the UNIX domain socket. This is what this small script, /usr/local/bin/ssh-agent-wrapper.sh does on my backup server:

#!/bin/bash
SOCKET=~/.ssh/agent.socket
ENV=~/.ssh/agent.env
ssh-agent -a $SOCKET > $ENV


When being started in stand-alone mode (without a child process that it should control), ssh-agent outputs some information that can be sourced from other scripts:

SSH_AUTH_SOCK=/var/lib/backuppc/.ssh/agent.socket; export SSH_AUTH_SOCK;
SSH_AGENT_PID=1234; export SSH_AGENT_PID;
echo Agent pid 1234;


This file may sourced from the daemon user’s ~/.bash_profile:

test -s .ssh/agent.env && . .ssh/agent.env


However, this creates a condition where we can’t bootstrap the whole process for the first time. So it might be somewhat cleaner to just set SSH_AUTH_SOCK to a fixed value:

export SSH_AUTH_SOCK=~/.ssh/agent.socket


Here’s the workflow for initializing the SSH agent for my backuppc user after bootup:

root@foo:~ # su - backuppc
backuppc@foo:~ $ ssh-agent-wrapper.sh
backuppc@foo:~ $ ssh-add


In the meantime, what is happening to the backuppc daemon?
In /etc/init.d/backuppc, I have added the following line somewhere near the top of the script:

export SSH_AUTH_SOCK=~backuppc/.ssh/agent.socket


This means that immediately after boot-up, the daemon will be unable to log on to other systems, as long as ssh-agent has not been initialized using ssh-agent-wrapper.sh. After starting ssh-agent and adding the identity, the daemon will be able to authenticate. This also means that tasks in the daemon that do not rely on SSH access (in the case of BackupPC, things like housekeeping and smbclient backups of “Windows” systems) will already be in full operation.

Massengenerierung von PGP-Schlüsseln

Wenn man jemandem ins Pflichtenheft geschrieben hat, daß seine Software unter der Last von 10.000 PGP-Keys nicht schwächeln darf, muß man irgendwann auch mal ins kalte Wasser springen und tatsächlich 10.000 Keys generieren:

#!/bin/sh -e
export LANG=C
for I in `seq 1 10000`
do
        NUMBER=`printf "%5.5d\n" $I`
        USERNAME="Testuser #$NUMBER"
        COMMENT="auto-generated key"
        EMAIL="testuser-$NUMBER@pgp-loadtest.example.com"
        (
        cat <<EOF
        %echo Generating Key for $EMAIL
        Key-Type: DSA
        Key-Length: 1024
        Subkey-Type: ELG-E
        Subkey-Length: 1024
        Name-Real: $USERNAME
        Name-Comment: $COMMENT
        Name-Email: $EMAIL
        Expire-Date: 2009-01-01
        Passphrase: foo
        %commit
EOF
        ) | gpg --gen-key --batch --no-default-keyring \
            --secret-keyring /var/tmp/gpg-test.sec \
            --keyring /var/tmp/gpg-test.pub
done


Die schlechte Nachricht: Spätestens nach dem dritten Key wird GnuPG eine Pause einlegen, weil der Entropie-Pool ausgelutscht ist. Ich habe meine Lösung für dieses Problem gefunden, indem ich (und das ist wirklich sehr, sehr böse, bitte auf keinen Fall zuhause nachmachen) /dev/urandom zu /dev/random umgelötet habe. Kreative alternative Lösungsvorschläge für das schmutzige kleine Entropie-Problem werden auf jeden Fall gern entgegengenommen.

Swap im Reality Check

Swap unter Linux scheint so eine Sache zu sein, um die sich reichlich Mythen und Legenden ranken. Deshalb schreibe ich heute mal auf, was ich so von der Sache halte.
Wie wir alle wissen, wird der Swap-Bereich zusammen mit dem realen Arbeitsspeicher (RAM) zum sogenannten Virtual Memory (VM) zusammengefaßt. Auf einem System mit 2 GB RAM und 2 GB Swap steht also ein für Appikationen nutzbares VM von 4 GB zur Verfügung. Die Hälfte davon befindet sich als Swap auf Festplatte. Zugriffe darauf sind sehr viel langsamer als Zugriffe auf den normalen Arbeitsspeicher.
Mythos: “Jedes UNIX-System braucht Swap!”
Swap ist nicht mehr als eine sehr, sehr langsame Speichererweiterung. Ist der Arbeitsspeicher voll, werden Speichersegmente auf Festplatte ausgelagert. Dadurch, daß diese Auslagerung deutlich langsamer als normale RAM-Aktiviät geschieht, wird das System in aller Regel extrem langsam. Diese Auslagerungsaktivität kann im Rahmen einer Überwachung erkannt werden. Idealerweise wird auch die Problemquelle identifiziert und der entsprechende Prozeß von Hand beendet, so daß das System weiterlaufen kann.
Daraus folgt im Prinzip nichts anderes, als daß Swap nichts weiter bringt, als einen gefühlten Zeitgewinn für das Beenden von Speicherfressern.
Auf der Hand liegt andererseits auch, daß man z.B. ein Flash-basiertes (und damit read-only)-System ohne Swap betreiben können muß. Folglich gilt, daß ein Linux-System ohne Swap problemlos laufen kann, solange der Arbeitsspeicher für den gesamten Speicherbedarf aller zu benutzenden Applikationen ausreichend dimensioniert ist.
Mythos: “Swap muß immer auf einer eigenen Partition liegen!”
Unter Linux (und vermutlich auch den meisten anderen UNIX-Systemen) ist es problemlos möglich, anstelle einer Swap-Partiton ein Swapfile zu verwenden. Die Vorgehensweise dazu ist dort in der Manpage von mkswap beschrieben. Da ein swappendes System ohnehin ein schweres Problem mit der Performance von Speicherzugriffen hat, kann man den marginalen zusätzlichen Performanceverlust durch die Dateisystemebene praktisch vernachlässigen. (Eine Ausnahme gilt, die erwähne ich im übernächsten Absatz.)
Mythos: “Es muß immer doppelt soviel Swap vorhanden sein, wie Arbeitsspeicher!”
Das ist so eine ganz alte Daumenregel, deren historischer Hintergrund schwer durchschaubar ist. Sie ist vermutlich teilweise darin begründet, daß es UNIX-Systeme gegeben haben soll, bei denen der Arbeitsspeicher auf Swap gespiegelt wurde. Um also eine wirksame Vergrößerung des VM durch Swap zu haben, war also wesentlich mehr Swap als Arbeitsspeicher erforderlich.
Eine Mindestgröße für Swap ergibt sich bei tragbaren Systemen, die für die Hibernation ihren Arbeitsspeicher auf Swap auslagern. Dies ist meines Wissens die einzige Situation, in der nicht nur eine wirkliche Mindestgröße für Swap vorliegt, sondern in der es sich auch tatsächlich um eine Swap-Partition handeln muß.
Generell gilt, daß es über die altbekannte Daumenregel hinaus keine feststehende Regel für die Swap-Größe gibt. Wer sich an der alten Regel festhalten will, darf das gern tun. Man sollte sich aber durchaus fragen, welche Dinge man von einem System mit 8, 16 oder 32 GB Swap zu erwarten glaubt.
Mythos: “Aber tmpfs braucht Swap!”
Nur um dem unvermeidlichen Kommentar vorzubeugen: Das tmpfs-Dateisystem, z.B. unter Solaris und Linux, braucht nicht Swap, sondern Virtual Memory. Es wird also bei ausreichenden Platzverhältnissen im Arbeitsspeicher gehalten, kann aber von Applikationen auf Swap verdrängt werden. Es stellt sich die Frage, inwieweit ein dediziertes Filesystem für /tmp überhaupt eine Berechtigung hat, wenn seine Schreib- und Leseperformance im Prinzip unvorhersagbar sind.

What goes up, must come down. Ask any system administrator.