Când construiți aplicații Java cu Maven, cum ar fi aplicațiile Spring Boot sau Vert.x, o modalitate populară de a merge este să grupați codul aplicației și toate borcanele sale de dependență într-un singur vas de grăsime. De obicei, pluginul Maven Shade este utilizat în acest scop.

grase

Dar este într-adevăr o idee bună? Când creați un mic serviciu, codul aplicației dvs. va fi compilat în câteva sute de kiloocteți sau câțiva megaocteți de fișiere de clasă. Numai acest lucru va duce la un fișier jar destul de mic.

Dar așteptați până când pluginul de umbră s-a terminat. Acesta va adăuga fișierele de clasă ale tuturor dependențelor dvs. la acest fișier jar. Dintr-o dată, fișierul dvs. de aplicație poate crește cu ușurință la o dimensiune de câteva sute de megaocteți.

Așa cum a menționat Jonathan Haber în marele său articol în urmă cu câțiva ani, s-ar putea argumenta că borcanele nu au fost niciodată menite să fie folosite astfel. În plus, este predispus la erori să agregăm o mulțime de borcane într-un singur fișier.

S-ar putea să existe clase cu același nume și pachet printre dependențele dvs. Care va ajunge în borcanul tău gras?

În opinia mea, problema mai mare este ineficiența. Dacă publicați borcanul de grăsime într-un depozit de artefacte, dependențele incluse vor ocupa spațiu de stocare ca artefacte individuale, în timp ce vor fi stocate și ca parte a borcanului de grăsimi. Nu are prea mult sens dacă mă întrebi.

Construirea unui borcan de grăsime consumă mult timp. Combinarea conținutului tuturor borcanelor de dependență tot timpul în timp ce se ocupă de fișiere de clasă duplicate face ca versiunea dvs. să dureze mai mult decât este necesar. Aceasta este o cantitate considerabilă de I/O de fișiere pe care infrastructura dvs. CI trebuie să o gestioneze în mod regulat, adică pe fiecare construcție.

Rularea unui borcan de grăsime ca aplicație Java este destul de simplă:

Deoarece borcanul de grăsime este autonom, acesta este tot ce aveți nevoie.

Rularea unui fișier jar care necesită borcane suplimentare este mai complicată. Nu puteți specifica -jar împreună cu -cp. Acest lucru nu va funcționa:

Cu toate acestea, un fișier jar poate conține un fișier manifest care, la rândul său, poate defini un drum de clasă dedicat.

Deci soluția pentru noi este să construim un fișier jar subțire cu un manifest adecvat. Acesta din urmă are un traseu de clasă care include toate borcanele noastre de dependență. Din fericire, toate acestea pot fi realizate într-un mod automat printr-un pic de magie maven. Tot ce trebuie să faceți este să înlocuiți pluginul Maven Shade cu alte două pluginuri. Fișierul Docker are nevoie de o singură linie suplimentară.

Așa o faceți:

La început, va trebui să eliminăm pluginul nostru maven-shade, care ar putea arata uite asta:

Pluginul maven-dependency va descărca toate borcanele noastre de dependență în directorul nostru local/target/dependency-jar în timpul fazei pachetului.

Pluginul maven-jar va crea jarul aplicației noastre ca /target/application.jar. În plus, va crea un manifest și va adăuga toate borcanele din directorul/țintă/dependență-borcane către calea noastră de aplicație.

… Vom avea următoarea structură de directoare:

Fișierul application.jar conține un manifest generat care arată astfel:

Lansarea aplicației noastre cu această comandă:

… Va funcționa, atâta timp cât există directorul de dependență-borcane alături de toate dependențele necesare. Maven se va ocupa de asta.

Aspectul directorului nostru/target poate fi utilizat în timpul construirii unei imagini de andocare.

Dockerfile noastre copiază toate dependențele noastre și borcanul aplicației în imagine. La pornirea aplicației, toate dependențele vor fi disponibile.

Există un alt beneficiu pe care îl obținem din utilizarea Docker. Consultați acest paragraf din documentația Docker referitoare la cache-ul său de construire:

Când creați o imagine, Docker parcurge instrucțiunile din fișierul Docker, executând fiecare în ordinea specificată. Pe măsură ce fiecare instrucțiune este examinată, Docker caută o imagine existentă în memoria cache pe care o poate refolosi, mai degrabă decât să creeze o imagine nouă (duplicat).

Deci, atâta timp cât dependențele nu se schimbă, linia

va fi executat de Docker folosind cache-ul său de construire. Acest lucru poate fi văzut în ieșirea de depanare:

După cum puteți vedea, sunt necesare doar modificări minore la construcția dvs. originală și Dockerfile pentru a scăpa de un proces de construire prea lung și ineficient.

Veți scoate încărcarea de pe serverele dvs. CI, iar depozitele dvs. de software vor economisi spațiu de stocare atunci când vor mai trebui să vă păstreze vasele de grăsime.

Un exemplu complet de lucru al tuturor fragmentelor mele de cod poate fi găsit pe Github.