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.

Innehåll

  1. Träffa Hugo
  2. Förbereder dockerfilen
  3. Lär känna kaniko
  4. Lär känna qbec
  5. Testar Gitlab-runner med Kubernetes-executor
  6. Distribuera Helm-diagram med qbec
  7. Vi introducerar git-crypt
  8. Skapa en verktygslåda-bild
  9. Vår första pipeline och sammansättning av bilder med taggar
  10. Implementeringsautomatisering
  11. Artefakter och montering när man trycker för att bemästra
  12. Dynamiska miljöer
  13. Granska appar

1. Lär känna Hugo

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:

git submodule add https://github.com/matcornic/hugo-theme-learn themes/learn

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

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

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:

.
├── deploy
│   ├── app1
│   └── app2
└── dockerfiles
    ├── image1
    └── image2

  • 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:

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.1

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

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

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/:

mkdir deploy
cd deploy

Låt oss initiera vår första applikation:

qbec init website
cd website

Nu ser strukturen för vår applikation ut så här:

.
├── components
├── environments
│   ├── base.libsonnet
│   └── default.libsonnet
├── params.libsonnet
└── qbec.yaml

lå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 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:

{
  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',
    },
  },
}

Låt oss också skapa vår första komponent komponenter/webbplats.jsonnet:

local env = {
  name: std.extVar('qbec.io/env'),
  namespace: std.extVar('qbec.io/defaultNs'),
};
local p = import '../params.libsonnet';
local params = p.components.website;

[
  {
    apiVersion: 'apps/v1',
    kind: 'Deployment',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      replicas: params.replicas,
      selector: {
        matchLabels: {
          app: params.name,
        },
      },
      template: {
        metadata: {
          labels: { app: params.name },
        },
        spec: {
          containers: [
            {
              name: 'darkhttpd',
              image: params.image,
              ports: [
                {
                  containerPort: params.containerPort,
                },
              ],
            },
          ],
          nodeSelector: params.nodeSelector,
          tolerations: params.tolerations,
          imagePullSecrets: [{ name: 'regsecret' }],
        },
      },
    },
  },
  {
    apiVersion: 'v1',
    kind: 'Service',
    metadata: {
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      selector: {
        app: params.name,
      },
      ports: [
        {
          port: params.servicePort,
          targetPort: params.containerPort,
        },
      ],
    },
  },
  {
    apiVersion: 'extensions/v1beta1',
    kind: 'Ingress',
    metadata: {
      annotations: {
        'kubernetes.io/ingress.class': params.ingressClass,
      },
      labels: { app: params.name },
      name: params.name,
    },
    spec: {
      rules: [
        {
          host: params.domain,
          http: {
            paths: [
              {
                backend: {
                  serviceName: params.name,
                  servicePort: params.servicePort,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

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:

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-runner

Var:

  • https://gitlab.com — adressen till din Gitlab-server.
  • 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

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

Ä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.

Låt oss ansluta den med git-undermodulen:

git submodule add https://gitlab.com/gitlab-org/charts/gitlab-runner vendor/gitlab-runner

Nu katalogen leverantör/gitlab-runner Vi har ett arkiv med ett diagram för gitlab-runner.

På liknande sätt kan du koppla ihop andra arkiv, till exempel hela arkivet med officiella diagram https://github.com/helm/charts

Låt oss beskriva komponenten komponenter/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,
  }
)

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:

local secrets = import '../secrets/base.libsonnet';

{
  components: {
    gitlabRunner: {
      name: 'gitlab-runner',
      values: {
        gitlabUrl: 'https://gitlab.com/',
        rbac: {
          create: true,
        },
        runnerRegistrationToken: secrets.runnerRegistrationToken,
      },
    },
  },
}

Observera runnerRegistration Token vi tar från en extern fil secrets/base.libsonnet, låt oss skapa det:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Låt oss kolla om allt fungerar:

qbec show default

om allt är i sin ordning kan vi ta bort vår tidigare distribuerade version via Helm:

helm uninstall gitlab-runner

och distribuera det på samma sätt, men genom qbec:

qbec apply default

7. Introduktion till git-crypt

Git-krypt är ett verktyg som låter dig ställa in transparent kryptering för ditt arkiv.

För tillfället ser vår katalogstruktur 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 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:

git-crypt add-gpg-user [email protected]

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:

* filter=git-crypt diff=git-crypt
.gitattributes !filter !diff

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:

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,
      },
    ],
  },
]

Vi kommer också att beskriva de nya parametrarna i miljöer/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 namn för komponent RBAC

Låt oss kolla vad som har förändrats:

qbec diff default

och tillämpa våra ändringar på Kubernetes:

qbec apply default

Glöm inte heller att göra våra ändringar till git:

cd ../..
git add dockerfiles/toolbox
git commit -m "Add Dockerfile for toolbox"
git add deploy/gitlab-runner
git commit -m "Configure gitlab-runner to use toolbox"

9. Vår första pipeline och sammansättning av bilder med taggar

I grunden för 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:
      - tags

Observera att vi använder GIT_SUBMODULE_STRATEGY: normal för de jobb där du behöver explicit initiera undermoduler innan exekvering.

Glöm inte att genomföra våra ändringar:

git add .gitlab-ci.yml
git commit -m "Automate docker build"

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

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

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:

git crypt export-key /tmp/docs-repo.key
base64 -w0 /tmp/docs-repo.key; echo

Vi sparar den resulterande raden i Gitlab; för att göra detta, låt oss gå till våra projektinställningar:
Inställningar -> CI / CD -> Variabler

Och låt oss skapa en ny variabel:

Typ
Nyckel
Värde
Skyddad
Maskerad
Omfattning

File
GITCRYPT_KEY
<your string>
true (under träningen kan du false)
true
All environments

Skärmdump av den tillagda variabeln

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

Låt oss nu uppdatera vår .gitlab-ci.yml lägger till det:

.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 --yes

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.

Glöm inte att genomföra våra ändringar:

git add .gitlab-ci.yml
git commit -m "Automate deploy"

Och efter git push vi kommer att se hur våra applikationer har distribuerats:

Skärmdump av den andra pipelinen

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

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:

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 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:

{
  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 ä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

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

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:

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/**/*

förändringar låter dig övervaka ändringar i deploy/gitlab-runner/ och kommer att utlösa vårt jobb bara om det finns några

Glöm inte att genomföra våra ändringar:

git add .gitlab-ci.yml
git commit -m "Reduce gitlab-runner deploy"

git push, det är bättre:

Skärmdump av den uppdaterade pipeline

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

12. Dynamiska miljöer

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:

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/

Uppdatera sedan jobbet deploy_website, lägg till ett block där 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 associera jobbet med prod miljö och visa rätt länk till den.

Låt oss nu lägga till två jobb till:

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: manual

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

spec:
  environments:
    review:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443

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.

Glöm inte att genomföra våra ändringar:

git add .
git commit -m "Enable automatic review"

git push, git checkout -b test, git push ursprungstest, kolla upp:

Skärmdump av skapade miljöer i Gitlab

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

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:

# Indices
- source: /content/(.+?)_index.(md|html)/ 
  public: '1'

# Pages
- source: /content/(.+?).(md|html)/ 
  public: '1/'

Glöm inte att genomföra våra ändringar:

git add .gitlab/
git commit -m "Enable review apps"

git push, och kontrollera:

Skärmdump av knappen Granska app

Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

Jobbet är klart!

Projektkällor:

Tack för din uppmärksamhet, jag hoppas att du gillade den Testar nya verktyg för att bygga och automatisera distributionen i Kubernetes

Källa: will.com

Lägg en kommentar