
Hej! Under den senaste tiden har mÄnga fantastiska automatiseringsverktyg slÀppts bÄde för att bygga Docker-imagen och för att deploya till Kubernetes. DÀrför bestÀmde jag mig för att experimentera med GitLab, studera dess möjligheter ordentligt och sjÀlvklart konfigurera pipeline.
Inspirationen för detta arbete kom frÄn webbplatsen , som genereras automatiskt, och för varje inskickad pull-begÀran genererar roboten automatiskt en förhandsvisningsversion av webbplatsen med dina Àndringar och tillhandahÄller en lÀnk för att visa den. Jag har försökt bygga en liknande process frÄn grunden, men helt baserad pÄ GitLab CI och de fria verktyg som jag Àr van vid att anvÀnda för att deploya applikationer i Kubernetes. Idag kommer jag Àntligen att berÀtta mer om dem.
I artikeln kommer vi att gÄ igenom verktyg som:
kaniko
Hugo, qbec, med skapandet av dynamiska miljöer., git-crypt och GitLab CI InnehÄll
Introduktion till Hugo
Som exempel pÄ vÄrt projekt kommer vi att försöka skapa en webbplats för publicering av dokumentation, 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 vanliga webbplatser med databaser och nÄgot php, som genererar sidor i realtid vid anvÀndarförfrÄgningar, Àr statiska generatorer uppbyggda pÄ ett lite annat sÀtt. De lÄter en ta kÀllkoden, som vanligtvis Àr ett paket med Markdown-filer och temablanketter, och sedan kompilera dem till en helt fÀrdig webbplats.
Det vill sÀga, du fÄr en mappstruktur och en uppsÀttning genererade html-filer som du enkelt kan ladda upp pÄ valfri billig webbhotell och fÄ en fungerande webbplats.
Hugo kan installeras lokalt och prova pÄ riktigt:
Initiera en ny webbplats:
hugo new site docs.example.org
Och ocksÄ ett git-repo:cd docs.example.org git init
Tillsammans Àr vÄr webbplats fortfarande jungfrulig och för att nÄgot ska dyka upp pÄ den behöver vi först koppla ett tema, vilket bara Àr en uppsÀttning mallar och angivna regler som webbplatsen genereras utifrÄn.VÄr webbplats Àr fortfarande orörd, och för att nÄgot ska dyka upp pÄ den behöver vi först aktivera ett tema. Ett tema Àr bara en uppsÀttning mallar och regler enligt vilka vÄr webbplats genereras.
Som tema kommer vi att anvÀnda , vilket, enligt mig, passar perfekt för en dokumentationssajt.
Vi vill sÀrskilt nÀmna att vi inte behöver spara temafilerna i vÄrt projekts repository; istÀllet kan vi enkelt ansluta den med git submodule:
git submodule add https://github.com/matcornic/hugo-theme-learn themes/learnSÄledes kommer vÄrt repository endast att innehÄlla filer som direkt relaterar till vÄrt projekt, och det anslutna temat kommer att hÄllas som en lÀnk till ett specifikt repository och commit dÀr, vilket innebÀr att det alltid kan hÀmtas frÄn den ursprungliga kÀllan utan oro för inkompatibla Àndringar.
LÄt oss justera konfigurationen config.toml:
baseURL = "http://docs.example.org/"
languageCode = "sv-se"
title = "Min dokumentationssajt"
theme = "learn"Redan vid detta skede kan vi starta:
hugo serverOch vid adressen kontrollera vÄr nyss skapade webbplats; alla Àndringar som görs i katalogen uppdaterar automatiskt den öppna sidan i webblÀsaren, mycket praktiskt!
LÄt oss försöka skapa en titelns sida i content/_index.md:
# My docs site
## Welcome to the docs!
You will be very smart :-)SkÀrmdump av den just skapade sidan

För att generera webbplatsen rÀcker det med att köra:
hugoInnehÄllet i katalogen public/ kommer att utgöra din webbplats.
Ja, förresten, lÄt oss direkt lÀgga till den i .gitignore:
echo /public > .gitignoreKom ihÄg att commita vÄra Àndringar:
git add .
git commit -m "Ny webbplats skapad"2. Förbered Dockerfile
Det Àr dags att definiera strukturen för vÄrt repository. Vanligtvis anvÀnder jag nÄgot liknande:
.
âââ deploy
â âââ app1
â âââ app2
âââ dockerfiles
âââ image1
âââ image2- dockerfiles/ â innehĂ„ller kataloger med Dockerfiles och allt som behövs för att bygga vĂ„ra dockerbilder.
- deploy/ â innehĂ„ller kataloger för distribution av vĂ„ra applikationer i Kubernetes.
SÄledes kommer vÄr första Dockerfile att skapas pÄ dockerfiles/website/Dockerfile
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 Dockerfile tvĂ„ FRĂ
N, denna funktion kallas och gör det möjligt att utesluta allt onödigt frÄn den slutliga dockerbilden.
SĂ„ledes kommer den slutliga bilden att innehĂ„lla endast darkhttpd (en lĂ€tt HTTP-server) och public/ â innehĂ„llet pĂ„ vĂ„r statiskt genererade webbplats.
Kom ihÄg att commita vÄra Àndringar:
git add dockerfiles/website
git commit -m "LÀgg till Dockerfile för webbplatsen"3. Introduktion till kaniko
Som byggare av docker-bilder har jag beslutat att anvÀnda , eftersom det inte krÀver en docker-demon för att fungera, och byggprocessen kan genomföras pÄ vilken maskin som helst och cachen kan lagras direkt i registret, vilket eliminerar behovet av att ha en fullstÀndig persistent lagring.
För att bygga bilden rÀcker det med att starta en container med kaniko executor och skicka den aktuella byggkontexten till den, vilket kan göras lokalt via docker:
docker run -ti --rm
-v $PWD: /workspace
-v ~ / .docker / config.json: /kaniko / .docker / config.json:ro
gcr.io / kaniko-project / executor:v0.15.0
--cache
--dockerfile=dockerfiles/website/Dockerfile
--destination=registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1DĂ€r registry.gitlab.com/kvaps/docs.example.org/website â namnet pĂ„ din docker-bild, efter att den har byggts kommer den automatiskt att pushas till docker-registret.
Parameter âcache tillĂ„ter att cachen av lagren sparas i docker-registret, i det ovanstĂ„ende exemplet kommer de att sparas i registry.gitlab.com/kvaps/docs.example.org/website/cache, men du kan ange en annan vĂ€g med hjĂ€lp av parametern âcache-repo.
Screenshot av docker-registry

