Функция өндіріске енбес бұрын, күрделі оркестрлер мен CI/CD күндерінде тапсырмадан сынақтар мен жеткізуге дейін ұзақ жол бар. Бұрын FTP арқылы жаңа файлдарды жүктеп салуға болатын еді (енді мұны ешкім жасамайды, солай емес пе?) және «орналастыру» процесі бірнеше секундқа созылды. Енді біріктіру сұрауын жасап, мүмкіндіктің пайдаланушыларға жетуін ұзақ күту керек.
Бұл жолдың бір бөлігі Docker кескінін құру болып табылады. Кейде құрастыру минуттарға, кейде ондаған минуттарға созылады, оны қалыпты деп атауға болмайды. Бұл мақалада біз қарапайым қолданбаны аламыз, оны суретке біріктіреміз, құрастыруды жылдамдату үшін бірнеше әдістерді қолданамыз және осы әдістердің қалай жұмыс істейтінінің нюанстарын қарастырамыз.
Біздің медиа веб-сайттарды құру және қолдау бойынша жақсы тәжірибеміз бар:
Біз GitLab жүйесіне орналастырамыз. Біз кескіндерді жинаймыз, оларды GitLab тізіліміне итеріп, өндіріске шығарамыз. Бұл тізімдегі ең ұзақ нәрсе - кескіндерді жинау. Мысалы: оңтайландырусыз әрбір сервер құрастыру 14 минутқа созылды.
Соңында бұлай өмір сүре алмайтынымыз белгілі болды және біз суреттерді жинау неге ұзаққа созылғанын анықтау үшін отырдық. Нәтижесінде біз құрастыру уақытын 30 секундқа дейін қысқарттық!
Бұл мақалада Reminder ортасына қосылмау үшін бос Angular қолданбасын құрастыру мысалын қарастырайық. Сонымен, қосымшамызды жасайық:
ng n app
Оған PWA қосыңыз (біз прогрессивті):
ng add @angular/pwa --project app
Миллион npm пакеттері жүктеліп жатқанда, докер кескінінің қалай жұмыс істейтінін анықтайық. Docker қолданбаларды пакеттеу және оларды контейнер деп аталатын оқшауланған ортада іске қосу мүмкіндігін береді. Оқшаулаудың арқасында бір серверде бір уақытта көптеген контейнерлерді іске қосуға болады. Контейнерлер виртуалды машиналарға қарағанда әлдеқайда жеңіл, өйткені олар тікелей жүйе ядросында жұмыс істейді. Контейнерді қолданбамызбен іске қосу үшін алдымен қолданбаның іске қосылуы үшін қажеттінің барлығын жинайтын суретті жасау керек. Негізінде, сурет файлдық жүйенің көшірмесі болып табылады. Мысалы, Docker файлын алыңыз:
FROM node:12.16.2
WORKDIR /app
COPY . .
RUN npm ci
RUN npm run build --prod
Dockerfile – бұл нұсқаулар жиынтығы; Олардың әрқайсысын орындау арқылы 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
Құрар алдында докер ағымдағы контексте барлық файлдарды алып, оларды өзінің демонына жібереді Sending build context to Docker daemon 409MB
. Құрастыру мәтінмәні құрастыру пәрменінің соңғы аргументі ретінде көрсетіледі. Біздің жағдайда, бұл ағымдағы каталог - «.», - және Docker осы қалтадағы барлық нәрсені сүйреп апарады. 409 МБ өте көп: оны қалай түзетуге болатынын ойланайық.
Контекстті қысқарту
Мәтінмәнді азайту үшін екі нұсқа бар. Немесе құрастыруға қажетті барлық файлдарды бөлек қалтаға салып, докер контекстін осы қалтаға бағыттаңыз. Бұл әрқашан ыңғайлы болмауы мүмкін, сондықтан ерекше жағдайларды көрсетуге болады: контекстке нені апаруға болмайды. Ол үшін жобаға .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 КБ 409 МБ қарағанда әлдеқайда жақсы. Сондай-ақ сурет өлшемін 1.74-тен 1.38 ГБ-қа дейін азайттық:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 4942f010792a 3 minutes ago 1.38GB
Кескіннің өлшемін одан әрі кішірейтуге тырысайық.
Біз Alpine пайдаланамыз
Кескін өлшемін сақтаудың тағы бір жолы - кішкентай ата-аналық кескінді пайдалану. Ата-аналық бейне - бұл біздің бейнеміз дайындалған бейне. Төменгі қабат пәрмен арқылы көрсетіледі FROM
Docker файлында. Біздің жағдайда біз Ubuntu негізіндегі кескінді пайдаланып жатырмыз, онда nodejs орнатылған. Және оның салмағы ...
$ docker images -a | grep node
node 12.16.2 406aa3abbc6c 17 minutes ago 916MB
... гигабайтқа жуық. Alpine Linux негізіндегі кескінді пайдалану арқылы дыбыс деңгейін айтарлықтай азайтуға болады. Alpine - өте кішкентай Linux. Alpine негізделген nodejs үшін докер кескінінің салмағы небәрі 88.5 МБ. Ендеше үйлердегі жанды бейнемізді ауыстырайық:
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 МБ дейін төмендеді:
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 жүйесінде іске қосуға болады. Бірақ біз оны жеңілдетеміз. Төрт «у» әрпінен тұратын орыс сөзін тап. Дұрыс! Ынжынықсы. Nginx көмегімен суретке түсірейік, оған қалта салайық dist
және шағын конфигурация:
server {
listen 80 default_server;
server_name localhost;
charset utf-8;
root /app/dist;
location / {
try_files $uri $uri/ /index.html;
}
}
Көп сатылы құрылыс бізге мұның бәрін жасауға көмектеседі. Докер файлымызды өзгертейік:
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
, бірақ соңғы FROM-дан бастап біздің соңғы суретіміз дайындалады. Соңғы қадам - алдыңғы қадамдағы біздің жинақтың артефактісін nginx көмегімен соңғы кескінге көшіру. Кескіннің өлшемі айтарлықтай төмендеді:
REPOSITORY TAG IMAGE ID CREATED SIZE
app latest 2c6c5da07802 29 minutes ago 36MB
Контейнерді суретімізбен іске қосып, бәрі жұмыс істейтініне көз жеткізейік:
docker run -p8080:80 app
-p8080:80 опциясын пайдалана отырып, біз хост құрылғысындағы 8080 портын nginx жұмыс істейтін контейнер ішіндегі 80 портқа жібердік. Браузерде ашу
Кескін өлшемін 1.74 ГБ-тан 36 МБ-қа дейін азайту қолданбаңызды өндіріске жеткізуге кететін уақытты айтарлықтай қысқартады. Бірақ жиналу уақытына оралайық.
$ 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 бұл қабатты кэштен ғана емес, сонымен қатар барлық кейінгілерді де алмайды! Докер файлына шағын өзгерістер енгізейік.
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 кэші таза және жаңа болуы мүмкін. Докерге пісірілген қабаттарды алу үшін орын беру үшін оған алдыңғы салынған кескінді беріңіз.
GitHub әрекеттерінде қолданбаны құрудың мысалын алайық. Біз бұл конфигурацияны қолданамыз
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
Кескін екі минут 20 секундта жиналады және GitHub пакеттеріне жіберіледі:
Енді құрастыруды бұрынғы салынған кескіндерге негізделген кэш пайдаланылатын етіп өзгертейік:
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 кескінін жасаймыз. Жобада 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
...
Біздің мысалда құрастыру уақыты азайған жоқ, бірақ сізде көптеген жобалар болса және олардың әрқайсысында бірдей тәуелділіктерді орнату қажет болса, алдын ала жасалған кескіндер пайдалы болуы мүмкін.
Біз докер кескіндерін құруды жылдамдатудың бірнеше әдістерін қарастырдық. Орналастырудың жылдам өтуін қаласаңыз, оны жобаңызда пайдаланып көріңіз:
- контекстті азайту;
- кішкентай ата-аналық бейнелерді пайдалану;
- көп сатылы құрастыру;
- кэшті тиімді пайдалану үшін Dockerfile ішіндегі нұсқаулардың ретін өзгерту;
- CI/CD жүйелерінде кэшті орнату;
- бейнелерді алдын ала жасау.
Мысал Docker қалай жұмыс істейтінін түсіндіреді және сіз орналастыруды оңтайлы конфигурациялай аласыз деп үміттенемін. Мақаладағы мысалдармен ойнау үшін репозиторий құрылды
Ақпарат көзі: www.habr.com