Über den Wolken (Linux-Magazin, September 2012)

Wer schnell eine Web-Applikation zusammenklopfen und kostenlos im Netz veröffentlichen möchte, findet bei Heroku ein komfortables System, bei dem man sogar seinen eigenen Webserver mitbringen darf.

In letzter Zeit hört man auf Konferenzen häufig das Wort "Polyglot" ([2]): Passé sind die Tage, in denen Entwickler sich mit Tunnelblick auf eine Programmiersprache und ihr Ökosystem konzentrierten und Errungenschaften anderer Systeme roten Kopfes vom Tisch fegten. Vielmehr spioniert der Ingenieur von Welt heute gern in den Foren der Konkurrenz, erkennt fortschrittliche Entwicklungen und adaptiert sie zum Einsatz in der bevorzugten Sprache oder Applikations-Stack.

Auch moderne Hoster von Web-Applikationen verschließen sich diesem Trend nicht und bieten ihre Dienste für unterschiedlichste Programmiersprachen und Runtime-Umgebungen an. So zum Beispiel auch der Service auf heroku.com ([3]), der eine Abstraktionsebene über Amazons AWS-Cloud aufsetzt. Er hat seine Plattform bewußt sprach-agnostisch aufgesetzt und bietet Support für etwa ein Dutzend Programmiersprachen an. Der Entwickler entlässt seine Releases mittels des Revisionssystems Git in die Freiheit und findet sich losgelöst von den technischen Anforderungen realer Server. Dies erlaubt schnelle Entwicklungszyklen mit erstaunlich wenig administrativem Aufwand.

Listing 1 zeigt eine simple Web-Applikation, die das Framework Mojolicious vom CPAN nutzt, das gleich einen Standalone-Webserver eingebaut hat. Von der Kommandozeile mit dem Befehl daemon und einer Webadresse auf dem lokalen Rechner gestartet wie in Abbildung 1, übergibt es einem Browser wie in Abbildung 2 den fest einkodierten Textstring.

Listing 1: myapp.pl

    1 #!/usr/bin/env perl
    2 use Mojolicious::Lite;
    3 
    4 get '/' => { 
    5   text => "I'll be your server tonight." };
    6 
    7 app->start;

Abbildung 1: Die Mojolicious-Applikation startet auf Port 8888 des lokalen Rechners.

Abbildung 2: Das einfache Mojolicious-Skript erzeugt im Browser die gewünschte Ausgabe.

Um das Ganze im öffentlichen Internet auf einem Heroku-Server laufen zu lassen, sind nur wenige weitere Schritte notwendig. Als erstes muss der User sich kostenlos bei Heroku mit einer Email-Adresse registrieren, weitere Angaben entfallen (Abbildung 3). Bestätigt der User den Eingang der Email, darf er ein Passwort setzen. Das Kommandozeilentool "Heroku Toolbelt", das sich unter Ubuntu Linux mit

    wget -qO- https://toolbelt.heroku.com/install.sh | sh

herunterladen und installieren lässt, findet nach dem Aufruf von heroku login wie in Abbildung 4 die lokal verwendeten Public Keys und überträgt einen davon nach Bestätigung an den Heroku-Server. Ab diesem Zeitpunkt kommuniziert der User mit dem Heroku-System, ohne das Passwort ein weiteres Mal tippen zu müssen. Weitere Public Keys für weitere Rechner zur Wartung bereits installierter Applikationen lassen sich mit dem Werkzeuggürtel und dem Kommando heroku keys:add anfügen.

Abbildung 3: Heroku bietet einen kostenlosen Account bei Hinterlegung der Email-Adresse.

Abbildung 4: Das Kommandozeilen-Tool heroku des "Heroku Toolbelt" loggt den User ein.

Für eine Perl-Applikation benötigt Heroku noch die Dateien Makefile.PL und Perloku in Listing 2 und 3, um die nötigen CPAN-Module zu installieren, und den eingebauten Mojolicious-Server des Users anzuwerfen. Zusammen mit dem vorher erwähnten Applikationsskript myapp.pl aus Listing 1 liegen sie allesamt in einem lokalen Verzeichnis, in dem der User dann ein Git-Repository mit git init anlegt. Die Befehlsfolge git add * und git commit mit einem Commit-Kommentar zementiert die Version fest.

Die Perl-typische Datei Makefile.PL gibt das CPAN-Modul Mojolicious in der Version 3.05 als Abhängigkeit an. Mojolicious selbst ist in sich komplett, benötigt also keine weiteren CPAN-Module.

Listing 2: Makefile.PL

    1 #!/usr/bin/env perl
    2 use ExtUtils::MakeMaker;
    3 WriteMakefile(
    4       PREREQ_PM => {'Mojolicious' => '3.05'}
    5 );

Listing 3: Perloku

    1 #!/bin/sh
    2 ./myapp.pl daemon -l http://*:$PORT -m production

