Levy halpa, pilvi helppo

Rakas asiakkaamme, startup, tarvitsi äkillisesti teran tai kaksi levytilaa. Koska meillä on hyllyssä aina muutama varalevy, annoimme herralle kahden teran ulkoisen usbilevyn yhä kaupan muoveissa muistoksi hyvästä suunnittelupalaverista.

Unohdin asian täydellisesti, kunnes löysin levyn maanantaiaamuna hyllyn päältä. Yhä kaupan muoveissa.

Kuulemma levyn käyttöönotto olisi ollut kauhean vaivalloista. S3:sta tilan sai käyttöön paljon helpommin. Paketti lojui asiakkaan toimistolla kuukausia, kunnes tuli taas asiaa meidän toimistollemme, ja levy löysi tiensä takaisin kotiin.

Tapahtuneella herkutellessani Santeri kertoi, että ennen hän lähetti Codenton aamiaistilaisuuksien raakavideot editoijalle ulkoisella kovalevyllä. Tähän liittyi vaivalloinen fyysisen esineen toimittamisvaihe. Nykyään he kuulemma käyttävät Dropboxia.

(Codenton menneiden aamiaistilaisuuksien esitykset ovat muuten pääsääntöisesti verkossa katseltavissa.)

Eivät uudet tavat tietenkään välttämättä voita. Perinteinen tapa viedä data pilveen Antonov AN-225:llä täynnä irtolevyjä on kustannustehokkuudeltaan ja kaistanleveydeltään parempi kuin Amazon, kunhan ne levyt ovat täynnä. Useimmiten vain eivät ole.

// Kuva: Flickr CC, Daniel Spiess.

Tämän artikkelin on kirjoittanut Teemu Kalvas ja sitä ovat sittemmin muokanneet muut Codenton työntekijät.

C on Venuksesta, Go on Marsista

Tällä viikolla Google julkaisi Go-kielestään version 1.0 ensimmäisen julkaisukandidaatin; tuote kulkee tuttavallisesti nimellä Go 1 RC1. Lopullinen 1.0 versio tulee kevään aikana. Optimistisesti voisi sanoa, että Go pääsee versioon 1.0 alle vuoden ikäisenä. Marsilaisen vuoden, nimittäin.

Go tuntuu marsilaisten versiolta C:stä. Olen käyttänyt Gota viime aikoina harrastusohjelmointiin. Vanhakin koira voi oppia uusia ohjelmointikieliä, mutta kyllä se pari kertaa haukkuu oudosti sijoitetuille suluille tai erikoisille vaatimuksille.

Teemu kommentoi tuoreeltaan blogissamme vastasyntynyttä Gota. Hänen analyysinsä keskittyi itse kielen ominaisuuksiin. Parametrisoidut tyypit puuttuvat. Slicet ja mapit käyttäytyvät vieraalla tavalla. Nollaosoitin on sallittu.

Käytännön tasolla en ole kuitenkaan näihin rajoituksiin törmännyt. Jotkin tyypit ovat tasa-arvoisempia kuin toiset, mutta itse sopeuduin siihen ensimmäisen sadan koodirivin jälkeen. Kerran huomasin toivovani lampun hengeltä tyyppiparametreja… mutta hetken pohdinta osoitti tämänkin tarpeettomaksi.

Go:n erikoiset valinnat vaikuttavat käytännön työssä perustelluilta, vaikka ne paperilla pelottaisivatkin. Eniten käytännön työtä rajoitti kehno työkalusetti. Puoli (Maan) vuotta vanhassa stabiilissa versiossa (r60) oli käytössä vielä alkuperäinen gomake/goinstall/gotest -työkalusarja. Nämä olivat hieman kankeita; ne nojasivat määrämuotoisiin Makefileihin, jotka saivat omat karvani pystyyn. En minä Makea vastaan sinällään ole. Boilerplate-Makefile minua ärsyttää! Antakee armoa, C-mestarit!

Go 1 yhdistää tyypilliset työvaiheet go-komennon alle. Makefilet ovat tarpeettomia, sillä go-komento analysoi kooditiedostoja suoraan. Se kääntää, testaa, benchmarkaa, dokumentoi ja asentaa. Eikä siinä vielä kaikki! GOPATH-ympäristömuuttujan kanssa se tarjoaa yksinkertaisen (mutta riittävän) mekanismin projektin kirjastojen isolointiin.

Pieni esimerkki

Go-työkalu odottaa tietynlaisen hakemistorakenteen (GOPATH osoittaa workspace-hakemistoon):

  • workspace/
    • src/
      • paketti1/*.go
      • paketti2/*.go
      • paketti3/alipaketti/*.go
    • pkg/
    • bin/

Ohjelmoijan tarvitsee välittää vain src-hakemistosta. Go-työkalu luo sekä bin- että pkg-hakemistot. Ensin mainittuun asennetaan binäärit ja jälkimmäiseen käännetyt kirjastot.

Näillä oletuksilla testaaminen sujuu komennolla

go test paketti1 paketti2 paketti3/...

Tällöin työkalu kääntää lähdekoodin ja testit ja ajaa syntyneen testibinäärin. Go-kääntäjä on niin nopea, että koodauksen ja testauksen välinen feedback-looppi pysyy tehokkaana.

Pelkkä kääntäminen tapahtuu komennolla

go build <paketti>

Useammin kuitenkin voi suoraan ajaa komennon go install <paketti>, joka kääntää paketit ja asentaa ne bin- ja pkg-hakemistoihin.

Työkalu nojaa pitkälti konventioihin. GOPATH on käytännössä ainoa konfiguraatio, mitä työkalun käyttöön tarvitaan. Erittäin käyttäjäystävällistä!

Muuta miellyttävää

Testauskirjaston mukana tulee yksinkertainen (mutta riittävä) benchmark-työkalu. Suorituskykytestejä on yhtä suoraviivaista tehdä kuin yksikkötestejä. Helppous johtaa siihen, että teen näitä benchmarkkeja, mikä johtaa suorituskyvyn seuraamiseen, mikä johtaa suorituskyvyn parantumiseen. Lordi Kelvinin kuuluisa lause pätee: “Jos asiaa ei voi mitata, sitä ei voi parantaa”.

Rubyn DSL-pohjaisten testityökalujen syntaktiseen eleganssiin Go:n testing-kirjasto ei pääse. Eri XUnit-kirjastojen käyttäjät ovat kuitenkin kuin kotonaan.

Kääntäjä on miellyttävän tosikko. Esimerkiksi käyttämätön muuttuja ei johda varoitukseen, vaan siitä tulee suoraan virhe. Kääntäjän virheilmoitukset ovat pääasiassa ymmärrettäviä ja kohdistuvat oikeille riveille.

Googlen App Engine tarjoaa myös Go-ajoympäristöä. Ympäristö on rajoitettu, kuten kaikki GAE-ympäristöt. Kiusallisimmat rajoitukset koskevat rinnakkaisohjelmoinnin gorutiinien niputtamista yhteen säikeeseen. Tämä tekee gorutiineista hitusen raskaampia kuin mitä niiden pitäisi.

GAE-ympäristön suorituskyvystä on toistaiseksi vielä vaikea löytää numerotietoja. Seuraavat havainnot pohjaavat IRC-anekdootteihin ja sähköpostilistan tarinoihin. Suorituskyky on samalla tasolla Java-ympäristön kanssa. Muistinkäyttö pysyy pienenä ja erityiskehuja annetaan instanssien käynnistysajalle. Uudet instanssit käynnistyvät 50-100 millisekunnissa.

Mutuni vihjaa, että GAE-Go sopisi hyvin SOA-tyyppiseen pienistä palveluista koostuvaan arkkitehtuuriin.

Loppufiilikseni. Go 1 vaikuttaa erittäin lupaavalta. Työkalut ovat käyttökelpoisia. Dokumentaatio on kypsynyt. Windowsiakin tuetaan. Go ei koskaan tule olemaan uusi Java, mutta veikkaan sen valtaavan itselleen muutaman nichen. Jos olet etsinyt tekosyytä kokeilla Gota, kannattaa aloittaa Go 1:stä.

// Kuva: Flickr CC, Cyril Rana.

Tämän artikkelin on kirjoittanut Antti Rasinen ja sitä ovat sittemmin muokanneet muut Codenton työntekijät.

Hello EC2, Part 3: Self-healing Cluster

This is the third in a series of posts about setting up a scalable and robust “Hello World” web server using Amazon EC2. The target audience is developers with some experience using EC2. This third installment looks at setting up a self-healing, load-balanced cluster of EC2 instances.

Introduction

The very first installment introduced the idea of using instance metadata and cloud-init to cause an EC2 instance launched from a stock AMI to configure itself when it first starts up. Now we will leverage that technique to set up a self-healing cluster of instances behind Elastic Load Balancing (ELB). The instances will be launched by Amazon within a fixed-size Auto Scaling Group, which will utilize the health check configured into the load balancer to replace instances that are not functioning properly.

The Code

My code is available in bitbucket:

https://bitbucket.org/rimey/hello-ec2-launch/

The essential parameters defining the cluster are in the file named config.py:

cluster = 'testcluster'
puppet_source = 'git@bitbucket.org:rimey/hello-ec2-puppetboot.git'

region = 'us-east-1'
zones = ['us-east-1a', 'us-east-1b']
total_instances = 2

instance = {
    'image_id': 'ami-a0ba68c9',     # us-east-1 oneiric i386 ebs 20120222
    'instance_type': 't1.micro',
    'key_name': 'awskey',
    'security_groups': ['default'],
}

I use the cluster name (“testcluster”) to name the Load Balancer, the Auto Scaling Group, and the Launch Configuration. A Launch Configuration encapsulates the parameters that an Auto Scaling Group requires in order to launch a new EC2 instance.

The Python code for launching the cluster is in launch_cluster.py. It uses the boto library to do the following:

  1. Create a distributed HTTP load balancer forwarding port 80 to instances in the availability zones specified in config.py.
  2. Configure an HTTP health check to be performed on the instances behind the load balancer. This will query a specified resource (/health) and check for a 200 response status. The web server only needs to return some response for that resource.
  3. Define a Launch Configuration with the same parameters we previously passed to run_instances(), including the user data script.
  4. Create an autoscaling group using this launch configuration and the load balancer’s health check to maintain the specified total number of instances across the specified availability zones.

The same file also includes code for shutting down the cluster.

Trying it Out

To run launch_cluster.py, you must have boto installed, you must have your AWS credentials in ~/.boto, and you must have a suitable key pair and security group configured in AWS as I outlined earlier.

Running ./launch_cluster.py prints the following messages confirming that the cluster is starting up:

Creating load balancer.
URL = http://testcluster-374147217.us-east-1.elb.amazonaws.com/
Creating launch configuration.
Creating auto scaling group.

A few minutes later, the URL (which will be different each time) will begin serving a spartan web page saying “Hello EC2!”. You can use the AWS management console to follow the status of the EC2 instances as they start up (under EC2 / Instances), as well as their health status from the perspective of the load balancer (under EC2 / Load Balancers / Instances).

I tried simulating a failure by running “sudo /etc/init.d/nginx stop” on one of the instances, while simultaneously hitting the load balancer with 250 requests per second (“250 users”) from the blitz.io load testing service. The effect, as you can see in the Hit Rate chart below, was to cause about half of the requests to result in timeouts until the load balancer marked the malfunctioning instance as unhealthy about ten seconds later. The quick recovery is due to my use of the smallest possible values for the health check interval (5 seconds) and threshold (2 failed checks).

A couple of minutes later, the autoscaling group terminated the unhealthy instance and launched a replacement, to which the load balancer eventually started forwarding requests.

Conclusion

With autoscaling groups, the importance of having new instances come up serving requests is crystal clear, no matter whether that is accomplished by using cloud-init or custom AMIs.

Unfortunately, my script for launching and tearing down clusters is mainly only useful for demonstration purposes. In production, it would be important to be able to adjust the configuration of the load balancer and the autoscaling group while the cluster is operational. It is regrettable that the Amazon management console does not support autoscaling groups, since graphical dashboards can be particularly nice for seeing the current configuration and status at a glance and making small adjustments.

Don’t forget to tear down your cluster when you are done with it. Run “./launch_cluster.py --shutdown”, wait at least five minutes, and then run it again. It will print nothing at all if all resources have been released.

The next installment will look into deploying updates to running instances.

This article was written by Ken Rimey and has later been edited by other employees of Codento.