All posts by Florian Hackenberger

Backup for Btrfs with BtrBk

For my Linux laptop, I was looking for a replacement of my previous backup solution, which used rsync and was therefore quite slow, as it had to sift through the whole filesystem. As I migrated from ext4 to btrfs during my last OS upgrade, I switched my backup system to use the (quite simple) btrbk backup script.

My goals for this are:

  • Use an encrypted external SSD drive
  • Run snapshots regularly in the background through cron / systemd
  • Backup the snapshots to the external SSD automatically whenever I dock my laptop
  • Unmount the external SSD after the backup finished to I can un-dock my laptop

I attached an external SSD drive to my screen’s USB hub to keep things tidy, formatted it using LUKS and btrfs and set the following options to configure btrbk in /etc/btrbk/btrbk.conf:

lockfile                   /var/lock/btrbk.lock
# Snapshots are for locally retained snapshots
snapshot_preserve_min   2d
snapshot_preserve       14d
# Target is the actual backup drive
target_preserve_min     no
target_preserve         20d 10w *m

# Backup to external disk mounted on /media/backup-usb
volume /media/btrfs-root
  # Create snapshots in /mnt/btr_pool/btrbk_snapshots
  snapshot_dir btrbk_snapshots

  # Target for all subvolume sections:
  target /media/backup-usb

  subvolume @
  subvolume @home

You can read-up on the various options for snapshot/backup retention in the btrfs manpage. The other options basically do:

  • Tell btrbk to create snapshots of the subvolumes @ and @home from the root of by btrfs filesystem mounted at /media/btrfs-root
  • Use /media/backup-usb (the external SSD drive) as the target for backing up the snapshots

Then I changed the default btrbk systemd unit file to only create the snapshots (triggered through the default btrbk.timer unit).

/etc/systemd/system/btrbk.service.d/override.conf edited using sudo systemctl edit btrbk.service:

[Service]
ExecStart=/usr/bin/btrbk snapshot

And I added a mount point for the external drive in /etc/fstab:

/dev/mapper/luks-24e9cb66-62f4-4382-8bfe-92eae9f8eaba /media/backup-usb btrfs subvol=@,acl,noatime 0    1

Then I added an override for the (on the fly generated) mount point service: /etc/systemd/system/media-backup\x2dusb.mount.d/override.conf edited using sudo systemctl edit media-backup\\x2dusb.mount:

[Unit]
# Propagate start/stop/restart of the BindsTo service to this one. I.e. unmount the drive after completing the backup
BindsTo=btrbk-backup.service

And finally created a new systemd unit to run the backup part (as opposed to the snapshot part) of btrbk. /etc/systemd/system/btrbk-backup.service.

# Starts a 'btrbk resume' run (i.e. puts all snapshots onto the backup volume)
# after the backup volume has been mounted in the system
# ATTENTION:
# Also depends on /etc/systemd/system/btrbk.service.d/override.conf setting 'ExecStart=/usr/bin/btrbk snapshot' to only run the snapshot part from a timer
# And 'systemctl edit media-backup\\x2dusb.mount' setting BindsTo=btrbk-backup.service in order to start the backup and unmount the drive afterwards

[Unit]
Description=btrbk backup to external disk
Documentation=man:btrbk(1)
# After means If both this and the After service are started, wait for the After service before starting this one
After=media-backup\x2dusb.mount

[Service]
Type=oneshot
ExecStart=/usr/bin/btrbk resume

[Install]
# WantedBy means start this service when the WantedBy is started
WantedBy=media-backup\x2dusb.mount

After adding the new service you need to run:

$ sudo systemctl daemon-reload
$ sudo systemctl enable btrbk-backup.service

Now you can watch the logs for the relevant services and mount the drive to test it out:

$ sudo journalctl -f -u btrbk-backup.service -u media-backup\x2dusb.mount

It should run the btrbk resume command and unmount the drive straight away. What remains for me is setting up auto-mounting with gnome. I do store the LUKS password in my keyring, but it would be nice to auto-mount the external drive whenever I dock my laptop. Sure I can enable auto-mounting in general in gnome, but that’s not what I want.

How-to Telekom Glasfaser PPPoE modem

The 2.5Gigabit fiber modem that Deutsch Telekom sells needs a PPPoE connection, even with a subscription like Company Pro. Here’s a little how-to for testing the connection to it on Ubuntu, before trying to get it working with a third party router.

After connecting and registering the modem, I plugged it into my laptop. It has the IP Address 192.168.100.1, so statically configure your laptop to e.g. 192.168.100.2 and connect to it to look at the web interface.

The modem requires a PPPoE connection with VLAN tag 7 to work. Most older routers are unable to provide the VLAN tagging, so they won’t work. Despite other infos on the internet, enabling EasyConnect on the Magenta portal, still requires a correct username/password for the connection.

