ProHoster > blogg > administration > Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes
Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes
Hallå! Nyligen har många coola automationsverktyg släppts både för att bygga Docker-avbildningar och för distribution till Kubernetes. I detta avseende bestämde jag mig för att leka med GitLab, noggrant studera dess kapacitet och, naturligtvis, sätta upp pipelinen.
Detta arbete har inspirerats av webbplatsen kubernetes.io, som genereras från källkoder automatiskt, och för varje poolförfrågan som skickas genererar roboten automatiskt en förhandsgranskningsversion av webbplatsen med dina ändringar och ger en länk för visning.
Jag försökte bygga en liknande process från grunden, men helt byggd på Gitlab CI och gratisverktyg som jag är van vid att använda för att distribuera applikationer till Kubernetes. Idag ska jag äntligen berätta mer om dem.
Artikeln kommer att diskutera verktyg som: Hugo, qbec, kaniko, git-krypt и GitLab CI med skapandet av dynamiska miljöer.
Som ett exempel på vårt projekt kommer vi att försöka skapa en dokumentationspubliceringssajt byggd på Hugo. Hugo är en statisk innehållsgenerator.
För dem som inte är bekanta med statiska generatorer, ska jag berätta lite mer om dem. Till skillnad från konventionella webbplatsmotorer med en databas och viss PHP, som, när en användare begär det, genererar sidor i farten, är statiska generatorer designade lite annorlunda. De låter dig ta källor, vanligtvis en uppsättning filer i Markdown-markerings- och temamallar, och sedan kompilera dem till en helt färdig webbplats.
Det vill säga, som ett resultat kommer du att få en katalogstruktur och en uppsättning genererade HTML-filer, som du helt enkelt kan ladda upp till ett billigt webbhotell och få en fungerande webbplats.
Du kan installera Hugo lokalt och prova det:
Initiera en ny webbplats:
hugo new site docs.example.org
Och samtidigt git-förrådet:
cd docs.example.org
git init
Än så länge är vår sida orörd och för att något ska visas på den måste vi först koppla ett tema; ett tema är bara en uppsättning mallar och specificerade regler som vår sida genereras av.
För det tema vi kommer att använda Lär, som enligt mig är perfekt lämpad för en dokumentationssajt.
Jag skulle vilja ägna särskild uppmärksamhet åt det faktum att vi inte behöver spara temafilerna i vårt projektförråd, utan vi kan helt enkelt ansluta det med hjälp av git -submodul:
Således kommer vårt arkiv endast att innehålla filer som är direkt relaterade till vårt projekt, och det anslutna temat kommer att finnas kvar som en länk till ett specifikt arkiv och en commit i det, det vill säga det kan alltid hämtas från den ursprungliga källan och inte vara rädd för oförenliga ändringar.
Låt oss korrigera konfigurationen config.toml:
baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"
Redan i detta skede kan du köra:
hugo server
Och på adressen http://localhost:1313/ kolla vår nyskapade webbplats, alla ändringar som görs i katalogen uppdaterar automatiskt den öppna sidan i webbläsaren, mycket bekvämt!
Låt oss försöka skapa ett försättsblad i content/_index.md:
# My docs site
## Welcome to the docs!
You will be very smart :-)
Skärmdump av den nyskapade sidan
För att skapa en webbplats, kör bara:
hugo
Kataloginnehåll offentlig/ och kommer att vara din webbplats.
Ja, förresten, låt oss genast lägga till det .gitignore:
echo /public > .gitignore
Glöm inte att genomföra våra ändringar:
git add .
git commit -m "New site created"
2. Förbereder dockerfilen
Det är dags att definiera strukturen för vårt förvar. Jag brukar använda något som:
dockerfiler/ — innehåller kataloger med Dockerfiler och allt som behövs för att bygga våra Docker-bilder.
distribuera/ — innehåller kataloger för att distribuera våra applikationer till Kubernetes
Således kommer vi att skapa vår första Dockerfile längs vägen dockerfiler/webbplats/Dockerfil
FROM alpine:3.11 as builder
ARG HUGO_VERSION=0.62.0
RUN wget -O- https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_${HUGO_VERSION}_linux-64bit.tar.gz | tar -xz -C /usr/local/bin
ADD . /src
RUN hugo -s /src
FROM alpine:3.11
RUN apk add --no-cache darkhttpd
COPY --from=builder /src/public /var/www
ENTRYPOINT [ "/usr/bin/darkhttpd" ]
CMD [ "/var/www" ]
Som du kan se innehåller Dockerfilen två FRÅN, kallas denna möjlighet flerstegsbyggande och låter dig utesluta allt onödigt från den slutliga docker-bilden.
Den slutliga bilden kommer alltså bara att innehålla mörkhttpd (lätt HTTP-server) och offentlig/ — innehållet på vår statiskt genererade webbplats.
Glöm inte att genomföra våra ändringar:
git add dockerfiles/website
git commit -m "Add Dockerfile for website"
3. Lär känna kaniko
Som en docker-bildbyggare bestämde jag mig för att använda kaniko, eftersom dess operation inte kräver en docker-demon, och själva byggandet kan utföras på vilken maskin som helst och cachen kan lagras direkt i registret, vilket eliminerar behovet av att ha en fullfjädrad beständig lagring.
För att bygga bilden, kör bara behållaren med kaniko exekutor och skicka den nuvarande byggkontexten; detta kan också göras lokalt, via docker:
var registry.gitlab.com/kvaps/docs.example.org/website — namnet på din docker-avbildning; efter byggandet kommer den automatiskt att startas i docker-registret.
Parameter --cache låter dig cachelagra i docker-registret; för exemplet kommer de att sparas i registry.gitlab.com/kvaps/docs.example.org/website/cache, men du kan ange en annan sökväg med parametern --cache-repo.
Skärmdump av docker-registry
4. Lär känna qbec
Qbec är ett distributionsverktyg som låter dig deklarativt beskriva dina programmanifest och distribuera dem till Kubernetes. Genom att använda Jsonnet som huvudsyntax kan du avsevärt förenkla beskrivningen av skillnader mellan flera miljöer och eliminerar också nästan helt kodupprepning.
Detta kan vara särskilt sant i de fall där du behöver distribuera en applikation till flera kluster med olika parametrar och vill beskriva dem deklarativt i Git.
Qbec låter dig också återge Helm-diagram genom att skicka de nödvändiga parametrarna till dem och sedan använda dem på samma sätt som vanliga manifest, inklusive att du kan tillämpa olika mutationer på dem, och detta i sin tur gör att du kan bli av med behovet av att använd ChartMuseum. Det vill säga, du kan lagra och rendera diagram direkt från git, där de hör hemma.
Som jag sa tidigare kommer vi att lagra alla distributioner i en katalog distribuera/:
Här är vi främst intresserade av spec.miljöer, qbec har redan skapat en standardmiljö åt oss och tagit serveradressen, samt namnutrymmet från vår nuvarande kubeconfig.
Nu när du distribuerar till standard miljö, kommer qbec alltid att distribuera endast till det angivna Kubernetes-klustret och till det angivna namnutrymmet, det vill säga du behöver inte längre växla mellan sammanhang och namnutrymmen för att utföra en distribution.
Om det behövs kan du alltid uppdatera inställningarna i den här filen.
Alla dina miljöer beskrivs i qbec.yaml, och i filen params.libsonnet, där det står var man kan hämta parametrarna för dem.
Därefter ser vi två kataloger:
komponenter / — alla manifest för vår applikation kommer att lagras här; de kan beskrivas både i jsonnet och vanliga yaml-filer
miljöer/ — här kommer vi att beskriva alla variabler (parametrar) för våra miljöer.
Som standard har vi två filer:
miljöer/base.libsonnet - den kommer att innehålla gemensamma parametrar för alla miljöer
miljöer/default.libsonnet — innehåller parametrar som åsidosatts för miljön standard
låt oss öppna miljöer/base.libsonnet och lägg till parametrar för vår första komponent där:
I den här filen beskrev vi tre Kubernetes-enheter samtidigt, dessa är: konfiguration, Service и Ingress. Om vi ville skulle vi kunna lägga in dem i olika komponenter, men i det här skedet räcker det med en för oss.
syntax jsonnet är väldigt lik vanlig json, i princip är vanlig json redan giltig jsonnet, så till en början kan det vara lättare för dig att använda onlinetjänster som yaml2json för att konvertera din vanliga yaml till json, eller, om dina komponenter inte innehåller några variabler, kan de beskrivas i form av vanlig yaml.
När man arbetar med jsonnet Jag rekommenderar starkt att du installerar ett plugin för din editor
Till exempel finns det ett plugin för vim vim-jsonnet, som aktiverar syntaxmarkering och körs automatiskt jsonnet fmt varje gång du sparar (kräver jsonnet installerat).
Allt är klart, nu kan vi börja distribuera:
För att se vad vi har, låt oss köra:
qbec show default
Vid utgången kommer du att se renderade yaml-manifest som kommer att tillämpas på standardklustret.
Bra, tillämpa nu:
qbec apply default
Vid utgången kommer du alltid att se vad som kommer att göras i ditt kluster, qbec kommer att be dig godkänna ändringarna genom att skriva y du kommer att kunna bekräfta dina avsikter.
Vår applikation är klar och distribuerad!
Om du gör ändringar kan du alltid göra:
qbec diff default
för att se hur dessa ändringar kommer att påverka den nuvarande implementeringen
Glöm inte att genomföra våra ändringar:
cd ../..
git add deploy/website
git commit -m "Add deploy for website"
5. Testar Gitlab-runner med Kubernetes-executor
Tills nyligen använde jag bara vanliga gitlab-runner på en förberedd maskin (LXC-container) med skal eller docker-executor. Från början hade vi flera sådana löpare globalt definierade i vårt gitlab. De samlade in docker-bilder för alla projekt.
Men som praktiken har visat är det här alternativet inte det mest idealiska, både när det gäller praktiskt och säkerhet. Det är mycket bättre och mer ideologiskt korrekt att ha separata löpare utplacerade för varje projekt, eller till och med för varje miljö.
Lyckligtvis är detta inte ett problem alls, eftersom vi nu kommer att distribuera gitlab-runner direkt som en del av vårt projekt i Kubernetes.
Gitlab tillhandahåller ett färdigt styrdiagram för att distribuera gitlab-runner till Kubernetes. Så allt du behöver göra är att ta reda på det registreringsbevis för vårt projekt i Inställningar -> CI / CD -> Löpare och skicka den till rodret:
yga8y-jdCusVDn_t4Wxc — registreringsbevis för ditt projekt.
rbac.create=true — ger löparen den nödvändiga mängden privilegier för att kunna skapa pods för att utföra våra uppgifter med kubernetes-executor.
Om allt är gjort korrekt bör du se en registrerad löpare i avsnittet Runners, i dina projektinställningar.
Skärmdump av den tillagda löparen
Är det så enkelt? – ja, så enkelt är det! Inget mer krångel med att registrera löpare manuellt, från och med nu kommer löpare att skapas och förstöras automatiskt.
6. Distribuera roderdiagram med QBEC
Sedan vi bestämde oss för att överväga gitlab-runner en del av vårt projekt är det dags att beskriva det i vårt Git-förråd.
Vi skulle kunna beskriva det som en separat komponent webbplats, men i framtiden planerar vi att distribuera olika kopior webbplats väldigt ofta, till skillnad från gitlab-runner, som endast kommer att distribueras en gång per Kubernetes-kluster. Så låt oss initiera en separat applikation för det:
cd deploy
qbec init gitlab-runner
cd gitlab-runner
Den här gången kommer vi inte att beskriva Kubernetes-entiteter manuellt, utan tar ett färdigt Helm-diagram. En av fördelarna med qbec är möjligheten att rendera Helm-diagram direkt från ett Git-förråd.
local env = {
name: std.extVar('qbec.io/env'),
namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.gitlabRunner;
std.native('expandHelmTemplate')(
'../vendor/gitlab-runner',
params.values,
{
nameTemplate: params.name,
namespace: env.namespace,
thisFile: std.thisFile,
verbose: true,
}
)
Det första argumentet till expandHelmTemplate då passerar vi vägen till sjökortet params.värden, som vi tar från miljöparametrarna, kommer sedan objektet med
namnmall — utgivningstitel
namespace — namnutrymme överfört till rodret
den här filen — en obligatorisk parameter som skickar sökvägen till den aktuella filen
mångordig - visar kommandot rodret mall med alla argument vid rendering av diagrammet
Låt oss nu beskriva parametrarna för vår komponent i miljöer/base.libsonnet:
Men att lagra hemligheter i Git är väl inte säkert? Så vi måste kryptera dem ordentligt.
Vanligtvis, för en variabels skull, är detta inte alltid vettigt. Du kan överföra hemligheter till qbec och genom miljövariablerna i ditt CI-system.
Men det är värt att notera att det också finns mer komplexa projekt som kan innehålla många fler hemligheter; att överföra dem alla genom miljövariabler kommer att vara extremt svårt.
Dessutom skulle jag i det här fallet inte kunna berätta om ett så underbart verktyg som git-krypt.
git-krypt Det är också bekvämt eftersom det låter dig spara hela historien om hemligheter, samt jämföra, slå samman och lösa konflikter på samma sätt som vi är vana vid att göra när det gäller Git.
Det första efter installationen git-krypt vi behöver generera nycklar för vårt arkiv:
git crypt init
Om du har en PGP-nyckel kan du omedelbart lägga till dig själv som medarbetare för detta projekt:
På så sätt kan du alltid dekryptera detta förråd med din privata nyckel.
Om du inte har en PGP-nyckel och inte förväntar dig det, kan du gå åt andra hållet och exportera projektnyckeln:
git crypt export-key /path/to/keyfile
Alltså alla som har en exporterad nyckelfil kommer att kunna dekryptera ditt arkiv.
Det är dags att skapa vår första hemlighet.
Låt mig påminna dig om att vi fortfarande finns i katalogen deploy/gitlab-runner/, där vi har en katalog hemligheter/, låt oss kryptera alla filer i den, för detta skapar vi en fil secrets/.gitattributes med följande innehåll:
Som framgår av innehållet är alla filer maskerade * kommer att köras igenom git-krypt, förutom de flesta .gitattributes
Vi kan kontrollera detta genom att köra:
git crypt status -e
Utdata kommer att vara en lista över alla filer i arkivet för vilka kryptering är aktiverad
Det är allt, nu kan vi säkert genomföra våra ändringar:
cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"
För att blockera ett arkiv, kör bara:
git crypt lock
och omedelbart kommer alla krypterade filer att förvandlas till binärt något, det kommer att vara omöjligt att läsa dem.
För att dekryptera förvaret, kör:
git crypt unlock
8. Skapa en verktygslåda-bild
En verktygslådanbild är en bild med alla verktyg som vi kommer att använda för att distribuera vårt projekt. Den kommer att användas av Gitlab-löparen för att utföra typiska distributionsuppgifter.
Allt är enkelt här, låt oss skapa en ny dockerfiles/toolbox/Dockerfile med följande innehåll:
FROM alpine:3.11
RUN apk add --no-cache git git-crypt
RUN QBEC_VER=0.10.3
&& wget -O- https://github.com/splunk/qbec/releases/download/v${QBEC_VER}/qbec-linux-amd64.tar.gz
| tar -C /tmp -xzf -
&& mv /tmp/qbec /tmp/jsonnet-qbec /usr/local/bin/
RUN KUBECTL_VER=1.17.0
&& wget -O /usr/local/bin/kubectl
https://storage.googleapis.com/kubernetes-release/release/v${KUBECTL_VER}/bin/linux/amd64/kubectl
&& chmod +x /usr/local/bin/kubectl
RUN HELM_VER=3.0.2
&& wget -O- https://get.helm.sh/helm-v${HELM_VER}-linux-amd64.tar.gz
| tar -C /tmp -zxf -
&& mv /tmp/linux-amd64/helm /usr/local/bin/helm
Som du kan se installerar vi i den här bilden alla verktyg som vi använde för att distribuera vår applikation. Vi behöver det inte här om inte kubectl, men du kanske vill leka med det under installationsfasen för pipeline.
Dessutom, för att kunna kommunicera med Kubernetes och distribuera till den, måste vi konfigurera en roll för poddarna som genereras av gitlab-runner.
För att göra detta, låt oss gå till katalogen med gitlab-runner:
cd deploy/gitlab-runner
och lägg till en ny komponent komponenter/rbac.jsonnet:
Jag tror att vi lugnt kan kalla detta en version v0.0.1 och lägg till taggen:
git tag v0.0.1
Vi kommer att lägga till taggar när vi behöver släppa en ny version. Taggar i Docker-bilder kommer att kopplas till Git-taggar. Varje push med en ny tagg kommer att initiera byggandet av bilder med denna tagg.
Vi gör det git push --taggar, och låt oss titta på vår första pipeline:
Skärmdump av den första pipelinen
Det är värt att uppmärksamma det faktum att montering med taggar är lämpligt för att bygga docker-bilder, men är inte lämpligt för att distribuera en applikation till Kubernetes. Eftersom nya taggar kan tilldelas gamla commits, i det här fallet, kommer initialisering av pipelinen för dem att leda till distributionen av den gamla versionen.
För att lösa detta problem är byggandet av docker-bilder vanligtvis kopplat till taggar och distributionen av applikationen till en filial Master, där versioner av de insamlade bilderna är hårdkodade. Det är här du kan initiera återställning med en enkel återställning Master-grenar.
10. Automatisering av driftsättning
För att Gitlab-runner ska dekryptera våra hemligheter måste vi exportera förvarsnyckeln och lägga till den i våra CI-miljövariabler:
Här har vi aktiverat flera nya alternativ för qbec:
--root some/app — låter dig bestämma katalogen för ett specifikt program
--force:k8s-context __incluster__ - det här är en magisk variabel som säger att utplaceringen kommer att ske i samma kluster som gtilab-runner körs i. Detta är nödvändigt eftersom qbec annars kommer att försöka hitta en lämplig Kubernetes-server i din kubeconfig
--vänta — tvingar qbec att vänta tills resurserna den skapar går in i Klar-tillståndet och avslutar först sedan med en framgångsrik exit-kod.
-ja - inaktiverar helt enkelt det interaktiva skalet Är du säker? vid utplacering.
Och efter git push vi kommer att se hur våra applikationer har distribuerats:
Skärmdump av den andra pipelinen
11. Artefakter och montering när man trycker till master
Vanligtvis är stegen som beskrivs ovan tillräckliga för att bygga och leverera nästan alla mikrotjänster, men vi vill inte lägga till en tagg varje gång vi behöver uppdatera webbplatsen. Därför kommer vi att ta en mer dynamisk väg och sätta upp en sammanfattningsinstallation i mastergrenen.
Tanken är enkel: nu bilden av vår webbplats kommer att byggas om varje gång du trycker in Master, och distribuera sedan automatiskt till Kubernetes.
Låt oss uppdatera dessa två jobb i vår .gitlab-ci.yml:
Observera att vi har lagt till en tråd Master к refs för jobb build_website och vi använder nu $CI_COMMIT_REF_NAME istället för $CI_COMMIT_TAG, det vill säga vi är obundna från taggar i Git och nu kommer vi att skjuta en bild med namnet på commit-grenen som initierade pipelinen. Det är värt att notera att detta också kommer att fungera med taggar, vilket gör att vi kan spara ögonblicksbilder av en webbplats med en specifik version i docker-registret.
När namnet på docker-taggen för en ny version av webbplatsen kan vara oförändrat måste vi fortfarande beskriva ändringarna i Kubernetes, annars kommer den helt enkelt inte att omdistribuera applikationen från den nya bilden, eftersom den inte kommer att märka några ändringar i implementeringsmanifest.
alternativ —vm:ext-str digest="$DIGEST" för qbec - låter dig skicka en extern variabel till jsonnet. Vi vill att den med varje version av vår applikation ska distribueras om i klustret. Vi kan inte längre använda taggnamnet, som nu kan vara oföränderligt, eftersom vi måste vara knutna till en specifik version av bilden och utlösa implementeringen när den ändras.
Här kommer vi att få hjälp av Kanikos förmåga att spara en sammanfattningsbild till en fil (alternativ --digest-fil)
Sedan kommer vi att överföra den här filen och läsa den vid tidpunkten för distributionen.
Låt oss uppdatera parametrarna för vår deploy/website/environments/base.libsonnet som nu kommer att se ut så här:
Klart, nu är alla åtaganden Master initierar bygget av docker-bilden för webbplats, och distribuera den sedan till Kubernetes.
Glöm inte att genomföra våra ändringar:
git add .
git commit -m "Configure dynamic build"
Vi kollar senare git push vi borde se något sånt här:
Skärmdump av pipeline för master
I princip behöver vi inte distribuera om gitlab-runner med varje push, såvida inte, naturligtvis, ingenting har ändrats i dess konfiguration, låt oss fixa det i .gitlab-ci.yml:
Det är dags att diversifiera vår pipeline med dynamiska miljöer.
Låt oss först uppdatera jobbet build_website i vår .gitlab-ci.yml, ta bort blocket från det endast, vilket kommer att tvinga Gitlab att trigga det på alla commit till vilken gren:
De kommer att lanseras vid push till alla grenar utom master och kommer att distribuera förhandsgranskningsversionen av webbplatsen.
Vi ser ett nytt alternativ för qbec: --app-tagg — det låter dig tagga distribuerade versioner av applikationen och endast arbeta inom den här taggen; när du skapar och förstör resurser i Kubernetes kommer qbec endast att arbeta med dem.
På så sätt kan vi inte skapa en separat miljö för varje recension, utan helt enkelt återanvända samma.
Här använder vi också qbec tillämpa granskning, istället för qbec tillämpa standard - detta är exakt det ögonblick då vi kommer att försöka beskriva skillnaderna för våra miljöer (granskning och standard):
Låt oss lägga till översyn miljö i deploy/website/qbec.yaml
Då deklarerar vi det deploy/website/params.libsonnet:
local env = std.extVar('qbec.io/env');
local paramsMap = {
_: import './environments/base.libsonnet',
default: import './environments/default.libsonnet',
review: import './environments/review.libsonnet',
};
if std.objectHas(paramsMap, env) then paramsMap[env] else error 'environment ' + env + ' not defined in ' + std.thisFile
Och skriv ner de anpassade parametrarna för den deploy/website/environments/review.libsonnet:
// this file has the param overrides for the default environment
local base = import './base.libsonnet';
local slug = std.extVar('qbec.io/tag');
local subdomain = std.extVar('subdomain');
base {
components+: {
website+: {
name: 'example-docs-' + slug,
domain: subdomain + '.docs.example.org',
},
},
}
Låt oss också titta närmare på jobu stop_review, kommer den att triggas när grenen tas bort och för att gitlab inte ska försöka checka ut används den GIT_STRATEGY: ingen, senare klonar vi Master-förgrena och ta bort recension genom den.
Det är lite förvirrande, men jag har inte hittat ett vackrare sätt än.
Ett alternativt alternativ skulle vara att distribuera varje recension till ett hotellnamnområde, som alltid kan rivas helt.
Allt fungerar? - bra, ta bort vår testgren: git checkout mästare, git push ursprung :test, kontrollerar vi att jobben för att ta bort miljön fungerade utan fel.
Här vill jag omedelbart klargöra att vilken utvecklare som helst i ett projekt kan skapa grenar, han kan också ändra .gitlab-ci.yml fil och åtkomst till hemliga variabler.
Därför rekommenderas det starkt att endast tillåta deras användning för skyddade grenar, till exempel i Master, eller skapa en separat uppsättning variabler för varje miljö.
13. Granska appar
Granska appar Detta är en GitLab-funktion som låter dig lägga till en knapp för varje fil i förvaret för att snabbt visa den i en distribuerad miljö.
För att dessa knappar ska visas måste du skapa en fil .gitlab/route-map.yml och beskriv alla vägtransformationer i den; i vårt fall kommer det att vara väldigt enkelt: