sunnuntai 19. elokuuta 2012

Some tomcat7-maven-plugin tricks

Here's some useful tomcat7-maven-plugin tricks that work also with other Maven plugins.

How to set java endorsed dir for tomcat7-maven-plugin

Sometimes you need to use Java endorsed standards override mechanism with Maven plugins. This can be accomplished simply by setting proper MAVEN_OPTS value. For example

export MAVEN_OPTS="-Djava.endorsed.dirs=/some/directory/where/endorsed/jars/are"

How to change tomcat7-maven-plugin classpath order

Very often you need to have the runtime classpath in certain order when running Tomcat or other Maven plugins. I found out by trial and error that the used classpath JAR ordering depends on the order of declared dependencies in pom.xml (most likely this is documented somewhere). So, if you need to get some libraries before others, just move them in the beginning of pom.xml. Of course this does not help, if you have many plugins that need a different order for each plugin.

How to list all loaded classes and their respective JAR files

This is something I had been looking for years and was extremely happy when I finally got to know it. At least Sun Java JRE provides a command line switch for showing all the classes that are loaded into JVM and the source JAR file of those classes. It's not always easy to know what classes are loaded, especially if your dependencies have dependencies to other JAR files that are somehow conflicting (for example different version) with JAR dependencies defined elsewhere. Anyway, to list the classes and their exact source, do the following.

export MAVEN_OPTS="-verbose:class"

The output is something like this.
[Loaded javax.xml.bind.annotation.XmlElement from /home/perttu/software/jdks/jdk1.6.0_31/jre/lib/rt.jar]
[Loaded javax.xml.bind.annotation.XmlElement$DEFAULT from /home/perttu/software/jdks/jdk1.6.0_31/jre/lib/rt.jar]
[Loaded javax.xml.ws.WebFault from /home/perttu/software/jdks/jdk1.6.0_31/jre/lib/rt.jar]

With these tools, any problem related to Tomcat Maven plugin and classpath should be easy to solve.

perjantai 25. toukokuuta 2012

Encrypting SWAP partition and taking previously encrypted home partition into use

Due to my personal Ubuntu release update process, I have to configure encryption of my boot and home partition manually every now and then. Because I do this regularly, I'm documenting my process here in my blog...

Here's my Ubuntu release update process.
  1. Make Ubuntu installation program to format the partition that held the previous Ubuntu version
  2. Format and configure existing boot partition as the new boot partition
  3. Configure existing swap partition as the new swap partition
  4. Install Ubuntu normally without dedicated home partition
After installation, my encrypted home partition is not accessible anymore, because it was not configured as part of the installation process. I haven't been able to configure either normal installer or alternative installer to take previously encrypted partitions into use. So, it has to be done after installation.

After installation has finished, install cryptsetup.

$ sudo apt-get install cryptsetup

Then configure /etc/fstab file by adding configuration for swap and home. For example.

/dev/mapper/sda7_crypt /home           ext4    defaults        0       2
/dev/mapper/sda5_crypt none            swap    sw              0       0

The actual values depend on your hard disk partitioning. Here's example from my laptop.

$ sudo fdisk -l

Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders, total 976773168 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x7250e0b9

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1            2048    24588287    12293120   27  Hidden NTFS WinRE
/dev/sda2        24588288    24793087      102400    7  HPFS/NTFS/exFAT
/dev/sda3   *    24793088    26746879      976896   83  Linux
/dev/sda4        26748926   976773119   475012097    5  Extended
/dev/sda5        26748928    34559999     3905536   82  Linux swap / Solaris
/dev/sda6        34562048   132216831    48827392   83  Linux
/dev/sda7       132218880   976773119   422277120   83  Linux

The next step is to configure /etc/crypttab by adding appropriate encryption setup. In my case it looks like this.

sda5_crypt /dev/sda5 /dev/urandom cipher=aes-cbc-essiv:sha256,size=256,swap
sda7_crypt UUID=5a9c38c3-2aa9-433b-9efd-c0e9357d0811 none luks

The swap partition setup is "universal" and it should work on any computer (of course the correct partition may differ from this example). The swap is encrypted with a key that is randomly generated on each system startup.

For the home partition you have to know the UUID of the partition. Here's one way to find it.

$ sudo blkid 
/dev/sda1: LABEL="Recovery" UUID="78F82CACF82C6A98" TYPE="ntfs" 
/dev/sda2: LABEL="System Reserved" UUID="62ACA8E6ACA8B5C7" TYPE="ntfs" 
/dev/sda3: UUID="7d526d52-018a-4b0c-9e26-64f1143cf0da" TYPE="ext4" 
/dev/sda5: UUID="0d0178b1-ade5-416f-8535-82455a8febd5" TYPE="swap" 
/dev/sda6: UUID="b0d7a0b7-0bb8-4cf9-978a-f7c6ebb2126f" TYPE="ext4" 
/dev/sda7: UUID="5a9c38c3-2aa9-433b-9efd-c0e9357d0811" TYPE="crypto_LUKS" 
/dev/sdb1: LABEL="siirtoNTFS" UUID="494640E516B11A6B" TYPE="ntfs" 
/dev/sdb2: LABEL="siirto" UUID="570857a6-4ab3-4d4b-99a2-88d383d3e588" TYPE="ext4" 

With this configuration, the system should ask for home partition's encryption key during system startup and everything should work as before.

sunnuntai 20. toukokuuta 2012

Jetty Maven Plugin with hot code replace



I'm often using Jetty Maven plugin to execute my web applications right from the build. It provides an easy and operating system independent way to execute the web app. You only have to check out the source code from version contol, execute Maven build and start Jetty with Maven command. What could be easier way to run your web application in development environment?

I want to point out the following benefits in using Maven Jetty plugin

  • IDE independent way to execute web application. Works on Eclipse, IDEA, NetBeans and whatever
  • Support hot code replace: you can change code inside methods without restarting the whole application
  • Works on Windows, Linux and Mac or any other Java compatible operating system. No need to setup the application server instance
  • Starts fast and it's easy to reload the application to the server after changes

Just add the XML snippet in the end of this post to your pom.xml and execute Maven with mvn jetty:run. As a result, you will get response from your web app in http://localhost:8180/example/. You can run the same goal from your favorite IDE and, thus, you get an IDE independent web app execution!

In case you want to try hot code replace, add the classpaths containing your code inside the extraclasspath-element. Then start the Maven build running Jetty in debug mode and connect to the debugging session with your favorite IDE. The easiest way to achieve this in Eclipse is to run the Maven build in debug mode and then connect to the process with Eclipse debugger. After Eclipse is properly connected to the Maven process running Jetty, all the code changes in method bodies are instantly visible in the running process  (of course the same code has to be modified by Eclipse so that the changes get to the classpath of Jetty).

