Summering av Jfokus 2015

Inte heller 2015 blir ett sannolikt år för robotarnas övertagande. I sessionen Creating Our Robot Overlords: Autonomous Drone Development vi fick konstaterat att en AR Drone 2.0 inte klarar att bära en Raspberry Pi med batteri och därmed får fortsätta att flyga ointelligent, eftersom att hjärnan fått lämnats kvar på marken. Men inte bara därför – den intelligenta programvaran saknades också.

IMG_20150203_090941

Konferensen som helhet bestyrker trenden att utvecklarkollektivet gillar AngularJS på frontend och Spring Boot på backend. Java EE 8 känns svalt, stora applikationsservrar är ute. Java SE 8, Websockets, Continuous delivery, Docker och microservices är inne. Scala fortsätter att vara ett litet elitspråk.

Arun Gupta höll en bra session om websockets och jag kommer hädanefter att förknippa websockets som en lösning på problemet med postkontor överfulla av farmödrar. Arun höll som vanligt flera stabilt bra föreläsningar även i år. Säkert, relevant och pedagogiskt.

Jeremy Deane höll en bra tutorial om en gammal arbetshäst i form av JMS/ActiveMQ som lever vidare i välmåga och behöver inte någon stor appservermiljö utan kan enkelt dras in som vilket lib som helst i till exempel en liten smidig Tomcat.

Flera sessioner avhandlade hur man asynkronifierar våra REST-servrar och visade att vi måste göra det för att få prestanda/skalbarhet i lösningarna. Men fortfarande innebär tyvärr asynkrona lösningar en betydande extra kostnad i form av ökad komplexitet. I den kostnaden ingår inte minst den extra svårigheten att konstruera automatiska tester med rimlig funktionstäckning.

Geert Bevin och jag hade en spännande stund sista dagen med att försöka laga musikinstrumentet som skulle spela en central roll på avslutande keynoten.

IMG_20150204_101840

Det lyckades och Geert höll sedan en bra avslutning på konferensen där han utnyttjade sina artistiska och konstnärliga talanger för att sätta färg på vår annars lite torra tekniska värld.

IMG_20150204_163426

Hade jag vetat hade jag i samband med lödningen tipsat att han borde ge den dansande animerade damen på storbildsskärmen en i detta sammanhang mer passande klädsel.

Årets coola pryl var en LeapMotion, vilket i princip är en alternativ mus-kontroll. Det är en liten stereokamera som man lägger framför tangentbordet som läser av händernas position och gester med förbluffande precision. LeapMotion förekom både i ett par sessioner och i avslutande keynoten där Geert visade på möjligheter med den inom sjukvård, VR och musik.

I CAGs monter demonstrerade Joel Binnquist och Fredrik Dahlman mjukvara de skrivit som kombinerade ihop en AR Drone 2.0 med en LeapMotion på så vis att man kunde styra dronen med händerna.

IMG_20150203_122821

Man såg lite ut som någon ur Harry Potter när man styrde dronen. Den blev en teknikentusiastmagnet. Förutom Dronen tjänstgjorde i montern dessutom den tidigare publikmagneten java-robotarmen med ansiktsdetektering.

Deep Learning fanns det inget material om på konferensen i år. Min gissning är dock att det dyker upp nästa år.

Tack Mattias Karlsson och Jfokus-teamet för ett mycket bra Jfokus 2015, jag ser redan fram emot nästa år!

IMG_20150204_161510

Publicerad i Continuous Delivery, DevOps, Java, Uncategorized

Den sista MacBook:en i sitt slag

Mitt MacBook-liv började år 2011. Året därpå hade jag sönder den mekaniska hårddisken, så jag monterade istället en pirat-SSD och ominstallerade OS:et. Året efter det tog utrymmet på den disken slut (500GB) och jag ersatte den sedan länge oanvända och meningslösa optiska enheten med ytterligare en SSD på 1TB. Med 16GB RAM och 1.5TB disk gjorde den sedan susen under lång tid.

Efter att MacBook:en gjort tjänst varje dag i 4 år, började först grafiken spraka till och gjorde så oftare och oftare till dess hela skärmen blev randig och datorn låste sig. Till slut gick den inte ens att starta om.

IMG_20150118_193842

Det visade sig att jag inte var ensam om detta fel, det är ett typfel på MacBook Pro Early 2011 som yttrar sig i att AMD-GPUn går sönder. Jag skrev på ett upprop som nummer 34000-nånting med krav på ersättning från Apple. Men något resultat därifrån har jag inte tid att vänta på.

Ett nytt logikkort behövdes. Apple service uppgav att de kunde byta det för c:a 6-7000:- Men då får man ett likadant kort, med samma typfel. Andra på nätet uppgav att de hade gjort så – betalt för ett nytt kort, för att ett eller två år senare råka ut för samma fel igen. Så detta alternativ kändes måttligt frestande.

Jag tror att det är så att den sista MacBook:en som man rår över, den som man vill ha, dvs som man kan byta minne och diskar på, är Mid 2012, precis innan Retina-skärm, fastlödda minnen och diskar med specialkontakt.

Alltså, om jag lyckas klämma in ett logikkort från Mid 2012 i min Early 2011 går jag från 2.2 till 2.6 GHz, från ett felkonstruerat AMD Radeon HD 6490M 256MB till ett NVIDIA GeForce GT 650M 1GB, från USB 2.0 till 3.0 samt Bluetooth 2.1 till 4.0 . Det är kliv värda att ta lite risker för.

No Pain no Gain. Friskt kopplat hälften brunnet. Jag beställde ett sådant kort. Totalpriset blev ungefär samma som om jag köpt jobbet av auktoriserad Apple-service och jag fick dessutom göra hela jobbet själv samt ta risken att det inte skulle fungera alls, och då har jag investerat sju tusen i en dörrstopp med en ytterligt opraktisk formfaktor.

UPS levererade kortet en måndag någon vecka senare. Jag satt hela måndagkvällen och ett par timmar innan tisdag frukost med små skruvmejslar, förstoringsglas och pannlampa, skruvades små små skruvar, tittandes på youtube-filmer för att reda ut åt vilket håll de små kontakterna skulle dras ut och för att förstå kritiska handgrepp.