See Telekom NetworkManager setup for the set-up I successfully used on my Ubuntu laptop to test the connection. I tried before to set up the connections using the Gnome NM UI, but was unable to establish the connection.

Here are the commands that worked. enx22e04ca4a742 is my USB ethernet interface. Network Manager will append .7 to it, but needs to truncate by two letters to attain to the maximum length. REPLACEME is the Personliches Kennwort that belongs to the Zugangsnummer. The username is of the format:

AnschlusskennungZugangsnummer#Mitbenutzernummer@telekom.de.

$ nmcli connection add type vlan vlan.parent enx22e04ca4a742 vlan.id 7 ipv4.method disabled ipv6.method ignore connection.autoconnect no
$ nmcli connection up vlan # Use tab to complete and choose the right
$ ip addr | grep ‘.7’ # Read the new interface name
$ nmcli connection add type pppoe username 002234098466551239265619#0001@t-online.de password REPLACEME ifname ppp1 pppoe.parent enx22e04ca4a7.7
$ nmcli connection up pppoe-ppp1 # Use tab to complete

Now you should see the following in the logs:

$ sudo journalctl -n 200 -f -u NetworkManager
PAP authentication succeeded
peer from calling number CC:E1:7F:AD:9C:FE authorized
nm-ppp-plugin: status 8 / phase ‘network’
PAP authentication succeeded
peer from calling number CC:E1:7F:AD:9C:FE authorized
local  IP address 80.153.148.32
remote IP address 62.156.244.30
primary   DNS address 217.237.149.205
secondary DNS address 217.237.151.51

How-to: PostgreSQL upgrades on large DBs

TL;DR; Use pglogical to upgrade between major versions of PostgreSQL with minimal downtime when running very large DB installations.

For databases beyond a couple of GB in size, an upgrade between major versions of PostgreSQL would need to use pg_basebackup and pg_upgrade, but at a size of around 1TB, even this means too long of a downtime for a SaaS application. The solution is to use logical asynchronous replication to create a hot-standby running a newer version of PostgreSQL. pglogical is an extension that works well for this, down to PostgreSQL version 9.4.

The upgrade procedure we used consists roughly of the following steps:

  • Prepare the new DB (primary keys, schema, users)
  • Install pglogical into the DB
  • Drop indices on large tables
  • Synchronise the data
  • Re-create indices and sync sequences
  • Switch over

At Infostars we upgraded the ~1TB Postgresql 9.4 database with multiple extensions (PostGIS, audit) to version 13 with a minimal downtime window of < 10 minutes. The production set-up consists of two DB servers (hot standby) with pgpool2 in front of the DB.

Preparation

As a preparation, we set-up a second set of master/standby servers and provisioned them using ansible. We use ansible to set-up the docker host and copy over a docker-compose set-up, which contains the backend, cron jobs and HTTP servers for the frontend.

In order to be able to use pglogical, it’s important that every table has a primary key constraint. The following query shows tables which are missing the primary key:

sudo -u postgres psql $DBNAME -c "SELECT
  n.nspname as schema,
  c.relname as table
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND NOT EXISTS (
  SELECT 1 FROM pg_constraint con
  WHERE con.conrelid = c.oid AND con.contype = 'p'
)
AND n.nspname <> ALL (
  ARRAY ['pg_catalog', 'sys', 'dbo', 'information_schema']
);"

We had to fix the schema a little, as there were some old tables left over from the days we used hibernate auto update, instead of liquibase.

In order to connect the new to the old servers, we use an OpenVPN tunnel. The servers have the following internal IPs that you’ll find in the scripts below:

10.56.34.2  New master DB server
10.56.34.1  Old master DB server

In order to make the migration a little easier, I set-up password-less SSH access from the new server to the old one. I also use a few environment variables in the scripts below, to make it more readable:

DBNAME=YOURDBNAME
DBUSER=YOURREGULARAPPDBUSER
SSHOLD='ssh root@10.56.34.1'
PSQLOLD="ssh root@10.56.34.1 sudo -u postgres psql"
PGLPASS="YOURSECRETPASSWORDFORPGLOGICAL"

We have all the data in a single database and use two namespaces (public and audit). We do use different database users to connect to the DB.

Copying the DB schema

Export the schema and DB users to files:

$ $SSHOLD sudo -u postgres pg_dump -Fc -C -s $DBNAME > $DBNAME_schema.dmp
$ $SSHOLD sudo -u postgres pg_dumpall -g > globals.sql

DBNAME_schema.dmp now contains a PostgreSQL binary dump of only the DB schema and globals.sql a text dump of the DB users. Now make sure your new DB postgresql cluster is up and running. In our case, we only started the master DB and kept the hot-standby off until after the initial synchronisation run.

$ sudo -u postgres psql < globals.sql
$ sudo -u postgres psql -c "CREATE DATABASE $DBNAME WITH TEMPLATE = template0 ENCODING = 'UTF8' LC_COLLATE = 'en_US.UTF-8' LC_CTYPE = 'en_US.UTF-8';"
$ sudo -u postgres pg_restore -F c -d $DBNAME < $DBNAME_schema.dmp

Set-up pglogical

Make sure the pglogical packages are installed on both the old and the new server. Then we set-up a config file with the required parameters for pglogical, as mentioned in the quick set-up.

$ PGDATA=/etc/postgresql/9.4/main/
$ echo "include 'pglogical.conf'" | $SSHOLD "tee -a $PGDATA/postgresql.conf";
$ echo -e "wal_level = 'logical'\nmax_worker_processes = 10\nmax_replication_slots = 10\nmax_wal_senders = 10\nshared_preload_libraries = 'pglogical'" | $SSOLD "tee $PGDATA/pglogical.conf"
$ $SSHOLD "sudo systemctl restart postgresql@9.4-main"

$ PGDATA=/etc/postgresql/13/main/
echo "include 'pglogical.conf'" >> $PGDATA/postgresql.conf; echo -e "wal_level = 'logical'\nmax_worker_processes = 10\nmax_replication_slots = 10\nmax_wal_senders = 10\nshared_preload_libraries = 'pglogical'" >> $PGDATA/pglogical.conf
$ sudo systemctl restart postgresql@13-main

We need to add the pglogical extension to the DB we want to replicate:

$ $PSQLOLD $DBNAME -c '"CREATE EXTENSION pglogical_origin;"' # Only for postgresql 9.4
$ $PSQLOLD $DBNAME -c '"CREATE EXTENSION pglogical;"'
sudo -u postgres psql $DBNAME -c "CREATE EXTENSION pglogical;"

And we need to grant permissions to allow schema changes through liquibase:

$ $PSQLOLD $DBNAME -c "\"GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA pglogical TO $DBUSER;\""
$ $PSQLOLD $DBNAME -c "\"GRANT USAGE ON SCHEMA pglogical TO $DBUSER;\""
$ $PSQLOLD -c "\"CREATE ROLE pglogical LOGIN REPLICATION SUPERUSER ENCRYPTED PASSWORD '$PGLPASS';\""
$ sudo -u postgres psql -c "CREATE ROLE pglogical LOGIN REPLICATION SUPERUSER ENCRYPTED PASSWORD '$PGLPASS';"

And the pglogical extension needs to have access to the DB. ATTENTION: Replace 10.56.24.0/24 with actual IP range of the peer and make sure the subscriber can access their own DB through the IP as well.

echo -e "# TODO Added for DB upgrade using pglogical remove after done\nlocal\treplication\tpglogical\ttrust\nhost\treplication\tpglogical\t10.56.24.0/24\tmd5\nlocal\t$DBNAME\tpglogical\ttrust\nhost\t$DBNAME\tpglogical\t10.56.24.0/24\tmd5" | $SSHOLD tee -a /etc/postgresql/9.4/main/pg_hba.conf > /dev/null
echo -e "# TODO Added for DB upgrade using pglogical remove after done\nlocal\treplication\tpglogical\ttrust\nhost\treplication\tpglogical\t10.56.24.0/24\tmd5\nlocal\t$DBNAME\tpglogical\ttrust\nhost\t$DBNAME\tpglogical\t10.56.24.0/24\tmd5" | tee -a /etc/postgresql/13/main/pg_hba.conf > /dev/null

Now reload PostgreSQL to apply the access changes. ATTENTION: Make sure you have log_min_messages in postgresql.conf (or in a conf.d file) set to ‘info’ as a minimum, or you won’t see why initial synchronisation fails later on.

$ $SSHOLD systemctl reload postgresql@9.4-main.service
$ systemctl reload postgresql@13-main.service

Optional: Traffic shaping

As we are using the same network interface for the OpenVPN connection that’s also connected to the WAN (i.e. clients using the server), I enabled traffic shaping using wondershaper:

$ $SSHOLD apt install wondershaper
$ $SSHOLD wondershaper -a tun2 -u 215040 -d 215040

tun2 is the OpenVPN network interface and I limited the bandwith to 210MBit/s (210 * 1024).

Dropping indices on the new DB

As pglogical is using the SQL COPY command to copy over data, the new DB would build the indices up while the data is being replicated. For large tables that would take ages and delay the initial copy phase. It’s way for efficient to drop the indices on the new DB before the initial replication and build them concurrently afterwards.

$ sudo -u postgres pg_dump -s -t LARGE_TABLE_NAME $DBNAME > LARGE_TABLE_schema.sql
$ grep INDEX LARGE_TABLE_schema.sql | sed 's/^.*INDEX \([^ ]*\).*/DROP INDEX \1;/' | sudo -u postgres psql $DBNAME

Creating the replication set

pglogical supports several replications sets, but we’ll just use the default as it’s meant for INSERT, UPDATE, DELETE and therefore fits our purpose. The pglogical extension needs to have one provider node and one subscriber node created, on the provider/subscriber respectively. The node as well as the subscription (refers to a replication set) need a postgresql DSN (how to connect) string as a parameter.

