Foardat in funksje yn produksje komt, yn dizze dagen fan komplekse orkestrators en CI / CD, is d'r in lange wei te gean fan commit nei tests en levering. Earder koenen jo nije bestannen uploade fia FTP (gjinien docht dat mear, krekt?), En it proses "ynset" naam sekonden. No moatte jo in fúzjefersyk oanmeitsje en in lange tiid wachtsje foar de funksje om brûkers te berikken.
In diel fan dit paad is it bouwen fan in Docker-ôfbylding. Soms duorret de gearstalling minuten, soms tsientallen minuten, wat amper normaal neamd wurde kin. Yn dit artikel sille wy in ienfâldige applikaasje nimme dy't wy yn in ôfbylding ferpakke, ferskate metoaden tapasse om de bou te fersnellen, en sjogge nei de nuânses fan hoe't dizze metoaden wurkje.
Wy hawwe goede ûnderfining yn it meitsjen en stypjen fan mediawebsiden:
Wy ynsette nei GitLab. Wy sammelje ôfbyldings, triuwe se nei GitLab Registry en rôlje se út nei produksje. It langste ding op dizze list is it gearstallen fan ôfbyldings. Bygelyks: sûnder optimalisaasje naam elke backend-build 14 minuten.
Uteinlik waard dúdlik dat wy sa net mear libje koene, en wy sieten om út te finen wêrom't de bylden sa lang duorre om te sammeljen. As gefolch hawwe wy it slagge om de gearstallingstiid te ferminderjen nei 30 sekonden!
Litte wy foar dit artikel, om net bûn te wêzen oan 'e omjouwing fan Reminder, nei in foarbyld sjen fan it gearstallen fan in lege Angular-applikaasje. Dat, litte wy ús applikaasje oanmeitsje:
ng n app
Foegje PWA ta (wy binne progressive):
ng add @angular/pwa --project app
Wylst in miljoen npm-pakketten wurde ynladen, litte wy útfine hoe't de docker-ôfbylding wurket. Docker biedt de mooglikheid om applikaasjes te pakken en út te fieren yn in isolearre omjouwing neamd in kontener. Mei tank oan isolaasje kinne jo in protte konteners tagelyk op ien server útfiere. Containers binne folle lichter as firtuele masines, om't se direkt op 'e systeemkernel rinne. Om in kontener mei ús applikaasje út te fieren, moatte wy earst in ôfbylding meitsje wêryn wy alles sille ferpakke dat nedich is foar ús applikaasje om te rinnen. Yn essinsje is in ôfbylding in kopy fan it bestânsysteem. Nim bygelyks de Dockerfile:
FROM node:12.16.2
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build --prod
In Dockerfile is in set fan ynstruksjes; Troch elk fan har te dwaan, sil Docker de wizigingen opslaan yn it bestânsysteem en se oerlaapje op 'e foarige. Elk team makket syn eigen laach. En de ôfmakke ôfbylding is lagen kombinearre.
Wat wichtich is om te witten: elke Docker-laach kin cache. As neat is feroare sûnt de lêste build, dan sil de docker ynstee fan it kommando útfiere, in klear makke laach nimme. Om't de wichtichste ferheging fan bousnelheid komt troch it gebrûk fan 'e cache, sille wy by it mjitten fan bousnelheid spesjaal omtinken jaan oan it bouwen fan in ôfbylding mei in klearmakke cache. Dus, stap foar stap:
- Wy wiskje de ôfbyldings lokaal sadat eardere runs gjin ynfloed hawwe op de test.
docker rmi $(docker images -q)
- Wy lansearje de bou foar it earst.
time docker build -t app .
- Wy feroarje de triem src/index.html - wy imitearje it wurk fan in programmeur.
- Wy rinne de bou in twadde kear.
time docker build -t app .
As de omjouwing foar it bouwen fan ôfbyldings goed is ynsteld (mear oer dat hjirûnder), as de bou begjint, sil Docker al in boskje caches oan board hawwe. Us taak is om te learen hoe't jo de cache brûke, sadat de bou sa rap mooglik giet. Om't wy oannimme dat it útfieren fan in build sûnder cache mar ien kear bart - de alderearste kear - kinne wy dêrom negearje hoe stadich dy earste kear wie. Yn tests is de twadde run fan 'e bou wichtich foar ús, as de caches al opwaarme binne en wy binne klear om ús taart te bakken. Guon tips sille lykwols ek ynfloed hawwe op 'e earste bou.
Litte wy it hjirboppe beskreaune Dockerfile yn 'e projektmap sette en de bou begjinne. Alle listings binne kondinsearre foar maklik lêzen.
$ 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
Wy feroarje de ynhâld fan src/index.html en rinne it in twadde kear.
$ 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
Om te sjen oft wy de ôfbylding hawwe, útfiere it kommando docker images
:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 79f335df92d3 About a minute ago 1.74GB
Foar it bouwen nimt docker alle bestannen yn 'e hjoeddeistige kontekst en stjoert se nei syn daemon Sending build context to Docker daemon 409MB
. De bouwkontekst wurdt oantsjutte as it lêste argumint foar it bouwkommando. Yn ús gefal is dit de hjoeddeistige map - ".", - en Docker sleept alles wat wy yn dizze map hawwe. 409 MB is in protte: lit ús tinke oer hoe't jo it reparearje.
Ferminderjen fan de kontekst
Om de kontekst te ferminderjen binne d'r twa opsjes. Of set alle bestannen dy't nedich binne foar montage yn in aparte map en wiis de docker-kontekst nei dizze map. Dit kin net altyd handich wêze, dus is it mooglik om útsûnderingen oan te jaan: wat moat net yn 'e kontekst sleept wurde. Om dit te dwaan, set it .dockerignore-bestân yn it projekt en jouwe oan wat net nedich is foar de bou:
.git
/node_modules
en fier de bou wer út:
$ 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 is folle better as 409 MB. Wy hawwe ek de ôfbyldingsgrutte fermindere fan 1.74 nei 1.38 GB:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 4942f010792a 3 minutes ago 1.38GB
Litte wy besykje de grutte fan 'e ôfbylding fierder te ferminderjen.
Wy brûke Alpine
In oare manier om te besparjen op ôfbyldingsgrutte is in lyts âlderôfbylding te brûken. It âlderlike byld is it byld op basis wêrfan ús byld taret is. De ûnderste laach wurdt oantsjutte troch it kommando FROM
yn Dockerfile. Yn ús gefal brûke wy in Ubuntu-basearre ôfbylding dy't al nodejs ynstalleare hat. En it weaget ...
$ docker images -a | grep node
node 12.16.2 406aa3abbc6c 17 minutes ago 916MB
... hast in gigabyte. Jo kinne it folume signifikant ferminderje troch in ôfbylding te brûken basearre op Alpine Linux. Alpine is in heul lyts Linux. De dockerôfbylding foar nodejs basearre op alpine waacht mar 88.5 MB. Dus litte wy ús libbene byld yn 'e huzen ferfange:
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
Wy moasten guon dingen ynstallearje dy't nedich binne om de applikaasje te bouwen. Ja, Angular bout net sûnder Python ¯(°_o)/¯
Mar de ôfbyldingsgrutte sakke nei 150 MB:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest aa031edc315a 22 minutes ago 761MB
Lit ús noch fierder gean.
Multistage gearstalling
Net alles wat yn 'e ôfbylding is wat wy nedich binne yn produksje.
$ 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
Mei help fan docker run app ls -lah
wy lansearre in kontener basearre op ús ôfbylding app
en útfierd it kommando dêryn ls -lah
, wêrnei't de kontener syn wurk foltôge.
Yn produksje hawwe wy allinich in map nedich dist
. Yn dit gefal moatte de bestannen op ien of oare manier bûten wurde jûn. Jo kinne wat HTTP-tsjinner op nodejs útfiere. Mar wy sille it makliker meitsje. Tink in Russysk wurd dat fjouwer letters "y" hat. Rjochts! Ynzhynyksy. Litte wy in ôfbylding nimme mei nginx, pleatse der in map yn dist
en in lytse konfiguraasje:
server {
listen 80 default_server;
server_name localhost;
charset utf-8;
root /app/dist;
location / {
try_files $uri $uri/ /index.html;
}
}
Multi-stage build sil ús helpe dit alles te dwaan. Litte wy ús Dockerfile feroarje:
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 .
No hawwe wy twa ynstruksjes FROM
yn 'e Dockerfile rint elk fan har in oare boustap. Wy belje de earste builder
, mar begjinnend fan 'e lêste FROM, sil ús definitive ôfbylding wurde taret. De lêste stap is om it artefakt fan ús gearkomste yn 'e foarige stap te kopiearjen yn' e definitive ôfbylding mei nginx. De grutte fan 'e ôfbylding is signifikant fermindere:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 2c6c5da07802 29 minutes ago 36MB
Litte wy de kontener útfiere mei ús ôfbylding en soargje derfoar dat alles wurket:
docker run -p8080:80 app
Mei de opsje -p8080:80 hawwe wy poarte 8080 op ús hostmasine trochstjoerd nei poarte 80 yn 'e kontener wêr't nginx rint. Iepenje yn browser
It ferminderjen fan de ôfbyldingsgrutte fan 1.74 GB nei 36 MB ferminderet de tiid dy't it nimt om jo applikaasje oan produksje te leverjen. Mar litte wy weromgean nei de gearstallingstiid.
$ 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
Feroarje de folchoarder fan lagen
Us earste trije stappen waarden yn 'e cache bewarre (hint Using cache
). By de fjirde stap wurde alle projektbestannen kopiearre en by de fyfde stap wurde ôfhinklikens ynstalleare RUN npm ci
- safolle as 47.338s. Wêrom reinstall ôfhinklikens eltse kear as se feroarje hiel komselden? Litte wy útfine wêrom't se net yn 'e cache waarden. It punt is dat Docker laach foar laach sil kontrolearje om te sjen oft it kommando en de dêrmei ferbûne bestannen binne feroare. By de fjirde stap kopiearje wy alle bestannen fan ús projekt, en ûnder har binne d'r fansels feroaringen, dus Docker nimt dizze laach net allinich út 'e cache, mar ek alle folgjende! Litte wy wat lytse feroaringen meitsje oan 'e 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 .
Earst wurde package.json en package-lock.json kopiearre, dan wurde ôfhinklikens ynstalleare, en pas dêrnei wurdt it hiele projekt kopiearre. Dêrtroch:
$ 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 sekonden ynstee fan 3 minuten - folle better! De krekte folchoarder fan lagen is wichtich: earst kopiearje wy wat net feroaret, dan wat feroaret selden, en op it lêst wat feroaret faak.
Folgjende, in pear wurden oer it gearstallen fan ôfbyldings yn CI / CD-systemen.
Foarige ôfbyldings brûke foar cache
As wy in soarte fan SaaS-oplossing brûke foar de bou, dan kin de lokale Docker-cache skjin en fris wêze. Om de docker in plak te jaan om de bakte lagen te krijen, jou him de foarige boude ôfbylding.
Litte wy in foarbyld nimme fan it bouwen fan ús applikaasje yn GitHub Actions. Wy brûke dizze konfiguraasje
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
De ôfbylding wurdt yn twa minuten en 20 sekonden gearstald en nei GitHub-pakketten stjoerd:
Litte wy no de bou feroarje sadat in cache wurdt brûkt op basis fan eardere boud ôfbyldings:
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
Earst moatte wy jo fertelle wêrom twa kommando's wurde lansearre build
. It feit is dat yn in multistage gearstalling de resultearjende ôfbylding in set fan lagen sil wêze fan 'e lêste poadium. Yn dit gefal wurde lagen fan eardere lagen net opnommen yn 'e ôfbylding. Dêrom, by it brûken fan de definitive ôfbylding fan in foarige build, Docker sil net by steat wêze om te finen klear lagen om te bouwen de ôfbylding mei nodejs (bouwer poadium). Om dit probleem op te lossen, wurdt in tuskenbyld makke $IMAGE_NAME-builder-stage
en wurdt skood nei GitHub-pakketten sadat it kin wurde brûkt yn in folgjende build as cache-boarne.
De totale gearstalling tiid waard fermindere nei ien en in heale minút. In heale minút wurdt bestege oan it opheljen fan eardere bylden.
Preimaging
In oare manier om it probleem fan in skjinne Docker-cache op te lossen is om guon fan 'e lagen nei in oare Dockerfile te ferpleatsen, it apart te bouwen, it yn it Container Registry te drukken en it as âlder te brûken.
Wy meitsje ús eigen nodejs-ôfbylding om in Angular-applikaasje te bouwen. Meitsje Dockerfile.node yn it projekt
FROM node:12.16.2-alpine3.11
RUN apk --no-cache --update --virtual build-dependencies add
python
make
g++
Wy sammelje en triuwe in iepenbiere ôfbylding yn Docker Hub:
docker build -t exsmund/node-for-angular -f Dockerfile.node .
docker push exsmund/node-for-angular:latest
No yn ús haad Dockerfile brûke wy de ôfmakke ôfbylding:
FROM exsmund/node-for-angular:latest as builder
...
Yn ús foarbyld is de boutiid net fermindere, mar pre-boude ôfbyldings kinne nuttich wêze as jo in protte projekten hawwe en deselde ôfhinklikens yn elk fan har moatte ynstallearje.
Wy seagen ferskate metoaden foar it rapperjen fan de bou fan docker-ôfbyldings. As jo wolle dat de ynset rap giet, besykje dan dit te brûken yn jo projekt:
- ferminderjen fan kontekst;
- gebrûk fan lytse âlderbylden;
- multistage gearstalling;
- feroarje de folchoarder fan ynstruksjes yn 'e Dockerfile om effisjint gebrûk fan' e cache te meitsjen;
- it ynstellen fan in cache yn CI / CD systemen;
- foarriedige skepping fan bylden.
Ik hoopje dat it foarbyld it dúdliker makket hoe't Docker wurket, en jo kinne jo ynset optimaal konfigurearje. Om mei de foarbylden út it artikel te boartsjen, is in repository makke
Boarne: www.habr.com