IMG_20150126_183935

Det nya kortet saknade skruvhål för två av kylflänsarna på det gamla kortet. Men kanske behövs de inte på det nya kortet? Jag limmade för säkerhets skull fast de gamla kylflänsarna på det nya kortet med snabblim. Alla kylflänsar fick ny kylpasta, bara ett riskorns mängd, har jag lärt mig. Det kändes också bra att bli av med allt gammalt damm.
Efter ett antal timmars arbete kunde jag provstarta datorn och den hoppade igång snällt, utan randig skärm och utan att hänga sig.

Men helt bra blev det inte, jag kanske har förstört något i farten, för istället för randig skärm fick jag lätt vågig skärm i underkant, vilket verkar bero på att LED-bakgrundbelysningen gått sönder på något sätt, så att varannan LED liksom lyser svagare eller inte alls.

Men jag har bestämt mig för att såna småsaker inte bekommer en riktig IT-krigare. Nu har jag ju en alldeles hemtrimmad MacBook Pro, en av de sista i sitt slag, redo för ytterligare en tids tjänstgöring som kraftfullt redskap för alla storslagna mjukvarubedrifter som komma skall.

Eftersom NVidia GPUn stödjer Cuda kommer det förhoppningsvis flyta på bättre att undersöka om http://deeplearning4j.org fungerar bra nog och är redo att vara med och lösa riktiga problem.

Publicerad i Java, OS X

Blue Green Deployment

Samarbetet kring vår open source Continuous Delivery Build Pipeline fortsätter i mobbprogrammeringsform. Den här gången bjöd Mikael Sennerholm in till Avega-kontoret på Östermalm eftersom det ryktades att någon en gång hade sett att man minsann kunde locka fram riktigt bra fart i det nätverket.

Vägen till DevOps-nirvanat visade sig gå genom att gräva fram det gamla sladdnätverket igen och plocka upp sin gamla RJ45-utrustning igen från längst ned i byrålådorna hemma, för Wifi fungerar fortfarande inte bra nog för att ladda ned alla maskinavbildningar på rimlig tid.

Men Avega är i gott sällskap, det tog år av tjat innan CAG fick ett på riktigt fungerande Wifi-nät med flås på Kista-kontoret. Så det borde väl inte vara helt omöjligt att få till det på Avega heller så småningom.

IMG_20150120_185748

