Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Voordat 'n funksie in produksie kom, in hierdie dae van komplekse orkestreerders en CI/CD, is daar 'n lang pad om te gaan van toewyding tot toetse en aflewering. Voorheen kon u nuwe lêers via FTP oplaai (niemand doen dit meer nie, reg?), en die "ontplooiing"-proses het sekondes geneem. Nou moet jy 'n samesmeltingsversoek skep en lank wag vir die kenmerk om gebruikers te bereik.

Deel van hierdie pad is om 'n Docker-beeld te bou. Soms duur die samekoms minute, soms tien minute, wat moeilik normaal genoem kan word. In hierdie artikel sal ons 'n eenvoudige toepassing neem wat ons in 'n prent sal verpak, verskeie metodes toepas om die bou te bespoedig, en kyk na die nuanses van hoe hierdie metodes werk.

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Ons het goeie ondervinding in die skep en ondersteuning van mediawebwerwe: TASS, The Bell, "Nuwe koerant", Republiek… Nie lank gelede nie het ons ons portefeulje uitgebrei deur 'n produkwebwerf vry te stel Herinnering (Reminder).. En terwyl nuwe kenmerke vinnig bygevoeg is en ou foute reggestel is, het stadige ontplooiing 'n groot probleem geword.

Ons ontplooi na GitLab. Ons versamel beelde, stoot dit na GitLab-register en rol dit uit na produksie. Die langste ding op hierdie lys is om beelde saam te stel. Byvoorbeeld: sonder optimalisering het elke backend-bou 14 minute geneem.

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Op die ou end het dit duidelik geword dat ons nie meer so kan lewe nie, en ons het gaan sit om uit te vind hoekom die beelde so lank neem om te versamel. As gevolg hiervan het ons daarin geslaag om die monteertyd tot 30 sekondes te verminder!

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Kom ons kyk vir hierdie artikel na 'n voorbeeld van die samestelling van 'n leë Angular-toepassing om nie aan die Reminder-omgewing gekoppel te wees nie. Dus, kom ons skep ons toepassing:

ng n app

Voeg PWA daarby (ons is progressief):

ng add @angular/pwa --project app

Terwyl 'n miljoen npm-pakkette afgelaai word, laat ons uitvind hoe die docker-beeld werk. Docker bied die vermoë om toepassings te verpak en uit te voer in 'n geïsoleerde omgewing wat 'n houer genoem word. Danksy isolasie kan u baie houers gelyktydig op een bediener laat loop. Houers is baie ligter as virtuele masjiene omdat hulle direk op die stelselkern loop. Om 'n houer met ons toepassing te laat loop, moet ons eers 'n prent skep waarin ons alles sal verpak wat nodig is om ons toepassing te laat loop. In wese is 'n prent 'n kopie van die lêerstelsel. Neem byvoorbeeld die Dockerfile:

FROM node:12.16.2
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build --prod

'n Dockerfile is 'n stel instruksies; Deur elkeen van hulle te doen, sal Docker die veranderinge aan die lêerstelsel stoor en dit op die voriges oorlê. Elke span skep sy eie laag. En die voltooide beeld is lae wat saam gekombineer is.

Wat belangrik is om te weet: elke Docker-laag kan kas. As niks verander het sedert die laaste bou nie, sal die docker 'n klaargemaakte laag neem, in plaas daarvan om die opdrag uit te voer. Aangesien die grootste toename in bouspoed te wyte sal wees aan die gebruik van die kas, sal ons, wanneer bouspoed gemeet word, spesifiek aandag gee aan die bou van 'n beeld met 'n klaargemaakte kas. Dus, stap vir stap:

  1. Ons vee die beelde plaaslik uit sodat vorige lopies nie die toets beïnvloed nie.
    docker rmi $(docker images -q)
  2. Ons begin die bou vir die eerste keer.
    time docker build -t app .
  3. Ons verander die src/index.html lêer - ons boots die werk van 'n programmeerder na.
  4. Ons voer die bouwerk 'n tweede keer uit.
    time docker build -t app .

As die omgewing vir die bou van beelde korrek opgestel is (meer hieroor hieronder), sal Docker reeds 'n klomp kas aan boord hê wanneer die bou begin. Ons taak is om te leer hoe om die kas te gebruik sodat die bou so vinnig as moontlik gaan. Aangesien ons aanneem dat die bestuur van 'n bou sonder 'n kas slegs een keer gebeur - die heel eerste keer - kan ons dus ignoreer hoe stadig daardie eerste keer was. In toetse is die tweede lopie van die bou vir ons belangrik, wanneer die kas reeds opgewarm is en ons gereed is om ons koek te bak. Sommige wenke sal egter ook die eerste bouwerk beïnvloed.

Kom ons plaas die Dockerfile wat hierbo beskryf word in die projekgids en begin die bou. Alle lyste is saamgevat vir maklike lees.

$ 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

Ons verander die inhoud van src/index.html en voer dit 'n tweede keer uit.

$ 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 sien of ons die prent het, voer die opdrag uit docker images:

REPOSITORY   TAG      IMAGE ID       CREATED              SIZE
app          latest   79f335df92d3   About a minute ago   1.74GB

Voordat jy bou, neem docker al die lêers in die huidige konteks en stuur dit na sy daemon Sending build context to Docker daemon 409MB. Die bou-konteks word gespesifiseer as die laaste argument vir die bou-opdrag. In ons geval is dit die huidige gids - ".", - en Docker sleep alles wat ons in hierdie gids het. 409 MB is baie: kom ons dink oor hoe om dit reg te stel.

Verminder die konteks

Om die konteks te verminder, is daar twee opsies. Of plaas al die lêers wat nodig is vir samestelling in 'n aparte vouer en wys die docker-konteks na hierdie vouer. Dit is dalk nie altyd gerieflik nie, so dit is moontlik om uitsonderings te spesifiseer: wat nie in die konteks ingesleep moet word nie. Om dit te doen, plaas die .dockerignore-lêer in die projek en dui aan wat nie nodig is vir die bou nie:

.git
/node_modules

en voer die bou weer uit:

$ 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 baie beter as 409 MB. Ons het ook die beeldgrootte van 1.74 tot 1.38 GB verminder:

REPOSITORY   TAG      IMAGE ID       CREATED         SIZE
app          latest   4942f010792a   3 minutes ago   1.38GB

Kom ons probeer om die grootte van die prent verder te verklein.

Ons gebruik Alpine

Nog 'n manier om prentgrootte te bespaar, is om 'n klein ouerprent te gebruik. Die ouerbeeld is die beeld op grond waarvan ons beeld voorberei word. Die onderste laag word deur die opdrag gespesifiseer FROM in Dockerfile. In ons geval gebruik ons ​​'n Ubuntu-gebaseerde beeld wat reeds nodejs geïnstalleer het. En dit weeg...

$ docker images -a | grep node
node 12.16.2 406aa3abbc6c 17 minutes ago 916MB

... amper 'n gigagreep. U kan die volume aansienlik verminder deur 'n prent te gebruik wat op Alpine Linux gebaseer is. Alpine is 'n baie klein Linux. Die docker-beeld vir nodejs gebaseer op alpine weeg slegs 88.5 MB. Laat ons dus ons lewendige beeld in die huise vervang:

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

Ons moes 'n paar dinge installeer wat nodig is om die toepassing te bou. Ja, Angular bou nie sonder Python ¯(°_o)/¯ nie

Maar die beeldgrootte het tot 150 MB gedaal:

REPOSITORY   TAG      IMAGE ID       CREATED          SIZE
app          latest   aa031edc315a   22 minutes ago   761MB

Kom ons gaan nog verder.

Multistadium samestelling

Nie alles wat in die beeld is, is wat ons nodig het in produksie nie.

$ 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

Met docker run app ls -lah ons het 'n houer op grond van ons beeld bekendgestel app en die opdrag daarin uitgevoer ls -lah, waarna die houer sy werk voltooi het.

In produksie het ons net 'n gids nodig dist. In hierdie geval moet die lêers op een of ander manier buite gegee word. U kan een of ander HTTP-bediener op nodejs laat loop. Maar ons sal dit makliker maak. Raai 'n Russiese woord wat vier letters "y" het. Reg! Ynzhynyksy. Kom ons neem 'n prent met nginx, plaas 'n gids daarin dist en 'n klein konfigurasie:

