
För att börja, lite teori. Vad Àr ?
Enkelt uttryckt, det hÀr Àr ett dokument som syftar till att förenkla utvecklingen av SaaS-applikationer. Det hjÀlper genom att informera utvecklare och DevOps-ingenjörer om de problem och bÀsta praxis som oftast förekommit i utvecklingen av moderna applikationer.
Dokumentet har skapats av utvecklarna pÄ plattformen Heroku.
Tolvfaktormetodologin (The Twelve-Factor App) kan tillÀmpas pÄ applikationer skrivna i vilket programmeringssprÄk som helst och som anvÀnder alla möjliga kombinationer av externa tjÀnster (backing services) (databaser, meddelandeköer, cache-minnen, etc.).
Kort om de faktorer som denna metodologi bygger pÄ:
- Kodbasis â En kodbas, spĂ„rad i versionskontrollsystem, â flera distributioner
- Beroenden â Deklarera och isolera beroenden tydligt
- Konfiguration â Spara konfigurationen i körmiljön
- Externa tjĂ€nster (Backing Services) â BetĂ€nk externa tjĂ€nster (backing services) som anslutna resurser
- Byggande, distribution, körning â Strikt Ă„tskiljning av bygg- och körsteget
- Processer â Kör applikationen som en eller flera processer som Ă€r stateless
- Portbindning (Port binding) â Exportera tjĂ€nster via portbindning
- Parallellism â Skala applikationen med hjĂ€lp av processer
- Avyttring (Disposability) â Maximera tillförlitlighet genom snabb start och korrekt stĂ€ngning
- Paritet mellan utveckling och produktion â HĂ„ll utvecklings-, staging- och produktionsmiljöerna sĂ„ lika som möjligt
- Loggning (Logs) â BetĂ€nk loggen som en ström av hĂ€ndelser
- Administrationsuppgifter â Utför administrationshantering med hjĂ€lp av enstaka processer
Mer information om de 12 faktorerna kan du fÄ frÄn följande resurser:
- â ett mĂ„ste att lĂ€sa
- â officiell översĂ€ttning
- â en ny blick pĂ„ 12 faktorer för att förbĂ€ttra dem.
Vad Àr Blue-Green deployment?
Blue-Green deployment Àr ett sÀtt att leverera applikationen production pÄ ett sÀtt som slutar anvÀndaren inte ser nÄgra förÀndringar frÄn sin sida. Med andra ord, distributionen av applikationen med noll dödtid.
Den klassiska schemat för BG Deploy ser ut som pÄ bilden nedan.

- I början finns det 2 fysiska servrar med exakt samma kod, applikation, projekt och det finns en router (lastbalanserare).
- Routern riktar ursprungligen alla förfrÄgningar till en av servrarna (grön).
- Vid den tidpunkten dÄ en ny release behöver göras, uppdateras hela projektet pÄ den andra servern (blÄ), som för nÀrvarande inte behandlar nÄgra förfrÄgningar.
- Efter att koden pÄ den blÄ servern Àr helt uppdaterad, fÄr routern kommandot att vÀxla frÄn den gröna till blÄ server.
- Nu ser alla kunder resultatet av koden frÄn den blÄ servern.
- Under en tid fungerar grön servern som en reserv pÄ fall att distributionen pÄ blÄ servern misslyckas och vid eventuella fel, vÀxlar routern anvÀndarflödet tillbaka till grön servern med den gamla stabila versionen, medan den nya koden skickas för omarbete och testning.
- Slutligen uppdateras Àven grön servern. Och efter dess uppdatering vÀxlar routern anvÀndarflödet tillbaka till grön server.
Det hela ser vÀldigt bra ut och vid första anblicken borde det inte vara nÄgra problem.
Men eftersom vi lever i en modern vÀrld, sÄ passar inte alternativet med fysisk vÀxling som anges i den klassiska schemat oss. HÄll denna information i Ätanke, vi Äterkommer till den senare.
DÄliga och bra rÄd
Ansvarsfriskrivning: I exemplen nedan anges verktyg / metoder som jag anvÀnder, du kan anvÀnda absolut vilka alternativ som helst med liknande funktioner.
Större delen av exemplen kommer pÄ ett eller annat sÀtt att överlappa med webbutveckling (överraskning), med PHP och Docker.
I punkterna nedan ges en enkel praktisk beskrivning av anvÀndning av faktorer med specifika exempel; om du vill ha mer teori om detta Àmne, hÀnvisa till kÀllorna ovan.
1. Kodbas
AnvÀnd FTP och FileZilla för att ladda upp filer till servrarna en i taget, lagra inte koden nÄgon annanstans Àn pÄ produktionsservern.
Projektet ska alltid ha en enhetlig kodbas, det vill sĂ€ga, all kod kommer frĂ„n en och samma Git repositories. Servrarna (production, staging, test1, test2 âŠ) anvĂ€nder kod frĂ„n grenar i ett gemensamt repository. PĂ„ sĂ„ sĂ€tt uppnĂ„r vi kodens konsekvens.
2. BerÀttelser
Ladda ner alla bibliotek i mappar direkt till rotmappen av projektet. Uppdateringar görs genom att helt enkelt flytta ny kod till mappen med den aktuella versionen av biblioteket. Installera alla nödvÀndiga verktyg direkt pÄ vÀrdservern dÀr 20 andra tjÀnster redan körs.
Projektet bör alltid ha en tydlig och begriplig lista över beroenden (med beroenden menar jag Àven miljön). Alla beroenden mÄste vara tydligt definierade och isolerade.
Som exempel tar vi Composer och Docker.
Composer â en pakethanterare som gör det möjligt att installera PHP-bibliotek. Composer ger möjlighet att strikt eller mer avslappnat ange versioner och definiera dem tydligt. Det kan finnas 20 olika projekt pĂ„ servern och varje projekt har sin egen lista över paket och bibliotek som inte pĂ„verkar varandra.
Docker â ett verktyg som möjliggör att definiera och isolera miljön dĂ€r applikationen kommer att köras. PĂ„ liknande sĂ€tt som med Composer, men mer grundligt, kan vi definiera vad applikationen arbetar med. VĂ€lja en specifik PHP-version, installera endast de paket som Ă€r nödvĂ€ndiga för projektets arbete utan att lĂ€gga till nĂ„got extra. Och viktigast av allt, inte korsa med paketen och miljön för vĂ€rdmaskinen och andra projekt. Det vill sĂ€ga, alla projekt pĂ„ servern som körs via Docker, kan anvĂ€nda vilken uppsĂ€ttning paket som helst och en helt annan miljö.
3. Konfiguration
Spara konfigurationerna som konstanter direkt i koden. Separata konstanter för testservern och separata för produktionen. Koppla ihop applikationens arbete med miljön direkt i affÀrslogiken i projektet med hjÀlp av if-else-konstruktioner.
Konfigurationer â Ă€r det enda som ska skilja mellan projektets distributioner (deployment). I idealfallet bör konfigurationer överföras via miljövariabler (env vars).
Det vill sĂ€ga, Ă€ven om du lagrar flera konfigurationsfiler .config.prod .config.local och byter namn pĂ„ dem vid distribution till .config (huvudkonfigurationen som anvĂ€nds av applikationen för att lĂ€sa data) â sĂ„ Ă€r det fortfarande en felaktig metod, eftersom informationen frĂ„n konfigurationerna dĂ„ blir allmĂ€nt tillgĂ€nglig för alla utvecklare av applikationen och data frĂ„n produktionsservern kommer att bli komprometterade. Alla konfigurationer ska lagras direkt i distributionssystemet (CI/CD) och genereras för olika miljöer med olika vĂ€rden som krĂ€vs för en specifik miljö just vid distribution.
4. TredjepartstjÀnster (Backing Services)
Bind dig hÄrt till miljön, anvÀnd olika anslutningar för samma tjÀnster i specifika miljöer.
Detta punkt överlappar mycket med punkten om konfigurationer, eftersom utan denna punkt Àr det omöjligt att skapa ordentliga konfigurationsdata, och möjlighet till konfiguration skulle helt försvinna.
Alla anslutningar till externa tjÀnster sÄsom kö-servrar, databaser och cache-tjÀnster bör vara enhetliga för bÄde den lokala miljön och den externa/produktionsmiljön. Med andra ord, jag kan nÀr som helst Àndra anslutningsstrÀngen för att byta frÄn databas #1 till databas #2 utan att behöva Àndra pÄ applikationens kod. Som ett exempel, vid skalning av tjÀnsten behöver du inte ange en speciell anslutning för den extra cache-servern.
5. Bygg, release, körning
Ha endast den fÀrdiga versionen av koden pÄ servern, utan möjlighet att rulla tillbaka en release. Det handlar om att inte belasta lagringsutrymmet. Den som tror att de kan skicka kod till produktion med ett fel Àr en dÄlig programmerare!
Alla steg i distributionen bör vara separerade frÄn varandra.
Ha möjlighet att rulla tillbaka. Gör releaser med snabba kopior av applikationen (redan byggda och klara för produktion) sÄ att du kan ÄterstÀlla en gammal version om fel uppstÄr. Det finns alltsÄ en mapp releases och en mapp current, och efter en lyckad distribution och byggning kopplas mappen current via en symbolisk lÀnk till den nya releasen som ligger inuti releases med ett hypotetiskt versionsnummer.
HÀr pÄminner vi om Blue-Green deployment, som inte bara gör det möjligt att vÀxla mellan kod, utan ocksÄ mellan alla resurser och till och med miljöer med möjlighet att rulla tillbaka allt.
6. Processer
Spara tillstÄndsdata för applikationen direkt i sjÀlva applikationen. AnvÀnd sessioner i applikationens minne. AnvÀnd sÄ mÄnga delade resurser mellan externa tjÀnster som möjligt. Bind dig vid att applikationen kan ha endast en process och tillÄt inte skalning.
NÀr det gÀller sessioner, spara data endast i cache som kontrolleras av externa tjÀnster (memcached, redis), sÄ att Àven om du har 20 applikationsprocesser igÄng, kan vilken som helst av dem, nÀr den kontaktar cachen, fortsÀtta arbeta med klienten i samma tillstÄnd som anvÀndaren var i nÀr denne arbetade med applikationen i en annan process. Med detta tillvÀgagÄngssÀtt fungerar det sÄ att ju fler kopior av externa tjÀnster du anvÀnder, desto mer problemfritt kommer allt att fungera utan problem med datÄtkomst.
7. Portbindning (Port binding)
Det Àr bara webbservern som ska veta hur man arbetar med externa tjÀnster. Det Àr Ànnu bÀttre att driva externa tjÀnster direkt inuti webbservern. Till exempel som en PHP-modul i Apache.
Alla dina tjÀnster bör vara tillgÀngliga för varandra genom att kontakta nÄgon adress och port (localhost:5432, localhost:3000, nginx:80, php-fpm:9000), det vill sÀga frÄn nginx kan jag fÄ tillgÄng till bÄde php-fpm och postgres, och frÄn php-fpm till postgres och nginx, och i sjÀlva verket frÄn varje tjÀnst kan jag fÄ tillgÄng till en annan tjÀnst. PÄ sÄ sÀtt beror livskraften hos en tjÀnst inte pÄ livskraften hos en annan.
8. Parallellism
Arbeta med en enda process, annars kanske flera processer inte kan samarbeta!
LÀmna utrymme för skalning. Perfekt för detta Àr docker swarm.
Docker Swarm Àr ett verktyg för att skapa och hantera kluster av containrar bÄde mellan olika maskiner och en mÀngd containrar pÄ en och samma maskin.
Genom att anvÀnda swarm kan jag bestÀmma hur mÄnga resurser jag ska tilldela varje process och hur mÄnga processer av samma tjÀnst jag ska starta. Den interna lastbalanseraren tar emot data pÄ den angivna porten och kommer automatiskt att proxy till processerna. Om jag ser att belastningen pÄ servern har ökat kan jag lÀgga till fler processer för att dÀrigenom minska belastningen pÄ vissa processer.
9. Utslippbarhet (Disposability)
AnvÀnd inte köer för att hantera processer och data. Att avsluta en process ska pÄverka hela applikationen. Om en tjÀnst faller, faller allt.
Varje process och tjĂ€nst kan stĂ€ngas av vilket ögonblick som helst utan att det pĂ„verkar andra tjĂ€nster (det handlar inte om att en tjĂ€nst blir otillgĂ€nglig för en annan tjĂ€nst, utan om att en annan tjĂ€nst inte ska stĂ€ngas av ihop med denna). Alla processer bör avslutas mjukt, sĂ„ att inga data skadas vid avslutningen och systemet fungerar korrekt vid nĂ€sta start. Ăven vid en nödstĂ€ngning ska inga data skadas (detta Ă„stadkommes med hjĂ€lp av transaktionsmekanismer, databasfrĂ„gor fungerar endast i grupper, och om nĂ„gon frĂ„ga i gruppen misslyckas eller ger ett fel, ska ingen annan frĂ„ga i gruppen faktiskt köras).
10. Paritet mellan utveckling och drift av applikationen
Produktions-, staging- och lokalversionen av applikationen bör vara olika. I produktion anvÀnder vi Yii Lite-ramverket, medan vi lokalt anvÀnder Yii, för att det fungerar snabbare i produktion!
I verkligheten bör alla distributioner och kodhanteringar ske i nÀstan identiska miljöer (det handlar inte om fysisk hÄrdvara). Dessutom bör alla anstÀllda inom utvecklingen kunna distribuera koden i produktion vid behov, inte en sÀrskilt utbildad devops-avdelning som endast kan starta applikationen i produktion tack vare sÀrskild kompetens.
Detta stöds ocksÄ av Docker. Genom att följa alla föregÄende punkter kommer anvÀndningen av Docker att göra distributionsprocessen inom bÄde produktion och lokal maskin sÄ enkel som att köra ett par kommandon.
11. Loggning (Logs)
Vi skriver loggar i filer och databaser! Vi rensar inte filer och databaser frÄn loggar. Vi köper bara en hÄrddisk pÄ 9000 petabyte och det Àr bra.
Alla loggar bör ses som en ström av hÀndelser. SjÀlva applikationen ska inte hantera loggar. Loggar ska antingen skickas till stdout eller över ett protokoll som udp, sÄ att hanteringen av loggar inte stör applikationen. Graylog Àr ett bra alternativ för detta. Graylog tar emot alla loggar via udp (för detta protokoll behövs inget svar pÄ att paketet har tagits emot) och stör dÀrmed inte applikationen, utan hanterar endast struktureringen och behandlingen av loggar. Applikationens logik förÀndras inte för att arbeta med sÄdana metoder.
12. Administrationsuppgifter
För att uppdatera data, databasen, etc. anvÀnd en separat skapad endpoint i API:t, vars körning tvÄ gÄnger i rad kan resultera i att allt dupliceras. Men ni Àr ju inte dumma och trycker inte tvÄ gÄnger, och vi behöver inga migrationer.
Alla administrationsuppgifter mĂ„ste utföras i samma miljö som hela koden, pĂ„ versionsnivĂ„. SĂ„ om vi behöver Ă€ndra databassens struktur, ska vi inte göra det manuellt genom att Ă€ndra kolumnnamn och lĂ€gga till nya genom nĂ„gon visuell databasverktyg. För sĂ„dana saker skapar vi separata skript â migrationer, som körs pĂ„ alla miljöer med ett enhetligt och tydligt resultat. För alla andra uppgifter, som att fylla projektet med data, bör liknande metoder tillĂ€mpas.
Exempel pÄ implementation med PHP, Laravel, Laradock, Docker-Compose
P.S. Alla exempel gjordes pÄ MacOS. Största delen fungerar Àven för Linux. AnvÀndare av Windows, ber om ursÀkt, men jag har inte arbetat med Windows pÄ lÀnge.
LÄt oss förestÀlla oss en situation dÀr vi inte har installerat nÄgon version av PHP pÄ vÄr dator och inte har nÄgot alls.
Vi installerar de senaste versionerna av docker och docker-compose. (detta kan hittas pÄ internet)
docker -v &&
docker-compose -v

1. Vi installerar
git clone https://github.com/Laradock/laradock.git &&
ls

NĂ€r det gĂ€ller Laradock kan jag sĂ€ga att det Ă€r en riktigt bra grej som samlar mĂ„nga containrar och hjĂ€lpfunktioner. Men att anvĂ€nda Laradock utan modifieringar i produktion â skulle jag inte rekommendera pĂ„ grund av dess överflödighet. Det Ă€r bĂ€ttre att skapa egna containrar baserat pĂ„ exemplen i Laradock, sĂ„ kan man optimera, eftersom ingen behöver allt som finns dĂ€r samtidigt.
2. Vi konfigurerar Laradock för att arbeta med vÄr applikation.
cd laradock &&
cp env-example .env

2.1. Ăppna katalogen habr (förĂ€ldramappen dĂ€r laradock har klonats) i en valfri editor. (I mitt fall PHPStorm)
I det hÀr steget ger vi bara projektet ett namn.

2.2. Starta bilden för workspace. (I ditt fall kommer bilderna att byggas under en tid)
Workspace Àr en sÀrskilt förberedd bild för arbete med ramverket frÄn utvecklarens sida.
GÄ in i containern med hjÀlp av
docker-compose up -d workspace &&
docker-compose exec workspace bash

2.3. Installera Laravel
composer create-project --prefer-dist laravel/laravel application 
2.4. Efter installationen kontrollerar vi om katalogen med projektet har skapats, och tar ner compose.
ls
exit
docker-compose down

2.5. Ă
tergÄ till PHPStorm och ange rÀtt sökvÀg till vÄr laravel-applikation i filen .env.

3. LÄt oss lÀgga till hela koden i Git.
För detta ÀndamÄl skapar vi ett repository pÄ Github (eller nÄgon annanstans). GÄ till katalogen habr i terminalen och kör följande kod.
echo "# habr-12factor" >> README.md
git init
git add README.md
git commit -m "första commit"
git remote add origin git@github.com:nzulfigarov/habr-12factor.git # hÀr kommer lÀnken till ditt repo
git push -u origin master
git status
Kontrollera att allt Àr i sin ordning.

För bekvÀmlighetens skull rekommenderar jag att anvÀnda nÄgot visuellt grÀnssnitt för Git, i mitt fall Àr det . (hÀr Àr en referralslÀnk)
4. Starta!
Innan du startar, se till att ingenting körs pÄ port 80 och 443.
docker-compose up -d nginx php-fpm 
DÀrmed bestÄr vÄrt projekt av 3 separata tjÀnster:
- nginx â webserver
- php-fpm â php för att ta emot förfrĂ„gningar frĂ„n webservern
- workspace â php för utvecklaren
För tillfÀllet har vi uppnÄtt det att vi har skapat en applikation motsvarande redan 4 punkter av 12, nÀmligen:
1. Kodbasis â All kod ligger i ett repository (en liten kommentar: det kan vara bĂ€ttre att föra in docker i projektet laravel, men det Ă€r inte avgörande).
2. Beroenden â Alla vĂ„ra beroenden Ă€r tydligt angivna i application/composer.json och i varje Dockerfile för varje container.
3. Externa tjĂ€nster (Backing Services) â Varje tjĂ€nst (php-fpm, nginx, workspace) lever sitt eget liv och Ă€r uppkopplad utifrĂ„n, och nĂ€r man arbetar med en tjĂ€nst pĂ„verkas ingen annan.
4. Processer â varje tjĂ€nst Ă€r en process. Ingen av tjĂ€nsterna sparar ett internt tillstĂ„nd.
5. Portbindning (Port binding)
docker ps

Som vi ser, varje tjÀnst körs pÄ sin egen port och Àr tillgÀnglig för alla andra tjÀnster.
6. Parallellism
Docker tillÄter oss att starta flera processer av samma tjÀnster med automatisk lastbalansering mellan dem.
Vi stoppar containrarna och startar dem med en flagga âscale
docker-compose down &&
docker-compose up -d --scale php-fpm=3 nginx php-fpm

Som vi ser, har php-fpm-containern skapat kopior. Vi behöver inte Àndra nÄgot i arbetet med denna container. Vi fortsÀtter ocksÄ att kommunicera med den via port 9000, och Docker hanterar belastningen mellan containrarna Ät oss.
7. Avyttring (Disposability) â varje container kan stoppas utan att pĂ„verka de andra. StĂ€ngning eller omstart av en container pĂ„verkar inte applikationens arbete vid framtida lanseringar. Varje container kan ocksĂ„ startas nĂ€r som helst.
8. Paritet mellan utveckling och produktion â alla vĂ„ra miljöer Ă€r identiska. NĂ€r du kör systemet pĂ„ en server i produktion behöver du inte Ă€ndra nĂ„got i dina kommandon. Allt kommer att baseras pĂ„ Docker pĂ„ exakt samma sĂ€tt.
9. Loggning (Logs) â alla loggar i dessa containrar strömmas och visas i Docker-konsolen. (i detta fall, i verkligheten, kan det vara annorlunda med andra anpassade containrar om du inte tar hand om det)
docker-compose logs -f 
Men hÀr finns det en hake: StandardvÀrdena i PHP och Nginx skriver ocksÄ loggar i filen. För att uppfylla 12-faktorprinciperna Àr det nödvÀndigt stÀnga av att loggar skrivs till filer i konfigurationerna för varje container individuellt.
Docker erbjuder Àven möjligheten att rikta loggar inte bara till stdout, utan ocksÄ till saker som graylog som jag nÀmnde ovan. Inom graylog kan vi hantera loggarna pÄ valfritt sÀtt, och vÄr applikation kommer inte att mÀrka nÄgot.
10. Administrationsuppgifter â alla administrationsuppgifter löses av laravel tack vare verktyget artisan precis som skaparna av 12-faktorapplikationen skulle vilja.
Som exempel visar jag hur nÄgra kommandon utförs.
Vi gÄr in i containern.
docker-compose exec workspace bash
php artisan list

Nu kan vi anvÀnda vilket kommando vi vill. (Observera att vi inte har konfigurerat databasen och cachen, sÄ hÀlften av kommandona kommer inte att fungera korrekt, eftersom de Àr avsedda för arbete med cache och databasen).

11. Konfigurationer och 12. Byggande, distribution, körning
Den hÀr delen ville jag Àgna Ät Blue-Green Deployment, men det visade sig vara för omfattande för denna artikel. Jag kommer att skriva en separat artikel om detta.
I korthet bygger konceptet pÄ CI/CD-system som Jenkins och Gitlab CI. I bÄda kan man stÀlla in miljövariabler kopplade till en specifik miljö. DÀrmed kommer punkt med Konfigurationer.
Och punkten om Byggande, distribution, körning löses av inbyggda funktioner i bÄda verktygen som kallas Pipeline.
Pipeline möjliggör att dela in deploymentsprocessen i flera steg, dÀr bygg-, release- och körningsfaserna framhÀvs. I Pipeline kan du ocksÄ skapa backup och faktiskt allt möjligt. Detta verktyg har en oÀndlig potential.
Kodbasen för applikationen ligger pÄ .
Kom ihÄg att initialisera submodulen nÀr du klonar detta repository.
P.S.: Alla dessa tillvÀgagÄngssÀtt kan anvÀndas med andra verktyg och programmeringssprÄk. Det viktigaste Àr att kÀrnan inte skiljer sig.
KĂ€lla: habr.com
