Simon Hawe

20 noiembrie 2019 · 7 min de citire

Vă amintiți acele zile în care ați scris un software minunat, dar nu ați putut să-l instalați pe computerul altcuiva sau s-a prăbușit acolo? Deși aceasta nu este niciodată o experiență plăcută, am putea spune întotdeauna

docker

În zilele noastre, asta nu mai este o scuză din cauza containerizării.

Pe scurt, cu containerizarea, vă împachetați aplicația și toate dependențele necesare într-o imagine. La execuție, rulați acea imagine ca un container. Cu aceasta, nu trebuie să vă deranjați cu sistemul altei persoane pentru ca software-ul dvs. să ruleze. Containerul și, prin urmare, software-ul dvs. ar trebui să ruleze oriunde, dacă rulează pe mașină. Acest lucru este util și pentru oamenii de știință de date atunci când implementează modele care depind de diferite pachete și versiuni ale acestora. Pentru mine, oamenii de știință ai datelor trebuie să știe cum să creeze imagini și containere.

După cum știți cu toții, Docker este jucătorul principal în acest domeniu, iar imaginile Docker sunt omniprezente. Acest lucru este minunat, deoarece puteți începe, de exemplu, baze de date din diferite versiuni, unul lângă altul, fără probleme. Crearea împreună a imaginilor pentru aplicațiile dvs. este, de asemenea, foarte simplă. Acest lucru se datorează numărului mare de imagini de bază și limbajului de definiție simplu. Cu toate acestea, atunci când hackezi imagini împreună fără să știi ce faci, ai două probleme.

  1. Veți pierde spațiu pe disc pe măsură ce imaginile dvs. se îngrașă inutil.
  2. Pierzi timpul așteptând construcții care durează prea mult.

În acest articol, vreau să vă arăt cum puteți atenua aceste două probleme. Din fericire, acest lucru necesită doar să cunoașteți câteva trucuri și tehnici oferite de Docker. Pentru a face tutorialul distractiv și util, vă arăt cum să împachetați o aplicație Python într-o imagine Docker. Puteți găsi tot codul la care se face referire mai jos în depozitul meu Github.

Sunteți gata? Sa incepem.

Să presupunem că toate codurile noastre trăiesc într-un singur fișier Python main.py. Deoarece suntem copii mișto, folosim cea mai recentă și mai bună versiune Python, care este 3.8 în momentul scrierii acestui articol. Aplicația noastră este doar un simplu server web și depinde de panda, fastapi și uvicorn. Stocăm dependențele într-un fișier requirements.txt. La nivel local, dezvoltăm aplicația într-un mediu virtual. Acest mediu se află într-un folder numit .venv în același folder cu codul (acest lucru devine important în curând). Acum, decidem să împachetăm toate acestea într-o imagine Docker. Pentru a face acest lucru, tot ce trebuie să facem este

  1. Utilizați o imagine de bază cu Python 3.8 disponibil.
  2. Copiați peste cod și fișierul de cerințe.
  3. Instalați cerințele și dependențele în imagine.
  4. Expuneți o comandă care rulează aplicația noastră

Prima versiune a imaginii noastre Docker arată

În afară de codul și cerințele noastre, trebuie să instalăm GCC deoarece FastApi necesită acest lucru la instalare. Ne construim imaginea prin

Dimensiunea acestei imagini este de aproximativ 683 MB și durează aproximativ un minut pentru ao construi (cu excepția descărcării imaginii de bază). Să vedem cum putem reduce acest lucru.

Imagine de bază

În ceea ce privește imaginea de bază, am făcut deja o alegere conștientă folosind Python slim. De ce am ales exact asta?

Aș fi putut lua de exemplu o imagine Ubuntu completă sau CentOS, care ar duce la o dimensiune a imaginii> 1 GB. Dar, deoarece am nevoie doar de Python, nu există niciun motiv pentru a instala toate acestea.

La capătul inferior al dimensiunii imaginii, am putea lua python: 3.8.0- alpin. Dar, codul meu se bazează pe panda, care este o durere de instalat pe alpine. Alpine are, de asemenea, probleme legate de stabilitate și securitate. În plus, slim este doar

80MB mai mare decât alpin, ceea ce este încă bine. Pentru mai multe informații despre cum să alegeți imaginea Python optimă, trimit cititorul interesat la acest articol.

Construiți context

Când creați imaginea, prima linie tipărită pe consola dvs. spune: Trimiterea contextului de construire la daemonul Docker. Pe computerul meu, acest lucru a durat aproximativ 5 secunde și s-au trimis 154 MB. Ce se intampla aici? Docker copiază peste toate fișierele și folderele care se află în contextul de construire, în daemon. Aici, contextul de construire este directorul în care este stocat fișierul Docker. Deoarece avem nevoie doar de două fișiere text, 154 MB sună destul de mult, nu-i așa? Motivul este că Docker copiază peste orice, de exemplu, folderul .venv care conține mediul virtual sau folderul .git.

Pentru a remedia acest lucru, trebuie doar să adăugați un fișier numit .dockerignore lângă fișierul Docker. În acest fișier, listați rând cu rând ceea ce Docker nu ar trebui să copieze. Este ca ceea ce face git cu fișierul .gitignore. Ca un mic exemplu, să presupunem că avem în dosar câteva fișiere Excel și PNG-uri pe care nu dorim să le copiem. Fișierul .dockerignore arată

În exemplul nostru, după ce am adăugat acest fișier, „trimiterea contextului de construire la andocare” durează doar câteva milisecunde și sunt trimise doar 7,2 kb. Am redus dimensiunea imaginii de la 683 Mb la 529 Mb, care este aproximativ dimensiunea contextului de construcție anterior. Grozav! Adăugarea unui fișier .dockerignore contribuie la accelerarea ambelor construiește și reducătoare marimea imaginii.

Layer Caching

După cum am spus deja, durează aproximativ 60 de secunde pentru a construi această imagine pe mașina mea. De cele mai multe ori, estimez

99,98%, este utilizat pentru instalarea cerințelor și dependențelor. S-ar putea să presupuneți că nu există mult loc de îmbunătățire aici. Dar există când trebuie să construiești imaginea frecvent! De ce? Docker poate folosi Layer Caching.

Fiecare linie dintr-un fișier Docker reprezintă un strat. Adăugând/eliminând ceva de pe linie sau schimbând un fișier sau folder la care face referire, schimbați stratul. Când se întâmplă acest lucru, acest strat și toate straturile de mai jos vor fi reconstruite. În caz contrar, Docker folosește o versiune cache a stratului respectiv. Pentru a exploata acest lucru, ar trebui să vă structurați fișierele Docker astfel încât

  1. Straturile care nu se schimbă des ar trebui să apară aproape de începutul fișierului Docker.Instalarea compilatorului este un bun exemplu aici.
  2. Straturile care se schimbă adesea ar trebui să apară aproape de sfârșitul fișierului Docker.Copierea codului sursă este exemplul perfect aici.

Misto. Destul de teorie, să revenim la exemplul nostru.

Să presupunem că nu modificați cerințele și că vă actualizați doar codul. Acest lucru este destul de comun atunci când se dezvoltă software. Acum, de fiecare dată când vă creați imaginea, aceste dependențe urâte sunt reinstalate. Construirea imaginii necesită întotdeauna aceeași perioadă de timp. Enervant! Nu folosim încă cache.

Aici vine noul fișier Docker magic care vă rezolvă problema

Nu pare foarte magic și diferit, nu? Singurul lucru pe care l-am făcut este să instalăm mai întâi GCC și să separăm copierea cerințelor și copierea codului sursă.

CCG și dependențele se schimbă foarte rar. De aceea acest strat apare acum foarte devreme. Cerințele se schimbă, de asemenea, lent, dar mai frecvent decât GCC. De aceea acest strat vine după cel GCC. Codul nostru sursă se schimbă foarte des. În consecință, copierea acestuia se întâmplă târziu. Acum, când modificăm codul sursă și reconstruim imaginea, dependențele nu sunt reinstalate, deoarece Docker folosește straturile cache. Reconstruirea acum nu durează aproape deloc. Este minunat, deoarece putem petrece mai mult timp testând și executând aplicația noastră!

Construiri în mai multe etape

În imaginea noastră de exemplu, trebuie să instalăm GCC pentru a instala FastApi și uvicorn. Dar, pentru a rula aplicația nu avem nevoie de un compilator. Acum imaginați-vă că nu aveți nevoie doar de GCC, ci și de alte programe precum Git sau CMake sau NPM sau ... Imaginea dvs. de producție devine din ce în ce mai grasă.

Construiți în mai multe etape pentru salvarea noastră!

Cu versiunile cu mai multe etape, puteți defini diferite imagini în același fișier Docker. Fiecare imagine efectuează un pas diferit. Puteți copia fișiere și artefacte produse de la o imagine la alta. Cel mai frecvent, aveți o imagine pentru a vă crea aplicația și alta pentru a o rula. Tot ce trebuie să faceți este să copiați artefactele de construcție și dependențele din imaginea de construire în imaginea aplicației.

Pentru exemplul nostru, arată așa

Când construim acest lucru, ajungem la o dimensiune finală a imaginii de producție de 353 MB. Aceasta este aproximativ jumătate din dimensiunea primei noastre versiuni. Felicitări, nu prea rău. Amintiți-vă cu cât imaginea de producție devine mai mică, cu atât este mai bună!

Ca o notă laterală, construcțiile în mai multe etape sporesc, de asemenea, securitatea. Hui, de ce este asta? Să presupunem că aveți nevoie de un secret, cum ar fi o cheie SSH, pentru a accesa anumite resurse la momentul construirii. Chiar dacă ștergeți acel secret într-un strat ulterior, acesta este încă acolo în straturile anterioare. Aceasta înseamnă că cineva cu acces la imaginea dvs. poate obține acel secret. Cu versiunile în mai multe etape, copiați numai artefactele de rulare necesare. În consecință, imaginea de producție nu vede niciodată secretul și ați rezolvat problema.

Există multe mai multe detalii despre versiunile în mai multe etape și trimit cititorul la acest articol și la acest articol.

În acest articol, v-am arătat câteva sfaturi și trucuri ușoare despre cum puteți crea imagini Docker mai mici, care sunt construite mai repede. Tine minte

  • Adăugați întotdeauna un fișier .dockerignore.
  • Gândiți-vă la ordinea straturilor dvs. și ordonați-le de la acțiuni care se schimbă lent până la schimbări rapide.
  • Încercați să utilizați și să exploatați versiunile în mai multe etape.

Sper că acest lucru vă va economisi spațiu și timp pe disc în viitor.

Vă mulțumesc că ați urmărit această postare. Ca întotdeauna, nu ezitați să mă contactați pentru întrebări, comentarii sau sugestii.