E gjitha filloi kur lideri i ekipit tonë të zhvilluesve kërkoi që të publikohej në provë aplikacioni i tyre i ri, i cili kishte pësuar containerizim një ditë më parë. E publikova. Rreth 20 minuta më vonë, erdhi një kërkesë për të azhurnuar aplikacionin, sepse kishin punuar një funksionalitet shumë të nevojshëm. E azhurnova. Pas pak orësh… mirë, ju e kuptoni çfarë ndodhi më pas…
Të jem i sinqertë, jam disi dembel (a e kam pranuar më parë këtë? jo?), dhe duke marrë parasysh se liderët e ekipeve kanë akses në Jenkins, ku e kemi tërë CI/CD, mendoj: le ta publikojë vetë, sa të dojë! Më erdhi në mendje një anekdotë: jepni një peshk njeriut dhe ai do të jetë i ngopur për një ditë; quajeni njeriun Ngopës dhe ai do të jetë i ngopur gjithë jetën. Dhe shkova të krijoj një punë, e cila mund të publikojë në kuber një kontejner me aplikacionin e çdo versioni të mbledhur me sukses dhe t'i kalojë atij çdo vlerë ENV (gjyshi im, — filolog, ish mësues i anglishtes, — tani do të më bënte shenjë dhe do të më shikonte shumë shprehurshëm kur ta lexonte këtë fjali).
Pra, në këtë shënim do të flas për si kam mësuar:
- Të përditësoj dinamikisht detyrat në Jenkins nga vetë detyra ose nga detyra të tjera;
- Të lidhem me konsolën cloud (Cloud shell) nga një nodë me agjentin e instaluar të Jenkins;
- Të publikoj ngarkesën e punës (workload) në Google Kubernetes Engine.
Në të vërtetë, sigurisht, po gënjej pak. Supozohet se të paktën një pjesë e infrastrukturës është në cloud-in e Google, dhe, për rrjedhojë, ju jeni një përdorues i tij dhe, sigurisht, keni një llogari në GCP. Por shënimi nuk është për këtë.
Kjo është një tjetër shënim i imi. Të tilla shënime dua t'i shkruaj vetëm në një rast: përpara meje qe një detyrë, nuk e dija fillimisht si ta zgjidhja, zgjidhja nuk u gjet e gatshme në internet, prandaj e kërkova në pjesë dhe në fund e zgjodha detyrën. Dhe për të siguruar që në të ardhmen, kur të harroj se si e bëra, të mos më duhet ta kërkoj sërish në cope e të mbledh përsëri, po shkruaj këto shënime për veten time.
Përjashtim: 1. Shënimi është shkruar "për veten", nuk pretendon të jetë best practice . Me kënaqësi do të lexoj alternativat "po do ishte më mirë ta bësh kështu" në komentet.
2. Nëse pjesa praktike e shënimit konsiderohet si kripë, atëherë, si të gjitha shënimet e mia të mëparshme, kjo është një solucion me kripë të dobët.
Përditësimi dinamik i cilësimeve të punëve në Jenkins
E paraqes pyetjen tuaj: çfarë ka të bëjë kjo me përditësimin dinamik të punës? Vendosa manualisht vlerën e parametrave dhe përpara!
Po ju përgjigjem: unë vërtet jam lenes dhe nuk më pëlqen kur ankohen: Misha, deploy është prishur, gjithçka është humbur! Fillon të shikosh dhe aty është një gabim në vlerën e ndonjë parametri përshkues të punës. Prandaj, preferoj të bëj gjithçka sa më të sigurt. Nëse ka mundësi të privosh përdoruesin nga mundësia për të futur të dhëna drejtpërdrejt, duke ofruar në vend të kësaj një listë zgjedhjeje, atëherë do ta organizoj zgjedhjen.
Plani është ky: krijojmë një punë në Jenkins, në të cilën para se të niste mund të zgjidhnim versionin nga lista, të specifikonim vlerat për parametrat e dërguar në kontejner përmes ENV, pastaj e ndërton kontejnerin dhe e dërgon në Container Registry. Më pas, nga aty, kontejneri niset në Kubernetes si workload me parametrat e caktuar në punë.
Procesi i krijimit dhe konfigurimit të punës në Jenkins nuk do ta shqyrtojmë, është jashtë temës. Do të nisim me supozimin që puna është e gatshme. Për realizimin e listës që përditësohet me versionet, na nevojiten dy gjëra: një listë burimi me numra versioni që janë parimisht të vlefshëm dhe një variabël e tipit Choice parameter në punë. Në shembullin tonë, le të quhet variabli BUILD_VERSION, por nuk do ta shqyrtojmë atë në detaje. Tani, le të ndalemi më në detaje te lista burim.
Nuk ka aq shumë variante. Më vijnë menjëherë në mendje dy:
- Të përdorim Remote access API, që Jenkins ofron për përdoruesit e tij;
- Të kërkojmë përmbajtjen e dosjes së largët të depozitës (në rastin tonë, kjo është JFrog Artifactory, që nuk është thelbësore).
Jenkins Remote access API
Për të respektuar traditën e shkëlqyer, preferoj të shmang shpjegimet e gjata.
Do të lejoj vetes një përkthim të lirë të një pjese të paragrafin të parë :
Jenkins ofron API për qasje të largët të kuptueshme për makinat në funksionalitetin e tij. Aksesi i largët ofrohet në stilin e ngjashëm me REST. Kjo do të thotë se nuk ka një pikë të vetme hyrjeje për të gjitha mundësitë, por në vend të saj përdoret një URL e llojit "…/api/", ku "…" përfaqëson objektin që i zbatohen funksionalitetet e API.
Në fjalë të tjera, nëse puna për deploy, për të cilën po flasim tani, është e arritshme në adresën http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build, API-të e këtij detyrimi janë të disponueshme në adresë http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/
Më pas kemi një zgjedhje se në cilin format të kemi daljen. Do të ndalemi te XML, pasi API lejon filtrimin vetëm në këtë rast.
Le të provojmë thjesht të marrim një listë të të gjithë ekzekutimeve të detyrës. Na intereson vetëm emri i ndërtimit (displayName) dhe rezultati i tij (result):
http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]I arritëm?
Tani do të filtrojmë vetëm ato ekzekutime që përfundimisht kanë rezultatin SUCCESS. Përdorim argumentin &exclude dhe si parametrin do t’i japim rrugën për një vlerë që nuk është e barabartë me SUCCESS. Po, po. Negativimi i dyfishtë është pohim. Përjashtojmë gjithçka që nuk na intereson:
http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]&exclude=freeStyleProject/allBuild[result!='SUCCESS'] Captura e listës së suksesshme

Dhe thjesht për argëtim, le të sigurohemi që filtri nuk na ka mashtuar (filtrat nuk gënjejnë kurrë!) dhe të nxjerrim listën e 'jo-suksesshme':
http://jenkins.mybuild.er/view/AweSomeApp/job/AweSomeApp_build/api/xml?tree=allBuilds[displayName,result]&exclude=freeStyleProject/allBuild[result='SUCCESS'] Captura e listës jo-suksesshme

Lista e versioneve nga folderi në serverin e largët
Ka edhe një mënyrë tjetër për të marrë listën e versioneve. Më pëlqen madje më shumë se sa të kërkoj në API-në e Jenkins. Sepse nëse aplikacioni është ndërtuar me sukses, atëherë është paketuar dhe vendosur në depo në folderin përkatës. Pra, depoja është fillimisht magazinë për versionet e punës së aplikacioneve. Pra, le të shohim se cilat versione janë në ruajtje. Do ta curl'ojmë, grep'ajmë dhe awk'ajmë folderin e largët. Nëse dikujt i intereson një one-liner, ai është nën spoiler.
Komanda një linjë
Kujdesuni për dy gjëra: po i kaloj akreditivët për lidhjen në header dhe nuk më nevojiten të gjitha versionet e folderit, dhe unë po filtroj vetëm ato që janë krijuar brenda një muaji. Redaktoni komandën në përputhje me realizimet dhe nevojat tuaja:
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[^/]+)Konfigurimi i detyrave dhe skedari i konfigurimit të detyrës në Jenkins
Tani që mësuam për burimin e listës së versioneve, le të inkuadrojmë listën në detyrë. Për mua, zgjidhja e dukshme ishte të shtoj një hap në detyrën për ndërtimin e aplikacionit. Një hap që do të ekzekutohej në rastin e rezultatit 'sukses'.
Hapim cilësimet e detyrës për ndërtimin dhe skrollojmë në fund. Klikojmë mbi butonat: Shto hap ndërtimi -> Hapi kusht (i vetëm). Në cilësimet e hapit zgjedhim kushtin Statusi aktual i ndërtimit, vendosim vlerën SUCCESS, veprimi që do të realizohet në rast suksesi Kryej komandën në shell.
Dhe tani gjëja më interesante. Konfiguracionet e detyrave Jenkins ruhen në skedarë. Në formatin XML. Në rrugën http://rruga-deri-tek-detyrimi/config.xml Përkatësisht, mund të shkarkoni skedarin e konfigurimit, ta redaktoni siç duhet dhe ta vendosni përsëri aty ku e morët.
Mos e harroni, më sipër ne ra dakord që për listën e versioneve do të krijojmë një parametër BUILD_VERSION?
Le të shkarkojmë skedarin e konfigurimit dhe të shikojmë brenda tij. Thjesht për të qenë të sigurt që parametri është në vend dhe është vërtet i nevojshëm.
Screenshot-i nën spoiler.
Fragmenti i dhënë i config.xml duhet të duket ashtu siç duhet. Me përjashtim të faktit se përmbajtja e elementit choices aktualisht mungon

U siguruat? Mirë, le të shkruajmë një skript që do të ekzekutohet në rastin e ndërtimit të suksesshëm.
Skripti do të marrë listën e versioneve, do të shkarkojë skedarin e konfigurimit, do të shkruajë në të listën e versioneve në vendin e nevojshëm, dhe pastaj do ta vendosë përsëri. Po. E gjitha është e saktë. Të shkruajmë listën e versioneve në XML aty ku tashmë ekziston lista e versioneve (do të jetë në të ardhmen, pas fillimit të parë të skriptit). E di, në botë ende ekzistojnë adhurues të fortë të shprehjeve të rregullta. Unë nuk ndahem prej tyre. Ju lutem, instaloni në atë makinë ku do të redaktohet konfigurimi. Mendoj se nuk është një tarifë aq e madhe për të shmangur redaktimin e XML me sed.
Nën spoiler po jap kodin që realizon tërë sekuencën e përshkruar më sipër.
Po shkruajmë në konfigurim listën e versioneve nga folderi në serverin e largët
#!/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.xmlNëse ju pëlqeu më shumë opsioni me marrjen e versioneve nga Jenkins dhe jeni po aq të lenë sa unë, atëherë nën spoiler është i njëjti kod, por lista nga Jenkins:
Po shkruajmë në konfigurim listën e versioneve nga Jenkins
Vetëm kini parasysh momentin: emri i ndërtimit është përbërë nga numri radhor dhe numri i versionit, të ndara me një dy pika. Prandaj, awk prish pjesën që nuk na nevojitet. Ndryshoni këtë rresht sipas nevojave tuaja.
#!/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.xmlNë teori, nëse keni provuar kodin e shkruar në përputhje me shembujt e mësipërm, atëherë në detyrën e depolimit duhet të keni tashmë një listë rëvizje me versionet. Kështu diku si në screenshot-in nën spoiler.
Lista e plotësuar saktë e versioneve

Nëse gjithçka ka funksionuar, atëherë kopjoni skriptin në Kryej komandën në shell dhe ruani ndryshimet.
Konektimi në Cloud shell
Koleksionistët tanë janë në kontejnerë. Për të dorëzuar aplikacione dhe si menaxher konfiguracionesh, përdorim Ansible. Prandaj, kur flitet për ndërtimin e kontejnerëve, në mendje më vijnë tri mundësi: të instaloj Docker në Docker, të instaloj Docker në makinën me Ansible, ose të ndërtoj kontejnerët në konsolën cloud. Rreth pluginëve për Jenkins u dakorduam të heshtim në këtë shënim. A e mbani mend?
Vendosa: mirë, nëse kontejnerët "nga kutia" mund të ndërtohen në konsolën cloud, pse të krijoj një kopsht? Mbaje të pastër, apo jo? Dua të ndërtoj kontejnerë me Jenkins në konsolën cloud dhe pastaj t'i dërgoj ato në kubernetes. Më shumë se kaq, brenda infrastrukturës së Google ka kanale shumë të pasura, që do të ndikojnë pozitivisht në shpejtësinë e ndërtimit.
Për t'u lidhur me konsolën cloud janë të nevojshme dy gjëra: gcloud dhe të drejtat e qasjes në Google Cloud API për atë VM të cilën do ta përdorim për këtë lidhje.
Për ata që planifikojnë të lidhen absolutisht nga jashtë Google Cloud
Google e lejon mundësinë e çaktivizimit të autorizimit interaktiv në shërbimet e tij. Kjo do të lejojë që të lidheni me konsolën edhe nga makinat e kafesë, nëse ato janë nën *nix dhe kanë vetë një konsol.
Nëse ka nevojë që të flas më hollësisht për këtë çështje në këtë shënim — shkruani në komentet. Nëse mblidhen mjaftueshëm vota — do të shkruaj një përditësim mbi këtë temë.
Mënyra më e thjeshtë për të dhënë të drejtat është përmes ndërfaqes së uebit.
- Nd停 një VM, nga e cila do të bëhet lidhja me konsolën cloud.
- Hapni Detajet e VM dhe klikoni Ndryshoni.
- Në fund të faqes së aksesit të VM, zgjidhni fushën e qasjes Qasje e plotë në të gjitha Cloud API.
Shkrep

- Ruani ndryshimet dhe rifilloni VM-në.
Pasi VM të përfundojë ngarkimin, lidheni me të përmes SSH dhe sigurohuni që lidhja të ndodhë pa gabim. Përdorni komandën:
gcloud alpha cloud-shell ssh Një lidhje e suksesshme duket diçka si kjo

Deploy në GKE
Duke pasur parasysh se ne përpiqemi të kalojmë plotësisht në IaC (Infrastrukturë si Kod), dockerfile-t tona ruhen në git. Kjo është njëra anë. Dhe deploy në kubernetes përshkruhet nga një skedari yaml, i cili përdoret vetëm nga ky detyrim, i cili vetë është gjithashtu një lloj kodi. Kjo është ana tjetër. Në përgjithësi, plani është kështu:
- Marrim vlerat e variablave BUILD_VERSION dhe, opcionalisht, vlerat e variableve që do të kalohen përmes ENV.
- Shkarkojmë skedarin docker nga giti.
- Gjenerojmë yaml për distribucionin.
- Ngarko këto dy skedarë përmes scp në konsolën e cloud.
- Ndërtojmë atje kontejnerin dhe e dërgojmë atë në Container registry
- Për aplikimin e skedarit të distribucionit në Kubernetes.
Të jemi më konkretë. Tani që e përmendëm ENV, le të supozojmë se na nevojitet të kalojmë vlerat e dy parametrave: PARAM1 dhe PARAM2. I shtojmë ata si detyra për aplikimin, lloji — String Parameter.
Shkrep

Do ta gjenerojmë yaml me një drejtim të thjeshtë echo në skedarin. Supozohet, natyrisht, që në skedarin docker keni PARAM1 dhe PARAM2, që emri i ngarkesës do të jetë awesomeapp, ndërsa kontejneri i ndërtuar me versionin e përcaktuar ndodhet në Container registry në rrugën gcr.io/awesomeapp/awesomeapp-$BUILD_VERSION, ku $BUILD_VERSION u zgjodh pikërisht nga lista e rënies.
Lista e komandave
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.yamlAgjenti i Jenkins pas lidhjes me gcloud alpha cloud-shell ssh modaliteti interaktiv nuk është i disponueshëm, prandaj kalojmë komandat në konsolën e cloud përmes parametrave —command.
Pastroni dosjen e shtëpisë në konsolën e cloud nga skedari docker i vjetër:
gcloud alpha cloud-shell ssh --command="rm -f Dockerfile"Vendosim skedarin docker të shkarkuar së fundmi në dosjen e shtëpisë në konsolën e cloud përmes scp:
gcloud alpha cloud-shell scp localhost:./Dockerfile cloudshell:~Ndërtojmë, etiketojmë dhe dërgojmë kontejnerin në 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"
Na jepet një qasje të ngjashme me skedarin e distribucionit. Vini re se në komandat më poshtë përdoren emra imagjinarë të klasës, ku po bëhet distribuimi (awsm-cluster) dhe emri i projektit (awesome-project), ku ndodhet klasteri.
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"Ne fillojmë detyrën, hapim daljen e konsolës dhe shpresojmë të shohim një ndërtim të suksesshëm të konteinerit.
Shkrep

Dhe pastaj, një depolim të suksesshëm të konteinerit të ndërtuar.
Shkrep

Qëllimisht e kam anashkaluar konfigurimin Ingress. Për një arsye të thjeshtë: një herë e konfiguroni me emrin e caktuar, ai do të mbetet funksional, pavarësisht se sa depolimesh me atë emër bëni. Në përgjithësi, kjo është pak jashtë historisë. workload Të gjitha hapat e përmendur më sipër, ndoshta mund të mos ishin bërë, por thjesht mund të kishte vendosur ndonjë plugin për Jenkins-in, janë mijëra. Por për një arsye, nuk i pëlqej plugins. Në fakt, i përdor ato vetëm nga dëshpërimi.
Në vend të rezultateve
Gjithashtu, thjesht më pëlqen të shqyktoj temën e re. Teksti më sipër është, po ashtu, një mënyrë për të ndarë gjetjet që kam bërë, duke zgjidhur detyrën e përshkruar në fillim. Të ndaj me ata që, ashtu si unë, nuk janë aspak willi në devops. Nëse gjetjet e mia ndihmojnë edhe një person, do të jem i kënaqur.
Po realizojmë një detyrë për depolim në GKE pa plugins, sms dhe regjistrim. Një sy të hedhim Jenkins-it nën xhaketë.
Burimi: habr.com

