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.