We creëren een deploymenttaak in GKE zonder plugins, sms en registratie. Een blik onder de jas van Jenkins

Alles begon met het feit dat de teamleider van een van onze ontwikkelteams vroeg om hun nieuwe applicatie in een testfase openbaar te maken, die de dag ervoor was gecontaineriseerd. Ik heb dat gedaan. Ongeveer twintig minuten later kwam het verzoek om de applicatie te updaten, omdat ze een erg belangrijk onderdeel hadden toegevoegd. Ik heb geüpdatet. Weer een paar uur later... nou, je raadt al wat er daarna gebeurde...

Ik geef toe, ik ben redelijk lui (was ik daar niet eerder voor uitgekomen? Nee?), en gezien het feit dat teamleiders toegang hebben tot Jenkins, waar ons hele CI/CD systeem draait, dacht ik: laat hem gewoon zelf uitrollen, zoveel als hij wil! Ik moest denken aan een grap: geef iemand een vis en hij zal een dag lang verzadigd zijn; noem de persoon Voldoende en hij zal zijn leven lang verzadigd zijn. En toen ging ik aan de slag met het maken van een job, die in staat zou zijn om een container met de applicatie van elke succesvol gebouwde versie naar Kubernetes te deployen en eventuele waarden door te geven ENV (mijn grootvader, een taalkundige en vroeger leraar Engels, zou nu met zijn vinger aan zijn slapen draaien en me heel veelzeggend aankijken als hij deze zin las).

Dus, in deze notitie vertel ik hoe ik heb geleerd:

  1. Opdrachten dynamisch bijwerken in Jenkins vanuit de taak zelf of vanuit andere taken;
  2. Verbinding maken met de cloudconsole (Cloud shell) vanaf de node met de geïnstalleerde Jenkins-agent;
  3. Een werklast (workload) deployen in Google Kubernetes Engine.


In werkelijkheid ben ik natuurlijk enigszins uit de lucht gevallen. Het wordt verondersteld dat ten minste een deel van de infrastructuur zich in de Google Cloud bevindt, wat betekent dat je een gebruiker bent en uiteraard een GCP-account hebt. Maar deze notitie gaat daar niet over.

Dit is weer een van mijn spiekbriefjes. Ik wil zeker zulke notities schrijven enkel in het geval dat ik een taak had die ik aanvankelijk niet wist op te lossen, de oplossing niet gemakkelijk kon vinden, dus heb ik deze in delen gegoogeld en uiteindelijk het probleem opgelost. En zodat ik in de toekomst, wanneer ik vergeet hoe ik het heb gedaan, niet alles opnieuw in delen hoef te googelen en samen te stellen, schrijf ik dergelijke spiekbriefjes voor mezelf.

Disclaimer: 1. De notitie is geschreven 'voor mezelf', en is niet bedoeld als beste praktijk . Ik lees graag de versies 'of het had beter zo moeten zijn' in de opmerkingen.
2. Als je het praktische deel van de notitie als zout beschouwt, dan is deze, net als al mijn vorige notities, een zwakke zoutoplossing.

Dynamische update van opdrachteninstellingen in Jenkins

Ik hoor je vraag al: wat heeft dynamisch updaten van een job ermee te maken? Ik voer handmatig de waarde van de stringparameter in en gaan!

Mijn antwoord: ik ben gewoon lui, ik houd er niet van als je hoort: Misha, de deploy crasht, alles is verloren! Je begint te kijken en daar is een typfout in de waarde van een opstartparameter van de opdracht. Daarom geef ik de voorkeur aan alles zo foutloos mogelijk te doen. Als er een mogelijkheid is om de gebruiker te verhinderen directe gegevens in te voeren, en in plaats daarvan een lijst met waarden te geven om uit te kiezen, dan organiseer ik die keuze.

Het plan is als volgt: we maken een taak in Jenkins, waarin je voor het starten een versie kunt kiezen uit een lijst, de waarden kunt opgeven voor de parameters die naar de container worden verzonden via ENV, vervolgens verzamelt het de container en pusht deze naar de Container Registry. Dan wordt de container daar vanuit gestart in de Kubernetes als workload met parameters die in de job zijn opgegeven.

We zullen het proces van het maken en configureren van een taak in Jenkins niet bespreken, dat is off-topic. Laten we ervan uitgaan dat de taak gereed is. Voor de implementatie van de vernieuwbare lijst met versies hebben we twee dingen nodig: een bestaande bronlijst met bij voorbaat geldige versienummers en een variabele van het type Choice parameter in de taak. In ons voorbeeld laten we de variabele de naam dragen van BUILD_VERSION, we zullen hier niet verder op ingaan. Maar laten we uitgebreider ingaan op de bronlijst.