If you prefer doing things in IDE, at least Eclipse has excellent Jetty plugin called Run Jetty Run (http://code.google.com/p/run-jetty-run/).

<build>
<plugins>
...
<plugin>
    <groupid>org.mortbay.jetty</groupid>
    <artifactid>jetty-maven-plugin</artifactid>
    <version>7.5.2.v20111006</version>
    <configuration>
        <stopport>9966</stopport>
        <stopkey>${project.artifactId}<stopkey>
        <!-- scanning is not used if reload is set to manual -->
        <scanintervalseconds>5</scanintervalseconds>
        <!-- application reloading by pressing enter in the console -->
        <reload>manual</reload>
        <webappconfig>
            <contextpath>/example</contextpath>
            <!-- Changes in these classes will be instantly applied to running Jetty process without restart -->
            <extraclasspath>target/classes;../dependant-project/target/classes;../another-dependant-project/target/classes</extraclasspath>
        </webappconfig>
        <!-- directories whose changes cause automated Jetty context reloading, not used if reload is manual -->
        <scantargets>
            <scantarget>../dependant-project/target/classes</scantarget>
        </scantargets>
        <connectors>
            <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector">
                <port>8180</port>
                <maxidletime>60000</maxidletime>
            </connector>
        </connectors>
        <systemproperties>
            <!-- system properties that are used for running Jetty -->
            <systemproperty>
                <name>some.system.property</name>
                <value>somevalue</value>
            </systemproperty>
        </systemproperties>
    </configuration>
    <dependencies>
        <!-- dependencies added to Jetty's classpath -->
        <dependency>
            <groupid>log4j</groupid>
            <artifactid>log4j</artifactid>
            <version>${log4j.version}</version>
            <type>jar</type>
        </dependency>
    </dependencies>
</plugin>
...
</plugins>
</build>

maanantai 9. huhtikuuta 2012

Automatisoidun asennuksen autuus

Kuinka monesti olet omassa projektissasi joutunut rutiininomaisesti asentamaan tekemäsi sovelluksen testi- ja tuotantoympäristöihin yhä uudelleen ja uudelleen? Kuinka monta kertaa muistat tehneesi huolimattomuusvirheen asennuksen aikana? Montako kertaa olet ihmetellyt testi- ja tuotantoympäristön konfiguraatiota, jonka joku muu on muuttanut tietämättäsi ja sovelluksesi rikkoen? Oma vastaukseni edellisiin kysymyksiin on lähes joka projektissa ja erittäin monta kertaa. Onneksi poikkeuksiakin mahtuu joukkoon. Näissä poikkeustapauksissa asennusautomaatio on tehty enemmän tai vähemmän täydelliseksi. Keskityn tässä blogauksessa käsittelemään näitä poikkeusprojekteja ja syitä siihen, miksi jokaisen projektin pitäisi automatisoida sovelluksen ja käyttöympäristön asennukset. Poikkeuksesta pitäisi siis tulla pääsääntö!

Asennuksen eri tasot

Oman kokemukseni perusteella sovelluksien asentamiseen liittyy neljä eritasoista asiaa.
  1. Laitteisto, joko fyysinen tai virtuaalinen
  2. Käyttöjärjestelmä ja sen päälle asennettavat perusbinäärit, kuten WWW-palvelin
  3. Ympäristön yleiset konfiguraatiot, jotka liittyvät käyttöympäristön ylläpitoon. Esimerkkinä ajastetusti suoritettavat varmuuskopioinnit
  4. Itse asennettavat sovellukset ja niiden konfiguraatiot
Nähdäkseni tason 1 asennusautomaation tekeminen on mahdollista vain, mikäli laitteisto on virtualisoitu. Esimerkiksi Amazon Web Services mahdollistaa virtuaalisen laitteiston luomisen komentoriviskripteillä. Laitteisto-käsitteeseen liitän tässä yhteydessä kaiken "raudan", kuten laskentakapasiteetin, tallennuskapasiteetin, verkkoyhteydet ja vaikkapa varmuuskopiointivälineet. Kun näiden luomisen automatisoi, tietää tarkasti ympäristön rakenteen ja tarvittaessa voi luoda helposti uusia vastaavia ympäristöjä. Laitteiston asennusautomaatiolla vältytään luomasta ympäristöjä, jotka ajan kanssa kasvavat tuntemattomiksi viidakoiksi.

Tasolla 2 sijaitsevat sekä käyttöjärjestelmä että käyttöjärjestelmässä ajettavat natiivisovellukset. Jos projektissa tehtävä sovellus on natiivisovellus, ei sitä silti lasketa tälle tasolle kuuluvaksi. Koska käyttöjärjestelmän ja natiivisovelluksien asentamisen automatisointi on yleensä vaikeata, eikä siitä välttämättä ole juurikaan hyötyä, voi tämän tason automatisoinnissa helposti oikaista tekemällä käsin asennetusta ympäristöstä levykuvan (disk image) ja käyttää sitä muussa automaatiossa. Mikäli projektissa tehtävälle sovellukselle tai sovelluksille riittää yhdenlainen käyttöympäristö, on manuaalisesti ylläpidettävä levykuva helpoin ratkaisu. Jos tarvitaan useita erilaisia käyttöympäristöjä, on syytä automatisoida myös käyttöjärjestelmän ja natiivisovelluksien asentaminen. Ainakin useimmissa Linux-pohjaisissa käyttöjärjestelmissä tämä on helppoa.

Tason 3 konfiguroinnilla tarkoitan sellaisia käyttöympäristöön liittyviä tehtäviä, jotka eivät liity suoranaisesti sovellukseen, mutta joiden pitää olla muutettavissa helposti kulloiseenkin tilanteeseen sopivasti. Yhtenä esimerkkinä voisi olla tietokannasta otettavat ajastetut varmuuskopiot. Periaatteessa ajastuksen voisi laittaa jo tasolla 2 suoraan vaikkapa levykuvaan, mutta tämä ei ole kovin joustavaa. On nimittäin mahdollista, että sovellus ottaa käyttöön esimerkiksi uuden tietokannan, joka pitäisi myös varmuuskopioda ja levykuvan muokkaaminen ja uudelleenasennus on työläs tehtävä näin pienen muutoksen takia. Tasolla 3 tehtävät muutokset elävät useammin sovelluksen kanssa, mutta ovat toisaalta siitä täysin irrallaan.

Tasolla 4 asennetaan ja konfiguroidaan projektin tuottama sovellus. Varsinkin testiympäristöissä sovellusta joudutaan asentamaan ja konfiguroimaan jatkuvasti, kenties jopa muutaman minuutin välein. Parhaimmillaan uusi sovellusversio voidaan asentaa jokaisen versionhallintaan tehtävän muutoksen jälkeen, jolloin uusimmat muutokset ovat jatkuvasti testattavissa oikeassa testiympäristössä.

Eri tasoilla tapahtuu muutoksia eri tahdilla. On selvää, että tason 1 muutokset ovat harvinaisimpia, koska laitteistoa ei yleensä ihan joka päivä muuteta. Tasolla 2 muutokset tapahtuvat harvoin, ehkäpä vain esimerkiksi käyttöjärjestelmän ja natiivisovellusten turvallisuuspäivitysten takia. Tasolla 3 voi olla jo päivittäisiä muutoksia, mutta muutoksia on varmasti paljon harvemmin kuin tasolla 4. Onhan projekteissa yleensä itse sovellus suurimpien muutosten kohteena.

Koska on paras hetki automatisoida?

Asennusautomaation tekeminen kannattaa aloittaa heti projektin alussa. Ensin voi asentaa vaikkapa vain tyhjän sovelluksen, jolla voi todentaa, että sovelluksen tarvitsemat ympäristön tarjoamat palvelut toimivat. Kun automaatiota tekee heti projektin alussa, tulee sovelluksestakin sellainen, että sen asennuksen voi automatisoida. On helppoa kirjoittaa vahingossa sellainen sovellus, jonka automaattinen asennus ja ylläpito ei ole kenenkään mielestä mukavaa.

Itse toimin asennusautomaation kanssa yleensä niin, että teen ensin jossain sopivassa ympäristössä käsin tarvittavat muutokset, joilla voin varmistua muutosten oikeellisuudesta. Kun tiedän tarkalleen mitä pitää tehdä, muokkaan automaattista sovellusasennusta vastaavasti. Näin vältän turhien asioiden tekemisen automaattisesti, vaikka riskinä onkin se, että unohdan automatisoida kaikki käsin tehdyt asiat.

Ikinä ei kannata jättää asennusautomaation tekemistä projektin loppupuolelle tai siihen hetkeen, kun "ei ole muutakaan tekemistä". Jos näin toimii, jää automaatio varmasti tekemättä ja käsin tehtävät asennukset maksavat ajallisesti nopeasti enemmän kuin automaation luominen ja ylläpito. Valitettavasti käsin asentamisen ja automaation kustannusten vertailu on niiden luonteen takia hankalaa. Projektijohto ei siten lyhytnäköisyyttään välttämättä ymmärrä antaa aikaa automaation rakentamiselle projektin alkuvaiheessa. Silloin kun on kiire tehdä muutakin ja alussa asennukset on nopeaa ja helppoa tehdä käsinkin, koska kaikki on vielä tuoreessa muistissa eikä sovellus ole kasvanut isoksi.

Automaation kustannukset

Automaattisen ja käsin tehtävän asennuksen kustannusvertailussa kannattaa ottaa huomioon seuraavat tekijät.
  • Käsin tehtävän asennuksen dokumentointi. Asennusta ei voi toistaa ilman laadukasta dokumentaatiota
  • Käsin tehtyjen asennusten huolimattomuusvirheiden korjaaminen. Ihminen tekee huolimattomuusvirheitä, kone ei
  • Käsin tehtyjen muutosten seurannan mahdottomuus. Useamman ihmisen porukassa kenelläkään ei ole tietoa mitä tarkkaan ottaen on tehty eikä yksinkään toimiva ihminen muista kaikkea mitä on tehnyt
  • Käsin tehtyjen asennusten henkilöriippuvuus. Asennuksen taitavan ihmisen pitää olla paikalla jokaisessa asennuksessa
  • Käsin tehtävien asennusten hitaus ja skaalausongelmat. Yksi ympäristö on helppo hallita, kaksi vaikeata ja kymmenen alkaa lähentelemään mahdottomuutta, vaikka asentajien määrää lisäisi
  • Käsin tehtävien asennusten vaatimien pääsyoikeuksien hallinta. Ketkä kaikki saavat päivittää eri ympäristöjä ja millä oikeuksilla. Automaation kautta voidaan sallia vähäisilläkin pääsyoikeuksilla kokonaisten ympäristöjen luominen ilman tietoturvaheikennyksiä
Asennusautomaation tekemisen käynnistäminen vaatii projektilta ison alkupanoksen. Ensin pitää valita käytettävä automatisointitapa. Tehdäänkö automaatio komentoriviskripteillä vai käytetäänkö jotain asennusautomaation erityisesti suunniteltua ohjelmistoa (http://en.wikipedia.org/wiki/Comparison_of_open_source_configuration_management_software).

Kun on tiedossa millä välineellä automaatiota lähdetään tekemään, pitää alkaa tutkimaan käytössä olevien ohjelmistojen konfiguroimista ilman sovelluksien omia GUI-työkaluja. Kaikki ohjelmistot eivät välttämättä tee automatisointia helpoksi. Yksi pahimmista näkemistäni ongelmista on se, että ohjelmistoja voi konfiguroida vain ja ainoastaan GUI-työkaluilla, jotka tuottavat binäärimuotoisia konfiguraatiotiedostoja. Näitä ohjelmistoja kannattaa välttää, mikäli mahdollista.

Automaation ylläpitäminen on myös työlästä, eikä jokainen kehittäjä välttämättä jaksa opetella miten automatisointi toimii. Tämä saattaa hidastaa projektin toimintaa, mikäli kaikkien kehittäjien on kuitenkin pakko opetella automaation salat tai mikäli kehittäjien pitää odotella erikoistuneita asennusautomaation tekijöitä.

Puhtaan pöydän lähestyminen

Asennusautomaation voi hoitaa kahdella tavalla, joko 1) automatisoimalla asennukset aina niin, että kaikki tehdään täysin puhtaalta pöydältä tai 2) niin, että asennuksissa jatketaan aina edellisen asennuksen tuottamasta tilasta.

Jos suinkin vain on mahdollista, kannattaa asennukset suorittaa aina puhtaalta pöydältä. Aina tätä vaihtoehtoa ei varmasti ole, mutta mikäli asennusautomaation ei tarvitse välittää edeltäneistä asennuksista, saadaan varmemmin täsmälleen haluttu lopputulos. Mikäli uusimman version asennus riippuu aina edellisen version asennuksesta, on migraatioskriptien luominen työlästä ja virheherkkää. Lisäksi kaikki sovellusversiot pitää asentaa tietyssä järjestyksessä kaikkiin ympäristöihin, koska migraatioskriptejä ei yleensä voi laatia toimimaan yhteensopivasti minkä tahansa lähtötilanteen kanssa. 

Jos puhtaan pöydän lähestymistapaan päätyy, on syytä varoa kahta asiaa.
  • Sovelluksen pitää toimia, vaikka sovelluksella ei ole käytössä aiemmin kerättyä dataa. Sovelluksessa voi olla esimerkiksi sisäisiä tarkistuksia, jotka olettavat, että sovelluksella on tietty määrä vanhaa kertynyttä dataa käytössä, mutta näinhän ei ole puhtaalta pöydältä lähdettäessä
  • Mikäli sovellus tallettaa aiemman käytön perusteella mitä tahansa tilatietoja, täytyy tila siirtää puhtaalta pöydältä asennettuun ympäristöön. Tällaista tilaa voi olla esimerkiksi lokitiedot.
Mitenkä asennukset sitten valitseekaan tehtäväksi, kannattaa varoa tallettamasta tilatietoja liian moneen paikkaan. Esimerkiksi erillisen tietokantapalvelimen ylläpito on paljon helpompaa kuin pyörittää tietokantaa ja sovellusta samalla koneella. Mikäli koko sovellus halutaan esimerkiksi asentaa puhtaaseen ympäristöön, mutta vanha data halutaan säilyttää, pitää tieto siirtää vanhasta ympäristöstä uuteen asennuksen yhteydessä. Mikäli käytetään erillistä tietokantapalvelinta, ei uuden version asennuksessa tarvitse tehdä mitään vanhan tiedon siirtämistä. Sama ajatus pätee pienempiinkin ympäristöihin, joissa ei pelata erillisillä koneilla: mieti aina minne tilan tallennat.

Automaation hyödyt

Mielestäni suurin hyöty automatisoidusta asennuksesta on työn mielekkyyden ylläpitäminen. Harvaa kiinnostaa saman rutiiniasian huolellinen toistaminen päivästä toiseen. Tästä seuraa asennuksen muut hyvät puolet.
  • Koska rutiinin toistamiselta ihmisivoimin vältytään, asennuksien laatu paranee.
  • Automatisoitu asennustapa on aina nopein, koska hidasta ihmistä ei tarvitse painelemaan nappeja.
  • Asennuksien dokumentaatio on aina ajan tasalla, koska kenenkään ei tarvitse päivittää ihmisen seurattavaa dokumentaatiota: asennusskriptit ovat asennukselle sama asia kuin ohjelmakoodi sovellukselle!
  • Projektin riippuvuus yksittäisistä ihmisistä vähenee, koska uuden version asennus ei vaadi asentajien paikalla oloa
  • Virheet korjataan täsmälleen kerran asennusautomaatioon eikä virheitä ja niiden ratkaisuita tarvitse muistaa joka asennuksen yhteydessä
  • Osaamisen siirto on helppoa, koska osaamisen siirrossa riittää pitkälti se, että selittää miten asennusautomaatio toimii. Yksityiskohdat näkee skripteistä

Automaation ongelmat

Automaation näkyvin ongelma lienee se, että jostain pitää projektin alkupuolella löytää aika automaation toteuttamiseen ja sen jälkeen ylläpitoon. Väitän, että aikaa ei kokonaisuudessaan mene enempää kuin työn tekemiseen käsin joka kerta, mutta tämä on vain näppituntumatietoutta. 

Automatisoitujen asioiden muuttaminen projektin paniikkitilanteissa, kuten järjestelmän tai tuotantoasennuksen yhtäkkisen sekoamisen yhteydessä on aivan liian hidasta. Pelastuskeinona paniikkitilanteissa voi käyttää asioiden selvittämistä käsityönä ja automatisoimista myöhemmin. 

Automatisoinnissa on riskinä, että projektin alussa valitaan väärä automaatiotapa ja toteutus menee hukkaan. Mikäli automatisointitoteutus joudutaan vaihtamaan kesken projektin, on se kuitenkin helpompaa kuin automaation tuominen kokonaan puhtaalta pöydältä aiemmin käsin asennettuun sovellukseen. Näin siksi, että vanha, vaikkakin huono, automaatio sisältää kaiken tarvittavan tiedon uudelle toteutukselle.

Täydellisesti automatisoitu asennus voi mennä täydellisesti metsään, jos toteutus ei tarkista asennuksen onnistumista sen edetessä. Hyvä virheen käsittely toki auttaa useimmiten. Vanha totuus on kuitenkin se, että mitenkään ei voi päästä niin suuriin ongelmiin kuin automaattisesti etenemällä (vrt. autonavigaattori vs. paperikartta). Asennuksen pitää siis kertoa kohtaamistaan ongelmista heti ja jäädä odottamaan ylläpitäjän korjaustoimenpiteitä.

Automaation sopivuus erilaisiin projekteihin

Uskon, että asennusautomaatio maksaa itsensä takaisin projektissa kuin projektissa. Ainoa mieleen tuleva poikkeus voisi olla joku start-up-projekti, missä kerta kaikkiaan kaikki resurssit on pakko laittaa yhden ainoan sovelluksen mahdollisimman pikaiseen tuottamiseen. Itse en ole tällaista projektia ikinä kokenut, joten mahdoton sanoa onko näin. Projektin asennusautomaatio kannattaa hoitaa kuntoon, niin pääsee keskittymään projektin oikeisiin ongelmiin!

tiistai 3. huhtikuuta 2012

Using Apache as reverse proxy through HTTP and HTTPS


HTTP reverse proxying


The ultimate goal is to reverse proxy SSL secured web site over Apache installed on Ubuntu server. This means that we are using Apache to serve content from a remote web site in a way that browser thinks its getting the data from our Apache and the remote web site thinks our Apache is a browser accessing the site data.

Let's start with reverse proxying without SSL. These instructions work on a fresh Ubuntu 10.04 installation (I'm using an image from Amazon Web Services). First install Apache.
$ sudo apt-get install apache2

Install mod_proxy_html on Apache.
$ sudo apt-get install libapache2-mod-proxy-html

It seems that this command also enables the mod_proxy_html automatically:
$ ls /etc/apache2/mods-enabled/proxy_html.*
/etc/apache2/mods-enabled/proxy_html.conf  /etc/apache2/mods-enabled/proxy_html.load

Enable the modules needed by proxying.
$ sudo a2enmod proxy_http
$ sudo a2enmod headers

Disable default site that comes with Apache installation.
sudo a2dissite 000-default

Create reverse proxy configuration. Add the following to file /etc/apache2/sites-available/reverseproxy
<VirtualHost *:80>
  ServerAdmin webmaster@localhost

  ErrorLog /var/log/apache2/reverseproxy_error.log

  # Possible values include: debug, info, notice, warn, error, crit,
  # alert, emerg.
  LogLevel info

  CustomLog /var/log/apache2/access.log combined

  # We're not an open proxy
  ProxyRequests off

  # Proxying is available for anyone
  <Proxy *>
    Order deny,allow
    Allow from all
  </Proxy>

  # The site we're proxying through http://oursite.fi/proxytest/
  ProxyPass /proxytest/ http://www.iltalehti.fi/
  ProxyPassReverse /proxytest/ http://www.iltalehti.fi/

  # Use mod_proxy_html to rewrite URLs
  SetOutputFilter proxy-html
  ProxyHTMLURLMap http://www.iltalehti.fi /proxytest
  ProxyHTMLURLMap  /      /proxytest/

  # Disable compressed communication between Apache and target server
  RequestHeader    unset  Accept-Encoding
</VirtualHost>

