Πριν ένα χαρακτηριστικό μπει στην παραγωγή, στις μέρες μας με πολύπλοκους ενορχηστρωτές και CI/CD, υπάρχει πολύς δρόμος από τη δέσμευση έως τις δοκιμές και την παράδοση. Παλαιότερα, μπορούσατε να ανεβάσετε νέα αρχεία μέσω FTP (κανείς δεν το κάνει πια, σωστά;) και η διαδικασία "ανάπτυξης" διαρκούσε δευτερόλεπτα. Τώρα πρέπει να δημιουργήσετε ένα αίτημα συγχώνευσης και να περιμένετε αρκετό χρόνο για να φτάσει η δυνατότητα στους χρήστες.
Μέρος αυτής της διαδρομής είναι η δημιουργία μιας εικόνας Docker. Μερικές φορές η συναρμολόγηση διαρκεί λεπτά, μερικές φορές δεκάδες λεπτά, κάτι που δύσκολα μπορεί να χαρακτηριστεί κανονικό. Σε αυτό το άρθρο, θα πάρουμε μια απλή εφαρμογή που θα συσκευάσουμε σε μια εικόνα, θα εφαρμόσουμε διάφορες μεθόδους για να επιταχύνουμε την κατασκευή και θα εξετάσουμε τις αποχρώσεις του τρόπου λειτουργίας αυτών των μεθόδων.
Έχουμε καλή εμπειρία στη δημιουργία και υποστήριξη ιστοτόπων πολυμέσων:
Αναπτύσσουμε στο GitLab. Συλλέγουμε εικόνες, τις προωθούμε στο GitLab Registry και τις διαθέτουμε στην παραγωγή. Το μεγαλύτερο πράγμα σε αυτή τη λίστα είναι η συναρμολόγηση εικόνων. Για παράδειγμα: χωρίς βελτιστοποίηση, κάθε δημιουργία backend χρειάστηκε 14 λεπτά.
Στο τέλος, έγινε σαφές ότι δεν μπορούσαμε πια να ζήσουμε έτσι και καθίσαμε να καταλάβουμε γιατί οι εικόνες αργούσαν τόσο πολύ να συλλεχθούν. Ως αποτέλεσμα, καταφέραμε να μειώσουμε τον χρόνο συναρμολόγησης στα 30 δευτερόλεπτα!
Για αυτό το άρθρο, για να μην είμαστε συνδεδεμένοι με το περιβάλλον του Reminder, ας δούμε ένα παράδειγμα συναρμολόγησης μιας άδειας εφαρμογής Angular. Λοιπόν, ας δημιουργήσουμε την εφαρμογή μας:
ng n app
Προσθέστε το PWA σε αυτό (είμαστε προοδευτικοί):
ng add @angular/pwa --project app
Ενώ γίνεται λήψη ενός εκατομμυρίου πακέτων npm, ας δούμε πώς λειτουργεί η εικόνα του docker. Το Docker παρέχει τη δυνατότητα συσκευασίας εφαρμογών και εκτέλεσης σε ένα απομονωμένο περιβάλλον που ονομάζεται κοντέινερ. Χάρη στην απομόνωση, μπορείτε να εκτελέσετε πολλά κοντέινερ ταυτόχρονα σε έναν διακομιστή. Τα κοντέινερ είναι πολύ πιο ελαφριά από τις εικονικές μηχανές επειδή τρέχουν απευθείας στον πυρήνα του συστήματος. Για να τρέξουμε ένα κοντέινερ με την εφαρμογή μας, πρέπει πρώτα να δημιουργήσουμε μια εικόνα στην οποία θα πακετάρουμε όλα όσα είναι απαραίτητα για την εκτέλεση της εφαρμογής μας. Ουσιαστικά, μια εικόνα είναι ένα αντίγραφο του συστήματος αρχείων. Για παράδειγμα, πάρτε το Dockerfile:
FROM node:12.16.2
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build --prod
Ένα Dockerfile είναι ένα σύνολο οδηγιών. Κάνοντας καθεμία από αυτές, το Docker θα αποθηκεύσει τις αλλαγές στο σύστημα αρχείων και θα τις επικαλύψει στις προηγούμενες. Κάθε ομάδα δημιουργεί το δικό της στρώμα. Και η τελική εικόνα είναι στρώματα συνδυασμένα μαζί.
Τι είναι σημαντικό να γνωρίζετε: κάθε επίπεδο Docker μπορεί να αποθηκεύσει προσωρινά. Εάν δεν έχει αλλάξει τίποτα από την τελευταία έκδοση, τότε αντί να εκτελέσει την εντολή, το docker θα πάρει ένα έτοιμο επίπεδο. Δεδομένου ότι η κύρια αύξηση της ταχύτητας κατασκευής θα οφείλεται στη χρήση της κρυφής μνήμης, κατά τη μέτρηση της ταχύτητας κατασκευής θα δώσουμε ιδιαίτερη προσοχή στη δημιουργία μιας εικόνας με μια έτοιμη κρυφή μνήμη. Λοιπόν, βήμα βήμα:
- Διαγράφουμε τις εικόνες τοπικά, έτσι ώστε οι προηγούμενες εκτελέσεις να μην επηρεάσουν τη δοκιμή.
docker rmi $(docker images -q)
- Ξεκινάμε την κατασκευή για πρώτη φορά.
time docker build -t app .
- Αλλάζουμε το αρχείο src/index.html - μιμούμαστε τη δουλειά ενός προγραμματιστή.
- Εκτελούμε την κατασκευή για δεύτερη φορά.
time docker build -t app .
Εάν το περιβάλλον για τη δημιουργία εικόνων έχει ρυθμιστεί σωστά (περισσότερα για αυτό παρακάτω), τότε όταν ξεκινήσει η κατασκευή, το Docker θα έχει ήδη μια δέσμη κρυφών μνήμων επί του σκάφους. Το καθήκον μας είναι να μάθουμε πώς να χρησιμοποιούμε την προσωρινή μνήμη, ώστε η κατασκευή να πηγαίνει όσο το δυνατόν γρηγορότερα. Εφόσον υποθέτουμε ότι η εκτέλεση μιας έκδοσης χωρίς προσωρινή μνήμη συμβαίνει μόνο μία φορά—την πρώτη φορά—μπορούμε επομένως να αγνοήσουμε πόσο αργή ήταν αυτή η πρώτη φορά. Στις δοκιμές, η δεύτερη εκτέλεση της κατασκευής είναι σημαντική για εμάς, όταν οι κρυφές μνήμες έχουν ήδη ζεσταθεί και είμαστε έτοιμοι να ψήσουμε το κέικ μας. Ωστόσο, ορισμένες συμβουλές θα επηρεάσουν και την πρώτη κατασκευή.
Ας βάλουμε το Dockerfile που περιγράφεται παραπάνω στον φάκελο του έργου και ας ξεκινήσουμε την κατασκευή. Όλες οι λίστες έχουν συμπυκνωθεί για ευκολία στην ανάγνωση.
$ time docker build -t app .
Sending build context to Docker daemon 409MB
Step 1/5 : FROM node:12.16.2
Status: Downloaded newer image for node:12.16.2
Step 2/5 : WORKDIR /app
Step 3/5 : COPY . .
Step 4/5 : RUN npm ci
added 1357 packages in 22.47s
Step 5/5 : RUN npm run build --prod
Date: 2020-04-16T19:20:09.664Z - Hash: fffa0fddaa3425c55dd3 - Time: 37581ms
Successfully built c8c279335f46
Successfully tagged app:latest
real 5m4.541s
user 0m0.000s
sys 0m0.000s
Αλλάζουμε τα περιεχόμενα του src/index.html και το τρέχουμε για δεύτερη φορά.
$ time docker build -t app .
Sending build context to Docker daemon 409MB
Step 1/5 : FROM node:12.16.2
Step 2/5 : WORKDIR /app
---> Using cache
Step 3/5 : COPY . .
Step 4/5 : RUN npm ci
added 1357 packages in 22.47s
Step 5/5 : RUN npm run build --prod
Date: 2020-04-16T19:26:26.587Z - Hash: fffa0fddaa3425c55dd3 - Time: 37902ms
Successfully built 79f335df92d3
Successfully tagged app:latest
real 3m33.262s
user 0m0.000s
sys 0m0.000s
Για να δούμε αν έχουμε την εικόνα, εκτελέστε την εντολή docker images
:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 79f335df92d3 About a minute ago 1.74GB
Πριν από τη δημιουργία, το docker παίρνει όλα τα αρχεία στο τρέχον πλαίσιο και τα στέλνει στον δαίμονά του Sending build context to Docker daemon 409MB
. Το περιβάλλον δημιουργίας καθορίζεται ως το τελευταίο όρισμα στην εντολή build. Στην περίπτωσή μας, αυτός είναι ο τρέχων κατάλογος - “.”, - και το Docker σύρει όλα όσα έχουμε σε αυτόν τον φάκελο. 409 MB είναι πολλά: ας σκεφτούμε πώς να το διορθώσουμε.
Μείωση του πλαισίου
Για να μειώσετε το πλαίσιο, υπάρχουν δύο επιλογές. Ή τοποθετήστε όλα τα αρχεία που απαιτούνται για τη συναρμολόγηση σε έναν ξεχωριστό φάκελο και τοποθετήστε το πλαίσιο του docker σε αυτόν τον φάκελο. Αυτό μπορεί να μην είναι πάντα βολικό, επομένως είναι δυνατό να καθοριστούν εξαιρέσεις: τι δεν πρέπει να σύρεται στο πλαίσιο. Για να το κάνετε αυτό, βάλτε το αρχείο .dockerignore στο έργο και υποδείξτε τι δεν χρειάζεται για την κατασκευή:
.git
/node_modules
και εκτελέστε ξανά την κατασκευή:
$ time docker build -t app .
Sending build context to Docker daemon 607.2kB
Step 1/5 : FROM node:12.16.2
Step 2/5 : WORKDIR /app
---> Using cache
Step 3/5 : COPY . .
Step 4/5 : RUN npm ci
added 1357 packages in 22.47s
Step 5/5 : RUN npm run build --prod
Date: 2020-04-16T19:33:54.338Z - Hash: fffa0fddaa3425c55dd3 - Time: 37313ms
Successfully built 4942f010792a
Successfully tagged app:latest
real 1m47.763s
user 0m0.000s
sys 0m0.000s
Τα 607.2 KB είναι πολύ καλύτερα από τα 409 MB. Μειώσαμε επίσης το μέγεθος της εικόνας από 1.74 σε 1.38 GB:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 4942f010792a 3 minutes ago 1.38GB
Ας προσπαθήσουμε να μειώσουμε περαιτέρω το μέγεθος της εικόνας.
Χρησιμοποιούμε Alpine
Ένας άλλος τρόπος για να εξοικονομήσετε μέγεθος εικόνας είναι να χρησιμοποιήσετε μια μικρή γονική εικόνα. Η γονική εικόνα είναι η εικόνα με βάση την οποία προετοιμάζεται η εικόνα μας. Το κάτω στρώμα καθορίζεται από την εντολή FROM
στο Dockerfile. Στην περίπτωσή μας, χρησιμοποιούμε μια εικόνα που βασίζεται στο Ubuntu που έχει ήδη εγκαταστήσει nodejs. Και ζυγίζει...
$ docker images -a | grep node
node 12.16.2 406aa3abbc6c 17 minutes ago 916MB
... σχεδόν ένα gigabyte. Μπορείτε να μειώσετε σημαντικά την ένταση χρησιμοποιώντας μια εικόνα που βασίζεται στο Alpine Linux. Το Alpine είναι ένα πολύ μικρό Linux. Η εικόνα docker για nodejs που βασίζονται σε alpine ζυγίζει μόνο 88.5 MB. Ας αντικαταστήσουμε λοιπόν τη ζωηρή εικόνα μας στα σπίτια:
FROM node:12.16.2-alpine3.11
RUN apk --no-cache --update --virtual build-dependencies add
python
make
g++
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build --prod
Έπρεπε να εγκαταστήσουμε κάποια πράγματα που είναι απαραίτητα για την κατασκευή της εφαρμογής. Ναι, το Angular δεν δημιουργεί χωρίς Python ¯(°_o)/¯
Αλλά το μέγεθος της εικόνας μειώθηκε στα 150 MB:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest aa031edc315a 22 minutes ago 761MB
Ας πάμε ακόμα παραπέρα.
Συναρμολόγηση πολλαπλών σταδίων
Δεν είναι ό,τι είναι στην εικόνα αυτό που χρειαζόμαστε στην παραγωγή.
$ docker run app ls -lah
total 576K
drwxr-xr-x 1 root root 4.0K Apr 16 19:54 .
drwxr-xr-x 1 root root 4.0K Apr 16 20:00 ..
-rwxr-xr-x 1 root root 19 Apr 17 2020 .dockerignore
-rwxr-xr-x 1 root root 246 Apr 17 2020 .editorconfig
-rwxr-xr-x 1 root root 631 Apr 17 2020 .gitignore
-rwxr-xr-x 1 root root 181 Apr 17 2020 Dockerfile
-rwxr-xr-x 1 root root 1020 Apr 17 2020 README.md
-rwxr-xr-x 1 root root 3.6K Apr 17 2020 angular.json
-rwxr-xr-x 1 root root 429 Apr 17 2020 browserslist
drwxr-xr-x 3 root root 4.0K Apr 16 19:54 dist
drwxr-xr-x 3 root root 4.0K Apr 17 2020 e2e
-rwxr-xr-x 1 root root 1015 Apr 17 2020 karma.conf.js
-rwxr-xr-x 1 root root 620 Apr 17 2020 ngsw-config.json
drwxr-xr-x 1 root root 4.0K Apr 16 19:54 node_modules
-rwxr-xr-x 1 root root 494.9K Apr 17 2020 package-lock.json
-rwxr-xr-x 1 root root 1.3K Apr 17 2020 package.json
drwxr-xr-x 5 root root 4.0K Apr 17 2020 src
-rwxr-xr-x 1 root root 210 Apr 17 2020 tsconfig.app.json
-rwxr-xr-x 1 root root 489 Apr 17 2020 tsconfig.json
-rwxr-xr-x 1 root root 270 Apr 17 2020 tsconfig.spec.json
-rwxr-xr-x 1 root root 1.9K Apr 17 2020 tslint.json
Με docker run app ls -lah
λανσάραμε ένα κοντέινερ με βάση την εικόνα μας app
και εκτέλεσε την εντολή σε αυτό ls -lah
, μετά την οποία το κοντέινερ ολοκλήρωσε τις εργασίες του.
Στην παραγωγή χρειαζόμαστε μόνο έναν φάκελο dist
. Σε αυτήν την περίπτωση, τα αρχεία με κάποιο τρόπο πρέπει να δίνονται έξω. Μπορείτε να εκτελέσετε κάποιο διακομιστή HTTP σε nodejs. Αλλά θα το κάνουμε πιο εύκολο. Μαντέψτε μια ρωσική λέξη που έχει τέσσερα γράμματα "y". Σωστά! Ynzhynyksy. Ας πάρουμε μια εικόνα με το nginx, βάλουμε ένα φάκελο μέσα dist
και μια μικρή διαμόρφωση:
server {
listen 80 default_server;
server_name localhost;
charset utf-8;
root /app/dist;
location / {
try_files $uri $uri/ /index.html;
}
}
Η κατασκευή πολλών σταδίων θα μας βοηθήσει να τα κάνουμε όλα αυτά. Ας αλλάξουμε το Dockerfile μας:
FROM node:12.16.2-alpine3.11 as builder
RUN apk --no-cache --update --virtual build-dependencies add
python
make
g++
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build --prod
FROM nginx:1.17.10-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/static.conf /etc/nginx/conf.d
COPY --from=builder /app/dist/app .
Τώρα έχουμε δύο οδηγίες FROM
στο Dockerfile, καθένα από αυτά εκτελεί ένα διαφορετικό βήμα κατασκευής. Καλέσαμε τον πρώτο builder
, αλλά ξεκινώντας από το τελευταίο ΑΠΟ, θα ετοιμαστεί η τελική μας εικόνα. Το τελευταίο βήμα είναι να αντιγράψουμε το τεχνούργημα της συναρμολόγησης μας στο προηγούμενο βήμα στην τελική εικόνα με το nginx. Το μέγεθος της εικόνας έχει μειωθεί σημαντικά:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 2c6c5da07802 29 minutes ago 36MB
Ας τρέξουμε το κοντέινερ με την εικόνα μας και βεβαιωθούμε ότι όλα λειτουργούν:
docker run -p8080:80 app
Χρησιμοποιώντας την επιλογή -p8080:80, προωθήσαμε τη θύρα 8080 στον κεντρικό υπολογιστή μας στη θύρα 80 μέσα στο κοντέινερ όπου εκτελείται το nginx. Ανοιγμα σε πρόγραμμα περιήγησης
Η μείωση του μεγέθους της εικόνας από 1.74 GB σε 36 MB μειώνει σημαντικά τον χρόνο που απαιτείται για την παράδοση της εφαρμογής σας στην παραγωγή. Ας επιστρέψουμε όμως στην ώρα της συναρμολόγησης.
$ time docker build -t app .
Sending build context to Docker daemon 608.8kB
Step 1/11 : FROM node:12.16.2-alpine3.11 as builder
Step 2/11 : RUN apk --no-cache --update --virtual build-dependencies add python make g++
---> Using cache
Step 3/11 : WORKDIR /app
---> Using cache
Step 4/11 : COPY . .
Step 5/11 : RUN npm ci
added 1357 packages in 47.338s
Step 6/11 : RUN npm run build --prod
Date: 2020-04-16T21:16:03.899Z - Hash: fffa0fddaa3425c55dd3 - Time: 39948ms
---> 27f1479221e4
Step 7/11 : FROM nginx:stable-alpine
Step 8/11 : WORKDIR /app
---> Using cache
Step 9/11 : RUN rm /etc/nginx/conf.d/default.conf
---> Using cache
Step 10/11 : COPY nginx/static.conf /etc/nginx/conf.d
---> Using cache
Step 11/11 : COPY --from=builder /app/dist/app .
Successfully built d201471c91ad
Successfully tagged app:latest
real 2m17.700s
user 0m0.000s
sys 0m0.000s
Αλλαγή της σειράς των επιπέδων
Τα πρώτα τρία μας βήματα αποθηκεύτηκαν στην κρυφή μνήμη (υπόδειξη Using cache
). Στο τέταρτο βήμα, αντιγράφονται όλα τα αρχεία έργου και στο πέμπτο βήμα εγκαθίστανται οι εξαρτήσεις RUN npm ci
- έως και 47.338 δευτ. Γιατί να επανεγκαθιστάτε τις εξαρτήσεις κάθε φορά, εάν αλλάζουν πολύ σπάνια; Ας καταλάβουμε γιατί δεν είχαν αποθηκευτεί προσωρινά. Το θέμα είναι ότι το Docker θα ελέγχει επίπεδο προς στρώμα για να δει εάν η εντολή και τα αρχεία που σχετίζονται με αυτήν έχουν αλλάξει. Στο τέταρτο βήμα, αντιγράφουμε όλα τα αρχεία του έργου μας, και μεταξύ αυτών, φυσικά, υπάρχουν αλλαγές, οπότε ο Docker όχι μόνο δεν παίρνει αυτό το επίπεδο από την προσωρινή μνήμη, αλλά και όλα τα επόμενα! Ας κάνουμε μερικές μικρές αλλαγές στο Dockerfile.
FROM node:12.16.2-alpine3.11 as builder
RUN apk --no-cache --update --virtual build-dependencies add
python
make
g++
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build --prod
FROM nginx:1.17.10-alpine
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx/static.conf /etc/nginx/conf.d
COPY --from=builder /app/dist/app .
Αρχικά, το package.json και το package-lock.json αντιγράφονται, στη συνέχεια εγκαθίστανται οι εξαρτήσεις και μόνο μετά από αυτό αντιγράφεται ολόκληρο το έργο. Σαν άποτέλεσμα:
$ time docker build -t app .
Sending build context to Docker daemon 608.8kB
Step 1/12 : FROM node:12.16.2-alpine3.11 as builder
Step 2/12 : RUN apk --no-cache --update --virtual build-dependencies add python make g++
---> Using cache
Step 3/12 : WORKDIR /app
---> Using cache
Step 4/12 : COPY package*.json ./
---> Using cache
Step 5/12 : RUN npm ci
---> Using cache
Step 6/12 : COPY . .
Step 7/12 : RUN npm run build --prod
Date: 2020-04-16T21:29:44.770Z - Hash: fffa0fddaa3425c55dd3 - Time: 38287ms
---> 1b9448c73558
Step 8/12 : FROM nginx:stable-alpine
Step 9/12 : WORKDIR /app
---> Using cache
Step 10/12 : RUN rm /etc/nginx/conf.d/default.conf
---> Using cache
Step 11/12 : COPY nginx/static.conf /etc/nginx/conf.d
---> Using cache
Step 12/12 : COPY --from=builder /app/dist/app .
Successfully built a44dd7c217c3
Successfully tagged app:latest
real 0m46.497s
user 0m0.000s
sys 0m0.000s
46 δευτερόλεπτα αντί για 3 λεπτά - πολύ καλύτερα! Η σωστή σειρά των επιπέδων είναι σημαντική: πρώτα αντιγράφουμε ό,τι δεν αλλάζει, μετά τι αλλάζει σπάνια και τέλος τι αλλάζει συχνά.
Στη συνέχεια, λίγα λόγια για τη συναρμολόγηση εικόνων σε συστήματα CI/CD.
Χρήση προηγούμενων εικόνων για προσωρινή μνήμη
Εάν χρησιμοποιήσουμε κάποιο είδος λύσης SaaS για την κατασκευή, τότε η τοπική προσωρινή μνήμη Docker μπορεί να είναι καθαρή και φρέσκια. Για να δώσετε στο docker ένα μέρος για να πάρει τα ψημένα στρώματα, δώστε του την προηγούμενη χτισμένη εικόνα.
Ας πάρουμε ένα παράδειγμα δημιουργίας της εφαρμογής μας στο GitHub Actions. Χρησιμοποιούμε αυτήν τη διαμόρφωση
on:
push:
branches:
- master
name: Test docker build
jobs:
deploy:
name: Build
runs-on: ubuntu-latest
env:
IMAGE_NAME: docker.pkg.github.com/${{ github.repository }}/app
IMAGE_TAG: ${{ github.sha }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to GitHub Packages
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $TOKEN
- name: Build
run: |
docker build
-t $IMAGE_NAME:$IMAGE_TAG
-t $IMAGE_NAME:latest
.
- name: Push image to GitHub Packages
run: |
docker push $IMAGE_NAME:latest
docker push $IMAGE_NAME:$IMAGE_TAG
- name: Logout
run: |
docker logout docker.pkg.github.com
Η εικόνα συναρμολογείται και προωθείται στα πακέτα GitHub σε δύο λεπτά και 20 δευτερόλεπτα:
Τώρα ας αλλάξουμε την κατασκευή έτσι ώστε να χρησιμοποιείται μια κρυφή μνήμη με βάση τις προηγούμενες χτισμένες εικόνες:
on:
push:
branches:
- master
name: Test docker build
jobs:
deploy:
name: Build
runs-on: ubuntu-latest
env:
IMAGE_NAME: docker.pkg.github.com/${{ github.repository }}/app
IMAGE_TAG: ${{ github.sha }}
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Login to GitHub Packages
env:
TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $TOKEN
- name: Pull latest images
run: |
docker pull $IMAGE_NAME:latest || true
docker pull $IMAGE_NAME-builder-stage:latest || true
- name: Images list
run: |
docker images
- name: Build
run: |
docker build
--target builder
--cache-from $IMAGE_NAME-builder-stage:latest
-t $IMAGE_NAME-builder-stage
.
docker build
--cache-from $IMAGE_NAME-builder-stage:latest
--cache-from $IMAGE_NAME:latest
-t $IMAGE_NAME:$IMAGE_TAG
-t $IMAGE_NAME:latest
.
- name: Push image to GitHub Packages
run: |
docker push $IMAGE_NAME-builder-stage:latest
docker push $IMAGE_NAME:latest
docker push $IMAGE_NAME:$IMAGE_TAG
- name: Logout
run: |
docker logout docker.pkg.github.com
Πρώτα πρέπει να σας πούμε γιατί εκκινούνται δύο εντολές build
. Το γεγονός είναι ότι σε μια πολυβάθμια συναρμολόγηση η εικόνα που προκύπτει θα είναι ένα σύνολο στρωμάτων από το τελευταίο στάδιο. Σε αυτήν την περίπτωση, τα επίπεδα από τα προηγούμενα επίπεδα δεν θα συμπεριληφθούν στην εικόνα. Επομένως, όταν χρησιμοποιείτε την τελική εικόνα από μια προηγούμενη έκδοση, το Docker δεν θα μπορεί να βρει έτοιμα επίπεδα για τη δημιουργία της εικόνας με nodejs (στάδιο δημιουργίας). Για να λυθεί αυτό το πρόβλημα, δημιουργείται μια ενδιάμεση εικόνα $IMAGE_NAME-builder-stage
και προωθείται στα πακέτα GitHub ώστε να μπορεί να χρησιμοποιηθεί σε μια επόμενη έκδοση ως πηγή προσωρινής μνήμης.
Ο συνολικός χρόνος συναρμολόγησης μειώθηκε σε ενάμιση λεπτό. Μισό λεπτό δαπανάται για να τραβήξετε προηγούμενες εικόνες.
Προαπεικόνιση
Ένας άλλος τρόπος για να λύσετε το πρόβλημα μιας καθαρής προσωρινής μνήμης Docker είναι να μετακινήσετε μερικά από τα επίπεδα σε ένα άλλο Dockerfile, να το δημιουργήσετε ξεχωριστά, να το σπρώξετε στο Μητρώο κοντέινερ και να το χρησιμοποιήσετε ως γονικό.
Δημιουργούμε τη δική μας εικόνα nodejs για να δημιουργήσουμε μια εφαρμογή Angular. Δημιουργήστε Dockerfile.node στο έργο
FROM node:12.16.2-alpine3.11
RUN apk --no-cache --update --virtual build-dependencies add
python
make
g++
Συλλέγουμε και προωθούμε μια δημόσια εικόνα στο Docker Hub:
docker build -t exsmund/node-for-angular -f Dockerfile.node .
docker push exsmund/node-for-angular:latest
Τώρα στο κύριο αρχείο μας Docker χρησιμοποιούμε την τελική εικόνα:
FROM exsmund/node-for-angular:latest as builder
...
Στο παράδειγμά μας, ο χρόνος δημιουργίας δεν μειώθηκε, αλλά οι προ-χτισμένες εικόνες μπορεί να είναι χρήσιμες εάν έχετε πολλά έργα και πρέπει να εγκαταστήσετε τις ίδιες εξαρτήσεις σε καθένα από αυτά.
Εξετάσαμε διάφορες μεθόδους για την επιτάχυνση της δημιουργίας εικόνων docker. Εάν θέλετε η ανάπτυξη να γίνει γρήγορα, δοκιμάστε να χρησιμοποιήσετε αυτό στο έργο σας:
- μείωση του πλαισίου·
- χρήση μικρών εικόνων γονέα.
- πολυβάθμια συναρμολόγηση?
- αλλαγή της σειράς των εντολών στο Dockerfile για αποτελεσματική χρήση της κρυφής μνήμης.
- εγκατάσταση κρυφής μνήμης σε συστήματα CI/CD.
- προκαταρκτική δημιουργία εικόνων.
Ελπίζω ότι το παράδειγμα θα καταστήσει σαφέστερο τον τρόπο λειτουργίας του Docker και θα μπορέσετε να διαμορφώσετε βέλτιστα την ανάπτυξή σας. Για να παίξετε με τα παραδείγματα από το άρθρο, έχει δημιουργηθεί ένα αποθετήριο
Πηγή: www.habr.com