Pipelinen bor just nu i ett repo på github under det missvisande namnet “dockerApplicationServer” (https://github.com/avegagroup/dockerapplicationserver). Namnet rättar vi någon regnig dag när allt annat som är viktigare har fixats.

Vi började med att kolla på vår backlog:

  • Centraliserad loggning med ELK
  • Blue-green deloyment
  • Integrera pipeline med Jira
  • Eget docker registry (måste man iaf göra när man gör det här på riktigt)
  • Lägga upp på docker.io?

Centraliserad loggning är väl egentligen inte centralt för Continuous Delivery och en pipeline, snarare en logisk följd av att produktionssätta med en massa Docker-instanser. Vi överenskom ändå att kolla på ELK-stacken vid nästa tillfälle (Elasticsearch-Logstash-Kibana).

Vi började treva på temat Jira-pipeline-integration.

Från molnfri höjd handlar det om att automatgenerera release notes från ärendehanteringssystem och från kodrepository. Vi började först resonera kring användningsfallen.

  • Underlag för ett manuellt deploy-steg.
  • Spårbarhet – att få en av människa enkelt läsbar logg på vad som produktionssatts.
  • Levererans av system till en part som behöver en sammanfattande beskrivning av vad som hänt i systemet sedan föregående leverans. I detta fall behöver vi en funktion där man matar in från-version och till-version.

Release notes innehåller följande sorters information: Nya och ändrade funktioner, åtgärdade felrapporter, kända fel och eventuella workarounds, migrering från föregående version

Den grundinformation vi har att tillgå är stängda issues i Jira, både i form av avklarade stories och åtgärdade felrapporter, fixade feature requests, öppna felrapporter, eventuell dedicerad release-notes information inuti ärendena samt listan med alla commit-kommentarer mellan de aktuella byggena.

Förmodligen behöver vi mangla innehållet manuellt om vi har ambitionen att skapa ett kompakt och läsbart dokument.

Var ska release-notes hamna, var ska vi spara dem? Ska vi skapa ett “release-ärende” som innehåller release informationen? Ska vi generera ett dokument med release-notes som vi committar i kodrepot?

Vi kanske inte skall spara det nånstans utan bara anse dokumentet att vara en alternativ vy av information som ändå sparas i kodrepo och ärendehanteringssystem.

Om vi manglar release-notes manuellt måste vi definitivt committa det manglade resultatet.

Här någonstans tyckte Daniel Fagerström att vi blev tråkiga och föreslog att vi skulle gå på blue-green deployment istället, och han fick bara lojt motstånd som snabbt mojnade från mig.

Över till blue green deployment

Vi måste ha två produktionsmiljöer alltså, som vi deployar varannan gång till. Det är väl bara att sätta igång. Först behöver vi en lastbalanserare. Vi landade snabbt utan någon vidare debatt på dockerfile/haproxy.

En blå dockerinstans, en grön dockerinstans, en lastbalanserare med haproxy. Vi struntar i sticky sessions och tänker stateless och RESTful, så slipper vi bygga börja dyrt och dåligt legacy-stöd.

Vi tar in dockerfile/haproxy, skapar en /haproxy/haproxy.cfg. Den hamnar på dockerinstansen under /haproxy-override. Överrids hela haproxy.cfg eller bara de parametrarna man skriver in där?

Hur gör man för att få haproxy att växla mellan blue och green? Ett par setuper vi sett gör det genom att automatiskt växla miljö beroende på vilken som är uppe. En annan variant är att låta applikationen själv tala om det på någon management-endpoint.

Ett argument emot att låta applikationen själv tala om huruvida den skall vara aktiv eller ej är att det kan vara något fel på applikationen som föranleder att vi vill deploya en ny version och att felet naturligtvis även kan omfatta denna mekanism.

Kan vi styra lastbalanseraren direkt, dvs vilket ben lastbalanseraren ska skicka till, utifrån utan att behöva ta ned, ändra konfig och sedan ta upp den? Det borde gå att växla ben med ett direktkommando. Finns det ett RESTful api kanske?

Andreas Folkesson grävde snabbt fram ett sådant: Snapr verkade vara precis det vi behövde:

snapr allows REST queries via HTTP in order to expose detailed haproxy statistics and allow various program control functions like enabling and disabling servers.

Det kändes bra ända tills:

It is currently in alpha and should be considered extremely messy and bleeding edge.

Bleeding edge tycker vi om, men Extremely messy låter inte så kul.

Mikael kör fortfarande sin gamla emacs med tangentbindningstrassel på mac på projektorn, Johan Elmström tar över rodret ett tag med en modern och fräsch Sublime.

Vi åker på att det är olika versioner av docker i go-agenten (alltid senaste) och docker i boot2docker. Socketprotokollet har tydligen ändrats mellan 1.4.1 som Go agenten använder och 1.3.0 som använda av boot2docker. Vi uppgraderar boot2docker till senaste, låser den versionen, kan vi låsa go-agentens docker version till samma? boot2dockers maintainer är seg att ta emot Mikaels pull requests.

Att rensa utan att ladda ned hela internet igen kräver lite tankearbete. Man skulle vilja kunna köra vagrant destroy -f men detta tar inte bort den underliggande vm-en. Mikael påpekar att det är fusk att direkt göra remove från Virtualbox. Rätt metod är att göra rm på vagrant-instansen som listas när man gör vagrant global-status | grep docker. Daniel Fagerström undrar om det också är fusk att kopiera docker id:t från listan eller om man måste skriva alla siffrorna själv på sitt tangentbord.

Vid deploy måste vi ta hänsyn till att haproxy redan är uppe eftersom den är gemensam mellan de två appserverinstanserna. Hur laddar vi om haproxy när konfigen ändrats? När laddar vi om docker-haproxy om den ändras?

Var lagrar vi vilken miljö som är igång? Ska Go komma ihåg det? Eller ska vi fråga haproxy? Vi kan inte heller kolla på versionen vilken som är nyast eftersom man även vill kunna backa versionen. Och man vill kunna deploya på ena benet utan att ta ned det andra, för att snabbt kunna backa tillbaka till det gamla benet om deployen inte blev bra.

Här nånstans beslutade vi att lösa allt det där senare och helt enkelt börja lägga in lastbalanseraren i vår exempelapplikation, doodleshop (https://github.com/dmarell/doodleshop).

Vi har en lite krånglig och svårarbetad setup med boot2docker. Men vi kommer förmodligen ha det länge än eftersom det nog kommer att ta år innan Docker stabiliserats så det duger med den Docker-versionen som skeppas med standard-OS-en.

När vi tar upp och ned grön och blå fick vi problem med docker-länkar på så vis att en länk som vi anger till en docker-instans tappas när man tar bort docker-instansen och skapar om den med samma namn. Då tappas länken. Vi går runt genom att mappa upp tjänsterna på blå och grön till olika portar och då istället använder gatewayens IP.

Vi råkade ut för standardfelet att namnet (i detta fall “haproxy”) är upptaget i boot2docker: ”The name haproxy is already assigned”. Man fixar det med:

$ vagrant global-status | grep docker
$ vagrant ssh <id>
$ docker ps a
$ docker rm haproxy

Detta står i vagrantfile under felsökning.

Vi förde en diskussion var lastbalanseraren egentligen borde höra hemma. Är det en del av pipelinen? Eller en del av applikationen? Ska vi se infrastrukturen som en egen ö mellan pipeline och applikation? Efter lite resonerande kom vi fram till den logiska lösningen att även lastbalanseraren var en del av applikationen och alltså ska dockerkonfigen för vår haproxy committas där.

Så vill man det skall vara i alla fall. Haproxy-konfigens exponering i docker-instansen krånglade till det så vi lade haproxy-konfigen i pipelinen tills vidare bara för att komma vidare.

John Blund hann ikapp oss ikväll också. Vi får fortsätta en annan kväll.

Publicerad i Continuous Delivery, DevOps

Infrastruktur som kod

Docker är nästan magiskt. Hur går det till när Docker startar upp en maskin och ett kommando på den maskinen på bara en bråkdel av en sekund?

Men förr eller senare vänjer man sig även vid magi, och Mikael Sennerholm från Avega och jag gjorde till slut en kurs av det, där vi under en heldag gick igenom bland annat Docker, men även Vagrant och Thoughtworks Go. Träning på olika smaker av Linux följde med på köpet. Mikael höll kursen och jag hjälpte till med labbarna.

Screen Shot 2014-11-24 at 22.47.19

Docker är ett sätt att förpacka applikationer och tjänster på Linux med standardiserade recept (Dockerfile). Man kan på ett resurssnålt sätt bygga en “image” tidigt och använda den imagen hela vägen ut i produktion. På så vis blir det vi kör på våra utvecklarmaskiner exakt samma som också körs i produktion. Och det vi testar i testmiljön är också samma som i produktion. Detta adresserar äntligen problemet “Fungerar på min maskin”.

Docker förenklar en hel del för såväl utvecklare som drift, bland annat slipper vi att hantera uppgraderingar eftersom alla deploys sker från scratch enligt receptet.

Labbarna var många och kursdeltagarna fick handgripligen lära sig att förpacka och köra lite blandade maskiner, till exempel Ubuntu med java webbapplikationer och WordPress.

På eftermiddagen tog vi det ett steg till och plockade fram en Go-pipeline byggd med Docker som inte bara bygger applikations-artefakter, utan också hela Docker images, där applikationen så att säga redan blir installerad i sin målmiljö. Thoughworks Go är sedan tidigare i år gratis att använda, precis som Jenkins. Go känns mer ändamålsenligt för att bygga pipelines med än Jenkins som är mer “freestyle”.

Jag ser fram emot när Docker på allvar börjar finna vägar in på driftssidan.

Mikael kommer att visa pipelinen även på DevOps Meetup den 2 december.

Publicerad i Cloud Computing, Continuous Delivery, DevOps, Java, LinkedIn, Linux

OS X Yosemite för Javautvecklare

Jag uppdaterade min Macbook till Yosemite idag och stötte bara på två små saker. Precis som vid uppdateringen till Mavericks så avinstallerar Apple Java 6. Det medför att IntelliJ inte startar efteråt, då dess launcher är hårdkodad att använda Java 6. Dessutom ser fonterna ut som skit rent ut sagt om man kör IntelliJ på en modern Java från Oracle.

Lösningen är att återinstallera Java 6 från Apple.

Den andra grejen var att Xcode behövde en refresh – av någon anledning hittade inte min installation command-line tools längre. Lösningen:

sudo xcode-select -r
sudo brew update

Efter att man godkänt licensavtalet för Xcode så fungerar Homebrew igen. Att använda sudo här är en engångsgrej efter uppdateringen till Yosemite. Givetvis ska man aldrig använda sudo för att köra brew i vanliga fall.

Ovan gällde för min maskin som hade fulla Xcode installerat sedan tidigare. När jag uppdaterade min nya maskin som bara hade command line tools, så fick man helt enkelt installera om dem:

xcode-select --install

På den maskinen har jag dessutom gått över till att köra Git från Homebrew, så där behövdes ingen uppdatering av command line tools för att göra brew update.

Publicerad i Java, OS X

Automatiskt byggnummer

Varje gång vi bygger en releasekandidat av systemet, deployar till en testmiljö eller kanske bara behöver en version vi vill kunna referera till i exempelvis felrapporter, ska bygget ges ett unikt versionsnummer.

Ett versionsnummer kan vara ett enkelt löpnummer eller kanske bygga på semantic versioning (1.2.42).

Det är praktiskt att tillgängligöra versionsnumret på ett antal olika sätt: Som inspekterbar i artefakten själv i MANIFEST.MF, i Mavens pom-versioner, maskinläsbar i runtime, till exempel i en endpoint /appversion i ett REST-gränssnitt eller synligt i användargränssnittet.

Screen Shot 2014-09-22 at 22.33.23

Versionen utgörs av två komponenter: pipeline-id:built-number. Pipeline-id identifierar en build pipeline (vilket utgörs av en viss branch av en viss applikation) och är ett löpnummer som skapas av byggservern Jenkins. Att Jenkins byggnummer utgör en versionskomponent är en fördel, eftersom det innebär att det inte är så lätt att släppa ut en version som inte är byggd och taggad på rätt sätt, kanske som innehåller lokala modifikationer i källkoden som utgör normalfallet på en utvecklarmaskin. En lokalt byggd version kommer med denna lösning heta något i stil med (det mest Maven-minimalistiska) 1-SNAPSHOT medan ett officiellt releasebygge heter i stil med 1.0.42.

Version i MANIFEST.MF

För att få in versionen i artefakten, lägg följande i pom.xml:

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <configuration>
                    <archive>
                        <manifestEntries>
                            <AppVersion>${project.version}.${BUILD_NUMBER}</AppVersion>
                        </manifestEntries>
                    </archive>
                </configuration>
            </plugin>
    </build>
 

Om man inspekterar MANIFEST.MF efter att ha byggt med detta innehåller den något i stil med:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Built-By: daniel
Start-Class: hello.Application
AppVersion: 1-SNAPSHOT.${BUILD_NUMBER}
Spring-Boot-Version: 1.2.0.M1
Created-By: Apache Maven 3.0.5
Build-Jdk: 1.8.0_11
Main-Class: org.springframework.boot.loader.JarLauncher

Då denna version är byggd lokalt och inte av byggservern blir versionen 0-SNAPSHOT.${BUILD_NUMBER} och den gör inte många glada, men den talar i alla fall ganska tydligt om att detta inte är ett officiellt release-bygge.

Vi kommer att konfigurera Jenkins-jobbet att sätta pom-versionen till något vettigt, till exempel 1.2, och BUILD_NUMBER kommer att initieras med aktuellt byggnummer, så den resulterande versionen om den byggs på Jenkins kommer då att bli till exempel 1.2.42.

Version i runtime

För att kunna stödja access till versionen i runtime behöver vi en utility-klass:

public final class BuildInfo {
    private BuildInfo() { }

    public static String getAppVersion() {
        InputStream in = BuildInfo.class.getResourceAsStream("/BuildInfo.properties");
        if (in != null) {
            try {
                Properties props = new Properties();
                props.load(in);
                return props.getProperty("AppVersion");
            } catch (IOException e) {
                return "Cannot read BuildInfo.properties";
            } finally {
                try {
                    in.close();
                } catch (IOException ignore) {
                }
            }
        }
        throw new IllegalArgumentException("Missing BuildInfo.properties");
    }
}

Eftersom det inte är rättframt att läsa in MANIFEST.MF i runtime så går vi den breda enkla vägen och duplicerar versionen i en propertyfil src/main/resources/BuildInfo.properties. Lägg in en rad i den:

AppVersion=${project.version}

Därefter slår vi på Mavens variabelsubstituering för resursfilen på följande (något pratiga) sätt:

        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>BuildInfo.properties</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>BuildInfo.properties</exclude>
                </excludes>
            </resource>
        </resources>

Så här ser det ut om man bara vill slå på filtreringen för just filen BuildInfo.properties och inte alla andra eventuella resursfiler.

När man nu bygger med Maven kommer texten ${project.version} i propertyfilen att bytas ut mot Maven-modulens pom-version (jag har valt att sätta den till 1-SNAPSHOT initialt så behöver jag aldrig mer ändra den i mainline/trunk).

REST endpoint /appversion

Nu kan vi enkelt exponera versionen i till exempel en REST endpoint:

@Controller
public class AppVersionController {
    @RequestMapping(value = "/appversion", method = RequestMethod.GET)
    @ResponseBody
    public String getAppVersion() {
        return BuildInfo.getAppVersion();
    }
}

Jenkins-jobb: yourapp-build-release

Ange URL till ditt Git repository, gör en clone.

Sätt Maven-version:

mvn versions:set -DnewVersion=1.0.${BUILD_NUMBER}

Bygg allt och deploya till artefakt repository och tagga:

mvn deploy scm:tag

Klart!

Så här enkelt kan man bygga en release. maven-release-plugin behövs inte längre tycker jag och Axel: Maven Release Plugin: The Final Nail in the Coffin

Publicerad i Continuous Delivery, Java, Uncategorized

Den nya nollnivån

Arbetet relaterat till Continuous Delivery har lett oss till en allt tydligare insikt om den grundfunktionalitet som bör finnas på plats i en kör- och utvecklingsmiljö redan från start, för att vi direkt ska kunna börja producera funktionalitet relaterad till kundnytta utan att börja ackumulera teknisk skuld.

Denna utvecklings- och körmiljö kan man också kalla Build Pipeline. Den omfattar såväl verktyg runtomkring systemet i fråga (till exempel Jenkins, Gradle eller Maven) som aspekter av själva systemet. En Build Pipeline av denna typ är att sträva efter såväl om man levererar systemet varje dag som en gång per kvartal.

Summan av den grundfunktionaliteten är vad jag här betecknar som nollnivån.

level-of-zero

Utvecklingsteam som arbetar i miljöer som saknar en eller flera av de egenskaper som beskrivs här kan naturligtvis också leverera nyttig funktionalitet, men dras med större eller mindre nackdelar, till exempel:

  • lägre utvecklingsfart
  • lägre kvalitet och sämre stabilitet
  • längre ledtid för ny funktionalitet
  • arbetsamma och riskfyllda produktionssättningar

Enligt mina iakttagelser krävs än så länge mycket erfarenhet och bred teknisk kunskap för att täcka upp de lösningar och mekanismer som krävs för att nå denna nollnivå för ett system och dess build pipeline.

I dagsläget är det inte säkert att det för alla system lönar sig att först ta sig till nollnivån innan man börjar ta sig an att implementera användningsfallen. Det beror på många olika faktorer, kanske främst systemets komplexitetsgrad och i hur hög grad systemet som utvecklas utgör en del av organisationens kärnverksamhet.

Vartefter lösningarna för att realisera en Build Pipeline med de egenskaper som beskrivs här blir enklare och mer standardiserade kommer det att löna sig att lyfta in mer och mer. Detta är just nu vad som nu håller på att hända – lösningarna håller som bäst på att bli möjliga att plocka ihop utan att man behöver utveckla stora och komplexa egna komponenter. Egna/proprietära icke-triviala lösningar och komponenter innebär såväl en initial utvecklings- eller införandekostnad som en underhållskostnad, varför nyttan av dessa måste vara mycket högre än den primitivare standardlösningar kan erbjuda.

De flesta mekanismer och behov jag beskriver här bör vara oberoende av plattform, språk och ramverk, men jag riktar in mig på att exemplifiera ett Java-baserat webbsystem då det är min främsta erfarenhet.

Byggprocedur

Systemet ska vara bygg- och körbart från källkod från kommandoraden, helst med ett kommando och med så få beroenden som möjligt till externa verktyg och konfigurationer. Kommandot mvn install är ett bra exempel.

Under utveckling bygger man systemet från en IDE också, men man vill inte vara tvungen att köra en viss IDE av en viss version och förlita sig på dess byggkonfiguration. Jag brukar undvika att ens committa IDE-specifika filer.

I den bästa av världar checkar man ut och bygger en ny arbetskopia av ett system med ett eller kanske två kommandon på en i övrigt ren maskin, bara genom att ange URL till källkodsrepot. För ett java-baserat system krävs kanske också att man först installerar JDK, Maven, Git eller Subversion, kanske också skruva på MAVEN_OPTS för att sätta heap size eller något annat. Ju färre och enklare moment i den initiala setupen, desto bättre.

Automatiska integrationstester

Med integrationstester avses tester som kör hela systemet som det är tänkt att köras i målmiljön, men med eventuella externa beroenden och system mockade (simulerade). Om systemet är en servlet-baserad webbapplikation så deployas applikationen på till exempel Tomcat och testerna använder applikationens externa tjänstegränssnitt (REST, SOAP, RMI etc.) och verifierar dels svaren på anropen och dels sidoeffekter i databaser och i mockar för externa system.

Dessa tester ska vara körbara från kommandoprompten (och naturligtvis även från din favorit-IDE), och kunna köras på lokalt modifierad, dvs ännu ej committad, kod för att kunna stödja en testdriven utvecklingsprocess. Den vanliga och enkla lösningen på detta innebär att man ser till att kunna köra åtminstone de funktionella acceptanstesterna på hela systemet med databaser och mockar för externa system lokalt på utvecklingsmaskinen.

Integrationstesterna består normalt i funktionella acceptanstester (med acceptanstester menar jag här tester som utgår från att testa kravuppfyllnad), implementerade kanske i SoapUI Pro, eller i Java eller Groovy i form av en Unit-test. Det är inte ovanligt att också komplettera med icke-funktionella tester, till exempel kapacitetstest eller mer tekniska tester som till exempel minnesläckagetest. Tester som testar GUI med till exempel ramverk som Selenium ingår också i denna kategori.

Olika klasser av tester görs körbara var och en för sig till exempel genom att gruppera dem i olika moduler.

Testramverket inkluderar initiering och laddning av eventuella databaser och initiering av mockar före exekveringen av varje testfall.

Byggserver

En byggserver lyssnar på commits och bygger då systemet och kör alla tester. De byggservrar jag ser är Jenkins, Go, Bamboo och Teamcity.

Eftersom vissa klasser av framför allt integrationstester kan ta ansenlig tid att köra är det praktiskt att dela upp dem i olika jobb, till exempel

  • Build
  • Integration test
  • GUI test
  • Load test

Enhetstest och kodmetrics

Utvecklingsmiljön måste ha ett bra stöd för enhetstester samt ett sätt att mäta olika aspekter av systemet som enhetstestkodtäckning, komplexitet, API-dokumentation, duplicerad kod m.m., där enhetstesttäckning är det jag väljer som viktigast att arbeta mot. 80% är en miniminivå jag strävar efter.

Rent mentalt blandar jag inte ihop enhetstest med test. Enhetstest är en metod utvecklare använder för att säkerställa att koden gör det utvecklaren avser att den ska göra och att den fortsätter att göra det när vi ändrar den. Men det är inte alls säkert att det utvecklaren avsåg är den funktion som uppfyller kraven, detta adresseras av test.

Enhetsteststödet i Java-baserade system med Maven, JUnit, Jenkins m.m. är moget och omfattande. För Javascript börjar det komma i och med ramverk som Jasmine, Karma, Protractor, m.m. och med ramverk som AngularJS, vilka separerar vy och kontrollogik, så blir enhetstestning av Javascript-logik genomförbart.

Automatisk versionsnumrering

Varje gång systemet byggs skapas dess versionsidentifikation. Den består kanske av ett enkelt bygglöpnummer eller ett versionsnummer enligt Semantic Versioning, på formen major.minor.buildNumber. Det är praktiskt att göra versionen tillgänglig på flera sätt:

  • Inbäddat i själva artefakten, till exempel i war-filens MANIFEST.MF
  • Mavens pom-versioner
  • I runtime i externa API-er. Ett REST-api kan till exempel ha en resurs /applicationVersion
  • I GUI

Versionen taggas automatiskt i kodrepositoriet.

En skarp version av systemet låter man lämpligen byggservern bygga, antingen som resultat av varje incheckning eller med ett speciellt jobb “build-release”.

Man vill kunna skilja på en lokalt byggt version av systemet och en “officiell version” byggt av byggservern. Byggproceduren ska med rimlig säkerhet kunna garantera att en viss version (som inte är av snapshot-typ) inte finns i olika inkarnationer med olika programkod.

Miljödetektering

Systemet ska själv kunna detektera i vilken miljö det kör: Till exempel lokal utvecklingsmiljö, integrationstestmiljö eller produktionsmiljö.

Ett enkelt sätt att göra detta är att sätta en miljövariabel, eller kontrollera hostnamn.

Miljökonfiguration

Den artefakt som byggs ska vara samma för alla miljöer, man vill inte bygga med olika profiler eller liknande mekanism, varför den måste innehålla alla parametrar för alla olika körmiljöer. Här avses parametrar som URL-er för externa tjänster, databaser, portnummer.

Man kan med rätta tycka att det är fel att lägga kunskap om körmiljöer i varje artefakt som ska köra i dem, och det är korrekt invändning i det generella fallet, men denna metod är rätt just i en Continuous Delivery Pipeline, där körmiljöerna i allra högsta grad är kända och man inte har något behov av att kunna ändra miljöparametrar dynamiskt utan att bygga en ny artefakt, eftersom hela poängen med en build pipeline är att det är enkelt att ändra konfigurationen i källkoden och trycka på release-knappen igen.

Feature toggles

Eftersom man utvecklar kontinuerligt i mainline, måste det till ett sätt att slå på och av funktioner, eller hela delar av systemet, snabbt och smidigt på ett enda ställe.

Ett enkelt och ändamålsenligt sätt att hantera feature-toggles på är att införa dessa toggles som parametrar i miljökonfigurationen. Något behov att slå på och av saker i runtime, och faktiskt heller inte ens utan att behöva bygga om artefakten, finns sällan behov av eftersom det i en Build Pipeline är enkelt att ändra i konfigurationen och trycka på release-knappen igen. Då får man dessutom spårbarhet även för ändringar av feature toggles genom sitt versionshanteringssystem.

Databashantering

System som omfattar databaser måste ha ett stöd för att skapa databaser som inte finns, ladda testdata inför varje testfallskörning och att migrera data och schema mellan olika versioner. När applikationen startar upp detekterar den vilken databas den har att göra med och uppdaterar schemat eller konverterar datat. Samma script eller kod används för att initiera och migrera databaser hela vägen från den lokala utvecklingsmiljön till produktionssystemet. På så vis kommer scripten att testas av kontinuerligt.

Det finns några olika verktyg för att stödja detta, till exempel DbDeploy, Liquibase och Flyway DB.

Maskinkonfigurationer

Även maskinkonfigurationen är en del av körmiljön och versionshanteras och automatiseras. Här avses till exempel mängd fysiskt minne, antal CPU-er, paketinstallationer, OS-konfigurationer m.m.

Dessa aspekter hanterar man enklast med verktyg som Vagrant, Puppet, Chef och Docker. Konfigurationsfilerna för dessa lägger man incheckade tillsammans med källkoden, vilket innebär att om man till exempel kommer fram till att man behöver mer fysiskt minne på servern så ändrar man till exempel i Vagrantfile och committar, så fixar din build pipeline resten: En ny maskinavbildning byggs ihop med de nya parametrarna, den gamla maskinen tas ned och den nya med mer minne startas. Inga manuella handgrepp här utan allt är automatiserat.

Releasedokumentation

Det finns flera sätt att hantera spårbarheten, dvs vilka buggfixar och nya funktioner som ingår i vilka byggen.

Den enklaste formen är att förlita sig på version history i systemets källkod. Ett mer ambitiöst och mer lättläst sätt att presentera releaseinformation är i form av release notes. Denna information kan med fördel genereras direkt från ett ärendehanteringssystem som Jira. Det förutsätter att man har disciplin att uppdatera ärenden med adekvat information för buggfixar, features och stories.

Release och deploy

Proceduren att bygga en release-version ska vara antingen helt automatisk, dvs byggservern bygger en releasekandidat så fort något committas, eller så bygger man en release med en explicit knapptryckning, till exempel via ett jobb “build-release” på byggservern.

Proceduren för att releasa systemet till testmiljö eller produktion ska vara i stil med att trycka på en knapp. En lösning är att skapa ett byggserverjobb “deploy-release” där man anger två parametrar:

  • Var systemet ska deployas (kanske en dropdown med “Test” och “Produktion”)
  • Vilken version man vill deploya

Lösningen kan också omfatta steg för manuell och utforskande testning.

Lyxmodellen av build pipeline visualiserar alla bygg och teststeg för alla versioner av systemet i ett verktyg, med koppling till ärendehantering och release-notes. Men allt det behövs inte för att man ska uppnå nollnivån som beskrivs här.

Summering

I den bästa av världar så borde det fungera så att man dag noll börjar med att stansa ut ett system och en Build Pipeline från en lämplig mall som passar in i övrig miljö och övriga rutiner, så att man kan börja producera kundnytta direkt.

Detta kommer förmodligen att vara möjligt så småningom. Idag måste man emellertid plocka ihop en egen lösning varenda gång utvecklingen av ett nytt system startar eller stegvis utveckla den vartefter. Eller alternativt ringa en vän som har gjort det förr.

Publicerad i Continuous Delivery, DevOps, Java, Test

Bash git-prompt för OS X eller Linux

Om man kör med git så blir man förr eller senare intresserad av att köra det mesta via kommandorad. En sak man då kan sakna är överblicken över det lokala repot – vilken branch står jag i? Har jag några inkommande/utgående förändringar? Är något lagt på stash-stacken?

Det finns ett utmärkt Githubprojekt som löser problemet genom att ge en mer informativ bash-prompt och som dessutom asynkront kör git status när man trycker retur. Jag har kört med den här lösningen i över ett år och det är numer det första jag installerar på en ny maskin.

För att installera detta i sin hemmakatalog så kan man göra följande:

git clone https://github.com/ogr3/bash-git-prompt.git ~/.bash-git-prompt

Lägg sedan till följande block i .bashrc (eller .bash_profile):

if [ -f ~/.bash-git-prompt/gitprompt.sh ] && [ -f ~/.bash-git-prompt/git-prompt-prefix.sh ]; then
source ~/.bash-git-prompt/git-prompt-prefix.sh
source ~/.bash-git-prompt/gitprompt.sh
fi

Om man vill fixera till en viss version, checkar man bara ut versionstaggen:

cd ~/.bash-git-prompt
git pull
git checkout v2.0

För att tweaka prompten kan man exportera GIT_PROMPT_START (görs av git-prompt-prefix.sh) och GIT_PROMPT_END.

För att bara ha en prompt på en rad kan man t.ex göra

export GIT_PROMPT_END=" "

Publicerad i DevOps, Linux, OS X

Continuous Delivery Pipeline med docker

Igår återsamlades vi en liten grupp DevOps-intresserade konsulter från flera olika konkurrerande bolag för att gemensamt bygga vidare på vår build pipeline. Den här gången höll vi till i CAGs lokaler inne i city.

IMG_20140604_201356[1]

Huvudtemat för dagen var docker. Idén var att införa docker som provider till Vagrant i vår build pipeline. docker innebär att nya maskiner blir betydligt mer lättviktiga än om varje maskin är en egen virtualbox-instans som vi hittils kört med Vagrant.

Eftersom docker-images får en hanterbar storlek vill vi direkt dra nytta av detta och ändra pipelinen att istället för att skicka runt applikationsartefakten (till exempel myapp.war) mellan byggstegen i pipelinen så låter vi första byggsteget bygga docker-images för hela miljön, till och börja med bara appserverns image, och skicka denna image mellan de olika teststegen i pipelinen: funktionell test, systemtest, GUI-test, lasttest och allt man kan hitta på.

Vi började med att skapa ett nytt github-projekt för att definiera en maskin att köra docker på. Den borde med facit i hand byta namn till dockerPipelineHost eller något liknande. Anledningen att vi öht skapar en docker host (alla Linux-maskiner kan ju köra docker native) är att vi vill kunna köra exakt samma pipeline såväl på utveckarmaskiner som i produktion.

Om vi lägger docker-definitionen i ett eget github-projekt kan vi bygga en docker-image som kan finnas i dockers repository (dockers affärsmodell är liknande githubs, de hostar images gratis så länge de är publika. Vill du ha privata docker images kostar det).

Vi provar att utgå från phusion/baseimage därför att den har stöd för ssh, default för docker är att ssh-stöd saknas. Tankemodellen för docker är att man kör igång en process och inte bootar en helt maskin, även om processen man kör igång får en egen maskin. Det kan dock underlätta att kunna ssh-a in till docker-containern för att felsöka.

Först skapade vi en VagrantFile, sedan körde vi igång den:

$ vagrant up --provider=docker

Vi slogs ett tag med att docker behöver port 4243 och det kolliderade med CrashPlan, så har man CrashPlan eller nåt annat på 4243 får man stoppa det.

Vi började med att skapa en databasmaskin. Vi tror att rätt modell med docker är att starta en docker-container per databas och inte försöka klämma in flera databaser i samma docker-container, även om det går om man absolut skulle vilja.

I nästa steg tänkte vi oss att skapa en appserver med tomcat. Vi fick trassel med hängning vid uppstart:

...
==> app: Creating the container...
app: Name: app
app: Image: phusion/baseimage
app: Cmd: /sbin/my_init --enable-insecure-key
app: Volume: /var/lib/docker/docker_1401893872_25527:/vagrant
app: Port: 2222:22

Här hänger den i evigheter vid vagrant up --provider=docker. Beror detta på att vi kör phusion/baseimage? Efter lång väntan lossnade maskinerna. Förmodligen nån nedladdning som tog tid här.

Efter att ha trasslat med att försöka starta tomcat som en process/kommando när startscriptet /etc/init.d/tomcat försöker starta en demon, övergick vi att köra en applikation som har tomcat inbäddad. Vi tror att detta är modellen – en applikation – en webcontainer – en maskin (som är en docker-container). Vi ser ingen större poäng att separera applikationen för att köra den i en standalone tomcat i något steg.

Vi övergick alltså från att köra petclinic till att köra doodleshop
doodleshop som är en nyligen ihopslängd demoapp på Spring Boot och embedded tomcat. Då behöver vi bara ha JRE på maskinen och kommandot för docker-containern blir enkelt:

java -jar doodleshop.jar

Vi tänkte oss först att lägga CoreOS i botten, dvs starta denna med Vagrant. CoreOS har docker installerat. Vår miljö implementerar vi som docker-containers: prodserver, testserver, databaserver, lastbalanserare och ci-server.

Vi måste mappa upp kataloger i containrarna till hostens filsystem. Annars försvinner datat när man plockar ned containern, eller när man gör vagrant halt på docker-hosten. Till exempel databasen skall lagra sitt data i en katalog som mappas till en katalog på dess docker-host CoreOS. Och denna katalog på CoreOS måste i sin tur mappas till en katalog i dess hosts filsystem, t.ex. en Mac OS X. Samma gäller appservrar för att få ut loggar.

Vi började drömma oss bort till centraliserad loggning här för att slippa att mappa upp appservrarna. Vi tror på logstash, ELK stack med logstash socket log appender i applikationen. Det får bli en annan kväll.

Sedan kom vi på att det blir svårt att starta nya docker-containers (prod, test, lb, db) från CI-servern om även denna ligger som en docker-container, det går säkert via dockers REST-api eller nåt, men det lät inte som enklaste vägen. Detta innebär att vi övergår till att installera CI på hosten, samma som docker själv kör på. Detta innebär i sin tur att det förmodligen är mest praktiskt att köra precise64 där istället för CoreOS.

Första steget i build pipeline är ”Build”. Denna bygger artefakten från senaste källkoden och kör enhetstester. Sedan skapas en docker-image. Integrationstestjobbet löses genom att pre-integration-test i maven deployar denna versions docker image till en docker container och startar denna. Integrationstesterna kör mot denna container. URL är miljöspecifik så man kan köra integrationtesterna lokalt också. post-integration-test tar ned containern och slänger den. Containern heter typ myapp-1.0.127, alltså byggnummer.

Vi fortsatte med att implementera detta i Go. Vi har behov av att flytta ut konfigurationsparametrarna för olika miljöer till environmentvariabler, t.ex. http-port. När man startar en docker-container med docker run anger man vilken port den skall exponera. Applikationen måste startas med samma port och porten beror av vilken docker-container applikationen kör. Denna parameter måste kunna styras utifrån för att informationen om vilken port vi kör på inte ska behöva finnas på två ställen, dvs först incheckad i den miljöspecifika konfigurationen och dessutom i den incheckade go-jobbkonfigen. Samma gäller andra parametrar som databas url.

Här någonstans hade klockan passerat läggdags och vi började krokna. Vi fortsätter vid ett senare tillfälle.

docker känns inte helt stabilt i alla lägen men verkar helt klart ha förutsättningar att bli något riktigt bra.

Förberedelser inför nästa mangling:

  • Mikael byter favoriteditor
  • Marell skaffar en fungerande VGA-adapter
  • Minst en av oss tar med en displayport-ethernet-adapter så vi kan tanka med mer än 15 MB/s
  • Någon fixar så vi slipper ladda ned hela Go varje gång CI-maskinen ska upp
Publicerad i Continuous Delivery, DevOps, Java, Linux

HelloServlet 3.0 Maven Project with Embedded Jetty 9 and spring-boot-maven-plugin

Yesterday evening I was playing around with my Raspberry Pi. I got Jetty 9 running on it with almost no pain, and then I was looking for my old HelloServlet 3.0 Maven project. But it appears I lost it. So I searched the Internet for a while for a modern simple servlet application and picked up a little here and there and ended up with the following – as small and compact as I could get it:

  • Maven based
  • Embedded jetty with executable war OR deployable war
  • Servlets 3.0 (no web.xml)

An executable war file means that you can run it directly without a web container:

$ java -jar helloservlet-1.0.war
2014-02-13 21:35:52.369:INFO:oejs.Server:main: jetty-9.1.2.v20140210
2014-02-13 21:35:52.498:INFO:oejw.StandardDescriptorProcessor:main: NO JSP Support for /, did not find org.apache.jasper.servlet.JspServlet
2014-02-13 21:35:52.558:INFO:oejsh.ContextHandler:main: Started o.e.j.w.WebAppContext@15d023f2{/,null,AVAILABLE}
2014-02-13 21:35:52.576:INFO:oejs.ServerConnector:main: Started ServerConnector@77ed2fe7{HTTP/1.1}{0.0.0.0:8090}

The application HelloServlet hangs here and is now available at http://localhost:8090/

helloservlet
With jetty server embedded into the war, its size becomes 1.4 MB. That is larger than before but still so small that I could hardly see it on my 1000 GB disk. Actually it fits on a 3.5″ floppy, for those of you who are old enough to know what that is.

The HelloServlet class is looking like this:

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {
    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.setContentType("text/html");
        response.getWriter().print("<html><body>" +
                "<h3>Hello Servlet</h3>" +
                "<form name=\"input\" method=\"post\">\n" +
                "Hello value: <input type=\"text\" name=\"helloValue\">\n" +
                "<input type=\"submit\" value=\"Submit\">\n" +
                "");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String value = request.getParameter("helloValue");
        System.out.println("doPost,helloValue=" + value);
        response.setContentType("text/html");
        response.getWriter().print("<html><body>helloValue=" + value + "</body></html>");
    }
}

It lays out a form and let you post a value.

When you start the application standalone, you will execute the Main class. It starts and sets up Jetty with HelloServlet like this:

public class Main {
    public static void main(String[] args) throws Exception {
        Server httpServer = new Server(8090);
        WebAppContext webAppContext = new WebAppContext();
        webAppContext.setContextPath("/");
        webAppContext.setBaseResource(Resource.newClassPathResource("web"));
        webAppContext.addServlet(HelloServlet.class.getName(), "/");
        webAppContext.setConfigurations(new Configuration[]{new WebXmlConfiguration()});
        HandlerList handlers = new HandlerList();
        handlers.setHandlers(new Handler[]{webAppContext, new DefaultHandler()});
        httpServer.setHandler(handlers);
        httpServer.start();
    }
}

If you deploy the war file on a standalone web container, the @WebServlet(urlPatterns="/hello") kicks in and the context root becomes http://localhost:8080/hello (assuming your web container is at localhost:8080).

The source structure is like this:

helloservlet structure

Lets walk though the Maven pom.xml.

  • <packaging>war</packaging> makes Maven create a war file instead of a jar
  • The section pluginRepositories is needed for spring-boot-maven-plugin below
  • We are using the servlet API, therefore javax.servlet-api is included (it is included in jetty-server but we should not rely on transitive dependencies)
  • jetty-server is what it appears to be
  • jetty-webapp is needed for configuring the web application
  • maven-war-plugin with failOnMissingWebXml=false is needed, or you have to include an empty web.xml file
  • maven-compiler-plugin is used to tell that we want to use 1.7 source
  • spring-boot-maven-plugin makes the war executable

Full code is available here.

Publicerad i Java
LinkedIn Auto Publish Powered By : XYZScripts.com