We are using two namespaces: public and audit (from the audit trigger) and need to exclude the table spatial_ref_sys from the PostGIS extension, as the table is automatically filled when the PostGIS extension is created (while we restored the schema to the new DB above). So you’ll have to adapt the commands here a little (also the IPs)

$ $PSQLOLD $DBNAME -c "\"SELECT pglogical.create_node(node_name := 'provider', dsn := 'host=10.56.34.1 port=5432 dbname=$DBNAME user=pglogical');\""
$ $PSQLOLD $DBNAME -c "\"SELECT pglogical.replication_set_add_all_tables('default', '{public}'::text[]);\""
$ $PSQLOLD $DBNAME -c "\"SELECT pglogical.replication_set_add_all_tables('default', '{audit}'::text[]);\""
$ $PSQLOLD $DBNAME -c "\"SELECT pglogical.replication_set_add_all_sequences(set_name := 'default', schema_names := '{public}'::text[], synchronize_data := true )\""
$ $PSQLOLD $DBNAME -c "\"SELECT pglogical.replication_set_add_all_sequences(set_name := 'default', schema_names := '{audit}'::text[], synchronize_data := true )\""
$ $PSQLOLD $DBNAME -c "\"SELECT pglogical.replication_set_remove_table(set_name := 'default', relation := 'spatial_ref_sys')\""

If you made a mistake, you can remove all tables / sequences in a replication set like this:

SELECT pglogical.replication_set_remove_table('default', set_reloid) FROM pglogical.replication_set_table;
SELECT pglogical.replication_set_remove_sequence('default', set_seqoid) FROM pglogical.replication_set_seq;

Start the replication

Now create the subscriber node and the actual subscription (starts the replication):

$ sudo -u postgres psql $DBNAME -c "SELECT pglogical.create_node(node_name := 'subscriber', dsn := 'host=10.56.34.2 port=5432 dbname=$DBNAME user=pglogical password=$PGLPASS');"
$ sudo -u postgres psql $DBNAME -c "SELECT pglogical.create_subscription(subscription_name := 'subscription', provider_dsn := 'host=10.56.34.1 port=5432 dbname=$DBNAME user=pglogical password=$PGLPASS', replication_sets := '{default}'::text[] );"

As you can see the connection string (DSN) for the node refers to the new primary DB host, whereas the connection string for the subscriptions refers to the old primary DB host.

Now look at the logs on the new primary. As mentioned above, make sure your log_min_messages parameter in postgresql.conf (or in a conf.d file) is set to ‘info’ as a minimum, or you won’t see why initial synchronisation fails. You can ignore messages like WARNING: snapshot 0x55a9335acdc8 still active.

tail -f -n400 /var/log/postgresql/postgresql-13-main.log | grep -v -e 'WARNING:  snapshot' -e 'apply COMMIT in commit'

If you like you can ask postgresql to show the status of the WAL replication (on the provider / old primary) and wait for initial synchronisation to complete (on the subscriber / new primary):

$ $PSQLOLD -c '"SELECT * from pg_stat_replication;"'
$ sudo -u postgres psql $DBNAME -c "SELECT pglogical.wait_for_subscription_sync_complete('subscription');"

Once the query returns, you should have the pglogical replication in replication state (2nd return column):

$ sudo -u postgres psql $DBNAME -c "SELECT pglogical.show_subscription_status('subscription')"

You can check whether you got all data from large tables by getting the largest ID and then doing the same query on both machines. This query uses a primary key index to be reasonably fast, a regular count(id) would do a full table scan.

BEGIN TRANSACTION; SELECT count(*) FROM (SELECT DISTINCT id FROM YOUR_LARGE_TABLE WHERE id < YOUR_MAX_ID) t; ROLLBACK;

Please be-ware that the replication sequences is a bit special. I handled them manually. See below.

In order to show subscripts, replication sets, and nodes just run a select on the respective tables:

SELECT * FROM pglogical.subscription;
SELECT * FROM pglogical.replication_set;
SELECT * FROM pglogical.node;

Re-create indices

Now re-create all indices that you’ve dropped before, concurrently on the subscriber:

$ grep INDEX LARGE_TABLE_schema.sql | sed 's/INDEX /INDEX CONCURRENTLY /' | sudo -u postgres psql $DBNAME

and wait for all indices to become active. You can check with \d LARGE_TABLE which will show the index as INVALID while it’s still being built. See also PostgreSQL CREATE INDEX.

During this time, you can ignore the following errors:
ERROR: could not read block 0 in file “base/16387/49241”: read only 0 of 8192 bytes
LOG: apply worker [1210644] at slot 1 generation 8 exiting with error

Switching over

Before we did the final switching over, we activated our new hot standby server. We’ve set-up pgpool2 in a way that this is a single command. See pgpool-II Online recovery for instructions.

The procedure of switching over is highly application specific. In our case it consisted of:

  • Disabling writes on the old primary application server
  • Syncing sequences to the new primary DB server
  • Stopping pglogical and shutting down the old DB server
  • Bringing up the app backend stack on the new primary server
  • Pointing our floating IP to the new primary server

One weak spot with pglogical are sequences. Contrary to the docs, they are never automatically synced in my set-up. I’ve been able to get pglogical.synchronize_sequence to work to sync them manually, but it adds 1000 to the last value, as documented. This is not what I want however, so I synced them manually as a part of switching the production system to the new DB using the following commands. Please make sure you do this after you disabled writing to the old primary DB and before you enable writing to the new primary DB.

# ATTENTION: Only includes the 'public' namespace, use e.g '\ds audit.*' to list sequences in other namespaces
$ sudo -u postgres psql $DBNAME -t -A -c "\ds" | awk -F '|' '{ print $2 }' | sort > sequences.txt
$ cat sequences.txt | while read seq; do lastval=$(sudo -u postgres psql $DBNAME -t -A -c "SELECT last_value FROM $seq" 2>/dev/null); echo $seq $lastval; done > sequence-vals.txt
# on subscriber
$ cat sequence-vals.txt | while read seq val; do sudo -u postgres psql $DBNAME -c "SELECT setval('$seq', $val);"; done

After disabling writing to the old DB, you stop the pglogical replication and then shut down the old primary DB just to be safe:

$ sudo -u postgres psql $DBNAME -c "SELECT pglogical.drop_subscription('subscription');"
$ sudo -u postgres psql $DBNAME -c "SELECT pglogical.drop_node(node_name := 'subscriber');"
$ $SSHOLD systemctl stop postgresql@9.4-main

For our own migration, at this point we would bring up pgpool2, all the docker services to re-enable our backend and point our floating IP to the new server.

$ echo DONE

Firefox 3.0 SSL encryption error page

Just so others can find this trick more easily: In order to make accepting self signed certificates a little easier in Firefox 3.0, visit the page about:config and set the following values:

  • browser.xul.error_pages.expert_bad_cert  true
  • browser.ssl_override_behavior 2

Here are the relevant links to the mozilla knowledge base: http://kb.mozillazine.org/Browser.ssl_override_behavior http://kb.mozillazine.org/Browser.xul.error_pages.expert_bad_cert

As soon as these values are set, firefox enables the Add Exception button (you had to click a blue link before the changes) and pre-fetches the certificate which saves you another click in the exception dialogue.

Recovering from missing (or wrong) LVM2 (EVMS) metadata

I just want to document one possible procedure, as it may prove helpful to others.

The setup of the system was as follows: Three disks in a RAID5 array, LVM2 with evms metadata on top and a single ext3 filesystem within an LVM2 logical volume spanning the whole RAID array. Two disks of the array were removed by the operating system (probably because of broken sectors) and the array was marked as corrupt. Unfortunately the failed disks caused problems in the LVM2 metadata and the logical volumes did not mount anymore after forcing the RAID to start (passing the –force parameter to the mdadm assemble command). Please note that the force option did not work on Ubuntu Edgy, probably due to a bug. I upgraded to Feisty during the recovery process.

I took advantage of the fact that I simply used the whole md0 as a container for my filesystem wrapped in LVM2. Because there was no striping over several PVs involved, I was sure that the filesystem is somewhere on md0 starting at block n. I simply dumped the first few hundred kibibytes from md0 and looked for an ext2/3 header. In order to sharpen your pattern recognition system for ext2/3 headers, generate a few filesystems of various sizes within a file and have a look at them. The amount of sectors (512 bytes) used by LVM2 at the beginning seems to be constant. It uses 384 sectors. Then I created a new device with dmsetup which started at the offset I just calculated and ended at the end of md0. A simple mount and I was up and running.

Say ‘blockdev –getsize /dev/md0’ returns 976783616 sectors. 976783616 – 384 = 976783232

sudo dmsetup create md0hack –table “0 976783232 linear /dev/md0 384”
sudo mount /media/mapper/md0hack /media/something

And you are up and running.

Does IBM know logic?

Today IBM advised me to sign up with their website in order to read a post on their developer network [1] (IBM: I know harvesting customer preferences is important, but just publishing it would me so much nicer). During the sign up process they asked a privacy related multiple choice question. I settled on the schizophrenic choice (I know it’s actually called dissociative amnesia, but that’s hard to rephrase as an adjective :-)). See for yourself:
Does IBM know logic?
[1] https://www6.software.ibm.com/developerworks/education/au-writersworkbench

Tarifa 10.5.2007 – 31.5.2007

Hunde am Strand

Also hier ein mal ein paar News aus Spanien. Die Anreise war ein wenig anstrengend. Die netten Leute von der ÖBB haben mir in Graz die Auskunft erteilt ich müsse vom Südbahnhof einen Bus zum Flughafen nehmen. “Immer den Schildern folgen” meinten sie. Das habe ich dann auch gemacht. Der Bus kam und wollte 6€ von mir. Super! In Wirklichkeit war die Information falsch, es gibt laut dem Busfahrer einen Zug nach Schwechat und von dort einen Bus der ÖBB zum Flughafen. Nach kurzer Diskussion hat er mir dann die Fahrt geschenkt :-). Der Bus steckte allerdings eine halbe Stunde im Stau. Bin ich froh, dass ich den früheren Zug genommen habe.

Die nächste kleine Panne gab es dann in Madrid. Bei meinem Anschlussflug stand bei der Ankunft als Terminal HJK dabei. Und direkt über dem Ausgang in die Wartehalle stand ebenfalls HJK. Ich habe mir gedacht, passt hier bin ich richtig. Nachdem mehr als eine Stunde Zeit war, habe ich gemütlich ein Buch gelesen. Ein paar Minuten vor dem Boardingzeitpunkt warf ich noch einmal einen Blick auf die Tafel (die war weiter weg). Nun stand dort als Terminal H6. Hmm…also HJK hieß also nur, dass in dieser Wartehalle die Terminals H, J und K untergebracht waren. Das Ankunftsterminal meines Fluges aus Wien war in Wirklichkeit K34 (ca.). Nun dämmerte es mir: Verdammt es gibt pro Buchstabe mehr als 34 Gates. D.h. ich muss zumindest 34*3-6=96 Gates weiter die Halle hinunter. Ok…also laufen. Und ich lief eine viertel Stunde lang! Förderbänder gibt es in Madrid leider nicht durchgängig. Naja ein wenig erschöpft habe ich meinen nächsten Flug noch rechtzeitig erreicht.

In Malaga angekommen war natürlich alles dicht (dreiviertel eins in der Nacht). Also nicht den günstigen Bus, sondern das teure Taxi zum Busbahnhof. Der Busbahnhof war nicht gerade klein und es gab einen Wachmann der während der Nacht (Busbahnhof zwischen 2 und 4:30 geschlossen) die Leute in ein kleines Wartezimmer hineinließ. Zuerst habe ich mir nicht viel dabei gedacht. Ich setzte mich auf eine Bank im offenen Teil (es war recht warm) und hörte ein wenig Musik. So ca. um 2 hat der Wachmann dann alle Leute in das Wartezimmer beordert (Widerstand war zwecklos). Das Wartezimmer war mit Kameras ausgestattet und hatte zwei Türen. Die eine in den Busbahnhof, die andere auf die Straße. Der nette Wachmann sperrte dann die Türe zur Straße zu. Hmm… Die anderen Leute in dem Warteraum sahen nicht gerade vertrauenerweckend aus und nun wollte der Security Mann auch noch alle Ausweise sehen und wissen mit welchem Bus man fahren wolle. Wohl um Obdachlose zu vertreiben. Offiziell war es sicher ein Wartezimmer, aber ich glaube der Wachmann hat das ein wenig anders gesehen. War so ein kleiner mit offensichtlichem Minderwertigkeitskomplex…der hat sich wohl gefreut, dass er für ein paar Stunden über seinen Warteraum herrscht *gg*. Der wirkliche Spaß begann aber erst als alle 5 Minuten jemand von der Straße in das Wartezimmer wollte. Die Tür war versperrt, der Wachmann irgendwo anders. Zu diesem Zweck gab es eine Klingel. Nur klingelte die nicht sondern war offenbar mit der Feuersirene verbunden und daher ohrenbetäubend laut. Das ging dann 2 einhalb Stunden so. Naja ich habs überstanden, aber irgendwie war das alles ein wenig surreal.

In Algeciras angekommen hatte ich weitere eineinhalb Stunden Zeit. Glücklicherweise gab es in der Nähe einen Markt der gerade öffnete (alle Cafés waren leider zu), so bin ich zu einem fruchtigen Frühstück gekommen. Glücklicherweise scheinen die Spanier das miese Plantagenobst nur zu exportieren, denn es schmeckte vorzüglich. Einen Bus von Tarifa Richtung Cadiź gibt es entgegen anderslautender Behauptungen nicht, also wieder das Taxi. Leichter gesagt als getan. Es gab zwar einen Taxistand um die Ecke, der war aber leer. Es brauchte über 25min bis sich ein Taxi blicken ließ. Naja was solls. Das Hotel ist wirklich nett (Hotel La Torre). Ein Familienbetrieb mit schönem Garten und sauberen Zimmern. Internetanschluss habe ich auch (hab einfach einen WLAN Accesspoint mitgebracht und durfte ihn installieren). Das Hotel liegt zwar nah am Strand aber leider sehr weit weg von der Surfstation (15min zu Fuß).

Blick aus meinem Hotelzimmer

Also hab ich mir ein Rad ausgeborgt. Die Verleihstation ist gleich neben dem Hotel, allerdings betrachten die Radfahren rein als Sport und wollen für ihreRäder für drei Wochen 255€. Wucher! Mit ein wenig Verhandlungsgeschick konnte ich dann ein weniger sportliches Modell für 115€ bekommen. Nicht billig aber in Ordnung. Die nächste Überraschung gab es dann beim Essen. Nix Sevilla Niveau (um 6€ bummsatt und auch noch gut gegessen), hier werden die Touristen ausgenommen wie die Weihnachtsgänse. Das Abendmenü im Hotel um 20€, das Hauptgericht anderswo ab 13€. Einziger Lichtblick: Menu della Noche an der Strandbar um 9€. Die verstehen allerdings ausschließlich Spanisch und können nicht mal mit Händen und Füßen irgendwas erklären. Naja nach ein paar Tagen und ein paarmal Wörterbuch aufschlagen konnte ich ungefähr erahnen was es zu Essen gab. Das Menü habe ich die letzten paar Tage gegessen. Bis gestern. Es gab Fisch mit Kartoffeln und Gemüse. Klingt ja nicht schlecht. Der Fisch war leider ungenießbar (ekelhafter Eigengeschmack, war wohl nicht ganz ein Speisefisch), die Kartoffeln ein Gatsch, genauso wie das Gemüse. Und alles komplett ungewürzt. Salz und Pfeffer bekommt man nur wenn man es sich selber holt. Schauderhaft. Also das Essen ist bisher ein ziemlicher Reinfall gewesen. Schluss mit Strandbar, ich esse ab jetzt lieber im Hotel.

Der nächste Reinfall war die Surfstation. Besser gesagt die Ausleihbedingungen. Laut Club Mistral in Deutschland muss man unbedingt vorher buchen, sonst kostet alles 20% mehr und man bekommt evtl. nichts mehr (was bei gutem Wind hier wirklich vorkommt). Außerdem muss man im Vorhinein entscheiden ob man drei Wochen durchgehend bucht oder zB 14 aus 21 Tagen nimmt. Tja stimmt alles nicht. Vor Ort kostet es für drei Wochen um 10€ weniger und außerdem zahlt man am Ende und immer die günstigste Variante. Das konnte ich natürlich nicht auf mir sitzen lassen. Also ein unfreundlicher Telefonanruf bei Club Mistral. Ups, das hätte ich besser lassen sollen. Anscheinend kochte die Surfschule hier ihr eigenes Süppchen, ohne dass die Herren aus Deutschland etwas davon wissen. Naja aber der Besitzer der Schule hat es dann anscheinend doch irgendwie hingebogen und war nicht böse auf mich. Gebracht hat der Anruf jedoch nicht viel (abgesehen von 10€ Ermäßigung). Nächstes Mal weiß ich es besser.

Der Wind war bis Sa hingegen einfach klasse. Die ersten zwei Tage ca 3-4Bf, ab dann eine Woche lang 7-8 (~70km/h). Ich hatte am Anfang zwar ein wenig zu kämpfen (mit einem 3.2qm Segel!) habe aber viel gelernt. Ein paar Sprünge sind mittlerweile schon drinnen.

Spock eines Locals

Diese Woche sieht es hingegen schlecht aus. Der Wind hat wieder auf West gedreht und mehr als 3Bf werden bis zum Wochenende wohl nicht zu erwarten sein. Bin ein wenig zum schreiben gekommen, aber leider nicht so viel wie ich erwartet hatte (na gut erhofft vielleicht eher :-)).

Insgesamt gefällt es mir jedoch gut hier! Es gibt viele nette Leute an der Surfstation und Tarifa ist wirklich eine super Mischung aus typisch Spanisch und hochgestylten Lokalen. Leider ein wenig weit weg.

Surffoto 2

Four rotor helicopter report #1

After about half a year spare time work, we finished the helicopter platform and tested it’s capabilities. Here is a picture of the helicopter on stakes to prevent it from tipping over. The platform has a diameter (measured rom the end of the carbon sticks) of 64.5 cm.

heli_platform1

The motors are Robbe Roxxy BL 2827-34 outrunner driven by Robbe BL-Control 818 regulators.

Motor topregulator

The motors are mounted on alloy elbow fittings screwed on plastic sockets which in turn are glued on carbon fiber sticks. These sticks are then clamped onto the base.

base from the bottom

The power source is a Kokam 3200/3S HD 20C 11.1V Lithium Polymer accumulator. We have tried to launch the helicopter once using the stakes and a leash to prevent it from acending more then 15cm above ground. Although steering it is very hard we confirmed that our construction works as intended. A problem is that we cannot mix all channels in a way which increases one channel, simultaneously decreases the speed of the opposite motor and increases the speed of the two remaining rotors to compensate for the torque about the vertical axis. However, as soon as our motor controller curcuit board is finished we can easily solve that problem.

Here is a part list of everything (neglecting small parts and tools) we used to build the platform:

  • 4 concave carbon fiber sticks, 1cm diameter (~ 6€/piece)
  • 20 cm x 20 cm x 5 mm plactic plate (unknown)
  • 8 metal clamps fitting the carbon sticks (unknown)
  • 8 1cm M4 screws and nuts (~ 3€)
  • 8 2cm M4 screws, nuts and M5 wingnuts to clamp the stakes (~ 4.5€)
  • 4 alloy sticks to be used as stakes for the platform (~ 3.5€/piece)
  • 4 plastic sockets fitting exactly on the fiber sticks and allowing the elbow fittings to be screwed on (unknown)
  • 4 elbow fittings (3 mm, self made) with holes for the motor fixing (make sure that the fittings are not bent, but casted-in). (unknown)
  • 4 Robbe BL-Control 818 regulators (~ 40€/piece)
  • 4 Robbe Roxxy BL 2827-34 motors (~ 30€/piece)
  • 1 R/C receiver with at least four channels (unknown)
  • 1 R/C remote control with a mixer (unknown)
  • 2 Graupner air screws 25cm diameter, 15 cm pitch with clock wise rotation (~ 4€/piece)
  • 2 Graupner air screws 25cm diameter, 15 cm pitch with counter clockwise rotation (order nr: 1317/25/15L) (~ 4€/piece)
  • 1 Kokam 3200/3S HD 20C 11.1V accumulator (~ 110€)

That’s in sum about 460€ (added 19€ for the pieces we got for free).

Frankreich Spanien Reise 6.7.

Wir mussten besonders früh (7h) aufstehen (ächz), weil wir ja einen weiten Weg vor uns hatten. Das Aufstehen war auch deshalb so anstrengend, weil wir fast alle schlecht geschlafen hatten wegen Hitze (naja, zumindest durften wir die AC für eine halbe Stunde andrehen) und zu kurzen Betten=Sofas. Aber es war echt sehr lieb von Markus uns für die 2 Nächte aufzunehmen, denn unsere Geldbörsen waren durch die ganzen Hotelrechnungen schon leicht geschrumpft. Wir haben uns also gleich in der Früh von Markus verabschiedet (der extra wegen uns aufgestanden ist, obwohl er noch ewig lang an einer Abgabe gebastelt hatte), uns vom Café Frühstück geholt, das Auto eingepackt und sind in Richtung Barcelona aufgebrochen. Beim Flughafen endete nun die Reise von Flo und Kathi, sie mussten sich von Christoph und Eva, dem guten Essen, der Sonne und von Spanien verabschieden. Eva und Christoph blieben noch ein paar Tage in Barcelona und Kathi und Flo wurden nach ewigem Check-In-Warten (eine Chinesin hatte Passprobleme, sie haben über 1 Stunde gewartet, obwohl nur 15 Leute vor ihnen waren) und einem langweiligen Flug in München von Flos Opa abgeholt. Dieser Urlaub bleibt dank der vielen Eindrücke und des großen Spaßes sicher unvergesslich!

Zum Abschluss noch ein Gruppenfoto (das einzige):

Ronda5

Frankreich Spanien Reise 5.7.

Gefrühstückt wurde genussvoll in einem Café in der gleichen Straße, dann sind wir tapfer (weil schon wieder sooo heiß, bzw. schwül) zu Fuß in die Innenstadt marschiert. Eva und Kathi haben beschlossen, die Shops zu durchstöbern, während Christoph und Flo einen kleinen sightseeing-Trip unternahmen.

Valencia1

Zu Mittag gabs für uns wieder Pinchos (yummy). Danach machten wir uns noch einmal, diesmal alle zusammen, in Richtung Einkaufsstraße auf (Eva und Kathi hatten sie mit dem Frauen-Instinkt ohne Plan gefunden gehabt und waren also nun die Wegführer). Flo und Kathi gaben aber bald auf und sind mit dem Taxi (eigentlich mit 2 Taxis, weil sie der erste Taxifahrer idiotischerweise nur blöd rumkutschiert hat, ohne zu wissen, wo die Straße eigentlich war) heim gefahren. Eva und Christoph kamen glücklich strahlend nach ihrem Einkaufs-Erfolgserlebnis nach (Eva hat eeendlich ihren Riesen-Kuscheltier-Nemo gefunden, der erstaunlicherweise sogar im Auto Platz fand). Der restliche Nachmittag war dann eher gemütlich, ausruhen halt.

Valencia2

Valencia3

Gegen 21h sind wir mit dem Taxi wieder in die Innenstadt gefahren, um dort Freunde von Markus zu treffen. Die hatten in einem kleinen Lokal reserviert und haben auch gleich für alle bestellt -verschiedene Tapas, auf jeden Fall genug um unseren Hunger zu stillen. Wieder sehr lecker, jeder hat überall durchprobiert. Das war Kathi und Flos letzter Abend in Spanien :-(.