server {
    listen 80 default_server;
    server_name localhost;
    charset utf-8;
    root /app/dist;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

Multi-stadium bou sal ons help om dit alles te doen. Kom ons verander ons 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 .

Nou het ons twee instruksies FROM in die Dockerfile loop elkeen van hulle 'n ander boustap. Ons het die eerste een gebel builder, maar vanaf die laaste FROM, sal ons finale beeld voorberei word. Die laaste stap is om die artefak van ons samestelling in die vorige stap na die finale prent met nginx te kopieer. Die grootte van die beeld het aansienlik verminder:

REPOSITORY   TAG      IMAGE ID       CREATED          SIZE
app          latest   2c6c5da07802   29 minutes ago   36MB

Kom ons hardloop die houer met ons beeld en maak seker alles werk:

docker run -p8080:80 app

Met die -p8080:80-opsie het ons poort 8080 op ons gasheermasjien aangestuur na poort 80 binne die houer waar nginx loop. Maak oop in blaaier http://localhost:8080/ en ons sien ons aansoek. Alles werk!

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Deur die beeldgrootte van 1.74 GB tot 36 MB te verklein, verminder dit die tyd wat dit neem om jou toepassing tot produksie te lewer aansienlik. Maar kom ons gaan terug na samekomstyd.

$ 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

Verander die volgorde van lae

Ons eerste drie stappe is gekas (wenk Using cache). By die vierde stap word alle projeklêers gekopieer en in die vyfde stap word afhanklikhede geïnstalleer RUN npm ci - soveel as 47.338s. Waarom afhanklikes elke keer weer installeer as dit baie selde verander? Kom ons vind uit hoekom hulle nie gekas is nie. Die punt is dat Docker laag vir laag sal kyk om te sien of die opdrag en die lêers wat daarmee geassosieer word, verander het. By die vierde stap kopieer ons al die lêers van ons projek, en onder hulle is daar natuurlik veranderinge, so Docker neem nie net hierdie laag uit die kas nie, maar ook al die daaropvolgendes! Kom ons maak 'n paar klein veranderinge aan die 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 .

Eerstens word package.json en package-lock.json gekopieer, dan word afhanklikhede geïnstalleer, en eers daarna word die hele projek gekopieer. As gevolg daarvan:

$ 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 sekondes in plaas van 3 minute - baie beter! Die korrekte volgorde van lae is belangrik: eers kopieer ons wat nie verander nie, dan wat selde verander, en laastens wat dikwels verander.

Volgende, 'n paar woorde oor die samestelling van beelde in CI/CD-stelsels.

Gebruik vorige beelde vir kas

As ons 'n soort SaaS-oplossing vir die bou gebruik, kan die plaaslike Docker-kas skoon en vars wees. Om die koppelaar 'n plek te gee om die gebakte lae te kry, gee hom die vorige geboude beeld.

Kom ons neem 'n voorbeeld van die bou van ons toepassing in GitHub Actions. Ons gebruik hierdie konfigurasie

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

Die prent word in twee minute en 20 sekondes saamgestel en na GitHub-pakkette gestoot:

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Kom ons verander nou die bou sodat 'n kas gebruik word gebaseer op vorige geboude beelde:

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

Eerstens moet ons jou vertel hoekom twee opdragte geloods word build. Die feit is dat in 'n meerfase-samestelling die resulterende beeld 'n stel lae van die laaste stadium sal wees. In hierdie geval sal lae van vorige lae nie by die prent ingesluit word nie. Daarom, wanneer die finale prent van 'n vorige bou gebruik word, sal Docker nie gereed lae kan vind om die prent met nodejs (bouer stadium) te bou nie. Om hierdie probleem op te los, word 'n tussenbeeld geskep $IMAGE_NAME-builder-stage en word na GitHub-pakkette gedruk sodat dit in 'n daaropvolgende bou as 'n kasbron gebruik kan word.

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Die totale monteringstyd is tot een en 'n half minuut verminder. 'n Halfminuut word spandeer om vorige beelde op te trek.

Voorafbeelding

Nog 'n manier om die probleem van 'n skoon Docker-kas op te los, is om sommige van die lae na 'n ander Docker-lêer te skuif, dit apart te bou, dit in die Container Registry te druk en dit as 'n ouer te gebruik.

Ons skep ons eie nodejs-beeld om 'n Angular-toepassing te bou. Skep Dockerfile.node in die projek

FROM node:12.16.2-alpine3.11
RUN apk --no-cache --update --virtual build-dependencies add 
    python 
    make 
    g++

Ons versamel en stoot 'n publieke beeld in Docker Hub:

docker build -t exsmund/node-for-angular -f Dockerfile.node .
docker push exsmund/node-for-angular:latest

Nou in ons hoof Dockerfile gebruik ons ​​die voltooide prent:

FROM exsmund/node-for-angular:latest as builder
...

In ons voorbeeld het die boutyd nie afgeneem nie, maar voorafgeboude beelde kan nuttig wees as jy baie projekte het en dieselfde afhanklikhede in elk van hulle moet installeer.

Enkele wenke oor hoe om die bou van Docker-beelde te bespoedig. Byvoorbeeld, tot 30 sekondes

Ons het na verskeie metodes gekyk om die bou van docker-beelde te bespoedig. As jy wil hê dat die ontplooiing vinnig moet gaan, probeer dit in jou projek gebruik:

  • vermindering van konteks;
  • gebruik van klein ouerbeelde;
  • meerfase-samestelling;
  • die volgorde van instruksies in die Dockerfile te verander om die kas doeltreffend te gebruik;
  • die opstel van 'n kas in CI/CD-stelsels;
  • voorlopige skepping van beelde.

Ek hoop dat die voorbeeld dit duideliker sal maak hoe Docker werk, en jy sal jou ontplooiing optimaal kan konfigureer. Om met die voorbeelde uit die artikel te speel, is 'n bewaarplek geskep https://github.com/devopsprodigy/test-docker-build.

Bron: will.com

Voeg 'n opmerking