Er zijn niet zo veel opties. Twee komen me snel in gedachten:

  • Gebruik maken van de Remote access API die Jenkins aan zijn gebruikers biedt;
  • De inhoud van een externe map in het repository opvragen (in ons geval is dat JFrog Artifactory, wat niet essentieel is).

Jenkins Remote access API

Zoals gebruikelijk zal ik lange verklaringen vermijden.
Ik laat me alleen een vrije vertaling van een stukje van de eerste alinea van de eerste pagina van de API-documentatie toestaan.:

Jenkins biedt een API voor machine-leesbare externe toegang tot zijn functionaliteit. Externe toegang wordt aangeboden in een REST-achtige stijl. Dit betekent dat er geen enkele toegangspunt is voor alle mogelijkheden, maar in plaats daarvan wordt een URL gebruikt van de vorm ".../api/", waar "…" verwijst naar het object waarop de API-mogelijkheden van toepassing zijn.

Met andere woorden, als de deployment job waar we het momenteel over hebben beschikbaar is op het adres http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build, de API-onderdelen voor deze taak zijn beschikbaar op http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/

Vervolgens hebben we de keuze in welk formaat we output willen ontvangen. Laten we ons concentreren op XML, aangezien de API alleen in dit geval filtering toestaat.

Laten we gewoon proberen om een lijst van alle uitvoeringen van de taak te krijgen. We zijn alleen geïnteresseerd in de naam van de build (displayName) en het resultaat ervan (resultaat):

http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]

Is het gelukt?

Laten we nu alleen de uitvoeringen filteren die uiteindelijk het resultaat SUCCESShebben. We gebruiken de parameter &exclude en geven als parameter het pad door naar een waarde die niet gelijk is aan SUCCESS. Ja, de dubbele ontkenning is een bevestiging. We sluiten alles uit wat ons niet interesseert:

http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]&exclude=freeStyleProject/allBuild[result!='SUCCESS']

Screenshot van de lijst met succesvolle
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

En om een beetje te spelen, laten we ervoor zorgen dat de filter ons niet heeft bedrogen (filters liegen nooit!) en laten we de lijst van 'niet-succesvolle' weergeven:

http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]&exclude=freeStyleProject/allBuild[result='SUCCESS']

Screenshot van de lijst met niet-succesvolle
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

Lijst van versies uit de map op de externe server

Er is ook een tweede manier om de lijst van versies te krijgen. Ik vind deze zelfs beter dan het aanspreken van de Jenkins API. Want als de applicatie succesvol is gebouwd, betekent dit dat deze is verpakt en in de repository in de bijbehorende map is geplaatst. Dus, de repository is standaard een opslagplaats voor werkende versies van applicaties. Laten we maar vragen welke versies er opgeslagen zijn. We zullen de externe map curl'en, grep'en en awk'en. Voor degenen die geïnteresseerd zijn in een one-liner, die vindt u onder de spoiler.

De eenregelige opdracht
Let op twee dingen: ik geef in de header de inloggegevens voor de verbinding door en ik heb niet per se alle versies uit de map nodig, en ik filter alleen diegene die in de afgelopen maand zijn gemaakt. Pas de opdracht aan volgens uw eigen situatie en behoeften:

curl -H "X-JFrog-Art-Api:VeryLongAPIKey" -s http://arts.myre.po/artifactory/awesomeapp/ | sed 's/a href=// | grep "$(date +%b)-$(date +%Y)|$(date +%b --date='-1 month')-$(date +%Y)" | awk '{print $1}' | grep -oP '>K[^/]+ )

Configuratie van taken en het configuratiebestand van de taak in Jenkins

We hebben de bron van de lijst met versies nu onder de knie. Laten we de verkregen lijst in de taak integreren. Voor mij was het een duidelijk oplossing om een stap toe te voegen aan de taak voor het bouwen van de applicatie. Een stap die uitgevoerd zou worden in het geval van het resultaat 'succes'.

Open de instellingen van de buildtaak en scroll naar beneden. Klik op de knoppen: Add build step -> Conditional step (single). In de instellingsfase kiezen we de voorwaarde Huidige build status, stellen we de waarde in SUCCESS, de actie die moet worden uitgevoerd in geval van succes Run shell command.

En nu komt het interessantste. De configuraties van Jenkins-taken worden opgeslagen in bestanden. In XML-formaat. Op het pad http://pad-naar-taak/config.xml Bijgevolg kunt u het configuratiebestand downloaden, het op de gewenste manier bewerken en het terugplaatsen naar de oorspronkelijke locatie.

Vergeet niet dat we eerder hebben afgesproken om een parameter te maken voor de lijst met versies. BUILD_VERSION?

Laten we het configuratiebestand downloaden en er een blik op werpen. Gewoon om te controleren of de parameter aanwezig is en daadwerkelijk het juiste formaat heeft.

Screenshot onder de spoiler.

Het fragment config.xml dat u heeft, moet er ongeveer hetzelfde uitzien. Met de uitzondering dat de inhoud van het element choices voorlopig ontbreekt.
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

Geverifieerd? Nou goed, we schrijven een script dat wordt uitgevoerd in geval van een succesvolle build.
Het script zal de lijst met versies ophalen, het configuratiebestand downloaden, de lijst met versies op de juiste plek in het bestand schrijven en het vervolgens weer terugplaatsen. Ja. Dat klopt. De lijst met versies in de XML schrijven op de plek waar al een lijst met versies is (dit zal in de toekomst zijn, na de eerste uitvoering van het script). Ik weet dat er in de wereld nog altijd fervente liefhebbers van reguliere expressies zijn. Ik behoor daar niet bij. Installeer alsjeblieft xmlstarlet op de machine waar de config zal worden bewerkt. Het lijkt me geen grote prijs om te betalen om te voorkomen dat je XML met sed moet bewerken.

Onder de spoiler geef ik de code die de hierboven beschreven stappen geheel uitvoert.

We schrijven de lijst met versies vanuit de map op de externe server in de config.

#!/bin/bash
############## Скачиваем конфиг
curl -X GET -u username:apiKey http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_k8s/config.xml -o appConfig.xml

############## Удаляем и заново создаем xml-элемент для списка версий
xmlstarlet ed --inplace -d '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]/a[@class="string-array"]' appConfig.xml

xmlstarlet ed --inplace --subnode '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]' --type elem -n a appConfig.xml

xmlstarlet ed --inplace --insert '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]/a' --type attr -n class -v string-array appConfig.xml

############## Читаем в массив список версий из репозитория
readarray -t vers < <( curl -H "X-JFrog-Art-Api:Api:VeryLongAPIKey" -s http://arts.myre.po/artifactory/awesomeapp/ | sed 's/a href=//' | grep "$(date +%b)-$(date +%Y)|$(date +%b --date='-1 month')-$(date +%Y)" | awk '{print $1}' | grep -oP '>K[^/]+' )

############## Пишем массив элемент за элементом в конфиг
printf '%sn' "${vers[@]}" | sort -r | 
                while IFS= read -r line
                do
                    xmlstarlet ed --inplace --subnode '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]/a[@class="string-array"]' --type elem -n string -v "$line" appConfig.xml
                done

############## Кладем конфиг взад
curl -X POST -u username:apiKey http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_k8s/config.xml --data-binary @appConfig.xml

############## Приводим рабочее место в порядок
rm -f appConfig.xml

Als je de optie met het ophalen van versies uit Jenkins leuker vindt en net zo lui bent als ik, dan vind je onder de spoiler dezelfde code, maar met de lijst uit Jenkins:

We schrijven de lijst met versies vanuit Jenkins in de config.
Maar houd rekening met het volgende: mijn buildnaam bestaat uit een volgnummer en een versienummer, gescheiden door een dubbele punt. Daarom snijdt awk het onnodige deel af. Pas deze regel aan voor je eigen behoeften.

#!/bin/bash
############## Скачиваем конфиг
curl -X GET -u username:apiKey http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_k8s/config.xml -o appConfig.xml

############## Удаляем и заново создаем xml-элемент для списка версий
xmlstarlet ed --inplace -d '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]/a[@class="string-array"]' appConfig.xml

xmlstarlet ed --inplace --subnode '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]' --type elem -n a appConfig.xml

xmlstarlet ed --inplace --insert '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]/a' --type attr -n class -v string-array appConfig.xml

############## Пишем в файл список версий из Jenkins
curl -g -X GET -u username:apiKey 'http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]&exclude=freeStyleProject/allBuild[result!=%22SUCCESS%22]&pretty=true' -o builds.xml

############## Читаем в массив список версий из XML
readarray vers < <(xmlstarlet sel -t -v "freeStyleProject/allBuild/displayName" builds.xml | awk -F":" '{print $2}')

############## Пишем массив элемент за элементом в конфиг
printf '%sn' "${vers[@]}" | sort -r | 
                while IFS= read -r line
                do
                    xmlstarlet ed --inplace --subnode '/project/properties/hudson.model.ParametersDefinitionProperty/parameterDefinitions/hudson.model.ChoiceParameterDefinition[name="BUILD_VERSION"]/choices[@class="java.util.Arrays$ArrayList"]/a[@class="string-array"]' --type elem -n string -v "$line" appConfig.xml
                done

############## Кладем конфиг взад
curl -X POST -u username:apiKey http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_k8s/config.xml --data-binary @appConfig.xml

############## Приводим рабочее место в порядок
rm -f appConfig.xml

In principe, als je de code die op basis van de bovenstaande voorbeelden is geschreven hebt getest, zou er in de deploy-taak al een dropdownlijst met versies moeten verschijnen. Het ziet er ongeveer zo uit als op de screenshot onder de spoiler.

Correct ingevulde lijst met versies.
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

Als alles goed heeft gewerkt, kopieer en plak het script in Run shell command en sla de wijzigingen op.

Verbinden met Cloud shell

Onze bouwers zijn in containers. Als middel voor het leveren van applicaties en als configuratiebeheerder gebruiken we Ansible. Dus als het gaat om het bouwen van containers, komen er drie opties in me op: Docker in Docker installeren, Docker op een machine met Ansible installeren, of containers bouwen in de cloudconsole. Over de plugins voor Jenkins hebben we afgesproken in dit artikel te zwijgen. Herinner je je dat nog?

Ik besloot: als containers "out of the box" in de cloudconsole kunnen worden gebouwd, waarom moeilijk doen? Houd het schoon, toch? Ik wil containers bouwen met Jenkins in de cloudconsole en ze daarna vanuit daar in Kubernetes schieten. Bovendien zijn de interne infrastructuurkanalen van Google erg breed, wat de snelheden van deploys ten goede zou komen.

Om verbinding te maken met de cloudconsole zijn er twee dingen nodig: gcloud en toegangsrechten tot Google Cloud API voor die VM-instantie waarmee deze verbinding zal worden gemaakt.

Voor degenen die helemaal niet vanuit de Google-cloud willen verbinden
Google staat de mogelijkheid toe om interactieve autorisatie in zijn diensten uit te schakelen. Dit maakt het mogelijk om verbinding te maken met de console, zelfs vanaf een koffiemachine, zolang deze onder *nix draait en zelf een console heeft.

Als je behoefte hebt dat ik deze kwestie uitgebreider belicht in het kader van dit artikel — laat het me weten in de reacties. Als er genoeg stemmen zijn — schrijf ik een update hierover.

De eenvoudigste manier om rechten te geven — via de webinterface.

  1. Stop de VM-instantie waarvan later verbinding met de cloudconsole zal worden gemaakt.
  2. Open de Gegevens van de instantie en klik op Wijzig.
  3. Onder aan de pagina kies je de toegangscategorie van de instantie Volledige toegang tot alle Cloud API's.

    Screenshot
    We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

  4. Sla de wijzigingen op en start de instantie.

Zodra de VM is opgestart, maak je verbinding via SSH en controleer je of de verbinding zonder fouten tot stand komt. Gebruik het commando:

gcloud alpha cloud-shell ssh

Een succesvolle verbinding ziet er ongeveer zo uit
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

Deploy naar GKE

Omdat we ernaar streven volledig over te schakelen naar IaC (Infrastructure as Code), worden onze Docker-bestanden in git opgeslagen. Dat is één kant. En de deploy in Kubernetes wordt beschreven in een yaml-bestand dat alleen voor deze taak wordt gebruikt, wat op zichzelf ook een soort code is. Dat is de andere kant. Kortom, mijn plan is als volgt:

  1. Neem de waarden van de variabelen BUILD_VERSION en, optioneel, de waarden van variabelen die via ENV.
  2. We downloaden het Docker-bestand uit Git.
  3. We genereren YAML voor deployment.
  4. We uploaden beide bestanden via scp naar de cloudconsole.
  5. We bouwen daar de container en pushen deze naar de Container registry
  6. We passen het deployment-bestand voor de workload toe in Kubernetes.

Laten we specifieker worden. Aangezien we het over ENVhebben, laten we aannemen dat we waarden voor twee parameters moeten doorgeven: PARAM1 en PARAM2. We voegen hun toewijzing aan de deployment toe, type — String Parameter.

Screenshot
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

We genereren YAML door eenvoudigweg te redirecten echo naar een bestand. We gaan ervan uit dat u in uw Docker-bestand hebt PARAM1 en PARAM2, dat de naam van de workload awesomeapp, en dat de samengevoegde container met de opgegeven versie in Container registry aan het pad gcr.io/awesomeapp/awesomeapp-$BUILD_VERSION, waar $BUILD_VERSION juist is gekozen uit de dropdownlijst.

Commands lijst

touch deploy.yaml
echo "apiVersion: apps/v1" >> deploy.yaml
echo "kind: Deployment" >> deploy.yaml
echo "metadata:" >> deploy.yaml
echo "  name: awesomeapp" >> deploy.yaml
echo "spec:" >> deploy.yaml
echo "  replicas: 1" >> deploy.yaml
echo "  selector:" >> deploy.yaml
echo "    matchLabels:" >> deploy.yaml
echo "      run: awesomeapp" >> deploy.yaml
echo "  template:" >> deploy.yaml
echo "    metadata:" >> deploy.yaml
echo "      labels:" >> deploy.yaml
echo "        run: awesomeapp" >> deploy.yaml
echo "    spec:" >> deploy.yaml
echo "      containers:" >> deploy.yaml
echo "      - name: awesomeapp" >> deploy.yaml
echo "        image: gcr.io/awesomeapp/awesomeapp-$BUILD_VERSION:latest" >> deploy.yaml
echo "        env:" >> deploy.yaml
echo "        - name: PARAM1" >> deploy.yaml
echo "          value: $PARAM1" >> deploy.yaml
echo "        - name: PARAM2" >> deploy.yaml
echo "          value: $PARAM2" >> deploy.yaml

De Jenkins-agent heeft geen interactieve modus beschikbaar na verbindingen, dus we geven commando's door naar de cloudconsole met de parameter gcloud alpha cloud-shell ssh —command We maken de thuismap in de cloudconsole vrij van het oude Docker-bestand:.

gcloud alpha cloud-shell ssh --command="rm -f Dockerfile"

We plaatsen het net gedownloade Docker-bestand in de thuismap van de cloudconsole via scp:

gcloud alpha cloud-shell scp localhost:./Dockerfile cloudshell:~

We bouwen, taggen en pushen de container naar de Container registry:

gcloud alpha cloud-shell ssh --command="docker build -t awesomeapp-$BUILD_VERSION ./ --build-arg BUILD_VERSION=$BUILD_VERSION --no-cache" gcloud alpha cloud-shell ssh --command="docker tag awesomeapp-$BUILD_VERSION gcr.io/awesomeapp/awesomeapp-$BUILD_VERSION" gcloud alpha cloud-shell ssh --command="docker push gcr.io/awesomeapp/awesomeapp-$BUILD_VERSION"

Op dezelfde manier handelen we het deployment-bestand af. Let op, in de onderstaande commando's worden fictieve namen van de cluster gebruikt waar de deployment plaatsvindt (

awsm-cluster) en de naam van het project (awesome-project) waar de cluster zich bevindt.), waar de cluster zich bevindt.

gcloud alpha cloud-shell ssh --command="rm -f deploy.yaml"
gcloud alpha cloud-shell scp localhost:./deploy.yaml cloudshell:~
gcloud alpha cloud-shell ssh --command="gcloud container clusters get-credentials awsm-cluster --zone us-central1-c --project awesome-project && 
kubectl apply -f deploy.yaml"

We starten de taak, openen de console-uitvoer en hopen een succesvolle containerbuild te zien.

Screenshot
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

En dan een succesvolle deployment van de opgebouwde container.

Screenshot
We maken een taak aan voor deployment in GKE zonder plugins, sms of registratie. We gluren even onder de jas van Jenkins.

Ik heb de configuratie opzettelijk overgeslagen. IngressOm een eenvoudige reden: zodra het is ingesteld met een bepaalde naam, blijft het functioneel, ongeacht hoeveel deployments met die naam er worden uitgevoerd. En dat valt ook een beetje buiten het verhaal. workload In plaats van resultaten

In plaats van resultaten

Al deze stappen hierboven had ik waarschijnlijk niet hoeven doen, maar gewoon een of andere plugin voor Jenkins kunnen installeren, er zijn er miljoenen. Maar om de een of andere reden houd ik niet van plugins. Nou ja, beter gezegd, ik gebruik ze alleen uit wanhoop.

Bovendien vind ik het gewoon leuk om een nieuw onderwerp dat voor mij onbekend is uit te pluizen. De tekst hierboven is ook een manier om de ontdekkingen die ik heb gedaan tijdens het oplossen van het in het begin beschreven probleem te delen. Om te delen met degenen die, net als ik, geen hardcore DevOps-expert zijn. Als mijn bevindingen iemand helpen, ben ik tevreden.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster