Vad är Docker: en kort utflykt till historia och grundläggande abstraktioner

Startade den 10 augusti i Slurm Docker videokurs, där vi analyserar det fullständigt - från grundläggande abstraktioner till nätverksparametrar.

I den här artikeln kommer vi att prata om Dockers historia och dess huvudsakliga abstraktioner: Image, Cli, Dockerfile. Föreläsningen är avsedd för nybörjare, så det är osannolikt att det är intressant för erfarna användare. Det blir inget blod, blindtarm eller djup nedsänkning. Själva grunderna.

Vad är Docker: en kort utflykt till historia och grundläggande abstraktioner

Vad är Docker

Låt oss titta på definitionen av Docker från Wikipedia.

Docker är programvara för att automatisera driftsättning och hantering av applikationer i containermiljöer.

Ingenting framgår av denna definition. Det är särskilt oklart vad "i miljöer som stödjer containerisering" betyder. För att ta reda på det, låt oss gå tillbaka i tiden. Låt oss börja med den era som jag vanligtvis kallar den "monolitiska eran."

Monolitisk era

Den monolitiska eran är tidigt 2000-tal, då alla applikationer var monolitiska, med en massa beroenden. Utvecklingen tog lång tid. Samtidigt fanns det inte många servrar, vi kände alla vid namn och övervakade dem. Det finns en sådan rolig jämförelse:

Husdjur är husdjur. Under den monolitiska eran behandlade vi våra servrar som husdjur, skötte och omhuldade och blåste bort dammfläckar. Och för bättre resurshantering använde vi virtualisering: vi tog en server och skar upp den i flera virtuella maskiner, vilket säkerställde isolering av miljön.

Hypervisor-baserade virtualiseringssystem

Alla har säkert hört talas om virtualiseringssystem: VMware, VirtualBox, Hyper-V, Qemu KVM etc. De tillhandahåller applikationsisolering och resurshantering, men de har också nackdelar. För att göra virtualisering behöver du en hypervisor. Och hypervisorn är en resursoverhead. Och den virtuella maskinen i sig är vanligtvis en hel koloss – en tung bild som innehåller ett operativsystem, Nginx, Apache och möjligen MySQL. Bilden är stor och den virtuella maskinen är obekväm att använda. Som ett resultat kan det vara långsamt att arbeta med virtuella maskiner. För att lösa detta problem skapades virtualiseringssystem på kärnnivå.

Virtualiseringssystem på kärnnivå

Virtualisering på kärnnivå stöds av OpenVZ, Systemd-nspawn, LXC-system. Ett slående exempel på sådan virtualisering är LXC (Linux Containers).

LXC är ett virtualiseringssystem på operativsystemnivå för att köra flera isolerade instanser av operativsystemet Linux på en enda nod. LXC använder inte virtuella maskiner utan skapar en virtuell miljö med eget processutrymme och nätverksstack.

LXC skapar i huvudsak containrar. Vad är skillnaden mellan virtuella maskiner och behållare?

Vad är Docker: en kort utflykt till historia och grundläggande abstraktioner

Behållaren är inte lämplig för att isolera processer: sårbarheter finns i virtualiseringssystem på kärnnivå som gör att de kan fly från behållaren till värden. Därför, om du behöver isolera något, är det bättre att använda en virtuell maskin.

Skillnaderna mellan virtualisering och containerisering kan ses i diagrammet.
Det finns hårdvaruhypervisorer, hypervisorer ovanpå operativsystemet och behållare.

Vad är Docker: en kort utflykt till historia och grundläggande abstraktioner

Hårdvaruhypervisorer är coola om du verkligen vill isolera något. Eftersom det är möjligt att isolera på nivån av minnessidor och processorer.

Det finns hypervisorer som ett program, och det finns containrar, och vi kommer att prata om dem vidare. Containeriseringssystem har ingen hypervisor, men det finns en Container Engine som skapar och hanterar containrar. Den här saken är mer lätt, så på grund av att arbeta med kärnan blir det mindre overhead eller ingen alls.

Vad används för containerisering på kärnnivå

De huvudsakliga teknikerna som gör att du kan skapa en behållare isolerad från andra processer är namnområden och kontrollgrupper.

Namnområden: PID, Nätverk, Montera och Användare. Det finns fler, men för att underlätta förståelsen kommer vi att fokusera på dessa.

PID-namnutrymme begränsar processer. När vi till exempel skapar ett PID Namespace och placerar en process där blir det med PID 1. Vanligtvis i system är PID 1 systemd eller init. Följaktligen, när vi placerar en process i ett nytt namnutrymme, får den också PID 1.

Networking Namespace låter dig begränsa/isolera nätverket och placera dina egna gränssnitt inuti. Mount är en filsystembegränsning. Användare – begränsning för användare.

Kontrollgrupper: Minne, CPU, IOPS, Nätverk - cirka 12 inställningar totalt. Annars kallas de också Cgroups ("C-grupper").

Kontrollgrupper hanterar resurser för en behållare. Genom kontrollgrupper kan vi säga att behållaren inte ska förbruka mer än en viss mängd resurser.

För att containeriseringen ska fungera fullt ut, används ytterligare tekniker: Capabilities, Copy-on-write och andra.

Förmåga är när vi berättar för en process vad den kan och inte kan göra. På kärnnivå är dessa helt enkelt bitmappar med många parametrar. Till exempel har root-användaren fulla privilegier och kan göra allt. Tidsservern kan ändra systemtiden: den har funktioner på Time Capsule, och det är det. Med hjälp av privilegier kan du flexibelt konfigurera begränsningar för processer och därmed skydda dig själv.

Copy-on-write-systemet låter oss arbeta med Docker-bilder och använda dem mer effektivt.

Docker har för närvarande kompatibilitetsproblem med Cgroups v2, så den här artikeln fokuserar specifikt på Cgroups v1.

Men låt oss gå tillbaka till historien.

När virtualiseringssystem dök upp på kärnnivå började de användas aktivt. Overheaden på hypervisorn försvann, men några problem kvarstod:

  • stora bilder: de trycker in ett operativsystem, bibliotek, en massa olika programvaror i samma OpenVZ, och i slutändan visar sig bilden fortfarande vara ganska stor;
  • Det finns ingen normal standard för förpackning och leverans, så problemet med beroenden kvarstår. Det finns situationer när två stycken kod använder samma bibliotek, men med olika versioner. Det kan finnas en konflikt mellan dem.

För att lösa alla dessa problem har nästa era kommit.

Containereran

När Era of Containers anlände ändrades filosofin att arbeta med dem:

  • En process - en behållare.
  • Vi levererar alla beroenden processen behöver till sin container. Detta kräver att monoliter skärs till mikrotjänster.
  • Ju mindre bild, desto bättre – det finns färre möjliga sårbarheter, den rullar ut snabbare och så vidare.
  • Förekomster blir tillfälliga.

Kommer du ihåg vad jag sa om husdjur kontra boskap? Tidigare var instanserna som husdjur, men nu har de blivit som boskap. Tidigare fanns det en monolit - en applikation. Nu är det 100 mikrotjänster, 100 behållare. Vissa behållare kan ha 2-3 kopior. Det blir mindre viktigt för oss att kontrollera varje container. Vad som är viktigare för oss är tillgängligheten för själva tjänsten: vad den här uppsättningen containrar gör. Detta förändrar synen på övervakning.

Under 2014-2015 blomstrade Docker - tekniken som vi kommer att prata om nu.

Docker ändrade filosofin och standardiserade applikationspaketeringen. Med Docker kan vi paketera en applikation, skicka den till ett arkiv, ladda ner den därifrån och distribuera den.

Vi lägger allt vi behöver i Docker-behållaren, så beroendeproblemet är löst. Docker garanterar reproducerbarhet. Jag tror att många människor har stött på irreproducerbarhet: allt fungerar för dig, du skjuter det till produktion och där slutar det att fungera. Med Docker försvinner detta problem. Om din Docker-container startar och gör vad den behöver göra, så kommer den med en hög grad av sannolikhet att starta i produktion och göra samma sak där.

Utvikning om overhead

Det finns alltid dispyter om omkostnader. Vissa människor tror att Docker inte bär en extra belastning, eftersom den använder Linux-kärnan och alla dess processer som är nödvändiga för containerisering. Som, "om du säger att Docker är overhead, då är Linux-kärnan overhead."

Å andra sidan, om du går djupare, finns det verkligen flera saker i Docker som med en sträcka kan sägas vara overhead.

Den första är PID-namnutrymmet. När vi placerar en process i ett namnområde tilldelas den PID 1. Samtidigt har denna process ett annat PID, som finns på värdnamnområdet, utanför behållaren. Till exempel lanserade vi Nginx i en container, det blev PID 1 (masterprocess). Och på värden har den PID 12623. Och det är svårt att säga hur mycket det är.

Den andra saken är Cgroups. Låt oss ta Cgroups efter minne, det vill säga möjligheten att begränsa minnet i en container. När det är aktiverat aktiveras räknare och minnesredovisning: kärnan måste förstå hur många sidor som har tilldelats och hur många som fortfarande är lediga för denna behållare. Detta är möjligen en overhead, men jag har inte sett några exakta studier om hur det påverkar prestandan. Och jag själv märkte inte att applikationen som körs i Docker plötsligt upplevde en kraftig prestandaförlust.

Och en anmärkning till om prestanda. Vissa kärnparametrar skickas från värden till behållaren. I synnerhet vissa nätverksparametrar. Därför, om du vill köra något högpresterande i till exempel Docker, något som aktivt kommer att använda nätverket, måste du åtminstone justera dessa parametrar. Vissa nf_conntrack, till exempel.

Om Docker-konceptet

Docker består av flera komponenter:

  1. Docker Daemon är samma Container Engine; lanserar containrar.
  2. Docker CII är ett Docker-hanteringsverktyg.
  3. Dockerfile - instruktioner om hur man bygger en bild.
  4. Bild — bilden från vilken behållaren rullas ut.
  5. Behållare.
  6. Docker registry är ett bildarkiv.

Schematiskt ser det ut ungefär så här:

Vad är Docker: en kort utflykt till historia och grundläggande abstraktioner

Docker-demonen körs på Docker_host och startar behållare. Det finns en klient som skickar kommandon: bygg bilden, ladda ner bilden, starta behållaren. Docker-demonen går till registret och kör dem. Docker-klienten kan komma åt både lokalt (till en Unix-socket) och via TCP från en fjärrvärd.

Låt oss gå igenom varje komponent.

Docker-demon - det här är serverdelen, den fungerar på värddatorn: laddar ner bilder och startar behållare från dem, skapar ett nätverk mellan behållare, samlar in loggar. När vi säger "skapa en bild" gör demonen det också.

Docker CLI — Docker-klientdel, konsolverktyg för att arbeta med demonen. Jag upprepar, det kan fungera inte bara lokalt utan också över nätverket.

Grundläggande kommandon:

docker ps - visa behållare som för närvarande körs på Docker-värden.
docker-bilder - visa bilder som laddats ner lokalt.
docker search <> - sök efter en bild i registret.
docker pull <> - ladda ner en bild från registret till maskinen.
docker build < > - samla in bilden.
docker run <> - starta behållaren.
docker rm <> - ta bort behållaren.
docker logs <> - container logs
docker start/stop/restart <> - arbetar med behållaren

Om du behärskar dessa kommandon och är säker på att använda dem, betrakta dig själv som 70 % skicklig i Docker på användarnivå.

Dockerfile - instruktioner för att skapa en bild. Nästan varje instruktionskommando är ett nytt lager. Låt oss titta på ett exempel.

Vad är Docker: en kort utflykt till historia och grundläggande abstraktioner

Så här ser Dockerfilen ut: kommandon till vänster, argument till höger. Varje kommando som finns här (och vanligtvis skrivs i Dockerfilen) skapar ett nytt lager i bilden.

Även om man tittar på vänster sida kan man ungefär förstå vad som händer. Vi säger: "skapa en mapp för oss" - det här är ett lager. "Få mappen att fungera" är ett annat lager, och så vidare. Skiktårta gör livet enklare. Om jag skapar en annan Dockerfil och ändrar något på sista raden - jag kör något annat än "python" "main.py", eller installerar beroenden från en annan fil - så kommer de tidigare lagren att återanvändas som en cache.

Bild - detta är behållareförpackningar; behållare startas från bilden. Om vi ​​ser på Docker från en pakethanterares synvinkel (som om vi skulle arbeta med deb- eller rpm-paket), så är image i huvudsak ett rpm-paket. Genom yum install kan vi installera applikationen, ta bort den, hitta den i förvaret och ladda ner den. Det är ungefär detsamma här: behållare startas från bilden, de lagras i Docker-registret (liknande yum, i ett arkiv), och varje bild har en SHA-256-hash, ett namn och en tagg.

Bilden är byggd enligt instruktionerna från Dockerfilen. Varje instruktion från Dockerfilen skapar ett nytt lager. Lager kan återanvändas.

Docker-registret är ett Docker-bildarkiv. I likhet med operativsystemet har Docker ett offentligt standardregister - dockerhub. Men du kan bygga ditt eget arkiv, ditt eget Docker-register.

Behållare - vad som startas från bilden. Vi byggde en bild enligt instruktionerna från Dockerfilen, sedan startar vi den från den här bilden. Denna behållare är isolerad från andra behållare och måste innehålla allt som behövs för att applikationen ska fungera. I det här fallet, en behållare - en process. Det händer att du måste göra två processer, men detta strider lite mot Docker-ideologin.

Kravet på "en behållare, en process" är relaterat till PID-namnområdet. När en process med PID 1 startar i Namespace, om den plötsligt dör, dör hela behållaren också. Om två processer körs där: en är vid liv och den andra är död, kommer behållaren fortfarande att leva. Men det här är en fråga om bästa praxis, vi kommer att prata om dem i andra material.

För att studera funktionerna och det fullständiga programmet för kursen mer i detalj, följ länken: "Docker videokurs".

Författare: Marcel Ibraev, certifierad Kubernetes-administratör, praktiserande ingenjör på Southbridge, talare och utvecklare av Slurm-kurser.

Källa: will.com

Lägg en kommentar