Siirsin viime viikonloppuna blogini vaihteeksi uudelle alustalle. Tällä kertaa se pyörii Amazonin EC2 Container Servicessä, joka tarjoaa alustan Docker-konttien ajamiseen. Kehitin myös pienen ECS Wrapper -työkalun, joka yksinkertaistaa ECS:n käyttöä komentoriviltä.

Siirtäminen oli melko helppoa, sillä olin jo aiemmin paketoinut blogini ja kaikki siihen liittyvät taustapalvelut Docker-konteiksi. Kontit ja niiden tarvitsema data piti vain siirtää Digital Oceanista Amazoniin.

Amazonin EC2 Container Service on ilmainen palvelu, joka huolehtii Docker-klusterin metadatasta. Käyttäjän täytyy itse käynnistää maksullinen EC2-virtuaalikone ja ajaa siinä ECS-Optimized Amazon Linuxia. Tämä Amazon Linuxin versio sisältää ECS Agentin, joka liittää virtuaalikoneen osaksi Docker-klusteria. Klusteriksi riittää yksikin t2.micro-tason virtuaalikone, joten ECS:ää voi kokeilla hyvin pienin kustannuksin.

ECS-palvelu toimii toistaiseksi vain Amazonin US-regionissa. Se on yhä preview-vaiheessa, eli se ei ole vielä vakavaan tuotantokäyttöön soveltuva ja rajapinnat muuttuvat ajan mittaan. Palveluun lisättiin hiljattain tuki data volumeille ja yksityisille Docker-imageille, mikä teki siitä vihdoin käyttökelpoisen esimerkiksi tällaisen oman blogin pyörittämiseen.

Task Definitionit, Taskit ja Containerit

ECS:n dokumentaatiosta löytyy paljon tietoa sen peruskäsitteistä, joten listaan ne tässä vain lyhyesti:

  • Task Definition määrittelee yhden Taskin, joka muodostuu yhdestä tai useammasta Docker-kontista. Se on käytännössä JSON-tiedosto, joka ladataan järjestelmään Register-rajapinnalla.
  • Uusi Task syntyy kun Task Definition käynnistetään Run-rajapinnalla ja kuolee, kun Task pysäytetään Stop-rajapinnalla.
  • Ajossa olevan Taskin sisältämiä kontteja voi tarkastella EC2-virtuaalikoneessa Dockerin komentorivityökaluilla.

ECS jättää jälkeensä sotkua. Kun Task pysäytetään Stop-rajapinnalla, siitä jää jäljelle kuollut Exited (137) -Docker-kontti. Kuolleet kontit täytyy itse käydä siivoamassa aika ajoin pois. Lisäksi ECS jää joskus epäsynkroniin tilaan, jossa kontti pyörii Dockerissa, mutta ECS ei tunnista sitä aktiiviseksi Taskiksi. Tällaiset tilat saa selvitettyä tappamalla Docker-kontit pois ja käynnistämällä Taskin uudelleen.

Muistinhallinta ja CPU-rajoitukset

ECS:n nykyisessä versiossa on mahdollista määritellä jokaiselle kontille oma CPU- ja muistirajoitus.

CPU-rajoitus on kokonaisluku väliltä 0-1024 (tai 0-2048 jos suoritinytimiä on kaksi, ja niin edelleen). 0 tarkoittaa, että kontin CPU-käyttöä ei rajoiteta mitenkään. 512 tarkoittaa, että kontti saa hyödyntää korkeintaan puolet CPU-tehosta, ja 1024 tarkoittaa, että se saa käyttöönsä kaiken CPU-tehon. ECS koordinoi CPU-tehon varaamista siten, että jos virtuaalikoneesta on jo käytetty kaikki 1024 CPU-yksikköä, siihen ei käynnistetä enempää kontteja. Jos koordinoinnin haluaa ohittaa, kaikille konteille voi antaa CPU-rajoitukseksi 0.

Muistirajoitus määritellään megatavuina, ja sen täytyy olla suurempi kuin 0. Kontille varataan kyseinen määrä muistia, ja Docker pakottaa kontin pysymään rajoituksen puitteissa. Kontissa oleva prosessi yleensä kuolee, jos se tarvitsee enemmän muistia kuin sille on varattu. Lisäksi ECS koordinoi muistivarauksia siten, että uusia kontteja ei voi enää käynnistää, jos kaikki virtuaalikoneen muisti on varattu.

Muistirajoitusten kanssa tasapainottelu voi tuottaa pientä päänvaivaa, jos haluaa ajaa useaa pientä konttia t2.micro-luokan virtuaalikoneella, jossa on vain yksi gigatavu RAM-muistia. Esimerkiksi Node.js allokoi vähintään 50MB muistia, joten jokaiselle kontille on varattava ainakin sen verran. Oletuksasetuksillaan MySQL puolestaan tuntuu kaipaavan noin 200MB. Muistia ei parane varata liikaa, koska muuten gigatavu tulee nopeasti täyteen.

Omien kokeilujeni perusteella ECS huomioi muistirajoituksissa sekä RAM-muistin että swap-muistin yhteenlasketun määrän. Eli jos virtuaalikoneella on 1GB RAM-muistia ja 2GB swap-muistia, kaikki kontit voivat yhteensä varata 3GB muistia. Sen jälkeen ECS rupeaa antamaan RESOURCE:MEMORY-virheilmoituksia.

Data volumet

Docker on yleisesti ottaen hyvin herkkä hukkaamaan konttien sisältämän datan, jos järjestelmälle tapahtuu jotain odottamatonta. Data kannattaa aina sijoittaa erilliselle data-volumelle, joka säilyy vaikka kontti tuhoutuisikin. Amazonissa täytyy lisäksi huomioida, että EC2-instanssin oletuslevy on tarkoitettu väliaikaiseksi työtilaksi. Onkin hyvä luoda dataa varten ihan erillinen Elastic Block Storage (EBS) -levy ja luoda kaikkien dataa tarvitsevien Docker-konttien datahakemistot sen sisään.

ECS tukee data volumeja, mutta se ei osaa itse liittää EBS-levyä palvelininstanssiin. Liitos täytyy siis tehdä itse siten, että EBS-levy näkyy palvelimella esimerkiksi polussa /data. Sen jälkeen ECS:ään voi lisätä Task Definitioneihin "volumes"-osioita, joiden "sourcePath" osoittaa jonnekin /data-hakemiston alle, sekä "mountPoints"-osioita, jotka liittävät volumet konttien sisään oikeisiin hakemistoihin.

En ole vielä ehtinyt kokeilla, toimisiko Amazon OpsWorks yhteen ECS:n kanssa siten, että OpsWorksin voisi antaa huolehtia EBS-levyjen liittämisestä. Silloin liittämistä ei tarvitsisi tehdä itse /etc/fstab-tiedostoa muokkaamalla.

Virtual hostit ja HTTP-reititys

ECS ei sisällä mitään erityistä tukea HTTP-virtualhosteille ja pyyntöjen reitittämiselle tiettyihin kontteihin. Jokainen kontti voi kuunnella omaa TCP-porttiansa, ja nämä TCP-portit voi avata maailmalle, mutta silloin esimerkiksi web-palvelut näkyvät hassusti satunnaisissa porteissa. Tarvitaan siis reititys, joka kuuntelee porttia 80, ja ohjaa pyynnöt oikeisiin kontteihin Host-otsakkeen mukaan.

Tällainen reititys on suhteellisen helppoa toteuttaa itse käynnistämällä yksi Nginx-kontti kuuntelemaan porttia 80 ja konfiguroimalla se tekemään proxy-reititys toisiin kontteihin. Upstreameja määritellessä tulee vain muistaa, että Nginx-kontin sisällä osoite 127.0.0.1 on virtualisoitu, ja muiden konttien julkaisemiin portteihin pääsee käsiksi osoitteen 172.17.42.1 kautta.

Jatkossa Amazonin Elastic Load Balancing (ELB) -palvelu pystyy toivon mukaan reitittämään pyynnöt suoraan oikeisiin kontteihin, jolloin erillinen Nginx-kerros ei olisi enää tarpeen. ELB ei kuitenkaan nykyisellään tue virtual host -reititystä, joten jokaiselle kontille pitäisi luoda oma kuormantasaajansa, mikä tulee melko kalliiksi. Amazon on pyytänyt aiheesta palautetta ja sitä on annettu, joten jatkossa sitten nähdään minkälainen kuormantasausratkaisu EC2 Container Serviceen tulee.