Um das Triplet an Heroku zu schicken, ruft der User das Kommando

    heroku create -s cedar \
      --buildpack http://github.com/judofyr/perloku.git

auf. Beim angegebenen buildpack handelt es sich um einen auf Github abgelegten Adapter, der Heroku Perl-Applikationen mittels einiger Skripts schmackhaft macht. Es definiert unter anderem, dass Heroku das Kommando zum Start der Applikation in der Datei Perloku findet. Im lokalen Git-Repository definiert das Kommando heroku create außerdem eine im Git-Jargon so genannte Remote, die auf Herokus Git-Repository zeigt, an das der der User den Code und eventuell fällige Updates mittels

    git push heroku master

schickt. Abbildung 5 zeigt die Ausgabe dieser Kommandozeile.

Abbildung 5: Schiebt der User die Applikation in ein Git-Repo auf Heroku, rollt das neue Release automatisch aus.

Zuerst schickt Git die drei Source-Dateien an Heroku, welches dann die in Makefile.PL angegebenen CPAN-Module installiert und zum Start des handgestrickten Servers das Kommando in der Datei Perloku aufruft. Heroku ruft das Shell-Skript Perloku per Konvention mit der Environment-Variablen PORT auf, die auf den Port gesetzt ist, auf dem der in myapp.pl eingebaute Webserver auf eingehenden Requests lauschen soll. Das Shell-Skript fügt den Wert in den Parameter für die Option -l ein und übergibt das Ganze an myapp.pl, das nun genau Bescheid weiß. Abschließend spuckt Heroku in Abbildung 5 die offizielle Web-Adresse der Applikation aus (im Beispiel http://hollow-fog-8976.herokuapp.com) und ein darauf eingenordeter Browser zeigt das erwartete Ergebnis (Abbildung 6).

Abbildung 6: Das einfache Hello-Skript läuft nun im offenen Internet auf einem Heroku-Server.

In der kostenlosen Version laufen die hochgeladenen Standalone-Server zwar durchgängig und speichern Daten im RAM-Speicher des Perl-Prozesses, doch für sogenannte "Add-Ons" wie Datenbanken zur persistenten Speicherung oder NoSQL-Lösungen wie Redis zur Kommunikation zwischen verschiedenen Instanzen zur besseren Skalierung verlangt Heroku eine "Verifizierung" des Accounts, was derzeit nur durch Angabe einer gültigen Kreditkarte möglich ist. Weiter ist zu beachten, dass Heroku die Prozesse nach einigen Stunden herunterfährt, falls die Requests ausbleiben und sie wieder hochfährt, falls neue Requests eintrudeln. Dies merkt der Webkunde durch Verzögerungen beim Laden der Web-Applikation und der Entwickler durch den Verlust aller im flüchtigen Speicher gehaltenen Daten.

Statt Mojolicious kann jedes beliebige vom CPAN installierbare Framework herhalten, so auch der asynchrone Baukasten AnyEvent mit seinem Standalone-Webserver AnyEvent::HTTPD. Listing 4 zeigt die Perl-Applikation, die den Webserver auf dem mit -p hereingegebenen Port startet, Requests von Webclients entgegennimmt und mit dem gleichen hart einkodierten Ausgabe-String beantwortet.

Listing 4: myapp.pl

    01 #!/usr/bin/env perl
    02 use AnyEvent::HTTPD;
    03 use Getopt::Std;
    04 
    05 getopts "p:", \my %opts;
    06 
    07 my $httpd = AnyEvent::HTTPD->new(
    08   port => $opts{ p } );
    09 
    10 $httpd->reg_cb (
    11     '/' => sub {
    12       my ( $httpd, $req ) = @_;
    13 
    14       $req->respond ( { 
    15         content => [ 'text/html', 
    16           "I'll be your server tonight." ] 
    17       } );
    18     },
    19 );
    20 
    21 my $cv = AnyEvent->condvar();
    22 $cv->recv();

Listing 5 und 6 zeigen die entsprechend angepassten Dateien Makefile.PL und Perloku, die nun ein anderes CPAN-Modul zur Installation anfordern und den von Heroku hereingereichten Port mit -p übergeben.

Listing 5: Makefile.PL

    1 #!/usr/bin/env perl
    2 use ExtUtils::MakeMaker;
    3 WriteMakefile(
    4   PREREQ_PM => {
    5     'AnyEvent::HTTPD' => '0.93',
    6   }
    7 );

Listing 6: Perloku

    1 #!/bin/sh
    2 ./myapp.pl -p $PORT

Statt eines statischen Strings sollte eine nützliche Applikation freilich lieber dynamische Daten zurückliefern, und so schickt sich Listing 7 an, die IP des Users anzuzeigen. Im verwendeten Mojolicious-Framework gibt die Methode remote_address() des mittels tx() erhältlichen Transaktions-Objektes des Requests die IP-Adresse des Besuchers an.

Listing 7: myapp.pl

    01 #!/usr/bin/env perl
    02 use Mojolicious::Lite;
    03 
    04 get '/whatsmyip' => sub { 
    05     my( $self ) = @_;
    06 
    07     $self->render( text => 
    08         "Your IP is " . 
    09         $self->tx->remote_address() );
    10 };
    11 
    12 app->start;

Abbildung 7 hält jedoch eine Überraschung bereit: Die dort angezeigte IP-Adresse ist im Bereich 10.x.x.x und damit eine nicht-routbare interne Heroku-Adresse, die offenbart, dass auf dieser Plattform magische Zwischenschichten agieren, bevor der Request bei der Applikation eintrifft.

Abbildung 7: Heroku zeigt statt der IP-Adresse des Besuchers eine interne IP-Adresse.

Wie sich nach einigem Experimentieren herausstellte, ist die Original-IP glücklicherweise noch im Header x-forwarded-for des eingehenden Requests verfügbar, und Listing 8 zaubert sie hervor, sodass Abbildung 8 sie korrekt anzeigt.

Listing 8: myapp.pl

    01 #!/usr/bin/env perl
    02 use Mojolicious::Lite;
    03 
    04 get '/whatsmyip' => sub { 
    05   my( $self ) = @_;
    06 
    07   $self->render( text => 
    08     "Your IP is " . 
    09     $self->req->headers->header(
    10         "x-forwarded-for" ) );
    11 };
    12 
    13 app->start;

Abbildung 8: Über den Header x-forwarded-for berichtet Heroku nun die richtige IP-Adresse.

PSGI und Plack

Aus dem Web-Ökosystem Rails der Programmiersprache Ruby stammt die Schnittstelle PSGI und die Implementierung Plack ([4]). Dort heißt das Interface WSGI und die Implementierung Rack und definiert eine einheitliche CGI-artige Schnittstelle zwischen beliebigen Webservern und Web-Applikationen. Wer hat sich nicht schon gefragt, warum jedes Mal, wenn eine Perl-Applikation von einem praktischen Standalone-Entwicklungsserver aus Performancegründen auf einen Apache-Produktionsserver übertragen wird, Entwicklungszeit vergeudet wird, weil mod_perl und CGI.pm Parameter unterschiedlich übergeben? Plack produziert den nötigen Superklebstoff, um jede PSGI-kompatible Web-Applikation (inklusive Web-Frameworks wie Catalyst oder Dancer) unmodifiziert auf jedem beliebigen Webserver laufen zu lassen, für den es einen PSGI-Adapter gibt. Code, der mit einem Standalone-Server wie Mojolicious enwickelt und getestet wurde, läuft dann auf Apache1 wie auf Apache2.

Listing 9 zeigt die fertig programmierte Applikation. Das zugehörige Makefile.PL zieht lediglich das CPAN-Modul Plack herein und Heroku startet den Server nach einem git push ohne viel Federlesens. Zu sehen ist, dass PSGI ein denkbar simples Interface erfordert: Alle eingehenden Parameter kommen als Referenz auf einen Hash herein und die Applikation liefert eine Referenz auf einen Array mit Status Code, Headern und schließlich dem Inhalt (Body) der Antwort. So ist sichergestellt, dass für jeden denkbaren Webserver mit einfachsten Mitteln ein passender Adapter programmierbar ist.

Das Modul Plack::Runner wirft den in Plack enthaltenen Standalone-Webserver an, so dass auch dieses Skript nach einem git push anstandslos auf heroku.com läuft.

Listing 9: myapp.pl

    01 #!/usr/bin/env perl
    02 use Plack::Runner;
    03 
    04 my $runner = Plack::Runner->new;
    05 $runner->parse_options( @ARGV );
    06 $runner->run( sub {
    07   my( $env ) = @_;
    08 
    09   return [ 200, 
    10     [ "Content-type" => "text/html" ], 
    11     [ "Hello from Plack"] 
    12   ];
    13 } );

Die Sprache Perl erlebt in letzter Zeit erstaunlicherweise wieder ein Come-Back in der Hoster-Szene: Nicht nur Heroku unterstützt Perl, sondern unter anderem auch die Firmen Dotcloud, Juju, Openshift und Stackato.

In eigener Sache

Und noch ein Hinweis in eigener Sache: Der erste Artikel der Perl-Snapshot-Reihe im Linux-Magazin erschien im Oktober 1997, also schließen wir diesen Monat das 15. Jahr ab! Ich bedanke mich für alle Leserzuschriften und freue mich auf weitere Anregungen.

Infos

[1]

Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2012/09/Perl

[2]

Tatsuhiko Miyagawa, "Becoming a Polyglot", YAPC::NA 2012

[3]

"Polyglot Platform", Heroku Blog, http://blog.heroku.com/archives/2011/8/3/polyglot_platform/

[4]

"Plack", der Superklebstoff für Webapplikationen in Perl: http://search.cpan.org/dist/Plack

Michael Schilli

arbeitet als Software-Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mschilli@perlmeister.com beantwortet er gerne Ihre Fragen.