4. Introduktion till qbec
â Ă€r ett deploymentsverktyg som lĂ„ter dig deklarativt beskriva manifest för din applikation och distribuera dem till Kubernetes. AnvĂ€ndning av Jsonnet som huvudsaklig syntax gör det mycket enklare att beskriva skillnader för flera miljöer och nĂ€stan helt eliminerar kodupprepning.
Detta kan vara sÀrskilt relevant nÀr du behöver distribuera en applikation i flera kluster med olika parametrar och du vill deklarativt beskriva dem i Git.
Qbec lÄter oss ocksÄ rendera Helm-diagram genom att skicka nödvÀndiga parametrar och sedan hantera dem som vanliga manifest, bland annat kan vi applicera olika mutationer pÄ dem, vilket i sin tur eliminerar behovet av att anvÀnda ChartMuseum. Det vill sÀga, vi kan lagra och rendera diagram direkt frÄn git, dÀr de verkligen hör hemma.
Som jag nÀmnde tidigare, kommer alla distributioner att lagras i katalogen deploy/:
mkdir deploy
cd deployLÄt oss initiera vÄr första applikation:
qbec init website
cd websiteNu ser strukturen för vÄr applikation ut sÄ hÀr:
.
âââ components
âââ environments
â âââ base.libsonnet
â âââ default.libsonnet
âââ params.libsonnet
âââ qbec.yamllĂ„t oss titta pĂ„ filen qbec.yaml:
apiVersion: qbec.io/v1alpha1
kind: App
metadata:
name: website
spec:
environments:
default:
defaultNamespace: docs
server: https://kubernetes.example.org:8443
vars: {}HÀr Àr vi först och frÀmst intresserade av spec.environments, qbec har redan skapat en standardmiljö Ät oss och tagit serveradressen samt namespace frÄn vÄr aktuella kubeconfig.
Nu, vid distribution i default miljö, kommer qbec alltid att distribuera endast till den angivna Kubernetes-klustret och det angivna namespace, vilket betyder att du inte lÀngre behöver vÀxla mellan kontexter och namespaces för att utföra distribution.
Vid behov kan du alltid uppdatera instÀllningarna i denna fil.
Alla dina miljöer beskrivs i qbec.yaml, och i filen params.libsonnet, dÀr det anges varifrÄn parametrarna ska hÀmtas för dem.
Sedan ser vi tvÄ kataloger:
- components/ â hĂ€r kommer alla manifest för vĂ„r applikation att lagras, de kan beskrivas bĂ„de som jsonnet och som vanliga yaml-filer.
- environments/ â hĂ€r kommer vi att beskriva alla variabler (parametrar) för vĂ„ra miljöer.
Som standard har vi tvÄ filer:
- environments/base.libsonnet â den kommer att innehĂ„lla gemensamma parametrar för alla miljöer.
- environments/default.libsonnet â innehĂ„ller parametrar som Ă€r överskrivna för miljön. default
LÄt oss öppna environments/base.libsonnet och lÀgga till parametrar för vÄr första komponent:
{
components: {
website: {
name: 'example-docs',
image: 'registry.gitlab.com/kvaps/docs.example.org/website:v0.0.1',
replicas: 1,
containerPort: 80,
servicePort: 80,
nodeSelector: {},
tolerations: [],
ingressClass: 'nginx',
domain: 'docs.example.org',
},
},
}Skapa ocksÄ vÄr första komponent components/website.jsonnet:
lokal miljö = {
namn: std.extVar('qbec.io/env'),
namnrymd: std.extVar('qbec.io/defaultNs'),
};
lokal p = import '.. /params.libsonnet';
lokal params = p.components.website;
[
{
apiVersion: 'apps/v1',
typ: 'Deployment',
metadata: {
etiketter: { app: params.name },
namn: params.name,
},
spec: {
repliker: params.replicas,
vÀljare: {
matchLabels: {
app: params.name,
},
},
mall: {
metadata: {
etiketter: { app: params.name },
},
spec: {
containrar: [
{
namn: 'darkhttpd',
bild: params.image,
portar: [
{
containerPort: params.containerPort,
},
],
},
],
nodVĂ€ljare: params.nodeSelector,
toleranser: params.tolerations,
bildHĂ€mtningsHemligheter: [{ namn: 'regsecret' }],
},
},
},
},
{
apiVersion: 'v1',
typ: 'Service',
metadata: {
etiketter: { app: params.name },
namn: params.name,
},
spec: {
vÀljare: {
app: params.name,
},
portar: [
{
port: params.servicePort,
targetPort: params.containerPort,
},
],
},
},
{
apiVersion: 'extensions/v1beta1',
typ: 'Ingress',
metadata: {
anteckningar: {
'kubernetes.io/ingress.class': params.ingressClass,
},
etiketter: { app: params.name },
namn: params.name,
},
spec: {
regler: [
{
vÀrd: params.domain,
http: {
vÀgar: [
{
backend: {
serviceName: params.name,
servicePort: params.servicePort,
},
},
],
},
},
],
},
},
]I den hÀr filen har vi beskrivit tre Kubernetes-entiteter, dessa Àr: Deployment, TjÀnst och Ingress. Om vi ville kunde vi dela upp dem i olika komponenter, men pÄ detta stadium rÀcker det med en.
Syntax jsonnet Àr mycket lik vanlig json; i princip Àr vanlig json redan en giltig jsonnet, sÄ det första tiden kan det vara lÀttare att anvÀnda online-tjÀnster som yaml2json för att konvertera ditt vÀlkÀnda yaml till json, eller, om dina komponenter inte innehÄller nÄgra variabler, kan de mycket vÀl beskrivas i form av vanlig yaml.
NÀr man arbetar med jsonnet Jag rekommenderar verkligen att installera ett plugin för din editor.
Till exempel finns det ett plugin för vim vim-jsonnet, som inkluderar syntaxmarkering och automatiskt kör jsonnet fmt vid varje sparande (krÀver att jsonnet Àr installerat).
Allt Àr klart, nu kan vi börja med deployment:
För att se vad vi har Ästadkommit, kör vi:
qbec show defaultSom resultat kommer du att se de renderade yaml-manifester som kommer att tillÀmpas i klustret default.
UtmÀrkt, nu tillÀmpar vi:
qbec apply defaultSom resultat kommer du alltid att se vad som kommer att göras i ditt kluster, qbec kommer att be dig att godkÀnna Àndringarna genom att skriva y du kommer att kunna bekrÀfta dina avsikter.
Klart, nu Àr vÄr applikation distribuerad!
Om du gör Àndringar kan du alltid utföra:
qbec diff defaultför att se hur dessa Àndringar pÄverkar den aktuella distributionen
Kom ihÄg att commita vÄra Àndringar:
cd ..../..
git add deploy/website
git commit -m "LÀgg till distribution för webbplats"5. Vi testar Gitlab-runner med Kubernetes-executor
Tidigare har jag endast anvÀnt vanliga gitlab-runner pÄ en förberedd maskin (LXC-container) med shell- eller docker-executor. Till en början hade vi flera sÄdana runners globalt definierade i vÄr Gitlab. De byggde docker-bilder för alla projekt.
Men som praktiken har visat sig â den hĂ€r lösningen Ă€r inte den mest idealiska, vare sig praktiskt eller sĂ€kerhetsmĂ€ssigt. Det Ă€r mycket bĂ€ttre och ideologiskt mer korrekt att ha separata runners distribuerade för varje projekt, eller till och med för varje miljö.
Lyckligtvis Àr detta inte ett problem, eftersom vi nu kommer att distribuera gitlab-runner direkt som en del av vÄrt projekt rakt in i Kubernetes.
Gitlab tillhandahĂ„ller en fĂ€rdig helm-chart för distribution av gitlab-runner i Kubernetes. SĂ„ allt du behöver göra Ă€r att ta reda pĂ„ registreringstoken för vĂ„rt projekt i InstĂ€llningar â CI / CD â Runners och överföra det till helm:
helm repo add gitlab https://charts.gitlab.io
helm install gitlab-runner
--set gitlabUrl=https://gitlab.com
--set runnerRegistrationToken=yga8y-jdCusVDn_t4Wxc
--set rbac.create=true
gitlab/gitlab-runnerHvor:
- â adressen till din Gitlab-server.
- yga8y-jdCusVDn_t4Wxc â registreringstoken för ditt projekt.
- rbac.create=true â ger runnern nödvĂ€ndiga rĂ€ttigheter för att kunna skapa pods för att utföra vĂ„ra uppgifter med hjĂ€lp av kubernetes-executor.
Om allt Àr gjort korrekt bör du se den registrerade runnern i sektionen Runners, i instÀllningarna för ditt projekt.
SkÀrmbild av den tillagda runnern

SĂ„ enkelt? â ja, sĂ„ enkelt! Ingen mer krĂ„ngel med att registrera runners manuellt, frĂ„n och med nu kommer runners att skapas och förstöras automatiskt.
6. Distribution av Helm-charts med QBEC
Eftersom vi har beslutat att betrakta gitlab-runner som en del av vÄrt projekt, Àr det dags att beskriva det i vÄrt Git-repo.
Vi skulle kunna beskriva det som en separat komponent website, men vi planerar att distribuera olika kopior website mycket ofta, till skillnad frÄn gitlab-runner, som bara 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-runnerDenna gÄng kommer vi inte att beskriva Kubernetes-entiteter manuellt, utan vi kommer att anvÀnda en fÀrdig Helm-diagram. En av fördelarna med qbec Àr möjligheten att rendera Helm-diagram direkt frÄn ett Git-repositorium.
LÄt oss koppla det med hjÀlp av git submodule:
git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runnerNu innehÄller katalogen vendor/gitlab-runner ett repositorium med diagrammet för gitlab-runner.
PÄ samma sÀtt kan vi ansluta andra repositorier, till exempel hela repositoriet med officiella diagram.
LÄt oss beskriva komponenten components/gitlab-runner.jsonnet:
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,
}
)Som första argument till expandHelmTemplate ger vi sökvÀgen till diagrammet, sedan params.values, som vi hÀmtar frÄn miljöparametrarna, och dÀrefter ett objekt med
- nameTemplate â namnet pĂ„ releasen
- namnrymd â namnrymden som skickas till Helm
- thisFile â ett obligatoriskt parameter som anger sökvĂ€gen till den aktuella filen
- verbose â visar kommandot helm template med alla argument vid rendering av diagrammet.
Nu beskriver vi parametrarna för vÄr komponent i environments/base.libsonnet:
local secrets = import '../secrets/base.libsonnet';
{
components: {
gitlabRunner: {
name: 'gitlab-runner',
values: {
gitlabUrl: 'https://gitlab.com/',
rbac: {
create: true,
},
runnerRegistrationToken: secrets.runnerRegistrationToken,
},
},
},
}Observera runnerRegistrationToken hÀmtar vi frÄn en extern fil secrets/base.libsonnet, lÄt oss skapa den:
{
runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}LÄt oss kontrollera om allt fungerar:
qbec show defaultom allt Àr bra, kan vi radera vÄr tidigare release som distribuerades via Helm:
helm uninstall gitlab-runneroch distribuera den igen, men nu via qbec:
qbec apply default7. Introduktion till git-crypt
Àr ett verktyg som gör det möjligt att stÀlla in transparent kryptering för ditt repositorium.
För nÀrvarande ser strukturen i vÄr katalog för gitlab-runner ut sÄ hÀr:
.
âââ components
â âââ gitlab-runner.jsonnet
âââ environments
â âââ base.libsonnet
â âââ default.libsonnet
âââ params.libsonnet
âââ qbec.yaml
âââ secrets
â âââ base.libsonnet
âââ vendor
âââ gitlab-runner (submodule)Men det Ă€r osĂ€kert att lagra hemligheter i Git, eller hur? SĂ„ vi behöver kryptera dem pĂ„ rĂ€tt sĂ€tt.
Normalt sett Àr det inte alltid meningsfullt för enstaka variabler. Du kan överföra hemligheter i qbec och genom miljövariabler i ditt CI-system.
Men det Àr vÀrt att notera att det finns mer komplexa projekt som kan innehÄlla mycket fler hemligheter, och att överföra dem alla via miljövariabler skulle vara oerhört svÄrt.Dessutom skulle jag inte kunna berÀtta för er om det fantastiska verktyget som git-crypt.
git-crypt Àr sÄ bekvÀmt eftersom det lÄter dig spara hela historiken av hemligheter samt jÀmföra, slÄ samman och lösa konflikter pÄ samma sÀtt som vi Àr vana vid i Git.
Först och frÀmst efter installationen git-crypt mÄste vi generera nycklar för vÄrt förvar:
git crypt initOm du har en PGP-nyckel kan du genast lÀgga till dig sjÀlv som medarbetare för detta projekt:
git-crypt add-gpg-user kvapss@gmail.comPÄ sÄ sÀtt kan du alltid dekryptera detta förvar med din privata nyckel.
Om du inte har nÄgon PGP-nyckel och inte planerar att skaffa en, kan du ta en annan vÀg och exportera projektets nyckel:
git crypt export-key /path/to/keyfileDÀrmed kan alla som har den exporterade keyfile dekryptera ditt förvar.
Det Àr dags att konfigurera vÄr första hemlighet.
Kom ihÄg att vi fortfarande befinner oss i katalogen deploy/gitlab-runner/, dÀr vi har en katalog secrets/, lÄt oss kryptera alla filer i den, sÄ vi skapar en fil secrets/.gitattributes med följande innehÄll:
* filter=git-crypt diff=git-crypt
.gitattributes !filter !diffSom syns frÄn innehÄllet kommer alla filer som matchar mönstret * att passera genom git-crypt, med undantag för sjÀlva .gitattributes
Vi kan kontrollera detta genom att köra:
git crypt status -eUtdata kommer att vara en lista över alla filer i förvaret för vilka kryptering Àr aktiverad.
Det Àr klart, nu kan vi lugnt committa vÄra Àndringar:
cd ../../
git add .
git commit -m "Add deploy for gitlab-runner"För att lÄsa förvaret rÀcker det att köra:
git crypt lockoch omedelbart förvandlas alla krypterade filer till binÀrt skrÀp som inte gÄr att lÀsa.
För att dekryptera förvaret kör du:
git crypt unlock8. Skapa en toolbox-image
En toolbox-image Àr en sÄdan bild med alla verktyg som vi kommer att anvÀnda för att deploya vÄrt projekt. Den kommer att anvÀndas av gitlab-runners för att utföra typiska deploymentsuppgifter.
HÀr Àr det enkelt, skapa en ny dockerfiles/toolbox/Dockerfile med följande innehÄll:
FRĂ
N 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/helmSom ni kan se installerar vi i denna bild alla verktyg som vi anvÀnde för att distribuera vÄr applikation. Vi behöver egentligen inte kubectl, men ni kanske vill leka med det i steg för att sÀtta upp pipeline.
För att kunna kommunicera med Kubernetes och utföra deployment dÀr, behöver vi konfigurera en roll för de pods som genereras av gitlab-runner.
För detta gÄr vi till katalogen med gitlab-runner:
cd deploy/gitlab-runneroch lÀgger till en ny komponent components/rbac.jsonnet:
local env = {
name: std.extVar('qbec.io/env'),
namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.rbac;
[
{
apiVersion: 'v1',
kind: 'ServiceAccount',
metadata: {
labels: {
app: params.name,
},
name: params.name,
},
},
{
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'Role',
metadata: {
labels: {
app: params.name,
},
name: params.name,
},
rules: [
{
apiGroups: [
'*',
],
resources: [
'*',
],
verbs: [
'*',
],
},
],
},
{
apiVersion: 'rbac.authorization.k8s.io/v1',
kind: 'RoleBinding',
metadata: {
labels: {
app: params.name,
},
name: params.name,
},
roleRef: {
apiGroup: 'rbac.authorization.k8s.io',
kind: 'Role',
name: params.name,
},
subjects: [
{
kind: 'ServiceAccount',
name: params.name,
namespace: env.namespace,
},
],
},
]LÄt oss ocksÄ beskriva nya parametrar i environments/base.libsonnet, som nu ser ut sÄ hÀr:
local secrets = import '../secrets/base.libsonnet';
{
components: {
gitlabRunner: {
name: 'gitlab-runner',
values: {
gitlabUrl: 'https://gitlab.com/',
rbac: {
create: true,
},
runnerRegistrationToken: secrets.runnerRegistrationToken,
runners: {
serviceAccountName: $.components.rbac.name,
image: 'registry.gitlab.com/kvaps/docs.example.org/toolbox:v0.0.1',
},
},
},
rbac: {
name: 'gitlab-runner-deploy',
},
},
}Observera $.components.rbac.name refererar till name för komponenten rbac
LÄt oss kontrollera vad som har Àndrats:
qbec diff defaultoch tillÀmpa vÄra Àndringar i Kubernetes:
qbec apply defaultGlöm inte att committa vÄra Àndringar i git:
cd ../..
git add dockerfiles/toolbox
git commit -m "LÀgg till Dockerfile för toolbox"
git add deploy/gitlab-runner
git commit -m "Konfigurera gitlab-runner för att anvÀnda toolbox"9. VÄr första pipeline och byggande av bilder baserat pÄ taggar
I roten av projektet kommer vi att skapa .gitlab-ci.yml med följande innehÄll:
.build_docker_image:
stage: build
image:
name: gcr.io/kaniko-project/executor:debug-v0.15.0
entrypoint: [""]
before_script:
- echo "{"auths":{"$CI_REGISTRY":{"username":"$CI_REGISTRY_USER","password":"$CI_REGISTRY_PASSWORD"}}}" > /kaniko/.docker/config.json
build_toolbox:
extends: .build_docker_image
script:
- /kaniko/executor --cache --context $CI_PROJECT_DIR/dockerfiles/toolbox --dockerfile $CI_PROJECT_DIR/dockerfiles/toolbox/Dockerfile --destination $CI_REGISTRY_IMAGE/toolbox:$CI_COMMIT_TAG
only:
refs:
- tags
build_website:
extends: .build_docker_image
variables:
GIT_SUBMODULE_STRATEGY: normal
script:
- /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_TAG
only:
refs:
- tagsObservera att vi anvÀnder GIT_SUBMODULE_STRATEGY: normal för de jobb dÀr det Àr nödvÀndigt att uttryckligen initiera submoduler före körning.
Kom ihÄg att commita vÄra Àndringar:
git add .gitlab-ci.yml
git commit -m "Automatisera dockerbygget"Jag tror att vi kan kallade detta version v0.0.1 och sÀtta en tagg:
git tag v0.0.1Vi kommer att sÀtta taggar varje gÄng 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 bygget av bilder med den taggen.
Vi genomför git push --tags, och ser vÄrt första pipeline:
SkÀrmbild av första pipelinen

Det Àr vÀrt att notera att bygget med taggar Àr lÀmpligt för att bygga dockerbilder, men inte passar för att deploya applikationen i Kubernetes. Eftersom nya taggar kan tilldelas gamla commits, kommer initieringen av pipelinen för dessa att leda till en deployment av den gamla versionen.
För att lösa detta problem kopplas vanligtvis bygget av dockerbilder till taggar, medan deployment av applikationen kopplas till en gren master, i vilken versionerna av de byggda bilderna Àr hÄrdkodade. Just i detta fall kan du initiera en rollback med en enkel revert master-gren.
10. Automatisering av deployment
För att Gitlab-runner ska kunna dekryptera vÄra hemligheter behöver vi exportera nyckeln för repot och lÀgga till den i vÄra CI-miljövÀrden:
git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echoDen resulterande strÀngen kommer att sparas i Gitlab, för detta gÄr vi till instÀllningarna för vÄrt projekt:
Settings -> CI / CD -> Variables
Och skapar en ny variabel:
Type
Nyckel
VĂ€rde
Skyddad
Maskerad
OmfÄng
Arkiv
GITCRYPT_KEY
<your string>
true (under utbildningen kan Àven false)
true
Alla miljöer
SkÀrmbild av den tillagda variabeln

Nu uppdaterar vi vÄrt .gitlab-ci.yml genom att lÀgga till:
.deploy_qbec_app:
stage: deploy
only:
refs:
- master
deploy_gitlab_runner:
extends: .deploy_qbec_app
variables:
GIT_SUBMODULE_STRATEGY: normal
before_script:
- base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
script:
- qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
deploy_website:
extends: .deploy_qbec_app
script:
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yesHÀr har vi anvÀnt flera nya alternativ för qbec:
- âroot some/app â gör att du kan definiera katalogen för en specifik applikation
- âforce:k8s-context __incluster__ â detta Ă€r en magisk variabel som sĂ€ger att deployment kommer att ske i samma kluster som gitlab-runner körs. Detta Ă€r nödvĂ€ndigt, annars kommer qbec att försöka hitta en lĂ€mplig Kubernetes-server i din kubeconfig.
- âwait â fĂ„r qbec att vĂ€nta tills de resurser som skapas befinner sig i Ready-status och avslutas först dĂ€refter med en framgĂ„ngsrik exit-kod.
- âyes â stĂ€nger bara av den interaktiva shellen. Ăr du sĂ€ker? vid deployment.
Kom ihÄg att commita vÄra Àndringar:
git add .gitlab-ci.yml
git commit -m "Automatisera distribution"Och efter git push kommer vi att se hur vÄra applikationer har distribuerats:
SkÀrmdump av den andra pipelinen

11. Artefakter och bygg vid push till master
De ovan beskrivna stegen Àr vanligtvis tillrÀckliga för att bygga och leverera nÀstan vilken mikrotjÀnst som helst, men vi vill inte bara sÀtta en tagg varje gÄng vi behöver uppdatera webbplatsen. DÀrför kommer vi att ta en mer dynamisk vÀg och stÀlla in deployment efter digest i master-grenen.
Idén Àr enkel: nu kommer vÄr website att byggas om varje gÄng vi gör en push till master, och efter det automatiskt distribueras till Kubernetes.
LÄt oss uppdatera dessa tvÄ jobb i vÄr .gitlab-ci.yml:
build_website:
extends: .build_docker_image
variables:
GIT_SUBMODULE_STRATEGY: normal
script:
- mkdir -p $CI_PROJECT_DIR/artifacts
- /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
artifacts:
paths:
- artifacts/
only:
refs:
- master
- tags
deploy_website:
extends: .deploy_qbec_app
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"Observera att vi har lagt till grenen master till refs för jobbet build_website och vi anvÀnder nu $CI_COMMIT_REF_NAME istÀllet för $CI_COMMIT_TAG, det vill sÀga vi frigör oss frÄn taggar i Git och kommer nu att pusha bilden med namnet pÄ den gren som committade och initierade pipelinen. Det Àr vÀrt att notera att detta ocksÄ kommer att fungera med taggar, vilket gör att vi kan spara snapshots av webbplatsen med en viss version i docker-registry.
NÀr namnet pÄ docker-taggen för den nya versionen av webbplatsen kan vara oförÀndrat, mÄste vi ÀndÄ beskriva Àndringarna för Kubernetes, annars kommer det helt enkelt inte att Äterdistribuera appen frÄn den nya bilden, eftersom det inte kommer att upptÀcka nÄgra Àndringar i distributionsmanifestet.
Alternativ âvm:ext-str digest=»$DIGEST» för qbec â gör att externa variabler kan skickas till jsonnet. Vi vill att vĂ„r applikation ska Ă„terdistribueras i klustret med varje release. Att anvĂ€nda en tagg som nu kan förbli oförĂ€ndrad Ă€r inte lĂ€ngre möjligt, eftersom vi behöver koppla oss till den specifika versionsbilden och trigga distributionen vid dess Ă€ndring.
HĂ€r kan Kanikos möjlighet att spara bildens digest i en fil hjĂ€lpa oss (alternativ âdigest-file)
Denna fil kommer vi att skicka med och lÀsa vid distributionstillfÀllet.
Vi uppdaterar parametrarna för vÄr deploy/website/environments/base.libsonnet som nu kommer att se ut sÄ hÀr:
{
components: {
website: {
name: 'example-docs',
image: 'registry.gitlab.com/kvaps/docs.example.org/website@' + std.extVar('digest'),
replicas: 1,
containerPort: 80,
servicePort: 80,
nodeSelector: {},
tolerations: [],
ingressClass: 'nginx',
domain: 'docs.example.org',
},
},
}Klart, nu kommer varje commit i master att initiera byggandet av docker-bilden för website, och sedan dess distribution i Kubernetes.
Kom ihÄg att commita vÄra Àndringar:
git add .
git commit -m "Konfigurera dynamisk bygg"Vi kollar, efter git push vi ska se nÄgot liknande:
SkÀrmdump av pipeline för master

I princip har vi ingen anledning att Äterdistribuera gitlab-runner vid varje push, om inget har Àndrats i dess konfiguration, lÄt oss Àndra detta i .gitlab-ci.yml:
deploy_gitlab_runner:
extends: .deploy_qbec_app
variables:
GIT_SUBMODULE_STRATEGY: normal
before_script:
- base64 -d "$GITCRYPT_KEY" | git-crypt unlock -
script:
- qbec apply default --root deploy/gitlab-runner --force:k8s-context __incluster__ --wait --yes
only:
changes:
- deploy/gitlab-runner/**/*changes kommer att övervaka Àndringar i deploy/gitlab-runner/ och kommer att trigga vÄrt jobb endast om sÄdana finns
Kom ihÄg att commita vÄra Àndringar:
git add .gitlab-ci.yml
git commit -m "Minska gitlab-runner distribution"git push, sÄ hÀr Àr det bÀttre:
SkÀrmdump av den uppdaterade pipelinen

12. Dynamiska miljöer
Det Àr dags att diversifiera vÄr pipeline med dynamiska miljöer.
Först uppdaterar vi jobbet build_website i vÄr .gitlab-ci.yml, och tar bort blocket only, vilket fÄr Gitlab att trigga det vid varje commit i vilken gren som helst:
build_website:
extends: .build_docker_image
variables:
GIT_SUBMODULE_STRATEGY: normal
script:
- mkdir -p $CI_PROJECT_DIR/artifacts
- /kaniko/executor --cache --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/dockerfiles/website/Dockerfile --destination $CI_REGISTRY_IMAGE/website:$CI_COMMIT_REF_NAME --digest-file $CI_PROJECT_DIR/artifacts/website.digest
artifacts:
paths:
- artifacts/Sedan uppdaterar vi jobbet deploy_website, vi lÀgger till en block miljö:
deploy_website:
extends: .deploy_qbec_app
environment:
name: prod
url: https://docs.example.org
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"Detta gör att Gitlab kan koppla jobbet till prod miljön och visa den korrekta lÀnken till den.
Nu lÀgger vi till ytterligare tvÄ jobb:
deploy_website:
extends: .deploy_qbec_app
environment:
name: prod
url: https://docs.example.org
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply default --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST"
deploy_review:
extends: .deploy_qbec_app
environment:
name: review/$CI_COMMIT_REF_NAME
url: http://$CI_ENVIRONMENT_SLUG.docs.example.org
on_stop: stop_review
script:
- DIGEST="$(cat artifacts/website.digest)"
- qbec apply review --root deploy/website --force:k8s-context __incluster__ --wait --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
only:
refs:
- branches
except:
refs:
- master
stop_review:
extends: .deploy_qbec_app
environment:
name: review/$CI_COMMIT_REF_NAME
action: stop
stage: deploy
before_script:
- git clone "$CI_REPOSITORY_URL" master
- cd master
script:
- qbec delete review --root deploy/website --force:k8s-context __incluster__ --yes --vm:ext-str digest="$DIGEST" --vm:ext-str subdomain="$CI_ENVIRONMENT_SLUG" --app-tag "$CI_ENVIRONMENT_SLUG"
variables:
GIT_STRATEGY: none
only:
refs:
- branches
except:
refs:
- master
when: manualDe kommer att köras vid push till alla grenar utom master och kommer att distribuera en förhandsversion av webbplatsen.
Vi ser en ny option för qbec: âapp-tag â det lĂ„ter dig mĂ€rka de distribuerade versionerna av applikationen och arbeta endast inom den taggen. Vid skapande och radering av resurser i Kubernetes kommer qbec endast att hantera dessa.
PÄ sÄ sÀtt behöver vi inte skapa en separat miljö för varje granskning, utan kan helt enkelt ÄteranvÀnda samma.
HĂ€r anvĂ€nder vi ocksĂ„ qbec apply review, istĂ€llet för qbec apply default â det Ă€r just det ögonblicket dĂ„ vi försöker beskriva skillnaderna mellan vĂ„ra miljöer (granskning och standard):
LÄt oss lÀgga till granskning miljö i deploy/website/qbec.yaml
spec:
environments:
review:
defaultNamespace: docs
server: https://kubernetes.example.org:8443DĂ€refter deklarerar vi den i 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.thisFileOch vi sparar anpassade parametrar för den i 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Ä ta en nÀrmare titt pÄ jobbet stop_review, det kommer att triggas vid borttagning av en gren och för att Gitlab inte ska försöka göra checkout pÄ den anvÀnds GIT_STRATEGY: none, senare klonar vi master-grenen och tar bort granskningen genom den.
Lite besvÀrligt, men jag har Ànnu inte hittat ett mer elegant sÀtt.
Ett alternativ kan vara att deploya varje review i ett eget namespace som alltid kan tas bort helt.
Kom ihÄg att commita vÄra Àndringar:
git add .
git commit -m "Aktivera automatisk review"git push, git checkout -b test, git push origin test, kontrollerar:
SkÀrmdump av skapade miljöer i Gitlab

Fungerar allt? â utmĂ€rkt, vi tar bort vĂ„r testgren: git checkout master, git push origin :test, vi kontrollerar att jobben för att ta bort miljön har körts utan fel.
HÀr vill jag genast klargöra att vilken utvecklare som helst i projektet kan skapa grenar, han kan ocksÄ Àndra .gitlab-ci.yml filen och fÄ tillgÄng till hemliga variabler.
DÀrför rekommenderas det starkt att tillÄta deras anvÀndning endast för skyddade grenar, till exempel i master, eller att skapa en separat uppsÀttning variabler för varje miljö.
13. Review Apps
det Àr en funktion i Gitlab som gör det möjligt att för varje fil i repositoryt lÀgga till en knapp för snabb förhandsvisning i den distribuerade miljön.
För att dessa knappar ska dyka upp mÄste du skapa en fil .gitlab/route-map.yml och beskriva alla vÀgtransformationer i den, i vÄrt fall blir det mycket enkelt:
# Indices
- source: /content/(.+?)_index.(md|html)/
public: '1'
# Pages
- source: /content/(.+?).(md|html)/
public: '1/'Kom ihÄg att commita vÄra Àndringar:
git add .gitlab/
git commit -m "Aktivera review apps"git push, och vi kontrollerar:
SkÀrmdump av Review App-knappen

Jobbet Àr klart!
Projektets kÀllkod:
- pÄ Gitlab:
- pÄ GitHub:
Tack för uppmÀrksamheten, jag hoppas att ni gillade det ![]()
KĂ€lla: habr.com
