Seit neuneinhalb Jahren nutze ich Debian und war immer ausgesprochen zufrieden damit. Als technisch versierterer Benutzer kann man ja dank testing und unstable ausreichend frische Softwareversionen nutzen. Der neueste Schrei ist nun der Ersatz von SysVinit durch systemd als Init-System. Da es bei meinem Setup noch kleinere Haken mit systemd gibt und mir systemd nach wie vor etwas ungeheuer ist, hatte ich vor so lange wie möglich bei SysVinit zu bleiben.

Die letzten Wochen mehrten sich dann aber mehrere Probleme:

  • Mein Notebook zeigte nach dem starten kein Bild mehr an, wenn beim starten der Splash von Plymouth aktiv war.
  • Auf sämtlichen Rechnern wird das tun-Modul für openvpn nicht mehr automatisch geladen, sondern muss per Hand geladen werden.
  • Suspend-to-RAM und Suspend-to-Disk waren im “Verlassen”-Bildschirm von KDE nicht mehr zu finden und das schließen des Notebookdeckels legte ihn auch nicht mehr schlafen.

Insbesondere der letzte Punkt nervte mich und ich machte mich auf die Suche nach der Ursache. Die fand ich auch recht schnell in Form eines Debian Bugreports: #752413 kde-workspace-bin: upower-1.0 transition. Spannend dabei die Aussage im letzten Kommentar:

It does mean as a consequence, that kde will depend on systemd/logind
for suspend/hibernate support.

Kurz entschlossen habe ich mein Notebook mal mit systemd neu gestartet und siehe da: Alle oben genannten Probleme waren mit einem Mal verschwunden. Das ärgert mich, denn es zeigt, dass SysVinit in Zukunft wohl kaum mehr vernünftig als Init-System für Desktoprechner verwendet werden können wird, wenn sogar jetzt schon ohne systemd Probleme auftreten, die mit ihm nicht auftreten.

Die Bezeichnung “Cloud” ist schon seit einer ganzen Weile ein Hype-Begriff und wurde von mir lange belächelt. Denn was unterscheidet schon “die Cloud” von dem, was schon immer das Internet selbst definiert?

Seit gut einem halben Jahr habe ich nun beruflich recht intensiv mit ebendieser Cloud zu tun, um genau zu sein mit Amazon Web Services (AWS).
Grundsätzlich stehe ich solchen großen Unternehmen wie Amazon erstmal skeptisch gegenüber, denn je größer ein Unternehmen desto wahrscheinlicher ist eine marktbeherrschende Stellung und die Ausnutzung dieser. Auch Amazon hat bereits mit seinem Online-Shop gezeigt wie gut sie in der Lage sind eine marktbeherrschende Stellung zu übernehmen.

Auch der Bereich Cloud-Computing ist bei Amazon inzwischen so groß, dass sie in diesem Bereich der größte Anbieter sind und er in Zukunft voraussichtlich ähnlich hohe Gewinne abwerfen wird wie ihr Online-Shop. Doch wodurch kommt das? Beim Online-Shop sind die wichtigsten Kriterien für den Erfolg meiner Meinung nach niedrige Preise und guter Service. Zumindest mit niedrigen Preisen kann AWS auf den ersten Blick nicht punkten.

Während man bei üblichen Hostern im niedrigen Preissegment in Deutschland problemlos Server mit Quadcore Prozessor, 16GB RAM und mehreren Terabyte an Plattenplatz für 50 Euro im Monat bekommt, zahlt man bei AWS in der billigsten Region (und zwar us-east-1 und us-west-2, beide in den USA) mindestens 300 Dollar für ähnliche Hardware. Bei Mehrausgaben von 500% muss also entweder ein relevanter Mehrwert vorhanden sein oder die Angebote von Amazon sind maßlos überteuert.

Die Erfahrungen der letzten Monate haben mir gezeigt, dass die Preise durchaus berechtigt sind und was “die Cloud” von herkömmlichen Webhostern unterscheidet.

  1. Amazon bietet nicht einfach wie andere Webhoster reine Hardware, sondern eine komplette Infrastruktur mit unterschiedlichsten Dienstleistungen und dazugehörigen Programmierschnittstellen (APIs) für diverse Sprachen an. So ist unglaublich viel Funktionalität um deren Bereitstellung man sich sonst selbst kümmern müsste bereits vorhanden und lässt sich neben einem Webinterface komplett über APIs nutzen und somit einfach in eigene Anwendungen einbinden.
  2. Hardwarenahe Probleme gehören der Vergangenheit an. Darum kümmert sich Amazon und da alles in irgendeiner Form virtualisiert läuft, bekommt man als Anwender davon auch nur in Ausnahmefällen etwas mit.
  3. Die Dokumenation der einzelnen Dienste ist umfangreich und verständlich und als größerer Kunde bekommt man unglaublich gute Betreuung und ausgezeichneten Support.
  4. Amazon ruht sich nicht auf seiner Marktstellung aus, sondern veröffentlicht fast im Tagesrythmus neue Verbesserungen ihrer Cloud-Plattform.
  5. Eigene Dienste lassen sich ohne großen Aufwand in lokal gruppierten, aber räumlich getrennten Rechenzentren (Availability Zones) oder auch weltweit verteilt über fünf Kontinente (Regions) betreiben.

Dazu kommt, dass die Preise aus dem oben genannten Beispiel bei intensiverer Nutzung je nach Anwendungsfall drastisch sinken, wenn man Reserved Instances oder Spot Instances nutzt.

Mir macht es, aufgrund der oben genannten Gründe, unglaublich viel Spaß mit AWS zu arbeiten und für mich ist inzwischen gut nachvollziehbar, warum Amazon im Bereich Cloud-Computing diese überragende Marktstellung hat: So schade das für gesunden Wettbewerb ist, aber es gibt einfach keinen Anbieter, der auch nur ansatzweise die gleichen Dienste bieten kann und wenn ich mir das Tempo an Innovationen bei Amazon anschaue, dann wird das auch noch lange so bleiben.

Die Debatte um die Deutsche Telekom, die sich in neuen DSL-Flatrate-Verträgen nun vorbehält nach dem überschreiten eines bestimmten Übertragungsvolumens die Verbindung zu drosseln, ist ja inzwischen nun schon ziemlich breit durchgekaut worden. Als Option wird es wohl möglich sein, gegen Aufpreis wieder eine echte Flatrate zu bekommen.
Ich frage mich allerdings, warum sich die Telekom überhaupt diesen medialen Stress gemacht hat? Sie hätte doch auch einfach die neuen Verträge nicht mehr Flatrate nennen und stattdessen entsprechend teurere Verträge mit der Bezeichnung Flatrate einführen können. Dann hätten sie PR-technisch sogar noch entgegengesetzt argumentieren können, dass bei ihren Nicht-Flatrate-Verträgen nach erreichen des Limits ja “nur” gedrosselt wird, statt gleich komplett abzuschalten.

Google hat heute eine Webseite zum schon länger in Entwicklung befindlichen “Project Glass” veröffentlicht. Glass ist platt gesagt eine intelligente Brille mit Kamera und Display, die es erlaubt im alltäglichen Leben ins eigene Sichtfeld zusätzliche Informationen eingeblendet zu bekommen und das eigene Sichtfeld zu filmen.
Auf der Webseite ist unter anderem auch das im Folgenden eingebettete Video zu finden.

Beim anschauen des Videos drängt sich mir förmlich auf, dass Google Glass, sobald es für den Massenmarkt für einen bezahlbaren Preis verfügbar ist (und dafür wird Google sorgen), das Produkt sein wird, nach dem Apple so krampfhaft sucht: The Next Big Thing.

Some time ago I had the requirement to limit the number of requests per IP to a CouchDB-instance. I couldn’t find an option for CouchDB to achieve that, but as communication with CouchDB is based on HTTP-requests I thought that it should be possible to use nginx as reverse proxy with rate-limiting capabilities for CouchDB. The CouchDB-wiki lists some basic steps how to use nginx as reverse proxy, but rate limiting isn’t mentioned there.

Beside the actual rate limiting I noticed that nginx won’t work, because it doesn’t support chunked request-bodies out of the box, which CouchDB seems to rely onto. But to the rescue there is a nginx module called ngx_chunkin, which adds chunkin support to nginx. To get that module in Debian you need to install nginx-extras from the repository (available since squeeze-backports). The configuration of that module is straight forward as described on the wiki page. All you have to do is to put the following code snippet into the server-context of your nginx configuration:

        chunkin on;

        error_page 411 = @my_411_error;
        location @my_411_error {
                chunkin_resume;
        }

Having solved that issue, doing the actual rate limiting was the next step. Therefore nginx ships the module ngx_http_limit_req_module. All I had to do was to configure a zone for the limited requests in the http-context of the configuration:

limit_req_zone $binary_remote_addr zone=couchdb_write:10m rate=10r/s;

and the logic what to rate limit (writing requests in my case) into the server-context:

        location / {
                # POST, PUT, DELETE ... requests will get rate limiting
                if ($request_method !~* GET) {
                        rewrite ^(.*)$ /throttled$1 last;
                }

                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /throttled {
                internal;
                limit_req        zone=couchdb_write burst=10000;
                rewrite /throttled(.*) $1 break;
                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

So if you put together the chunkin support and the rate limiting, you’ll get the following piece of configuration for a server which does rate limiting of writing CouchDB-requests using nginx:

limit_req_zone $binary_remote_addr zone=couchdb_write:10m rate=10r/s;

server {
        listen 5984;

        chunkin on;

        error_page 411 = @my_411_error;
        location @my_411_error {
                chunkin_resume;
        }

        location / {
                # POST, PUT, DELETE ... requests will get rate limiting
                if ($request_method !~* GET) {
                        rewrite ^(.*)$ /throttled$1 last;
                }

                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }

        location /throttled {
                internal;
                limit_req        zone=couchdb_write burst=10000;
                rewrite /throttled(.*) $1 break;
                proxy_pass       http://127.0.0.1:5985;
                proxy_buffering  off;
                proxy_set_header Host $host;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        }
}

I really enjoyed figuring that out, because it was a lot of fun to be able to use a great tool (nginx) to extend another great tool (CouchDB), because they’re using the same protocol (HTTP).

die geladenen Elemente auf der "Microsite" von IKEA PAXAuf der deutschen IKEA-Webseite gibt es aktuell eine PAX-“Microsite”. Diese ist echt schick. So bekommt man beispielsweise durch scrollen verschiedene Schrankkombinationen und Befüllungen innerhalb desselben Bildes angezeigt. Auch werden moderne Technologien, wie zum Beispiel jQuery und Modernizr, benutzt.
Was mich dann aber doch etwas staunend zurück ließ ist die Tatsache, dass beim Aufruf der Seite über 1500 HTTP-Requests ausgelöst werden und die damit angeforderten Daten eine Gesamtgröße von über 35MB haben? Srsly? Das geht doch auch besser.

Ursprünglich mochte ich NetworkManager nicht. Grundsätzlich machte er mehr Probleme bei der Handhabung von Netzwerkverbindungen unter Linux, als er löste. Das ist allerdings schon einige Jahre her und spätestens seit der grandiosen Integration von WWAN-Hardware ist er ein richtig brauchbares Hilfsmittel geworden.

Eine Sache, die mir nun fehlte, war die Möglichkeit nach dem verbinden in ein WLAN eine Authentifizierung via Browser zu automatisieren. Nach einer kurzen Suche stellte sich heraus, dass es mit NetworkManager ein leichtes ist, auch das zu automatisieren. Dazu reicht es ein Shell-Skript in /etc/NetworkManager/dispatcher.d/ abzulegen, dass als ersten Parameter den Namen der betroffenen Verbindung und als zweiten Parameter die Statusänderung der Verbindung (up oder down) erwartet. Daraufhin war es ein leichtes, ein kleines Skript zu basteln, dass die Authentifizierung via HTTP übernimmt, nachdem eine bestimmte Verbindung aktiviert wurde:

#!/bin/sh

# network manager connection name
CONN_NAME="sample_wlan"

# check if this script is triggered for the wlan interface
if [ "$1" != "wlan0" ]; then
    exit 0;
fi

# check if the action is the enabling of the interface
if [ "$2" = "up" ]; then
    # check if the connection we want to automate, is active
    if [ "$(nmcli con status | grep -o $CONN_NAME)" = "$CONN_NAME" ]; then
        wget -q --post-data="username=john&password=123456&possible_other_post_parameters=xyz" https://fqdn-of-the-captive-portal.tld/login.html -O - > /dev/null
    fi
fi

Finde den Fehler:

#!/usr/bin/env python

import sys
if sys.version_info.major < 2 or (sys.version_info.major == 2 and sys.version_info.minor < 7):
    print "python <2.7"
else:
    print "python >=2.7"

Auflösung:
Der Ansatz ist eine nette Idee, funktioniert allerdings erst ab Python 2.7, da Python erst seit dem die benannten Parameter .major und .minor kennt. Damit der Code auch mit Python-Versionen kleiner als 2.7 funktioniert, müsste er wie folgt aussehen:

#!/usr/bin/env python

import sys

if sys.version_info[0] < 2 or (sys.version_info[0] == 2 and sys.version_info[1] < 7):
    print "python <2.7"
else:
    print "python >=2.7"

Siehe auch: http://docs.python.org/library/sys.html#sys.version_info

Das sind Kleinigkeiten, auf die man stößt, wenn man versucht ein auf einem System mit Python 2.7 geschriebenes Skript mit Python 2.6 auszuführen. Wäre ja aber langweilig, wenn alles auf Anhieb glatt laufen würde.

Innerhalb der letzten paar Wochen hatte ich das Vergnügen mehrere ejabberd-Instanzen mit vollkommen unterschiedlichen Use-Cases aufsetzen zu dürfen. Abgesehen davon, dass das Format der Konfigurationsdateien von ejabberd ein wenig gewöhnungsbedürftig ist und ejabberd in diversen Fehlerfällen keine sonderlich aussagekräftigen Fehlermeldungen produziert, waren das sehr gute Erfahrungen. Ich war jedes Mal wieder erstaunt, mit wie wenig Anpassungen an der Konfiguration ejabberd das tat, was er tun sollte.
Die Use-Cases will ich euch natürlich auch nicht vorenthalten. Das waren grob die Folgenden:

  • Instanz mit LDAP-Authentifizierung und Shared-LDAP-Roster
  • Instanz ohne S2S, dafür mit HTTP-Binding nach XEP-0124
  • Instanz mit virtuellen Hosts und Authentifizierung über ein externes Skript

Im Jahr 2007 war ich PHP und all die damit verbundenen Nachteile satt und stieß auf der Suche nach einer Alternative zur Webentwicklung auf Python und das damals noch recht junge Webframework Django. Es war Liebe auf den ersten Blick. Während ich mich in Django einarbeitete und die ersten kleinen “Apps” schrieb, hatte ich regelmäßig aha-Effekte, wenn ich sah, wie einfach sich verschiedene Funktionalitäten implementieren ließen und das wievielfache an PHP-Code man für die gleiche Funktionalität gebraucht hätte.
Doch nicht nur für Webentwicklung lässt sich Django wunderbar nutzen. Wie ich damals recht schnell feststellte, lassen sich aufgrund der Modularität von Django, einzelne Komponenten wunderbar eigenständig nutzen.
Zwei Möglichkeiten möchte ich im Folgenden kurz vorstellen.

Für ein Projekt brauchte ich die Möglichkeit statische Webseiten aus einem via Cron laufenden Skriptes zu generieren. Was bot sich also mehr an, als die Templateengine von Django zu verwenden?
Für ein simples Beispiel reichen sogar schon acht Zeilen Code:

#!/bin/env python

from django.conf import settings
from django.template.loader import render_to_string

settings.configure(TEMPLATE_DIRS=('/home/john/project/templates',))
rendered = render_to_string('template.html', { 'foo': 'cookie' })
print rendered

Das dazugehörige Template könnte wie folgt aussehen:

This is a simple template with {{ foo }}.

Das Ganze lässt sich natürlich nicht nur für HTML-Dateien, sondern für jede Art von Textdateien verwenden.
Ein anderes Anwendungsbeispiel ist die Nutzung des Object-Relational Mappers (ORM) von Django, für hübschen Datenbankzugriff aus beliebigen Python-Anwendungen:

#!/bin/env python

from django.conf import settings

settings.configure(DATABASES={
                    'default': {
                        'ENGINE': 'django.db.backends.sqlite3',
                        'NAME': 'database.sqlite'
                    }
})

from django.db import models

class Animal(models.Model):
    species = models.CharField(max_length=32)
    name = models.CharField(max_length=32)
    
    class Meta:
        app_label = 'animals'

Animal(species='dog', name='rex').save()

for i in Entry.objects.all():
    print i.name

Zu obigem Beispiel seien ein paar Punkte angemerkt:

  • Dass der Import der Models nach dem Konfigurieren der Einstellungen kommt ist Absicht. Django benötigt die Einstellungen zum Laden der Models.
  • Die Meta-Klasse mit der Option app_label muss explizit angegeben werden, sofern man den ORM ohne den Rest von Django nutzt.
  • Das obige Beispiel wird so nicht funktionieren, weil keine Datenbank mit entsprechenden Tabellen existiert. Diese müsste man (in diesem Beispiel) auf anderem Wege anlegen.

Wie man sieht, ist Django toll. Und das nicht nur für Webanwendungen, sondern für alles Mögliche, was auf Python basiert. Wollte ich nur mal gesagt haben.