Enable our reverse proxy site and restart Apache
$ sudo a2ensite reverseproxy
$ sudo service apache2 restart
Now you should be able to see Iltalehti (http://www.iltalehti.fi) through your site under /proxytest.

Securing proxied connection with SSL (HTTPS reverse proxying)


Create self signed certificates. These commands are explained on page https://help.ubuntu.com/10.04/serverguide/C/certificates-and-security.html
$ openssl genrsa -des3 -out server.key 1024
$ openssl rsa -in server.key -out server.key.insecure
$ mv server.key server.key.secure
$ mv server.key.insecure server.key
$ openssl req -new -key server.key -out server.csr
$ openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
$ sudo cp server.crt /etc/ssl/certs
$ sudo cp server.key /etc/ssl/private

Disable plain HTTP based reverse proxy.
$ sudo a2dissite reverseproxy

Add the following to file /etc/apache2/sites-available/reverseproxy-ssl.
<VirtualHost *:443>

  ServerAdmin webmaster@localhost

  ErrorLog /var/log/apache2/reverseproxy-ssl_error.log

  # Possible values include: debug, info, notice, warn, error, crit,
  # alert, emerg.
  LogLevel info

  CustomLog /var/log/apache2/access-ssl.log combined

  # We're not an open proxy
  ProxyRequests off

  # Proxying is available for anyone
  <Proxy *>
    Order deny,allow
    Allow from all
  </Proxy>

  # The site we're proxying through http://oursite.fi/proxytest/
  ProxyPass /proxytest/ https://www.veikkaus.fi/
  ProxyPassReverse /proxytest/ https://www.veikkaus.fi/

  # Use mod_proxy_html to rewrite URLs
  SetOutputFilter proxy-html
  ProxyHTMLURLMap https://www.veikkaus.fi:443 /proxytest
  ProxyHTMLURLMap https://www.veikkaus.fi /proxytest
  ProxyHTMLURLMap  /      /proxytest/

  # Disable compressed communication between Apache and target server
  RequestHeader    unset  Accept-Encoding

  #   SSL Engine Switch:
  #   Enable/Disable SSL for this virtual host.
  SSLEngine on

  # Allows the proxying of an SSL connection
  SSLProxyEngine On

  # A self-signed certificate
  SSLCertificateFile    /etc/ssl/certs/server.crt
  SSLCertificateKeyFile /etc/ssl/private/server.key
</VirtualHost>

Enable HTTPS based reverse proxy.
$ sudo a2enmod ssl
$ sudo a2ensite reverseproxy-ssl
$ sudo service apache2 restart

Now you should be able to see Veikkaus (https://www.veikkaus.fi) through your site under path /proxytest.

tiistai 6. maaliskuuta 2012

Jaettu lähdekoodin omistajuus - miksei jaettu tuoteomistajuus?

Kuulen erittäin usein ihmisten hehkuttavan kuinka maailma on parempi, mikäli kaikki versionhallinnassa oleva lähdekoodi on kaikkien muokattavissa. Projektipäälliköt ja tuotepäälliköt erityisesti ovat innoissaan siitä, että kun lähdekoodi on kaikkien muokattavissa, projekteihin ei pääse muodostumaan pullonkauloja kiireisimpien ihmisten kohdalle. Kuka tahansa projektin kehittäjä kun voi muokata mitä tahansa ominaisuutta!

Koska lähdekoodin muokkaaminen käy keneltä tahansa, niin voisiko tuoteomistaja ja projektipäällikkökin olla vain resurssi muiden joukossa. Jos yrityksessä on useampi tuoteomistaja tai projektipäällikkö, voidaanko näistä ihmisistä ottaa kuka tahansa vastaamaan tuotteita tai projekteja vaivaaviin kysymyksiin? Jos tuotepäälliköltä kysyy, niin vastaus on useimmiten ei. Eihän muut tuotepäälliköt välttämättä tunne tuotetta niin hyvin eivätkä tuotteeseen halutut ominaisuudet ole kaikkien tiedossa yhtä kirkkaasti. Projektipäällikkö puolestaan helposti toteaa, että pitäähän sitä tuntea projektin sidosryhmät ja aikataulut sekä muut vaatimukset, että voi tehdä hyviä päätöksiä. Todettakoon, että en ole tehnyt aiheesta kattavaa kyselyä ja siksi en tiedä kuinka moni tuotepäällikkö tai projektipäällikkö esittämälläni tavalla ajattelee.

Oletetaan nyt kuitenkin, että edellisessä kappaleessa esittämäni arvaukset ovat totta ja tuoteomistajat sekä projektipäälliköt eivät pidä järkevänä geneeristen tuoteomistaja- ja projektipäällikköresurssialtaiden luomista. Onko sovelluskehittäjän työ sitten niin yksinkertaista ja rutiininomaista, että kuka tahansa voi tehdä muutoksia mihin tahansa tuloksen säilyessä hyvänä? Olettavatko kaikki ei-sovelluskehittäjät näin? Olettavatko monet sovelluskehittäjät, että näin on? Miksei sovelluskehittäjän tarvitse tuntea eri ominaisuuksien taustoja tai tulevia tarpeita, jos kerran asiasta päättävien tarvitsee?

Myönnän, että suuri osa lähdekoodin kirjoittamisesta on täysin rutiininomaista ja tylsää puuhastelua, johon pystyy lähes kuka tahansa sovelluskehittäjä, mikäli välineet ovat tuttuja. Silti väitän, että rutiininomaisesta puuhastelusta syntyy hyvin nopeasti kokonaisuuksia, joita ei ymmärrä, ellei ole tekemisissä kyseessä olevan lähdekoodin kanssa tarpeeksi ja ajallisesti lähellä. On helppo tehdä muutaman rivin muutoksia yksinkertaisiin ominaisuuksiin. Sen sijaan ei ole helppoa tehdä isompia muutoksia saati pystyä johdonmukaisesti muuttamaan isompia kokonaisuuksia, mikäli ei tunne vanhaa toteutusta.

Ihmettelen myös sitä, että jaetussa lähdekoodin omistajuudessa ei kukaan tunnu tietävän keneltä voisi kysyä, mikäli joku asia ei toimi. Joskus vastaus tähän ongelmaan on se, että koska kaikki on kaikkien muokattavissa, niin voit korjata ongelman itse. Tämähän toimii mainiosti, mikäli ongelman hoksaa ajoympäristöjen ylläpitäjä, testaaja tai loppukäyttäjä - eikö vain? Mitä isompi ryhmä vastaa tuotteen toteutuksesta, sen vaikeampi on löytää ihmistä keneltä voi kysyä. Yleensä tekijätkään eivät hetken päästä muista mitä kaikkea ovat toteuttaneet ja minne, koska yksityiskohdat tuppaavat unohtumaan. Vastaava ongelma kohdattaisin nopeasti, mikäli eri tuoteomistajat tai projektipäälliköt joutuisivat vastailemaan erikseen samoihin kysymyksiin: vastaukset olisivat 100% varmuudella erilaisia.

Minun mielestäni on hyvä, että ihmiset tietävät noin suunnilleen mistä palikoista he ovat vastuussa. Tämä tarkoittaa myös sitä, että he muokkaavat tiettyjä lähdekoodin osia pääosin itse ja vastaavat muidenkin tekemistä muutoksista vastuullaan oleviin osiin. Kaikki saavat muokata kaikkea, mutta kaikille osille pitää löytyä selkeät vastuulliset. Eri vastuualueiden välistä raja-aitaa voi helposti pienentää koodikatselmoinneilla ja vastuuttamalla nimettyjä ihmisiä toistensa varahenkilöiksi lomien ja muiden poissaolojen ajaksi. Näin ainakin pari ihmistä kyttää jokaiselle alueelle tapahtuvia muutoksia.

Varsinkin hyvin suuren lähdekoodimäärän ylläpitämisessä ei ole mitään järkeä lähteä hajottamaan kaikkien tekemisiä kaikkialle, koska tällöin varmistetaan se, että oikeastaan kukaan ei kunnolla tunne mitään osaa järjestelmästä. Samalla muutokset rapauttavat järjestelmää kokonaisuutena, koska kukaan kehittäjistä ei välttämättä ymmärrä tai välitä kokonaisuudesta. Voidaan toki väittää, että kaikkien häärääminen kaikkialla pelkästään vahvistaa kokonaiskuvaa järjestelmästä. Isommissa järjestelmissä osaamista pitäisi kuitenkin yleensä olla tähtitieteellisen paljon, jotta ylläpitäjänä voi nopeasti hahmottaa sekä kokonaisuuden toiminnan että kaikki yksityiskohdat.

Projekteissa on usein mukana henkilöitä (useimmiten pitkään projektissa työskennelleet tai muuten vaan keskimääräistä pätevämmät kaverit), jotka pystyvät hallitsemaan erittäin laajoja kokonaisuuksia. Näille poikkeusihmisille ei tuota mitään ongelmia muuttaa koko lähdekoodimassaa koherentisti ja heille ei ole välttämätöntä rajata omaa hiekkalaatikkoa. Toisaalta, vaikka ko. ihmisille annetaan koko projektin lähdekoodiin vaikuttavia tehtäviä, pitää heidän silti muistaa myös keskustella muutoksista eri alueiden vastuullisten kanssa.

Jos tuoteomistajana tai projektipäällikkönä luet tätä tekstiä, niin mietihän seuraavan kerran kahdesti, kun joku tulee vouhottamaan jaetun lähdekoodin autuudesta. Vasta kun olet valmis antamaan tuotteesi tai projektisi muiden satunnaisesti johtamaksi, voit kirkkain silmin todeta jaetun lähdekoodin omistajuuden olevan absoluuttisesti hyvä asia.

maanantai 27. helmikuuta 2012

Cell based positioning with TK-202

The TK-202 tracker sends the mobile network data, when you configure it to reply in "SMS format" and request current location with smsone+password command.

The last four numbers in the SMS define MCC (Mobile Country Code), MNC (Mobile Network Code), LAC (Location Area Code) and Cell Id. The last two are in hexadecimal format. To find out the location of the tracker without GPS signal, you can use for example the following services:

  1. http://cellphonetrackers.org/gsm/gsm-tracker.php (before inserting the LAC and Cell Id, remember to convert them into decimal format!)
  2. http://www.track-position.com/loc?c=cellid-lac-mnc-mcc (replace word cell id and lac with decimal converted values; replace words mnc and mcc with their respective values)
  3. http://openbmap.org/api/getGPSfromCellular.html
  4. https://labs.ericsson.com/apis/mobile-location/ (requires registration)
  5. http://opencellid.org/ (requires registration)

sunnuntai 26. helmikuuta 2012

TK-202 tracker

TK-202 tracker
Be warned: this blog post is not about software. It's about hardware!

Here's some usage instructions for TK-202 (or TK202) tracker manufactured by Xexun. Although Xexun offers user manuals on their web site, the manuals are far from perfect. By combining different manuals for different products, you can find almost all the information written below.

Some comments about the tracker

It seems that the tracker stays functional over 30 hours if it's only using GPS and GSM connections. I haven't tried to use it with the home station that would allow the tracker to shut down GPS (and maybe even GSM) modules.

The TK-202 is quite well built. However, it has some features I don't like too much. First of all, you cannot change the time or date shown by the watch. When TK-202 has a GPS signal, it sets the time automatically to the current time. I guess it should also change the date according the the GPS data, but I have never seen it working. The time is set to UTC (GMT) time and user has to configure the offset from UTC time to get the local time correctly shown.

Another disturbing feature is that the small buttons the watch has are very difficult to operate. If you manage to press the buttons, you don't get any feedback from the watch. There are two buttons on the side of the watch. One of them sends SOS message and the other shows current tracker coordinates. To show the current tracker coordinates, you need to hold down the coordinate switching button for a couple of seconds. If the tracker doesn't know it's coordinates, it just shows blank screen. When coordinates are shown, the tracker displays them with blue back light on the screen. You can easily send an SOS message by pressing a button, but there's no way to tell whether the watch really has sent the SOS message or not. To turn the tracker on and off, you have to hold down the both buttons simultaneously for some seconds. It seems that you must press the coordinate switching button just before pressing the SOS button. Otherwise the tracker just sends the SOS message and doesn't turn off. Even when the tracker is turned off, it uses a lot of battery.

Small side buttons
The worst feature of TK-202 is that it's quite bad in locating itself. I have been walking with the tracker by the sea (guaranteeing that there's nothing that could disturb the GPS signal) for more than half an hour and the tracker hasn't been able to find its location. If the tracker looses the GPS signal, it may not be able to recover or the recovery takes ages.



General  instructions for using SMS commands
The + sign used in the tracker SMS commands means that the text should be catenated together without any other characters such as space.

After each command, the tracker sends some form of OK message. Don't try to send more than one command at a time.

Tracker initialization

begin+password

When the tracker is in default configuration (first time use), the command is the following.

begin123456

Send this command after the tracker has acquired its position for the first time.

Changing the password
To change the default password, use the following command.

password+old password+space+new password

For example to change password from 123456 to 654321, send the following SMS.

password123456 654321

Adding authorized phone numbers
Authorized phone numbers are allowed to query tracker's position either by SMS or call (depending on the operating mode). Although authorization is required by some TK-202 features, it's generally not mandatory. To make all features easily  accessible from your own cell phone, you may authorize it (and by doing this, you also block random people from accessing your tracker).

admin+password+space+cell phone

For example, to authorize Finnish cell phone number 040 1234567 so that it would work also outiside Finland (i.e. using international format, the Finnish country code is +358), you could use the following.

admin123456 00358401234567

Note that the + sign is coded as double zeroes! The rest of the authorized numbers (there may be five at most) must be configured by the first authorized number.

To remove one authorized number,  send the following SMS (I haven't tried this).

noadmin+password+space+authorized number

To remove all authorized numbers, use normal begin command that was used when the tracker was taken into use (I haven't tried this). For example.

begin123456

Changing answering modes (positioning, monitoring, two-way calling)

For some reason the TK-202 manual does not tell how to set up two-way calling mode. This is weird, because on of the selling points of TK-202 is definitively the two way calling feature (I haven't found many watch trackers with microphone and loudspeaker that would allow user to make two way calls)!

There are three modes in TK-202 that I'm aware of: tracking, monitoring and two-way talk. When the tracking mode is on, the tracker sends its position as SMS when it's being called by an authorized number. In monitoring mode, the tracker hangs up when called by an authorized number and calls back automatically. When call is answered, the authorized caller is able to hear what's happening in the surroundings of the tracker. In two-way talk mode, a call from authorized number is automatically answered by the tracker and the caller and the person carrying the tracker may talk to each other. Here's how to change between the modes.

tracker+password
monitor+password
talk+password

For example.

tracker123456
monitor123456
talk123456

In my opinion, both the monitoring and two-way talking are very well implemented. There is a faint echo on the line, but it's still very easy to communicate.

Changing the time zone (to set the time shown by LCD display)


The tracker is showing time in UTC time, if not changed by configuration. You can set the time difference between UTC and local time with this command.

time zone+password+space+GMT time

For example, for Finnish winter time, you should use the following.

time zone123456 2

Use negative numbers for time zones "before" UTC.


Changing the SMS format sent by the tracker

This is also something that's completely missing from the TK-202 manual. User is allowed to change the format of the SMS sent by the tracker. Here's some instructions.

To get one Google map link from the tracker, send the following message.

smslinkone+password

To get the results always in Google maps format, send the following message.

smslink+password

To get the results always in plain SMS format (default mode), send the following message (I haven't tried this).

smstext+password

It's also possible to get a single SMS formatted message.

smsone+password

Tracking periodically

You can configure the tracker to send multiple location SMSs with one command. You can specify how many location messages you want and how often these messages should be sent. However, it seems that the tracker is somehow failing to count the number of messages correctly and you may get more messages than you requested.
The command form is the following.

tXXXsYYYn+password

The part XXX is number of seconds, minutes or hours that the tracker should wait between the messages. Replace s with m to get minutes and h to get hours, respectively. The YYY part is the total number of messages the tracker should send. Both XXX and YYY have range of 001 - 255 and you must always use three digits.

To turn of periodic tracking, use this command.

notn+password

Some features to be tried later

TK-202 may have the following features that I haven't tried them yet (I'm not sure as I found these from manuals for other products).

  1. tlimit+password+space+distance in meters (for example tlimit123456 50)
    • This command should set the tracker to alarm if it is being moved more than 50 meters away from the point where the command was given. Distance of 0 meters will cancel the warning.
  2. GpsAutoSearch+password+space+120 (for example GpsAutoSearch123456 120)
    • This command should change the refreshing frequency of GPS measured coordinates in seconds. The accepted values are between 120 - 600 seconds.
  3. restart+password (for example restart123456)
    • This command should remotely restart the tracker. Perhaps useful in some situations
    • I tried this once and the tracker stopped functioning. The only way to get it working again was to remove the battery
  4. volume+password+number
    • This command should change the loud speaker volume. Accepted range is 0 to 99.
  5. Some other commands? Geofencing is also interesting, but not useful for me...
Battery cover
Battery front
Battery back




sunnuntai 19. helmikuuta 2012

Sovelluksien konfiguroinnista sananen

Olen projekteissani törmännyt moneen erilaiseen tapaan tehdä sovelluskonfiguraatiota. Konfiguraatiolla tarkoitan tässä sitä, että sama käännöstuote eli (yleensä) binääri saadaan toimimaan eri tavalla konfiguraatiota muokkaamalla ilman tarvetta tehdä uudelleenkäännöstä lähdekoodista. Esimerkiksi tietokannan osoite ja tunnus ovat yleensä konfiguraatiotiedostoon erotettuna, koska testi- ja tuotantoympäristöissä käytetään usein eri tietokantaa.

Mielestäni on parempi viedä konfiguraatiota erillisiin tiedostoihin mieluummin enemmän kuin liian vähän. Joidenkin mielestä kuitenkin erilliset konfiguraatiotiedostot ovat jopa turhia, koska heidän ajatusmallinsa mukaan koodi voidaan aina kääntää ja asentaa uudelleen, kun lähdekoodissa oleviin asetuksiin tehdään muutoksia. Ylenmääräisestä konfiguroitavuudesta on kyllä haittaakin ja parasta on, jos melkein kaikille konfiguroitaville asioille voi antaa järkevän oletusarvon joko suoraan sovelluksen lähdekoodissa tai sitten erillisissä oletusarvoja sisältävissä konfiguraatiotiedostoissa.

Nähdäkseni on olemassa neljä eri tapaa tehdä sovelluksen konfiguraatiota.

  1. Lähdekoodissa olevat vakiot, joiden muuttaminen vaatii lähdekoodin uudelleenkääntämisen
  2. Konfigurointi erillisiin tiedostoihin (Javassa esimerkiksi properties-tiedostot), joiden sisältö kiinnitetään käännösaikaisesti esimerkiksi Maven filttereillä.
  3. Konfiguraatiotiedostot, jotka eivät mene binääripakettien (JAR, WAR, EAR ja vastaavat eri kielissä) sisään ja joita voidaan muutella kääntämisen jälkeen vapaasti. Muutosten käyttöönotto vaatii prosessin uudelleenkäynnistyksen
  4. Prosessin ajoaikainen konfiguraatio eli asetusten muuttaminen ilman prosessin uudelleenkäynnistystä. Tämä tapa on käytössä melkein kaikissa graafisen käyttöliittymän sisältävissä sovelluksissa, mutta erittäin harvoin taustaprosesseissa
Esittämäni konfiguroitavuuden tasot ja niiden järkevä käyttö projektissa riippuu täysin projektin koosta ja tavoitteista.

Jos koodailee itsekseen tai hyvin pienellä ryhmällä sovellusta, jonka ajoympäristö on yksinkertainen ja build-prosessin sekä kehittäjäryhmän hallitsema, voidaan rajoittaa konfiguroitavuus tapaan 1. Tällöin vaatimuksena on se, että kehittäjän on äärimmäisen helppo viedä pienetkin mutokset suoraan tuotantoon ja erillisiä testiympäristöjä ei juurikaan käytetä. On huomattava, että mikäli käytössä on yksikin ympäristö tuotannon lisäksi, se näkyy aina lähdekoodissa. Jokainen tuettava lisäympäristö ja poikkeama konfiguraatiossa vaatii muutoksia lähdekoodiin.

Tapa 2 on käyttökelposuudeltaan hyvin lähellä tapaa 1. Näillä on kuitenkin se merkittävä ero, että uudet ympäristöt tai erilaiset konfiguroinnit eivät enää vaadikaan välttämättä lähdekooditason muutoksia. Toki sovelluksen pitää osata hakea asetukset koodista erillään olevista tiedostoista. Yleensä tavassa 2 erilliset konfiguraatiotiedostot leivotaan binääripakettien sisään ja niiden buildin jälkeinen muuttaminen vaatii pakettien avaamista ja uudelleen paketointia käsipelillä. Tapa 2 soveltuu pieniin projekteihin, joiden ajoympäristö on projektien kehittäjien täydellisessä hallinnassa ja jossa kehittäjän on äärimmäisen helppo viedä pienetkin muutokset suoraan eri testi- ja tuotantoympäristöihin.

Tavassa 3 konfiguraatiomuutokset eivät koskaan vaadi lähdekoodin muutoksia tai binääripaketin uudelleen kasaamista. Kaikki konfiguraatio on erillään varsinaisesta binääristä ja binäärille vain kerrotaan mistä päin sen tulisi käynnistyksen yhteydessä hakea konfiguraationsa. Tämä tapa soveltuu lähes kaikkiin projekteihin eikä ole juurikaan monimutkaisempi toteuttaa kuin tapa 2.

Tapa 4 on käytössä yleensä vain graafisen käyttöliittymän sisältävissä sovelluksissa. Joskus tosin taustaprosessejakaan ei saa niin vaan käynnistellä uudelleen ja niiden konfiguraatiota pitää pystyä muuttamaan ajonaikaisesti. Vaikka tapa 4 kuulostaa parhaalta ja joustavimmalta tavalta sovelluskonfiguraatioon, on sen toteuttaminen kaikelle konfiguraatiolle useimmiten haastavaa ja aivan liian työlästä saavutettuun hyötyyn nähden.

Olen ollut huomaavinani, että Maven filteröintiä käytetään vähän liian innokkaasti sovelluskonfiguraatioon, jossa tavoitteena on tavan 3 joustavuus, mutta sitä yritetään tehdä tavan 2 välinein. Vaikka Mavenin filteröinnillä saadaan helposti tehtyä erilaisia profiilikohtaisia konfiguraatioita, ei filteröintiä voi soveltaa järkevästi kaikelle konfiguraatiolle kuin hyvin suppeissa projekteissa. Esimerkiksi projektissa, jossa varsinaisesta ajoympäristöstä vastaa muu kuin kehitysporukka, täytyisi ajoympäristön ylläpitäjien jatkuvasti kertoa ajoympäristön muutoksista (vaikkapa uusi tietokantasalasana), jotta ne saataisiin sovelluksen konfiguraatioon Maven buildin yhteydessä. Vaikka kehitysporukka vastaisikin kokonaan eri ajoympäristöistä ja asennukset voitaisiin tehdä uudestaan konfiguraatiomuutoksia sisältävän Maven buildin jälkeen, joudutaan joka ajoympäristölle ajamaan erillinen build eri profiililla. Uusi build eri profiililla sisältää riskin siitä, että eri build-tuloksissa on muitakin eroja kuin vain erilaisia filtteröitäviä konfiguraatioita (Mavenin kanssa on helppo mokata tällaisessa erityisesti release-pluginia käyttäessä).


Kun itse lähden tekemään taustaprosessina pyörivää sovellusta (eli suurin osa esimerkiksi selaimella käytettävistä sovelluksista, jotka pyörivät sovelluspalvelimen sisällä), lähden toteutuksessa aina siitä, että konfiguraatioon voi käyttää tapoja 1, 2 ja 3. Jos teen graafisella käyttöliittymällä varustettua sovellusta, otan lähtökohtaisesti tavat 1, 2, 3 ja 4 käyttöön. Yleensä tavan 3 tukeminen on helppo toteuttaa, joten ei ole mitään mieltä jättää sitä pois ja tyytyä vain tapohin 1 ja 2 edes ihan pienimmissä projekteissa. Hyödylliset pienet projektit yleensä kasvavat isommiksi projekteiksi ja tavat 1 ja 2 eivät skaalaudu projektin koon kasvaessa. On kuitenkin huomattava, että mikään esitetyistä tavoista ei yksistään ole yleensä paras tapa. Paras lähestymistapa on tukea ainakin kolmea ensimmäistä tapaa ja tehdä niiden käyttö sovelluskehittäjälle yhtä helpoksi. Näin sovelluskehittäjän on helppo tehdä tekemästään ominaisuudesta halutulla tasolla konfiguroitava.

sunnuntai 5. helmikuuta 2012

Apachetop on Amazon Linux

There's handy utility for monitoring Apache logs almost in real time called apachetop. Apachetop shows the traffic your Apache is handling based on the log entries Apache writes. Of course you can read the log files with some file viewer too, but apachetop gives you an overall picture of what's currently happening on your web site, which is not easy to achieve by reading only the log files.

Initially I had some trouble on installing apachetop on EC2 running Amazon Linux, but I managed to do it with these simple commands.

% wget http://www6.atomicorp.com/channels/atomic/centos/5/x86_64/RPMS/libadns-1.4-3.el5.art.x86_64.rpm
% wget http://pkgs.repoforge.org/apachetop/apachetop-0.12.6-3.el5.rf.x86_64.rpm
% yum install libadns-1.4-3.el5.art.x86_64.rpm
% yum install apachetop-0.12.6-3.el5.rf.x86_64.rpm

The commands above download the Centos RPM files and install them using yum. It's better to download the RPMs and install them manually than add some random Centos repositories to your system. Although Amazon is mostly compatible with Centos, it's risky business use standard Centos repositories.

After installing, you can start the apachetop to monitor your Apache. If you have several virtual hosts each writing to different log file, you can add all of them to a single apachetop session. Let's assume you have virtual hosts vhost1 and vhost2, which write logs vhost1_access_log and vhost2_access_log. To monitor both of these, use the following command.

apachetop -f vhost1_access_log -f vhost2_access_log -H 5000 -s 1 -l

I also added some extra parameters here. The -H sets how many total hits the monitoring shows before starting to rotate the log data. The -s sets how many URL path elements are used to distinguish different URLs. For example -s 2 would set mean that /path1/path2/path3 and /path1/path2/path4 would be counted as one URLs. On the other hand, /path1/path3/path2 and /path1/path2/path3 would be different. The last switch -l just says that all URLs should be changed to lowercase before comparing them to each other. Use man apachetop to see more details.

keskiviikko 1. helmikuuta 2012

Remote VisualVM session through SSH tunneling

VisualVM is standard Java profiling&monitoring tool that comes with Oracle's SDK. Probably you knew this already, if you have read this far.

Remote VisualVM session through SSH seems to be quite difficult. I found several instructions on how to do it, but they didn't say what to do exactly, especially in my Amazon EC2 environment. The EC2 host we're monitoring here has public elastic IP as well as private IP address.

VisualVM requires SOCKS tunneling instead of standard SSH port forwarding for some reasons I didn't study too well. And anyway, you don't have to know either to get it working...

VisualVM connection setup
  1. Create SOCKS tunnel to remote host (in this example it's an EC2 instance)
    % ssh -D 10000 user@remote.host.com
  2. Prepare the process you want to monitor. In this case, we're monitoring a Tomcat process so we're adding the following JAVA_OPTS to the Tomcat process. Notice that the JMX port is set to 9000 and this is the port we're connecting to later.
    JAVA_OPTS="...all_the_required_java_opts_here... -Dcom.sun.management.jmxremote.port=9000 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
    
  3. Start the Tomcat with the JAVA_OPTS given above
  4. Create permissions.txt for jstatsd with the following content
    grant {
      permission java.security.AllPermission;
    };
    
  5. Start jstatsd process
    jstatd -J-Djava.security.policy=permissions.txt
    
  6. Start VisualVM on your localhost
  7. Navigate to Tools -> Options -> Network and set there the following values
    1. SOCKS Proxy: localhost
    2. Port: 10000
  8. Check the local IP of the remote host by executing ifconfig (assuming Linux as operating system) on the remote host. The output below is edited slightly to hide some details
    % ifconfig 
    eth0      Link encap:Ethernet  HWaddr XX:XX:XX:XX:XX:XX  
              inet addr:10.1.1.1  Bcast:10.1.205.255  Mask:255.255.255.0
    ...
    
  9. In VisualVM, add new remote monitoring session from File -> Add Remote Host
    1. Host name is the internal IP address of the remote host, in this case 10.1.1.1
  10. Right click the created remote host and select Add JMX Connection
    1. The correct value in this example is 10.1.1.1:9000
  11. Now you're monitoring should start working! SSH was complaining about connection timeouts, but they don't seem to matter too much
What's important to notice is that public elastic IP does not work in VisualVM connection setup!

tiistai 31. tammikuuta 2012

Käytä UTC-aikaa sovelluksissa


Universal time, Coordinated eli lyhemmin UTC tai koordinoitu yleisaika on nollameridiaanilla käytetty aikavyöhyke. UTC-aikaan ei vaikuta vuodenaika eli mahdollinen kesä- ja talviajan vaihtelu. Yleensä kaikissa järjestelmissä on helppo toimia UTC-ajan puitteissa, eikä sen käyttäminen vaadi erityisiä muunnoksia.

Mitä hyötyä on UTC-ajan käyttämisestä sovelluksen sisäisenä aikana? UTC-aika helpottaa ajan käsittelyä sovelluksen sisällä ainakin Suomessa, koska talvi- ja kesäaikaa ei tarvitse huomioida muuten kuin korkeintaan käyttöliittymässä. Sovelluksen ulkoisissa rajapinnoissa on hyvä käyttää UTC-aikaa, koska mikäli sovelluksen rajapintoihin ollaan yhteydessä muualta kuin Suomen aikavyökkeeltä, ei asiakassovellusten tarvitse välttämättä tehdä lainkaan muunnoksia aikavyöhykkeiden välillä (jos myös asiakas käyttää UTC-aikaa sisäisesti) eikä asiakassovelluksen tarvitse huomioida Suomen kesä- ja talviajan vaihtelua.

Käytännössä ainoastaan ihmiskäyttäjää kiinnostaa tietää mitä sovelluksen käyttämä aika on käyttäjän paikallisessa aikavyöhykkeessä. Käyttöliittymässä on hyvin yksinkertaista tehdä muunnokset UTC-ajasta paikalliseen aikaan ja takaisin. Usein ainoastaan käyttöliittymä tietää mikä on käyttäjän paikallinen aika, ellei käyttäjän aikavyöhykettä sitten välitetä jatkuvasti taustajärjestelmään ja toteuteta taustajärjestelmässä tarvittavat muunnokset. Aikavyöhykkeen esitysongelmat tulevat esiin erityisesti paikkariippumattomien asiakassovellusten, kuten puhelinten ja muiden kannettavien laitteiden kanssa sekä selainpohjaisissa sovelluksissa, joita käytetään monelta aikavyöhykkeeltä.

UTC-aikaa käyttävä palvelinsovellus on helppo asentaa tai siirtää toiselle aikavyöhykkeelle. Palvelinsovellus toimii oikein eikä käyttöympäristön aikavyöhyke sotke aikaleimoja suuntaan tai toiseen. Mikäli sovellusta käytetään monikansallisessa ympäristössä, käyttöympäristö voi hyvinkin vaihtua toiseen maahan sovelluksen elinkaaren aikana.

UTC-ajan käyttö helpottaa sovelluksen testaamista. Kesä- ja talviajan vaihtumiset aiheuttavat usein yllättäviä tilanteita, mikäli testaamista ei ole suoritettu huolella. UTC-ajan käyttäminen sovelluksen sisällä ei poista kesä- ja talviajan vaihtumisen testaamisen tarvetta käyttöliittymästä, mutta muualta järjestelmästä ongelman pitäisi hävitä.

Olen nähnyt useita projekteja, joissa aikavyöhykeongelmien tai kesä- ja talviaikaongelmien huomioiminen tai huomiotta jättäminen projektin alussa on tuottanut ongelmia projektin edetessä tai vasta tuotantokäytössä.

Eräässä järjestelmässä samaa palvelinsovellusta ajettiin yhden maan sisällä kahdessa eri paikassa. Koska kyseessä oli suuri maa ja nämä kaksi eri paikkaa sijaitsivat eri aikavyöhykkeillä, eivät sovelluksen eri instanssit toimineet keskenään suunnitellusti. Ongelma johtui siitä, että molemmat instanssit toimivat eri aikavyöhykkeillä, eikä tätä oltu mitenkään huomoioitu sovellusten välisessä tiedonsiirrossa. Vastaavia ongelmia ei olisi ilmennyt, mikäli koko järjestelmä olisi toiminut UTC-ajassa.

Toisessa järjestelmässä palvelinsovellus toimi yhdellä aikavyöhykkeellä ja palvelimen asiakkaat ympäri maailmaa. Järjestelmässä ei oltu huomioitu toimimista usealla aikavyöhykkeellä, vaikka se oli keskeistä järjestelmän toiminnan kannalta. Järjestelmän sisällä aikaleimat olivat aluksi käytännössä satunnaisia, koska aikavyöhyketietoja ei ollut käytettävissä. Ongelmia ei toki olisi ollut, mikäli sovelluksen rajapinnoissa olisi välitetty aikavyöhyketieto ja rajapintojen takana olisi tehty sopivia muunnoksia. Näin kansainvälisessä sovelluksessa olisi kuitenkin kannattanut käyttää palvelimella pelkästään UTC-aikaa, koska aikaleimojen muuttaminen paikalliseen aikaan ei olisi kuitenkaan hyödyttänyt ylläpitäjiä. Asiakassovelluksien käyttämä aika olisi ollut hyödyllistä pitää myös UTC-aikana, koska käyttäjän oletettiin liikkuvan maasta toiseen, jolloin käyttäjän päätteenkin aikavyöhykkeen voi olettaa vaihtuvan. Lisäksi kaikkien osapuolten käyttäessä UTC-aikaa sisäisesti ja rajapinnoissaan ei minkään osapuolen tarvitsisi tehdä aikavyöhykemuunnoksia kuin korkeintaan käyttöliittymässä.

Kolmantena esimerkkinä vielä tilanne, jossa palvelinsovelluksen tiedetään pysyvän yhdellä aikavyöhykkeellä eikä sen sijaintia aiota ikinä vaihtaa toiselle aikavyöhykkeelle. Vaikka mielestäni on rohkeata olettaa, että hyödyllistä sovellusta ei ikinä siirretä muualle eikä siihen tule ulkoisia kansainvälisesti käytettäviä rajapintoja, tämä lienee yleisin argumentti paikallisen ajan käyttämiseen sovelluksen sisällä. Mielestäni kuitenkin UTC-ajan käyttäminen tällaisessakin tapauksessa on hyödyllistä, koska UTC-aikavyöhykkeen käytöllä vältetään ongelmat kesä- ja talviajan kanssa. Esimerkiksi ajastuksia käytettäessä täytyy huomioida, että talviaikaan siirryttäessä kellonaika siirtyy tunnilla taaksepäin. Jos talviaikaan siirtymistä ei huomioi, saattaa käydä niin, että ajastettu operaatio suoritetaan kahdesti ja tästä aiheutuu virhetoimintaa.

Vaikka järjestelmä toimii sisäisesti UTC-ajassa, pitää käyttäjän edelleen käyttää järjestelmää omassa ajassaan ja huomoioida sovelluksen käyttö halutussa aikavyöhykkeessä. Jos esimerkiksi sovelluksen tarjoaman palvelun laskutus riippuu kellonajasta siten, että osa päivästä on halvempaa kuin muuten, pitää käyttäjän luonnollisesti konfiguroida sovelluksen laskutus nimenomaan haluttuun aikavyöhykkeeseen nähden. Jos käytössä on kaksi instanssia sovelluksesta kahdessa eri aikavyöhykkeessä, kuten aiemmassa esimerkissä, pitää olla erityisen tarkkana, että palvelun laskutus toimii molemmissa aikavyöhykkeissä perustuen oikeaan aikaan. Jos kahden instanssin järjestelmässä olisi erillinen laskutuspalvelu ja molemmista instansseista tulisi UTC-aikaleimoja, pitäisi laskutuspalvelun kohdella erillisiä instansseja kuitenkin eri tavalla, vaikka kaikki käyttäisivät UTC-aikaa. Aikavyöhykkeet aiheuttavat siis ongelmia, vaikka sovelluksessa olisi olisi käytössä sisäisesti vain yksi aikavyöhyke.

Mitä haittaa on UTC-ajan käyttämisestä sovelluksen sisällä? Ensinnäkin useimmiten kaikki järjestelmät toimivat oletuksena paikallisessa ajassa. Aluksi on työlästä selvittää ja huomioida eri järjestelmien konfigurointi UTC-aikaan tai järjestää yhteensopivuuskerros, joka paikallisen aikavyöhykkeen huomioi. Sovelluksen ylläpitäjän täytyy mahdollisesti oppia huomioimaan se, että aikaleimat ovat lokeissa ja tietovarastoissa UTC-ajassa. Mikäli järjestelmässä mahdollisesti olevat ajastukset konfiguroidaan UTC-ajassa, ylläpitäjän pitää tämäkin huomioida konfigurointeja tehdessään.

Itse rinnasta aikavyöhykeongelman kaikille tuttuun merkistön koodausongelmaan. Useimmiten merkistöongelmat ratkeavat, kun sovellus käyttää mahdollisimman useassa kohtaa vain ja ainoastaan UTF-8 -koodausta. Tästä on helppo tehdä tarvittavat muunnokset ja toisaalta järjestelmän on helpompaa toimia sisäisesti vain yhdessä merkistökoodauksessa. Analogisesti järjestelmän on helpompaa toimia sisäisesti vain yhdessä aikavyöhykkeessä ja konversiot tehdään vai järjestelmän ulkopuolisia tahoja varten, joista yksi on loppukäyttäjä.

Ajan kanssa tulee olla joka tapauksessa tarkkana, sillä varsinkin viranomaiskäytössä sekä esimerkiksi laskutuksessa väärä aikaleima voi merkittävästi vaikuttaa palvelun oikeellisuuteen.

maanantai 23. tammikuuta 2012

lauantai 21. tammikuuta 2012

Tomcat clustering and session persistency in AWS


Tomcat supports clustering out-of-the-box (see http://tomcat.apache.org/tomcat-7.0-doc/cluster-howto.html). However, since Amazon EC2 environment has some pecualiar features such as not supporting IP multicasting (http://aws.amazon.com/vpc/faqs/#R4), the traditional(?) way of Tomcat clustering cannot be used. Instead of using direct communication between different cluster nodes, you can use HTTP session sharing via database. Although this is not as fast or flexible as direct communication, it's still better than no clustering.

How session sharing works

Tomcat copies session data to a database when it's shut down. It also makes a periodical copying of idle sessions every now and then (the configuration given below doesn't define the exact maximum session idle time before Tomcat does replication to database. The configured maximum time is about maxIdleBackup + processExpiresFrequency * engine.backgroundProcessorDelay. Google for more details...).

Because all the Tomcats are sharing a commong session repository i.e. database, they all have access to each other's sessions that have been persisted at least once. Whenever a new Tomcat instance starts, it reads the existing sessions from common session repository and is then able to serve any request to any Tomcat in the cluster up to start-up time of the instance.

Because of delays in sharing the sessions, you should enable sticky sessions in you load balancer. This means that each request from a user will be directed to the same Tomcat as long as user HTTP session exists.

Use application generated cookie in ELB

In order to use database session sharing, you must use sticky sessions in your load balancer. Amazon ELB has the options of using either ELB generated session cookie or application generated session cookie. I think that in this case one should use application generated session cookie (JSESSIONID), because ELB generated session cookie may have different expiration time than your application's session. If ELB cookie expires before you application's session, user may get served by different Tomcat, although application's session has not changed. This may lead into nasty problems.

Setup RDS based database for Tomcat session storing

The easiest way of using a common session replication database in Amazon is RDS service. Of course you may also set up your own database on one EC2 instance, but this would be a single point of failure. So, I'm using RDS and MySQL in this example. Create a session database and a session table in the database.
CREATE DATABASE tomcat_data DEFAULT CHARACTER SET UTF8 COLLATE utf8_swedish_ci;
GRANT ALL ON tomcat_data.* TO "tomcat"@"%" IDENTIFIED BY "tomcat";


create table tomcat_data.tomcat_sessions (
  session_id varchar(100) not null primary key,
  valid_session char(1) not null,
  max_inactive int not null,
  last_access bigint not null.
  app_name varchar(255),
  session_data mediumblob,
  KEY kapp_name(app_name)
);

MySQL connection from Tomcat

You need to copy the mysql-connector-java-x.x.xx.jar under $CATALINA/lib (on Ubuntu, it's /usr/share/tomcat6/lib/ depending on your Ubuntu/Tomcat version). This JAR is needed to create connection from Tomcat to MySQL.

Here's context.xml you need. Remember to change localhost to something more meaningful in the XML file.

<Context>

...

    <!-- The idle time in seconds before persisting session is
         maxIdleBackup + processExpiresFrequency * engine.backgroundProcessorDelay.
         The backgroundProcessorDelay is around 10 seconds. This configuration should make Tomcat
         to persist sessions after 20 seconds of idling and hold them in memory as long as each
         instance wants -->
    <Manager className="org.apache.catalina.session.PersistentManager"
      saveOnRestart="true"
      minIdleSwap="-1"
      maxIdleSwap="-1"
      maxIdleBackup="20"
      processExpiresFrequency="1">
      <Store className="org.apache.catalina.session.JDBCStore"
        connectionURL="jdbc:mysql://localhost/tomcat_data"
        driverName="com.mysql.jdbc.Driver"
        connectionName="tomcat"
        connectionPassword="tomcat"
        sessionIdCol="session_id"
        sessionValidCol="valid_session"
        sessionMaxInactiveCol="max_inactive"
        sessionLastAccessedCol="last_access"
        sessionTable="tomcat_sessions"
        sessionAppCol="app_name"
        sessionDataCol="session_data"
      />
    </Manager>
...

</Context>


Problems in this solution

If Tomcat dies unexpectedly, the new sessions served by dead instance are lost. Most likely with the configuration above, sessions newer than 30 seconds will disappear. Also new changes that have been done to session data will be lost.

Session related database traffic could grow quite high in a busy web site. I have no cure for this, but of course you can at least use a different database for sessions than what's being used for other data.

maanantai 16. tammikuuta 2012

Versioi tietokantasi!


Mitä on tietokannan versiointi? Tietokannan versiointi tarkoittaa sitä, että tietokannasta pystyy selvittämään helposti tietokannan skeeman version sekä mahdollisesti tietokantaan skripteillä ajetun datan version.

Mitä hyötyä on tietokannan versioinnista? Tietokannan versiointi mahdollistaa tietokannan sisältämän tiedon systemaattisen ylläpidon. Saman tietokannan eri versioiden päivittäminen uudempaan versioon on helppoa, mikäli tietokannan sisällöstä voi koska tahansa päätellä mitä päivityksiä tietokannasta puuttuu. Versioiminen mahdollistaa päivityspolkujen rakentamisen. Jos et tiedä missä tilassa tietokanta on, et voi mitenkään toteuttaa tietokantapäivityksien automaatiota, eivätkä käsin tehtävät päivityksetkään ole helppoja.

Jotta versiointia voi tehdä, pitää tietokantaan lisätä tietokannan versiota kuvaava taulu. Kenties yksinkertaisin tapa tietokannan versioimiseen on sisällyttää tietokantaa päivittäviin komentosarjoihin alkuun tietokannan versiotaulua päivittävä komento. Jokainen komentosarja voi lisätä versiotauluun rivin, jossa kerroataan komentosarjan sisältävän tiedoston nimi ja sen yksikäsitteisesti tunnistava versionumero. Huonoa ratkaisussa on se, että versiotaulun päivitys unohtuu helposti ja komentoja ajetaan ilman versiotiedon päivittämistä.

Hieman edistyneempi ratkaisu on luoda tietokannan päivityksiä varten oma sovelluksensa. Sovellus voi olla pelkkä komentotulkin varassa toimiva käskysarja. Kun sovellusta käsketään päivittämään tietokanta, sille annetaan versionhallinnassa oleva tiedosto parametrina. Sovellus hakee versionhallinnasta tiedoston, tarkistaa että tiedosto sopii päivitykseksi kohdetietokannalle, päivittää tietokannan sisällön tiedoston mukaan ja lopuksi muuttaa tietokannan versiotaulun ajan tasalle. Sovelluksen käyttäminen jättää tietokantaa päivittävälle ihmiselle vähemmän erehtymisen mahdollisuuksia.

Kehitystyössä on hankalaa toimia usean erillisen tietokantaa eri versioihin päivittävän tiedoston kanssa. Yleensä on helpompaa, jos kehittäjällä on käytössä yksi (tai muutama) tiedosto, jolla voi alustaa tyhjän tietokannan kehityskäyttöön sopivaksi ilman pitkän päivitysketjun seuraamista. Kehitystyön aikana onkin järkevää ylläpitää yksinkertaista tapaa luoda ajantasainen tietokanta. Kun projekti etenee uuden version julkaisuvaiheeseen, tutkitaan minkälainen ero on edellisen julkaisun ja uuden julkaisun tietokantojen kesken. Tämän pitäisi olla helppoa, mikäli edellisen version tuottamasta tietokannasta voi tarkistaa helposti version (versionhallinnasta sitten näkee edellisen version tietokannan luovat komennot). Sovelluksen päivittäminen tuoreeseen versioon tuotannossa on turvallista, koska sovelluksen päivityksessä voidaan helposti tarkistaa, että sovelluksen mukana päivitettävä tietokanta on tietyn versioinen. Erityisen tärkeitä tarkistukset ovat tilanteessa, jossa tuotantokäytössä voi olla samaan aikaan useaa eri versiota samasta sovelluksesta. Jos tuotannon versiota ei tarkisteta ennen päivitystä ja uusin versio vaatii useamman päivitystiedoston käyttämistä, saattaa joku päivityspolun vaatimista päivityksistä jäädä tekemättä ja esimerkiksi vain viimeisin päivitys tulee ajettua tietokantaan.

Usein tietokannan skeeman voi tuottaa suoraan lähdekoodista. Itse en suosittelisi tietokannan automaattista luomista kehityskäytössä lähdekoodin perusteella. On parempi, että kehitysaikanakin tietokannan skeeman kehitys hallitaan versionhallintaan talletettavien tiedostojen kautta. Lähdekoodin muutokset voivat joskus varsin yllättäen vaikuttaa siitä generoitavaan skeemaan ja toisaalta versionhallinasta voidaan koko ajan seurata skeeman kehitystä koodiin verrattuna. Jos testaus suoritetaan aina versionhallinassa olevaa skeemaa vastaan, jäävät koodin toimintaan vaikuttavat muutokset kiinni jo kehitysaikaisessa testauksessa, koska versionhallinasta tuleva skeema ei muutu automaattisesti lähdekoodin mukaiseksi.

Mitä haittaa on tietokannan versioinnista? Tietokannan versioinnista ei ole mitään suoranaista haittaa, mutta jonkin verran lisätyötä versiointi vaatii. Työmäärä on luultavasti kuitenkin vähäisempi kuin myöhemmin epämääräisistä tietokantaversioista huolehtimiseen käytettävä aika. Ainakin riskit epäonnistuneisiin tietokantapäivityksiin pienentyvät. Lisäksi tietokantaa käyttävän sovelluksen päivitykset helpottuvat, kun aina voidaan sanoa mitä tietokantapäivityksiä kunkin version asentaminen vaatii.

Jos projektisi tietokantaa ei ole vielä versioitu, juuri nyt on hyvä aika aloittaa versiointi!

sunnuntai 1. tammikuuta 2012

Amazonin Virtual Private Cloud ja NAT


Tavoitteena luoda kuvan mukainen suljettu verkko (Virtual Private Cloud, VPC), johon on kytketty kuormantasaaja (ELB) ulkoa tulevan liikenteen ohjaukseen ja jos pääsee internetiin vain NAT-koneen kautta. 

Kuvasta näkee, että aliverkko 10.0.1.0/24 on yksityinen verkko, josta ei pääse ulos kuin NAT-koneen kautta. Verkko 10.0.2.0/24 puolestaan on julkinen verkko, jonne sijoitetaan ELB, internet gateway ja NAT-kone. On syytä huomata (jotain mitä en itse heti tajunnut), että ELB:llä on kaksoisrooli internetissä sekä VPC:ssä: ELB näkyy internetiin julkisella IP:llä ja VPC:n sisällä puolestaan VPC:n IP:llä. ELB:n pitää olla VPC:ssä, josta pääsee IGW:n kautta ulos tai ELB ei toimi. Koska NAT-koneelle on annettu julkinen Elastic IP, sekin näkyy ulos suoraan internetiin.

Tekstikuvaus ei johda ihan suoraviivaisesti kuvan tilanteeseen, joten kannattaa lukea huolella, jos jotain kohtaa ajattelee hyödyntää.

Huomaathan, että tämän tekstin ohjeiden kokeilu maksaa! Itselleni kokeilu kustansi $0,93, koska käytin kahta small-instanssia 4 tunnin ajan.

VPC-verkon luominen yhdellä aliverkolla 
Luodaan VPC 10.0.0.0/16 CIDR-lohkolla (Classless Inter-Domain Routing).
$ ec2-create-vpc 10.0.0.0/16
VPC vpc-1cb6db75 pending 10.0.0.0/16 dopt-10b6db79 default

Luodaan aliverkko 10.0.1.0/24. Aliverkossa on 256 IP-osoitetta, joista Amazon varaa 4 ensimmäistä ja viimeisen eli käytettäväksi jää 251 IP-osoitetta.
$ ec2-create-subnet --vpc vpc-1cb6db75 -i 10.0.1.0/24
SUBNET subnet-f3b1dc9a pending vpc-1cb6db75 10.0.1.0/24 251 eu-west-1c

Aliverkko luotiin Amazonin valitsemaan saatavuusalueeseen (Availability Zone, AZ) eu-west-1c.


Internet-yhdyskäytävän luominen VPC:lle 
VPC:ssä olevilla koneilla ei ole pääsyä internetiin ilman, että niille on luotu internet-yhdyskäytävä (Internet gateway, IGW). Luodaan VPC:lle yhdyskäytävä ja liitetään se VPC:hen.
$ ec2-create-internet-gateway
INTERNETGATEWAY igw-ef86eb86
$ ec2-attach-internet-gateway igw-ef86eb86 --vpc vpc-1cb6db75
ATTACHMENT vpc-1cb6db75 attaching

AWS on luonut automaattisesti reititystaulun. Katsotaan taulun tiedot.
$ ec2-describe-route-tables 
ROUTETABLE rtb-1eb6db77 vpc-1cb6db75
ROUTE local  active 10.0.0.0/16
ASSOCIATION rtbassoc-11b6db78 main

Lisätään IGW reititystauluun.
$ ec2-create-route rtb-1eb6db77 -r 0.0.0.0/0 --gateway igw-ef86eb86 
ROUTE igw-ef86eb86   0.0.0.0/0

EC2-instanssin luominen aliverkkoon
Luodaan aluksi pääsyoikeusryhmä, jolla sallitaan pääsy VPC:ssä oleviin koneisiin. Huomaa, että pääsyoikeusryhmä on VPC-kohtainen. Määritetään samalla SSH-portti (22) ja HTTP-portti (80) aukinaisiksi tässä ryhmässä.
$ ec2-add-group vpc_public_access -d "VPC access from public Internet" -vpc vpc-1cb6db75
$ ec2-authorize sg-06acb16a -P tcp -p 22 -s 0.0.0.0/0
GROUP sg-06acb16a    
PERMISSION   ALLOWS tcp 22 22 FROM CIDR 0.0.0.0/0 ingress
$ ec2-authorize sg-06acb16a -P tcp -p 80 -s 0.0.0.0/0
GROUP sg-06acb16a    
PERMISSION   ALLOWS tcp 80 80 FROM CIDR 0.0.0.0/0 ingress

Luodaan aliverkkoon 32-bittinen Ubuntu 10.04-palvelin (AMI löytyy täältä: http://uec-images.ubuntu.com/releases/lucid/release/), jolla pyörii Apache. Käytetään palvelimen luonnissa aiemmin tehtyä (http://pedanttinen.blogspot.com/2011/12/amazon-web-services-ilmainen-kokeilu_29.html) testkeypair-avainta. Annetaan palvelimelle aliverkon ensimmäinen vapaa osoite.
$ ec2-run-instances ami-c00e3cb4 --instance-count 1 --instance-type m1.small --key testkeypair --group sg-06acb16a --subnet subnet-f3b1dc9a --private-ip-address 10.0.1.4
RESERVATION r-3eb35377 093338577702 
INSTANCE i-8cb527c5 ami-c00e3cb4   pending testkeypair 0  m1.small 2011-12-30T08:54:45+0000 eu-west-1c aki-4deec439   monitoring-disabled  10.0.1.4vpc-1cb6db75 subnet-f3b1dc9a ebs     paravirtual xen  sg-06acb16a default

Kuten yltä nähdään, luotu instanssi on tyyppiä small. Small-tyyppinen palvelin maksaa Euroopassa $0,095 tunnilta. Small-tyyppinen palvelin on 32-bittinen ja siksi myös käyttöjärjestelmäksi valittiin 32-bittinen versio.

Tässä vaiheessa luodulle koneelle ei pääse mistään käsiksi. Sille pitää siis antaa hetkeksi julkinen IP-osoite eli VPC Elastic IP -osoite. Huomaa, että myös VPC Elastic IP on maksullinen, mikäli sitä ei ole sidottu mihinkään ajossa olevaan instanssiin ($0.01 tunnilta, jos IP ei ole käytössä kokonaisen tunnin aikana)!
$ ec2-allocate-address -d vpc
ADDRESS 176.34.130.141  vpc eipalloc-d186ebb8
$ ec2-associate-address --allocation-id eipalloc-d186ebb8 --instance i-8cb527c5
ADDRESS  i-8cb527c5 eipalloc-d186ebb8 eipassoc-b486ebdd

Nyt voidaan tehdä yhteyskokeilu palvelimelle SSH:lla.
$ ssh -i ~/aws/certs/testkeypair_private.key ubuntu@176.34.130.141
Jos kaikki meni putkeen, luotu instanssi vastaa huuteluihin. 

Apachen virittäminen instanssille
Kirjaudu sisään instanssille em. komennolla. Jostain syystä nimipalvelu antaa 10-verkon osoitteita Ubuntun päivityspalvelimen osoitteeksi:
$ nslookup eu-west-1.ec2.archive.ubuntu.com
Server:  217.30.180.230
Address: 217.30.180.230#53

Non-authoritative answer:
Name: eu-west-1.ec2.archive.ubuntu.com
Address: 10.228.171.176
Name: eu-west-1.ec2.archive.ubuntu.com
Address: 10.226.230.15

$ nslookup archive.ubuntu.com
Server:  217.30.180.230
Address: 217.30.180.230#53

Non-authoritative answer:
Name: archive.ubuntu.com
Address: 91.189.92.183
...

Tämän takia tehdään "pieni" säätö instanssin hosts-tiedostoon, että päästään sujuvasti eteenpäin. Lisää alla oleva rivi.
91.189.92.183 eu-west-1.ec2.archive.ubuntu.com

Aja seuraavat komennot.
$ sudo apt-get update
$ sudo apt-get install apache2

Nyt Apachen pitäisi vastata osoitteesta http://176.34.130.141/. 

Kuormantasaajan luominen
Luodaan kuormantasaaja (ELB), jonka kautta pitäisi Apachen tarjoilemat sivut näkyviin.
elb-create-lb vpc-lb --subnets subnet-f3b1dc9a --groups sg-06acb16a --listener "protocol=HTTP, lb-port=80, instance-port=80"
elb-create-lb:  Service error: InvalidSubnetID.NotFound 
 AWSRequestId:690642d8-32db-11e1-a58c-ab42d741c3e0

Jostain syystä yllä oleva komento epäonnistuu, mutta täsmälleen sama onnistuu AWS Management Consolen kautta ilman ongelmia. Luodaan kuormantasaaja em. parametrein AWS Management Consolesta (ohjeita täällä: http://docs.amazonwebservices.com/ElasticLoadBalancing/2011-11-15/DeveloperGuide/USVPC_creating_basic_lb.html). Hassua kyllä, netistä ei löytynyt mitään tietoa miksi komentoriviltä homma ei luonnistu.

Kuormantasaajan käynnistymisessä saattaa mennä jonkin aikaa. Joskus instanssit eivät näy heti ELB:llä ja ELB vain sanoo "instance registration is still in progress". Kun kuormantasaaja toimii, näkyy Apache kuormantasaajan läpi: http://vpc-lb-ui-1608557811.eu-west-1.elb.amazonaws.com/.

Palvelu näkyy kuormantasaajan kautta, vaikka Ubuntu-instanssilta poistaisi julkisen IP:n. Kuormantasaajan kautta voi siis päästä käsiksi VPC:n sisälle, vaikka sinne ei muutoin olisi julkista pääsyä.

Ulospäin menevän liikenteen ohjaus NAT:n kautta
Luodaan ensiksi Amazonin tarjoaman VPC NAT AMI:n pohjalta uusi instanssi.
$ ec2-run-instances ami-095b6c7d --instance-count 1 --instance-type m1.small --key testkeypair --group sg-06acb16a --subnet subnet-f3b1dc9a --private-ip-address 10.0.1.5
RESERVATION r-a69272ef 093338577702 
INSTANCE i-3c5dcf75 ami-095b6c7d   pending testkeypair 0  m1.small 2011-12-30T12:15:36+0000 eu-west-1c aki-4deec439   monitoring-disabled  10.0.1.5 vpc-1cb6db75 subnet-f3b1dc9a ebs     paravirtual xen  sg-06acb16a default

Poistetaan SrcDestCheck-attribuutti NAT-instanssilta.
$ ec2-modify-instance-attribute --source-dest-check false i-3c5dcf75
sourceDestCheck i-3c5dcf75 false

Siirretään aiemmin luotu julkinen IP-osoite NAT-instanssille.
$ ec2-describe-addresses
ADDRESS 176.34.130.141 i-8cb527c5 vpc eipalloc-d186ebb8 eipassoc-979cf1fe
$ ec2-disassociate-address --association-id eipassoc-979cf1fe
ADDRESS    eipassoc-979cf1fe
$ ec2-associate-address --allocation-id eipalloc-d186ebb8 --instance i-3c5dcf75
ADDRESS  i-3c5dcf75 eipalloc-d186ebb8 eipassoc-2899f441

Kirjaudutaan sisään NAT-instanssille. SSH valittaa todennäköisesti tässä kohtaa vastapään tunnisteen vaihtuneen. Ongelma ratkeaa .ssh/known_hosts-tiedostoa editoimalla.
ssh -i ~/aws/certs/testkeypair_private.key ec2-user@176.34.130.141

Tämän jälkeen voi kopioida testkeypair_private.key-tiedoston NAT-koneelle ja tehdä seuraavan tempun.
[ec2-user@ip-10-0-1-5 ~]$ ssh -i testkeypair_private.key ubuntu@10.0.1.4

Aliverkon sisällä liikenne toimii siis ok.

NAT-aliverkon luominen
Luodaan uusi aliverkko NAT:lle.
$ ec2-create-subnet --vpc vpc-1cb6db75 -i 10.0.2.0/24
SUBNET subnet-249af74d pending vpc-1cb6db75 10.0.2.0/24 251 eu-west-1c

Poistetaan vanha NAT-kone ja tehdään uusi tilalle uuteen aliverkkoon.
$ ec2-terminate-instances i-3c5dcf75
INSTANCE i-3c5dcf75 running shutting-down
$ ec2-run-instances ami-095b6c7d --instance-count 1 --instance-type m1.small --key testkeypair --group sg-06acb16a --subnet subnet-249af74d --private-ip-address 10.0.2.4
RESERVATION r-189c7c51 093338577702 
INSTANCE i-3648da7f ami-095b6c7d   pending testkeypair 0  m1.small 2011-12-30T13:10:09+0000 eu-west-1c aki-4deec439   monitoring-disabled  10.0.2.4 vpc-1cb6db75 subnet-249af74d ebs     paravirtual xen  sg-06acb16a default

Poistetaan IGW 1-verkon reititystaulusta.
$ ec2-describe-route-tables 
ROUTETABLE rtb-1eb6db77 vpc-1cb6db75
ROUTE local  active 10.0.0.0/16
ROUTE igw-ef86eb86  active 0.0.0.0/0
ASSOCIATION rtbassoc-11b6db78 main
$ ec2-delete-route rtb-1eb6db77 --cidr 0.0.0.0/0
RETURN true

Lisätään NAT-kone 1-verkon reititystauluun.
$ ec2-create-route rtb-1eb6db77 -r 0.0.0.0/0 --instance i-3648da7f
ROUTE  i-3648da7f  0.0.0.0/0

Luodaan uusi reititystaulu uudelle aliverkolle.
$ ec2-create-route-table vpc-1cb6db75
ROUTETABLE rtb-cd95f8a4 vpc-1cb6db75
ROUTE local  active 10.0.0.0/16

Lisätään uuteen reititystauluun aiemmin luotu IGW.
$ ec2-create-route rtb-cd95f8a4 -r 0.0.0.0/0 --gateway igw-ef86eb86 
ROUTE igw-ef86eb86   0.0.0.0/0

Liitetään uusi reititystaulu uuteen aliverkkoon.
$ ec2-associate-route-table rtb-cd95f8a4 --subnet subnet-249af74d
ASSOCIATION rtbassoc-5f94f936 rtb-cd95f8a4 subnet-249af74d

Asetetaan NAT-koneelle julkinen IP.
$ ec2-associate-address --allocation-id eipalloc-d186ebb8 --instance i-3648da7f
ADDRESS  i-3648da7f eipalloc-d186ebb8 eipassoc-6894f901

Poistetaan SrcDestCheck-attribuutti NAT-instanssilta.
$ ec2-modify-instance-attribute --source-dest-check false i-3648da7f
sourceDestCheck i-3648da7f false

Nyt pitäisi olla mahdollista mennä NAT-koneen kautta julkisella IP:llä NAT-koneelle ja siitä suoraan Ubuntu-koneelle. Tämän jälkeen pitää vielä liikuttaa ELB julkiseen (uuteen) aliverkkoon eli samaan, missä NAT-kone asustaa. Kun tämä on tehty, pitäisi liikenteen kulkea myös kuormantasaajan kautta. Kuten aiemmin, uusi kuormantasaaja luodaan AWS Management Consolen kautta.

Lopuksi poistetaan kaikki käytetyt resurssit
$ ec2-terminate-instances i-8cb527c5 i-3648da7f
INSTANCE i-8cb527c5 running shutting-down
INSTANCE i-3648da7f running shutting-down
$ elb-delete-lb vpc-lb-ui
$ ec2-release-address --allocation-id eipalloc-d186ebb8 176.34.130.141
ADDRESS    eipalloc-d186ebb8
$ ec2-disassociate-route-table rtbassoc-5f94f936
RETURN true
$ ec2-delete-subnet subnet-249af74d
SUBNET subnet-249af74d
$ ec2-delete-subnet subnet-f3b1dc9a
SUBNET subnet-f3b1dc9a
$ ec2-delete-route-table rtb-cd95f8a4
RETURN true
$ ec2-delete-route rtb-1eb6db77 -cidr 0.0.0.0/0
RETURN true
$ ec2-detach-internet-gateway --vpc vpc-1cb6db75  igw-ef86eb86
RETURN true
$ ec2-delete-internet-gateway igw-ef86eb86
RETURN true
$ ec2-delete-group sg-06acb16a
RETURN true
$ ec2-delete-group sg-9facb1f3
RETURN true
$ ec2-delete-vpc vpc-1cb6db75
VPC vpc-1cb6db75
$ ec2-delete-volume vol-27d0c94e
VOLUME vol-27d0c94e
$ ec2-delete-volume vol-2bd6cf42
VOLUME vol-2bd6cf42

Kevyt klusterointi Amazon Web Servicessä



Kokeillaan rakentaa kahden weppipalvelimen klusteri yllä olevan kuvan mukaisesti ja käyttää Amazonin tarjoamaa kuormantasaajaa (ELB, Elastic Load Balancing) komentorivikomennoilla. Weppipalvelimena toimivat kaksi Apachea, jotka tarjoilevat molemmat yhtä sivua.

Klusterin palvelimien pystytys
Klusterin palvelimet voisi pystyttää yksitellen edellisen blogauksen ohjeiden perusteella. On kuitenkin mielenkiintoisempaa rakentaa image, jolta molemmat koneet käynnistetään.

Luodaan ensin yksi EC2-instanssi Ubuntu 10.04:n AMI:n pohjalta.

$ ec2-run-instances ami-cc0e3cb8 --instance-count 1 --instance-type t1.micro --key testkeypair --group public_access
RESERVATION r-adfdf5db 093338577702 public_access
INSTANCE i-14cc735d ami-cc0e3cb8 pending testkeypair 0 t1.micro 2011-12-04T16:59:46+0000 eu-west-1a aki-4feec43b monitoring-disabled ebs paravirtual xen sg-12ac4965 default

Asennetaan koneelle Apache-palvelin. Apachen asentaminen vaatii koneelle kirjautumista ja koneelle kirjautuminen edellyttää koneen nimen tietämistä, joten selvitetään nimi ensin.

$ ec2-describe-instances
RESERVATION r-adfdf5db 093338577702 public_access
INSTANCE i-14cc735d ami-cc0e3cb8 ec2-46-137-22-203.eu-west-1.compute.amazonaws.com ip-10-48-9-210.eu-west-1.compute.internal running testkeypair 0 t1.micro 2011-12-04T16:59:46+0000 eu-west-1a aki-4feec43b monitoring-disabled 46.137.22.203 10.48.9.210 ebs paravirtual xen sg-12ac4965 default
BLOCKDEVICE /dev/sda1 vol-7551591c 2011-12-04T17:00:04.000Z

Kirjaudutaan koneelle ja asennetaan Apache.

$ ssh -i certs/testkeypair_private.key ubuntu@ec2-46-137-22-203.eu-west-1.compute.amazonaws.com
$ sudo apt-get update
$ sudo apt-get install apache2

Todetaan selaimella, että Apache toimii eli mennään osoitteeseen http://ec2-46-137-22-203.eu-west-1.compute.amazonaws.com..

Tehdään luodusta Ubuntu-asennuksesta asennusmedia, josta on helppo luoda klusteriin uusia instansseja.

$ ec2-stop-instances i-14cc735d
INSTANCE i-14cc735d running stopping
$ ec2-create-image i-14cc735d --name "weppipalvelin" --description "Klusterin weppipalvelimien luomiseen image"
IMAGE ami-73073b07

Imagen olemassaolon voi vielä tarkistaa näin.

$ ec2-describe-images
IMAGE ami-73073b07 093338577702/weppipalvelin 093338577702 available private x86_64 machine aki-4feec43b ebs paravirtual xen
BLOCKDEVICEMAPPING /dev/sda1 snap-aaef70c2 8

Sitten pitäisi käynnistää klusterin koneet. Poistetaan ensin kuitenkin aiemmin luotu instanssi sotkemasta muuta säätöä.

$ ec2-terminate-instances i-14cc735d

Uusien koneiden käynnistäminen.

$ ec2-run-instances ami-73073b07 --instance-count 2 --instance-type t1.micro --key testkeypair --group public_access
RESERVATION r-2b89815d 093338577702 public_access
INSTANCE i-10bf0059 ami-73073b07 pending testkeypair 0 t1.micro 2011-12-04T18:09:57+0000 eu-west-1c aki-4feec43b monitoring-disabled ebs paravirtual xen sg-12ac4965 default
INSTANCE i-12bf005b ami-73073b07 pending testkeypair 1 t1.micro 2011-12-04T18:09:57+0000 eu-west-1c aki-4feec43b monitoring-disabled ebs paravirtual xen sg-12ac4965 default

Koneiden status käynnistyksen jälkeen.

$ ec2-describe-instance-status
INSTANCE i-10bf0059 eu-west-1c running 16
INSTANCE i-12bf005b eu-west-1c running 16

AWS antoi koneille DNS-nimet ec2-79-125-46-213.eu-west-1.compute.amazonaws.com ja ec2-46-137-144-163.eu-west-1.compute.amazonaws.com. Muutetaan jälkimmäisen koneen tiedostoa /var/www/index.html ja lisätään sinne tunnisteen "Kone 2", jotta koneet voi myöhemmin erottaa kuormantasaajan takaa.

Kuormantasaajan luominen
Aivan ensiksi pitää noutaa kuormantasaajan komentorivityökalut.

$ wget http://ec2-downloads.s3.amazonaws.com/ElasticLoadBalancing.zip

Paketti pitää purkaa ja sen jälkeen vielä editoida aiemmin luotua env.sh-tiedostoa, jotta ELB:n työkalut saadaan polkuun ja jotta ELB:n työkalut tietävät mistä löytyvät. Lisää siis seuraavat rivit.

export AWS_ELB_HOME=~/aws/ElasticLoadBalancing-1.0.15.1
export PATH=$PATH:$EC2_HOME/bin:$AWS_ELB_HOME/bin
export EC2_REGION=eu-west-1

ELB:n luonti.

$ elb-create-lb klusteri-lb --listener "protocol=HTTP, lb-port=80, instance-port=80" --availability-zones eu-west-1c
DNS_NAME klusteri-lb-828561786.eu-west-1.elb.amazonaws.com

Lisätään instanssit ELB:lle.

$ elb-register-instances-with-lb klusteri-lb --instances i-10bf0059,i-12bf005b
INSTANCE_ID i-12bf005b
INSTANCE_ID i-10bf0059

Selaimella voi käydä tarkistamassa, että kuormantasaaja heittelee pyyntöjä molemmille koneille: http://klusteri-lb-828561786.eu-west-1.elb.amazonaws.com.

Jälkien siivoaminen 
Lopuksi poistetaan kaikki varatut resurssit, jotta AWS ei lähettele laskuja perään.

$ elb-delete-lb klusteri-lb
$ ec2-terminate-instances i-12bf005b i-10bf0059

Luodun AMI:n poistaminen on karvan verran hankalampaa.

$ ec2-describe-snapshots
SNAPSHOT snap-aaef70c2 vol-7551591c completed 2011-12-04T17:22:54+0000 100% 093338577702 8 Created by CreateImage(i-14cc735d) for ami-73073b07 from vol-7551591c
$ ec2-deregister ami-73073b07
IMAGE ami-73073b07
$ ec2-delete-snapshot snap-aaef70c2
SNAPSHOT snap-aaef70c2

Voiko kahden koneen klusterin tekeminen mennä vielä paljon helpommaksi?