Vi har allerede flere gange fortalt om vores GitOps-værktøj , og denne gang vil vi gerne dele erfaringer fra opbygningen af webstedet med dokumentationen fra selve projektet — (den russiske version er — ). Det er et almindeligt statisk websted, men opbygningen er interessant, fordi den er bygget med brug af et dynamisk antal artefakter.

Vi vil ikke gå i detaljer med webstedets struktur: generering af et fælles menu for alle versioner, sider med information om udgivelser osv. I stedet vil vi fokusere på spørgsmål og særlige forhold vedrørende dynamisk opbygning og lidt på tilhørende CI/CD-processer.
Introduktion: hvordan webstedet fungerer
Lad os starte med, at dokumentationen til werf opbevares sammen med sin kode. Dette stiller visse krav til udviklingen, som generelt ligger uden for rammerne af denne artikel, men man kan i det mindste sige, at:
- Nye funktioner i werf må ikke udgives uden opdatering af dokumentationen, og omvendt, enhver ændring i dokumentationen indebærer en ny version af werf;
- Projektet har en ret intensiv udvikling: nye versioner kan udgives flere gange om dagen;
- Enhver manuel operation vedrørende deployment af webstedet med den nye version af dokumentationen er i det mindste trættende;
- Projektet anvender en tilgang til semantisk , med 5 kanaler for stabilitet. Udgivelsesprocessen indebærer en sekventiel gennemgang af versionerne gennem kanalerne i rækkefølge af stigende stabilitet: fra alpha til rock-solid;
- Webstedet har en russisk version, der "lever og udvikler sig" (dvs. hvis indhold opdateres) parallelt med den primære (dvs. engelsksprogede) version.
For at skjule al denne "indre køkken" for brugeren og tilbyde det, der "bare fungerer", har vi lavet et separat værktøj til installation og opdatering af werf — det er . Det er tilstrækkeligt at angive versionsnummeret og stabilitetskanalen, som du er villig til at bruge, og multiwerf vil tjekke, om der er en ny version på kanalen, og hente den ved behov.
I menuen til valg af versioner på webstedet er de seneste versioner af werf tilgængelige i hver kanal. Som standard åbnes adressen den mest stabile kanals version for den seneste udgivelse — den indekseres også af søgemaskiner. Dokumentationen for kanalen er tilgængelig på separate adresser (f.eks. til beta-udgivelsen 1.0).
I alt er følgende versioner tilgængelige for hjemmesiden:
- roden (åbnes som standard),
- for hver aktiv opdateringskanal for hver version (f.eks. ).
For at generere en specifik version af hjemmesiden er det generelt tilstrækkeligt at kompilere den ved hjælp af , ved at køre den relevante kommando i /docs repoet for werf (jekyll build), efter først at have skiftet til den nødvendige Git-tag.
Det skal blot tilføjes, at:
- værktøjet selv (werf) bruges til bygningen;
- CI/CD-processerne er bygget på GitLab CI;
- og alt dette fungerer selvfølgelig i Kubernetes.
Opgaver
Lad os nu formulere opgaverne, der tager højde for al den beskrevne specifikation:
- Efter opgradering af werf-versionen på enhver opdateringskanal skal dokumentationen på hjemmesiden automatisk opdateres..
- For udvikling skal der være mulighed for af og til at se forudsigelser af hjemmesiden..
Hjemmesiden skal omkompileres efter opgradering af versionen på enhver kanal fra de relevante Git-tags, men i løbet af bygningen af billedet vil vi få følgende egenskaber:
- Da listen over versioner på kanalerne ændrer sig, er det kun nødvendigt at genopbygge dokumentationen for de kanaler, hvor versionen er ændret. Det er ikke særlig pænt at skulle genopbygge alt fra bunden.
- Selve sætningen af kanaler til versioner kan ændre sig. På et givet tidspunkt kan der f.eks. mangle versioner på kanaler, der er mere stabile end early-access udgivelsen 1.1, men de vil komme med tiden — er det nødvendigt at ændre kompileringen manuelt i dette tilfælde?
Det viser sig, at bygningen afhænger af ændrede eksterne data..
Implementering
Valg af tilgang
Et alternativ kunne være at køre hver nødvendig version som en separat pod i Kubernetes. Denne tilgang indebærer flere objekter i klyngen, hvilket vil vokse i takt med antallet af stabile udgivelser af werf. Dette indebærer i sin tur mere kompleks vedligeholdelse: hver version får sin egen HTTP-server, hvilket skaber en lille belastning. Selvfølgelig medfører det også højere ressourceomkostninger.
Vi har valgt vejen med at samle alle nødvendige versioner i ét billede.Den kompilerede statik af alle versioner af hjemmesiden findes i en container med NGINX, og traffikken til den relevante Deployment kommer gennem NGINX Ingress. Den enkle struktur — stateless-applikation — gør det muligt at skalere Deployment let (afhængigt af belastningen) ved brug af Kubernetes selv.
For more precision, we create two images: one for the production environment, and another additional one for the dev environment. The additional image is used (launched) only on the dev environment alongside the main one and contains the version of the site from the review commit, with routing between them managed through Ingress resources.
werf vs git clone and artifacts
As mentioned earlier, to generate the static site for a specific version of the documentation, you need to perform a build by switching to the corresponding tag of the repository. It would be possible to do this by cloning the repository each time during the build and selecting the appropriate tags from the list. However, this is quite resource-intensive and also requires writing non-trivial instructions... Another serious drawback is that with this approach, there is no possibility to cache anything during the build.
Here, the werf utility itself comes to our aid, implementing intelligent caching and allowing the use of . Using werf to add code from the repository will significantly speed up the build since werf effectively clones the repository once and then performs kun fetch if necessary. Moreover, when adding data from the repository, we can select only the necessary directories (in our case, it's the directory docs), which significantly reduces the amount of data added.
Since Jekyll is a tool designed for compiling static content and is not needed in the final image, it would make sense to perform the compilation in , and import only the compilation result into the final image. We write werf.yaml.
So, we have decided to compile each version in a separate werf artifact. However, we
do not know how many artifacts will be created during the build, so we cannot write a fixed build configuration (strictly speaking, we could, but it would not be very efficient).werf allows the use of
Go templates ), which gives us the ability towerf.yamlgenerate the config "on the fly" based on external data (which is needed!). The external data in our case is information about versions and releases, based on which we assemble the necessary number of artifacts and end up with two images: werf-doc werf-dev og werf-dev til at køre på forskellige konturer.
Eksterne data overføres gennem miljøvariabler. Her er deres indhold:
-
UDGIVELSER— en streng med en liste over udgivelser og den relevante aktuelle version af werf, som en liste adskilt af mellemrum i formatet%. Eksempel:1.0%v1.0.4-beta.20 -
KANALER— en streng med en liste over kanaler og den relevante aktuelle version af werf, som en liste adskilt af mellemrum i formatet%. Eksempel:1.0-beta%v1.0.4-beta.20 1.0-alpha%v1.0.5-alpha.22 -
ROOT_VERSION— versionen af werf-udgivelsen til at blive vist som standard på hjemmesiden (det er ikke altid nødvendigt at vise dokumentationen for den højeste udgivelsesnummer). Eksempel:v1.0.4-beta.20 -
REVIEW_SHA— hash af review-committet, som versionen skal samles fra til testkonturen.
Disse variabler vil blive fyldt i GitLab CI pipeline, og hvordan det gøres, er skrevet nedenfor.
Først skal vi for nemheds skyld definere i werf.yaml Go-skabelonvariabler, ved at tildele dem værdier fra miljøvariabler:
{{ $_ := set . "WerfVersions" (cat (env "CHANNELS") (env "RELEASES") | splitList " ") }}
{{ $Root := . }}
{{ $_ := set . "WerfRootVersion" (env "ROOT_VERSION") }}
{{ $_ := set . "WerfReviewCommit" (env "REVIEW_SHA") }} Beskrivelsen af artefaktet til at kompilere statikken af versionen af hjemmesiden er generelt ens for alle nødvendige tilfælde (inklusive generering af roden version, samt versionen til dev-konturen). Derfor vil vi tage det ud i en separat blok ved hjælp af funktionen define — til efterfølgende genbrug ved hjælp af include. Skabelonen vil modtage følgende argumenter:
-
Version— den genererede version (tag-navn); -
Channel— navnet på opdateringskanalen, for hvilket artefaktet genereres; -
Commit— hash af committet, hvis artefaktet genereres for review-committet; - sammenhæng.
Beskrivelsen af artefaktets skabelon
{{- define "doc_artifact" -}}
{{- $Root := index . "Root" -}}
artifact: doc-{{ .Channel }}
from: jekyll/builder:3
mount:
- from: build_dir
to: /usr/local/bundle
ansible:
install:
- shell: |
export PATH=/usr/jekyll/bin/:$PATH
- name: "Install Dependencies"
shell: bundle install
args:
executable: /bin/bash
chdir: /app/docs
beforeSetup:
{{- if .Commit }}
- shell: echo "Review SHA - {{ .Commit }}."
{{- end }}
{{- if eq .Channel "root" }}
- name: "releases.yml HASH: {{ $Root.Files.Get "releases.yml" | sha256sum }}"
copy:
content: |
{{ $Root.Files.Get "releases.yml" | indent 8 }}
dest: /app/docs/_data/releases.yml
{{- else }}
- file:
path: /app/docs/_data/releases.yml
state: touch
{{- end }}
- file:
path: "{{`{{ item }}`}}"
state: directory
mode: 0777
with_items:
- /app/main_site/
- /app/ru_site/
- file:
dest: /app/docs/pages_ru/cli
state: link
src: /app/docs/pages/cli
- shell: |
echo -e "werfVersion: {{ .Version }}nwerfChannel: {{ .Channel }}" > /tmp/_config_additional.yml
export PATH=/usr/jekyll/bin/:$PATH
{{- if and (ne .Version "review") (ne .Channel "root") }}
{{- $_ := set . "BaseURL" ( printf "v%s" .Channel ) }}
{{- else if ne .Channel "root" }}
{{- $_ := set . "BaseURL" .Channel }}
{{- end }}
jekyll build -s /app/docs -d /app/_main_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/tmp/_config_additional.yml
jekyll build -s /app/docs -d /app/_ru_site/{{ if .BaseURL }} --baseurl /{{ .BaseURL }}{{ end }} --config /app/docs/_config.yml,/app/docs/_config_ru.yml,/tmp/_config_additional.yml
args:
executable: /bin/bash
chdir: /app/docs
git:
- url: https://github.com/flant/werf.git
to: /app/
owner: jekyll
group: jekyll
{{- if .Commit }}
commit: {{ .Commit }}
{{- else }}
tag: {{ .Version }}
{{- end }}
stageDependencies:
install: ['docs/Gemfile','docs/Gemfile.lock']
beforeSetup: '**/*'
includePaths: 'docs'
excludePaths: '**/*.sh'
{{- end }} Navnet på artefakten skal være unikt. Vi kan opnå dette ved for eksempel at tilføje kanalnavnet (variablen .Channel) som et suffiks til artefaktnavnet: artifact: doc-{{ .Channel }}. Men det er vigtigt at forstå, at når vi importerer fra artefakter, skal vi referere til de samme navne.
Når vi beskriver artefakten, anvendes en funktion i werf kaldet . Montering med angivelse af den servicemappe build_dir muliggør, at vi kan gemme Jekyll-cachen mellem pipeline-kørsler, hvilket væsentligt fremskynder genopbygningen.
Du har muligvis også bemærket brugen af filen releases.yml — dette er en YAML-fil med data om versioner, der hentes fra (artefakt opnået under pipeline-udførelsen). Den er nødvendig ved kompilering af hjemmesiden, men set i konteksten af artiklen er den interessant for os, da dens tilstand afhænger af genopbygning af kun én artefakt — artefakten for den rodversion (i andre artefakter er det ikke nødvendigt).
Dette er implementeret ved hjælp af den betingede operator if Go-skabeloner og konstruktionen {{ $Root.Files.Get "releases.yml" | sha256sum }} i trinnet . Det fungerer på følgende måde: når artefakten samles til den rodversion (variabel .Channel lige root) filens hash releases.yml påvirker signaturen for hele etape, da det er en komponent af navnet på Ansible-opgaven (parameter name). Således, ved ændring af indholdet filen releases.yml vil den tilsvarende artefakt blive genopbygget.
Bemærk også, at der arbejdes med et eksternt repository. I artefaktbilledet fra , tilføjes kun mappen /docs, idet data straks tilføjes afhængigt af de overførte parametre til den nødvendige tag eller review-commit.
For at bruge artefaktmønstret til at generere beskrivelsen af artefakterne fra de overførte versioner af kanaler og udgivelser, organiserer vi en løkke over variablen .WerfVersions i werf.yaml:
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ dict "Version" $VersionsDict._1 "Channel" $VersionsDict._0 "Root" $Root | include "doc_artifact" }}
---
{{ end -}} Eftersom løkken vil generere flere artefakter (det håber vi), skal separatoren mellem dem overvejes — rækkefølgen --- (læs mere om syntaksen i konfigurationsfilen i ). Som tidligere fastlagt overfører vi parametre for version, URL og rodindhold ved kald af mønsteret i løkken.
På samme måde, men uden løkken, kalder vi artefaktmønstret for «specielle tilfælde»: for rodversionen samt versionen fra review-commit:
{{ dict "Version" .WerfRootVersion "Channel" "root" "Root" $Root | include "doc_artifact" }}
---
{{- if .WerfReviewCommit }}
{{ dict "Version" "review" "Channel" "review" "Commit" .WerfReviewCommit "Root" $Root | include "doc_artifact" }}
{{- end }} Bemærk, at artefakten for review-committed kun vil blive opbygget, hvis variablen .WerfReviewCommit.
Artefakterne er klar — nu er det tid til import!
Det endelige billede, der er beregnet til at køre i Kubernetes, er en almindelig NGINX, hvortil der er tilføjet en serverkonfigurationsfil nginx.conf og statikken fra artefakterne. Udover artefakten for rodversionen af hjemmesiden skal vi gentage løkken over variablen .WerfVersions for at importere artefakterne fra versioner af kanaler og udgivelser + overholde navngivningsreglen for artefakterne, som vi tidligere har accepteret. Da hver artefakt gemmer versioner af hjemmesiden for to sprog, importeres de til de steder, som er specifikt angivet i konfigurationen.
Beskrivelsen af det endelige billede werf-doc
image: werf-doc
from: nginx:stable-alpine
ansible:
setup:
- name: "Setup /etc/nginx/nginx.conf"
copy:
content: |
{{ .Files.Get ".werf/nginx.conf" | indent 8 }}
dest: /etc/nginx/nginx.conf
- file:
path: "{{`{{ item }}`}}"
state: directory
mode: 0777
with_items:
- /app/main_site/assets
- /app/ru_site/assets
import:
- artifact: doc-root
add: /app/_main_site
to: /app/main_site
before: setup
- artifact: doc-root
add: /app/_ru_site
to: /app/ru_site
before: setup
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ $Channel := $VersionsDict._0 -}}
{{ $Version := $VersionsDict._1 -}}
- artifact: doc-{{ $Channel }}
add: /app/_main_site
to: /app/main_site/v{{ $Channel }}
before: setup
{{ end -}}
{{ range .WerfVersions -}}
{{ $VersionsDict := splitn "%" 2 . -}}
{{ $Channel := $VersionsDict._0 -}}
{{ $Version := $VersionsDict._1 -}}
- artifact: doc-{{ $Channel }}
add: /app/_ru_site
to: /app/ru_site/v{{ $Channel }}
before: setup
{{ end -}}Det ekstra billede, der køres sammen med hovedbilledet på dev-konturen, indeholder kun to versioner af webstedet: versionen fra review-commit og rodversionen af webstedet (der er almindelige aktiver og, hvis du husker, data om udgivelser). Dermed vil det ekstra billede adskille sig fra hovedbilledet kun ved importsektionen (og naturligvis ved navnet):
image: werf-dev
...
import:
- artifact: doc-root
add: /app/_main_site
to: /app/main_site
before: setup
- artifact: doc-root
add: /app/_ru_site
to: /app/ru_site
before: setup
{{- if .WerfReviewCommit }}
- artifact: doc-review
add: /app/_main_site
to: /app/main_site/review
before: setup
- artifact: doc-review
add: /app/_ru_site
to: /app/ru_site/review
before: setup
{{- end }} Som nævnt ovenfor vil artefaktet til review-commit kun blive genereret, når den installerede miljøvariabel aktiveres. REVIEW_SHA. Det ville faktisk være muligt slet ikke at generere billedet werf-dev, hvis der ikke er nogen miljøvariabel. REVIEW_SHA, men for at Docker-billeder i werf fungerer for billedet werf-dev, lader vi det kun blive bygget med artefakten fra rodversionen (det er allerede bygget), for at forenkle pipeline-strukturen.
Bygningen er klar! Vi går videre til CI/CD og vigtige nuancer.
Pipeline i GitLab CI og funktioner ved dynamisk bygning.
Ved starten af bygningen er det nødvendigt at indstille miljøvariabler, der anvendes i werf.yaml. Dette gælder ikke for variablen REVIEW_SHA, som vi vil indstille ved opkald af pipeline fra GitHub-webhook.
Generering af nødvendige eksterne data vil vi tage ud i et Bash-script, generate_artifacts, der vil generere to artefakter til GitLab-pipelinen:
- fil
releases.ymlmed data om udgivelser, - fil
common_envs.sh, der indeholder miljøvariabler til eksport.
Indholdet af filen generate_artifacts finder du i vores . Selve indhentningen af data er ikke emnet for artiklen, men filen common_envs.sh er vigtig for os, da det påvirker driften af werf. Et eksempel på dens indhold:
export RELEASES='1.0%v1.0.6-4'
export CHANNELS='1.0-alpha%v1.0.7-1 1.0-beta%v1.0.7-1 1.0-ea%v1.0.6-4 1.0-stable%v1.0.6-4 1.0-rock-solid%v1.0.6-4'
export ROOT_VERSION='v1.0.6-4' Output from such a script can be used, for example, with a Bash function source.
And now for the most interesting part. In order for both the build and deployment of the application to function correctly, it is necessary to ensure that werf.yaml var the same at least within the same pipeline. If this condition is not met, the signatures of the stages calculated by werf during the build and, for instance, deployment, will differ. This will lead to a deployment error since the necessary image for deployment will be missing.
In other words, if during the build of the site image the information about releases and versions is one thing, but at the moment of deployment a new version comes out and the environment variables have different values, then the deployment will fail: the artifact of the new version has not yet been built.
If the generation werf.yaml depends on external data (for example, a list of current versions, as in our case), the composition and values of such data must be fixed within the pipeline. This is especially important if external parameters change quite frequently.
We will acquire and fix external data in the first stage of the pipeline in GitLab (Prebuild) and pass it on as a GitLab CI artifact. This will allow launching and restarting pipeline jobs (build, deployment, cleanup) with the same configuration in werf.yaml.
Stage content Prebuild filen .gitlab-ci.yml:
Prebuild:
stage: prebuild
script:
- bash .\/generate_artifacts 1> common_envs.sh
- cat .\/common_envs.sh
artifacts:
paths:
- releases.yml
- common_envs.sh
expire_in: 2 weekBy fixing the external data in the artifact, you can carry out the build and deployment using the standard stages of GitLab CI pipeline: Build and Deploy. We trigger the pipeline from hooks in the GitHub repository werf (i.e., upon changes in the repository on GitHub). The data for them can be found in the project settings in GitLab under CI / CD Settings -> Pipeline triggers, and then we will create the corresponding Webhook in GitHub (Settings -> Webhooks).
The build stage will look as follows:
Build:
stage: build
script:
- type multiwerf && . $(multiwerf use 1.0 alpha --as-file)
- type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
- source common_envs.sh
- werf build-and-publish --stages-storage :local
except:
refs:
- schedules
dependencies:
- Prebuild GitLab will add two artifacts from the Prebuildbuild stage, so we will export variables with prepared input data using the construction source common_envs.sh. Vi starter byggefasen i alle tilfælde undtagen ved planlægningsudløsning af pipeline. Planlægningspipeline vil køre for rengøring — det er ikke nødvendigt at udføre bygningen i dette tilfælde.
I deploy-fasen beskriver vi to opgaver — separat for deployment på production- og dev-miljøer, ved hjælp af YAML-skabelonen:
.base_deploy: &base_deploy
stage: deploy
script:
- type multiwerf && . $(multiwerf use 1.0 alpha --as-file)
- type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
- source common_envs.sh
- werf deploy --stages-storage :local
dependencies:
- Prebuild
except:
refs:
- schedules
Deploy til Produktion:
<<: *base_deploy
variables:
WERF_KUBE_CONTEXT: prod
environment:
name: production
url: werf.io
only:
refs:
- master
except:
variables:
- $REVIEW_SHA
refs:
- schedules
Deploy til Test:
<<: *base_deploy
variables:
WERF_KUBE_CONTEXT: dev
environment:
name: test
url: werf.test.flant.com
except:
refs:
- schedules
only:
variables:
- $REVIEW_SHA Opgaverne adskiller sig i bund og grund kun ved angivelsen af konteksten for klyngen, hvor werf skal udføre deployment (WERF_KUBE_CONTEXT), og ved opsætning af miljøvariablerne for miljøet (environment.name og environment.url), som derefter bruges i Helm-chart skabelonerne. Indholdet af skabelonerne vil ikke blive præsenteret, da der ikke er noget interessant for det behandlede emne, men du kan finde dem i .
Den afsluttende touch
Da versioner af werf bliver udgivet ret hyppigt, vil nye billeder ofte blive bygget, og Docker Registry vil konstant vokse. Derfor er det vigtigt at konfigurere automatisk rengøring af billeder i henhold til politikker. Dette er meget enkelt at gøre.
For implementering er der behov for:
- Tilføje en rengøringsfase i
.gitlab-ci.yml; - Tilføje planlagt udførelse af rengøringsopgaven;
- Konfigurere en miljøvariabel med adgangstokens til skrivning.
Vi tilføjer rengøringsfasen i .gitlab-ci.yml:
Cleanup:
stage: cleanup
script:
- type multiwerf && . $(multiwerf use 1.0 alpha --as-file)
- type werf && source <(werf ci-env gitlab --tagging-strategy tag-or-branch --verbose)
- source common_envs.sh
- docker login -u nobody -p ${WERF_IMAGES_CLEANUP_PASSWORD} ${WERF_IMAGES_REPO}
- werf cleanup --stages-storage :local
only:
refs:
- schedules
Det meste har vi allerede set lidt tidligere — kun til rengøring skal man først logge ind i Docker Registry med en token, der har ret til at slette billeder i Docker Registry (den automatisk udstedte token fra GitLab CI har ikke sådanne rettigheder). Token skal oprettes i GitLab på forhånd og dens værdi angives i miljøvariablen WERF_IMAGES_CLEANUP_PASSWORD projektet (CI/CD-indstillinger -> Variabler).
Tilføjelse af rengøringsopgaven med den nødvendige tidsplan udføres i CI/CD ->
Schedules.
Alt: projekt i Docker Registry vokser ikke længere uendeligt af ubrugte billeder.
Afslutningsvis vil jeg minde om, at de komplette lister fra artiklen er tilgængelige i :
- ;
- .
Resultat
- Vi har fået en logisk struktureret bygning: én artefakt pr. version.
- Bygningen er universel og kræver ingen manuelle ændringer, når der kommer nye versioner af werf: dokumentationen på hjemmesiden opdateres automatisk.
- Der samles to billeder til forskellige konturer.
- Det fungerer hurtigt, da caching bruges maksimalt — når der kommer en ny version af werf eller ved GitHub-webhook-udløsning for review-commit — udføres der kun en genopbygning af den relevante artefakt med den ændrede version.
- Du behøver ikke at tænke på at slette ubrugte billeder: rydning i henhold til werf-politikkerne vil opretholde orden i Docker Registry.
Konklusioner
- Brugen af werf gør det muligt for bygningen at fungere hurtigt takket være caching både af selve bygningen og caching, når der arbejdes med eksterne repositoryer.
- Arbejdet med eksterne Git-repositoryer fjerner behovet for at klone repositoryet hver gang helt eller genopfinde cyklen med smart optimeringslogik. werf bruger cache og kloner kun én gang, og derefter bruger det
fetchog kun efter behov. - Muligheden for at bruge Go-templates i byggekonfigurationsfilen
werf.yamlgiver mulighed for at beskrive en bygning, hvis resultat afhænger af eksterne data. - Brugen af montering i werf fremskynder opbygningen af artefakter betydeligt — takket være cache, der er fælles for alle pipelines.
- werf gør det nemt at konfigurere rengøring, hvilket er særligt relevant ved dynamisk bygning.
P.S.
Læs også på vores blog:
- «»;
- «»;
- «»;
- «».
Kilde: habr.com
