Noțiuni introductive cu Docker: simplificarea DevOps
Publicat: 2022-03-11Dacă vă plac balenele sau sunteți pur și simplu interesat de livrarea continuă rapidă și fără durere a software-ului dvs. la producție, atunci vă invit să citiți acest tutorial introductiv Docker. Totul pare să indice că containerele software sunt viitorul IT, așa că haideți să facem o baie rapidă cu balenele container Moby Dock și Molly.
Docker, reprezentat de un logo cu o balenă cu aspect prietenos, este un proiect open source care facilitează implementarea aplicațiilor în interiorul containerelor de software. Funcționalitatea sa de bază este activată de caracteristicile de izolație a resurselor ale nucleului Linux, dar oferă un API ușor de utilizat pe lângă acesta. Prima versiune a fost lansată în 2013 și de atunci a devenit extrem de populară și este utilizată pe scară largă de mulți jucători mari, cum ar fi eBay, Spotify, Baidu și alții. În ultima rundă de finanțare, Docker a obținut 95 de milioane de dolari și este pe cale să devină un element de bază al serviciilor DevOps.
Analogia transportului de mărfuri
Filosofia din spatele lui Docker ar putea fi ilustrată cu următoarea analogie simplă. În industria transporturilor internaționale, mărfurile trebuie să fie transportate prin diferite mijloace, cum ar fi stivuitoare, camioane, trenuri, macarale și nave. Aceste mărfuri au diferite forme și dimensiuni și au cerințe diferite de depozitare: saci de zahăr, cutii de lapte, plante etc. Din punct de vedere istoric, a fost un proces dureros, care depindea de intervenția manuală la fiecare punct de tranzit pentru încărcare și descărcare.
Totul s-a schimbat odată cu preluarea containerelor intermodale. Deoarece vin în dimensiuni standard și sunt fabricate având în vedere transportul, toate utilajele relevante pot fi proiectate pentru a le gestiona cu intervenție umană minimă. Avantajul suplimentar al containerelor sigilate este că pot păstra mediul intern, cum ar fi temperatura și umiditatea pentru mărfurile sensibile. Drept urmare, industria transporturilor poate înceta să-și mai facă griji pentru mărfurile în sine și se poate concentra pe transportul lor de la A la B.
Și aici intervine Docker și aduce beneficii similare industriei software.
Cum este diferit de mașinile virtuale?
La o privire rapidă, mașinile virtuale și containerele Docker pot părea la fel. Cu toate acestea, diferențele lor principale vor deveni evidente atunci când aruncați o privire la următoarea diagramă:
Aplicațiile care rulează în mașinile virtuale, în afară de hypervisor, necesită o instanță completă a sistemului de operare și orice biblioteci de suport. Containerele, pe de altă parte, partajează sistemul de operare cu gazda. Hypervisor este comparabil cu motorul containerului (reprezentat ca Docker pe imagine) în sensul că gestionează ciclul de viață al containerelor. Diferența importantă este că procesele care rulează în interiorul containerelor sunt la fel ca procesele native de pe gazdă și nu introduc cheltuieli generale asociate cu execuția hypervisorului. În plus, aplicațiile pot reutiliza bibliotecile și pot partaja datele între containere.
Deoarece ambele tehnologii au puncte forte diferite, este obișnuit să găsim sisteme care combină mașini virtuale și containere. Un exemplu perfect este un instrument numit Boot2Docker descris în secțiunea de instalare Docker.
Arhitectura Docker
În partea de sus a diagramei arhitecturii există registre. În mod implicit, registrul principal este Docker Hub care găzduiește imagini publice și oficiale. Organizațiile își pot găzdui și registrele private dacă doresc.
În partea dreaptă avem imagini și containere. Imaginile pot fi descărcate din registre în mod explicit ( docker pull imageName
) sau implicit la pornirea unui container. Odată ce imaginea este descărcată, este stocată în cache local.
Containerele sunt exemplele de imagini - sunt ființa vie. Pot exista mai multe containere care rulează pe baza aceleiași imagini.
În centru, se află demonul Docker responsabil pentru crearea, rularea și monitorizarea containerelor. De asemenea, se ocupă de construirea și stocarea imaginilor. În cele din urmă, în partea stângă există un client Docker. Vorbește cu demonul prin HTTP. Socket-urile Unix sunt folosite atunci când sunteți pe aceeași mașină, dar gestionarea de la distanță este posibilă prin API-ul bazat pe HTTP.
Instalarea Docker
Pentru cele mai recente instrucțiuni, trebuie să consultați întotdeauna documentația oficială.
Docker rulează nativ pe Linux, așa că, în funcție de distribuția țintă, ar putea fi la fel de ușor ca sudo apt-get install docker.io
. Consultați documentația pentru detalii. În mod normal, în Linux, înaintează comenzile Docker cu sudo
, dar îl vom omite în acest articol pentru claritate.
Deoarece demonul Docker folosește caracteristici specifice kernel-ului Linux, nu este posibil să rulați Docker nativ în Mac OS sau Windows. În schimb, ar trebui să instalați o aplicație numită Boot2Docker. Aplicația constă dintr-o mașină virtuală VirtualBox, Docker în sine și utilitățile de gestionare Boot2Docker. Puteți urma instrucțiunile oficiale de instalare pentru MacOS și Windows pentru a instala Docker pe aceste platforme.
Folosind Docker
Să începem această secțiune cu un exemplu rapid:
docker run phusion/baseimage echo "Hello Moby Dock. Hello Molly."
Ar trebui să vedem această ieșire:
Hello Moby Dock. Hello Molly.
Cu toate acestea, în culise s-au întâmplat multe mai multe decât ați putea crede:
- Imaginea „phusion/baseimage” a fost descărcată din Docker Hub (dacă nu era deja în memoria cache locală)
- Un container bazat pe această imagine a fost pornit
- Comanda echo a fost executată în container
- Containerul a fost oprit când comanda a ieșit
La prima rulare, este posibil să observați o întârziere înainte ca textul să fie imprimat pe ecran. Dacă imaginea ar fi fost memorată în cache local, totul ar fi durat o fracțiune de secundă. Detaliile despre ultimul container pot fi preluate rulând docker ps -l
:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES af14bec37930 phusion/baseimage:latest "echo 'Hello Moby Do 2 minutes ago Exited (0) 3 seconds ago stoic_bardeen
Următoarea scufundare
După cum vă puteți da seama, rularea unei comenzi simple în Docker este la fel de ușoară ca și rularea directă pe un terminal standard. Pentru a ilustra un caz de utilizare mai practic, în restul acestui articol, vom vedea cum putem utiliza Docker pentru a implementa o aplicație simplă de server web. Pentru a menține lucrurile simple, vom scrie un program Java care gestionează cererile HTTP GET către „/ping” și răspunde cu șirul „pong\n”.
import java.io.IOException; import java.io.OutputStream; import java.net.InetSocketAddress; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; import com.sun.net.httpserver.HttpServer; public class PingPong { public static void main(String[] args) throws Exception { HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0); server.createContext("/ping", new MyHandler()); server.setExecutor(null); server.start(); } static class MyHandler implements HttpHandler { @Override public void handle(HttpExchange t) throws IOException { String response = "pong\n"; t.sendResponseHeaders(200, response.length()); OutputStream os = t.getResponseBody(); os.write(response.getBytes()); os.close(); } } }
Dockerfile
Înainte de a intra și de a crea propria imagine Docker, este o practică bună să verificați mai întâi dacă există una existentă în Docker Hub sau în orice registre private la care aveți acces. De exemplu, în loc să instalăm Java noi înșine, vom folosi o imagine oficială: java:8
.
Pentru a construi o imagine, mai întâi trebuie să decidem asupra unei imagini de bază pe care o vom folosi. Este notat prin instrucțiunea FROM . Aici, este o imagine oficială pentru Java 8 de la Docker Hub. Îl vom copia în fișierul nostru Java lansând o instrucțiune COPY . În continuare, îl vom compila cu RUN . Instrucțiunea EXPOSE indică faptul că imaginea va oferi un serviciu pe un anumit port. ENTRYPOINT este o instrucțiune pe care dorim să o executăm atunci când un container bazat pe această imagine este pornit și CMD indică parametrii impliciti pe care îi vom transmite.

FROM java:8 COPY PingPong.java / RUN javac PingPong.java EXPOSE 8080 ENTRYPOINT ["java"] CMD ["PingPong"]
După salvarea acestor instrucțiuni într-un fișier numit „Dockerfile”, putem construi imaginea Docker corespunzătoare executând:
docker build -t toptal/pingpong .
Documentația oficială pentru Docker are o secțiune dedicată celor mai bune practici privind scrierea Dockerfile.
Rularea Containerelor
Când imaginea a fost construită, o putem aduce la viață ca un container. Există mai multe moduri în care am putea rula containere, dar să începem cu unul simplu:
docker run -d -p 8080:8080 toptal/pingpong
unde -p [port-on-the-host]:[port-in-the-container] denotă porturile mapate pe gazdă și respectiv pe container. În plus, îi spunem lui Docker să ruleze containerul ca un proces demon în fundal, specificând -d . Puteți testa dacă aplicația de server web rulează încercând să accesați „http://localhost:8080/ping”. Rețineți că pe platformele în care este utilizat Boot2docker, va trebui să înlocuiți „localhost” cu adresa IP a mașinii virtuale pe care rulează Docker.
Pe Linux:
curl http://localhost:8080/ping
Pe platformele care necesită Boot2Docker:
curl $(boot2docker ip):8080/ping
Dacă totul merge bine, ar trebui să vedeți răspunsul:
pong
Ura, primul nostru container Docker personalizat este în viață și înoată! De asemenea, am putea porni containerul într-un mod interactiv -i -t . În cazul nostru, vom suprascrie comanda entrypoint , astfel încât ni se va prezenta un terminal bash. Acum putem executa orice comenzi dorim, dar ieșirea din container o va opri:
docker run -i -t --entrypoint="bash" toptal/pingpong
Există mai multe opțiuni disponibile pentru a porni containerele. Să mai acoperim câteva. De exemplu, dacă dorim să păstrăm datele în afara containerului, am putea partaja sistemul de fișiere gazdă cu containerul utilizând -v . În mod implicit, modul de acces este citire-scriere, dar poate fi schimbat în modul doar citire prin adăugarea :ro
la calea de volum intra-container. Volumele sunt deosebit de importante atunci când trebuie să folosim orice informații de securitate, cum ar fi acreditările și cheile private, în interiorul containerelor, care nu ar trebui să fie stocate pe imagine. În plus, ar putea preveni, de asemenea, duplicarea datelor, de exemplu prin maparea depozitului dvs. local Maven la container pentru a vă scuti de descărcarea de două ori pe Internet.
Docker are, de asemenea, capacitatea de a lega containerele între ele. Containerele conectate pot vorbi între ele chiar dacă niciunul dintre porturi nu este expus. Poate fi realizat cu –link other-container-name . Mai jos este un exemplu care combină parametrii menționați mai sus:
docker run -p 9999:8080 --link otherContainerA --link otherContainerB -v /Users/$USER/.m2/repository:/home/user/.m2/repository toptal/pingpong
Alte operațiuni cu containere și imagini
Deloc surprinzător, lista de operațiuni pe care s-ar putea aplica containerelor și imaginilor este destul de lungă. Pentru concizie, să ne uităm la doar câteva dintre ele:
- stop - Oprește un container care rulează.
- start - Pornește un container oprit.
- commit - creează o nouă imagine din modificările unui container.
- rm - Îndepărtează unul sau mai multe recipiente.
- rmi - Elimină una sau mai multe imagini.
- ps - Listează containerele.
- imagini - Listează imagini.
- exec - Rulează o comandă într-un container care rulează.
Ultima comandă ar putea fi deosebit de utilă în scopuri de depanare, deoarece vă permite să vă conectați la un terminal al unui container care rulează:
docker exec -i -t <container-id> bash
Docker Compose pentru lumea microserviciilor
Dacă aveți mai mult decât doar câteva containere interconectate, este logic să utilizați un instrument precum docker-compose. Într-un fișier de configurare, descrieți cum să porniți containerele și cum ar trebui să fie legate între ele. Indiferent de cantitatea de containere implicate și de dependențele acestora, le puteți avea pe toate în funcțiune cu o singură comandă: docker-compose up
.
Docker în sălbăticie
Să ne uităm la trei etape ale ciclului de viață al proiectului și să vedem cum ar putea fi de ajutor balena noastră prietenoasă.
Dezvoltare
Docker vă ajută să vă păstrați mediul de dezvoltare local curat. În loc să aveți mai multe versiuni ale diferitelor servicii instalate, cum ar fi Java, Kafka, Spark, Cassandra etc., puteți doar porni și opri un container necesar atunci când este necesar. Puteți face lucrurile cu un pas mai departe și puteți rula mai multe stive de software una lângă alta, evitând amestecul versiunilor de dependență.
Cu Docker, puteți economisi timp, efort și bani. Dacă proiectul dvs. este foarte complex de configurat, „dockerizați-l”. Treceți prin durerea de a crea o imagine Docker o dată și, din acest punct, toată lumea poate începe un container într-o clipă.
De asemenea, puteți avea un „mediu de integrare” care rulează local (sau pe CI) și puteți înlocui stub-urile cu servicii reale care rulează în containerele Docker.
Testare / Integrare continuă
Cu Dockerfile, este ușor să realizați versiuni reproductibile. Jenkins sau alte soluții CI pot fi configurate pentru a crea o imagine Docker pentru fiecare build. Puteți stoca unele sau toate imaginile într-un registru Docker privat pentru referințe viitoare.
Cu Docker, testați doar ceea ce trebuie testat și eliminați mediul din ecuație. Efectuarea de teste pe un container care rulează poate ajuta la menținerea lucrurilor mult mai previzibile.
O altă caracteristică interesantă a containerelor de software este că este ușor să scoateți mașinile slave cu configurația de dezvoltare identică. Poate fi util în special pentru testarea încărcării implementărilor în cluster.
Productie
Docker poate fi o interfață comună între dezvoltatori și personalul operațional, eliminând o sursă de frecare. De asemenea, încurajează utilizarea aceleiași imagini/binare la fiecare pas al conductei. În plus, posibilitatea de a implementa containerul complet testat fără diferențe de mediu ajută la asigurarea că nu sunt introduse erori în procesul de construire.
Puteți migra fără probleme aplicațiile în producție. Ceva care a fost cândva un proces obositor și fulgerător poate fi acum la fel de simplu ca:
docker stop container-id; docker run new-image
Și dacă ceva nu merge bine la implementarea unei noi versiuni, puteți oricând să reveniți rapid sau să treceți la alt container:
docker stop container-id; docker start other-container-id
… garantat că nu va lăsa nicio mizerie în urmă sau nu va lăsa lucrurile într-o stare inconsistentă.
rezumat
Un rezumat bun a ceea ce face Docker este inclus în propriul său motto: Build, Ship, Run.
- Build - Docker vă permite să vă compuneți aplicația din microservicii, fără a vă face griji cu privire la inconsecvențele dintre mediile de dezvoltare și de producție și fără a vă bloca în nicio platformă sau limbă.
- Ship - Docker vă permite să proiectați întregul ciclu de dezvoltare, testare și distribuție a aplicațiilor și să le gestionați cu o interfață de utilizator consistentă.
- Run - Docker vă oferă posibilitatea de a implementa servicii scalabile în siguranță și fiabil pe o mare varietate de platforme.
Distrează-te înotând cu balenele!
O parte din această lucrare este inspirată dintr-o carte excelentă Using Docker de Adrian Mouat.