Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Hallo! Nylig har mange kule automatiseringsverktøy blitt utgitt både for å bygge Docker-bilder og for distribusjon til Kubernetes. I denne forbindelse bestemte jeg meg for å leke med GitLab, studere mulighetene grundig og, selvfølgelig, sette opp pipelinen.

Dette arbeidet er inspirert av nettstedet kubernetes.io, som er generert fra kildekoder automatisk, og for hver gruppeforespørsel som sendes, genererer roboten automatisk en forhåndsvisningsversjon av nettstedet med endringene dine og gir en lenke for visning.

Jeg prøvde å bygge en lignende prosess fra bunnen av, men helt bygd på Gitlab CI og gratisverktøy som jeg er vant til å bruke for å distribuere applikasjoner til Kubernetes. I dag skal jeg endelig fortelle deg mer om dem.

Artikkelen vil diskutere verktøy som:
Hugo, qbec, kaniko, git-krypt и GitLab CI med å skape dynamiske miljøer.

Innhold

  1. Møt Hugo
  2. Forbereder Dockerfilen
  3. Bli kjent med kaniko
  4. Bli kjent med qbec
  5. Prøver Gitlab-runner med Kubernetes-executor
  6. Distribuerer Helm-diagrammer med qbec
  7. Vi introduserer git-crypt
  8. Opprette et verktøykassebilde
  9. Vår første pipeline og sammenstilling av bilder etter tagger
  10. Implementeringsautomatisering
  11. Artefakter og montering når du trykker for å mestre
  12. Dynamiske miljøer
  13. Gjennomgå apper

1. Bli kjent med Hugo

Som et eksempel på prosjektet vårt vil vi prøve å lage en dokumentasjonspubliseringsside bygget på Hugo. Hugo er en statisk innholdsgenerator.

For de som ikke er kjent med statiske generatorer, vil jeg fortelle deg litt mer om dem. I motsetning til konvensjonelle nettstedsmotorer med en database og noe PHP, som, når en bruker ber om det, genererer sider i farten, er statiske generatorer utformet litt annerledes. De lar deg ta kilder, vanligvis et sett med filer i Markdown-markerings- og temamaler, og deretter kompilere dem til et helt ferdig nettsted.

Det vil si at som et resultat vil du motta en katalogstruktur og et sett med genererte HTML-filer, som du ganske enkelt kan laste opp til en hvilken som helst billig hosting og få et fungerende nettsted.

Du kan installere Hugo lokalt og prøve det ut:

Initialisere et nytt nettsted:

hugo new site docs.example.org

Og samtidig git-depotet:

cd docs.example.org
git init

Så langt er nettstedet vårt uberørt, og for at noe skal vises på det, må vi først koble til et tema; et tema er bare et sett med maler og spesifiserte regler som nettstedet vårt genereres etter.

Til temaet vi skal bruke Lær, som etter min mening er perfekt egnet for en dokumentasjonsside.

Jeg vil være spesielt oppmerksom på at vi ikke trenger å lagre temafilene i prosjektlageret vårt; i stedet kan vi ganske enkelt koble det til ved å bruke git undermodul:

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

Dermed vil vårt depot bare inneholde filer som er direkte relatert til prosjektet vårt, og det tilknyttede temaet vil forbli som en lenke til et spesifikt depot og en forpliktelse i det, det vil si at det alltid kan hentes fra den opprinnelige kilden og ikke være redd for uforenlige endringer.

La oss korrigere konfigurasjonen config.toml:

baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "My Docs Site"
theme = "learn"

Allerede på dette stadiet kan du kjøre:

hugo server

Og på adressen http://localhost:1313/ sjekk vår nyopprettede nettside, alle endringer som er gjort i katalogen oppdaterer automatisk den åpne siden i nettleseren, veldig praktisk!

La oss prøve å lage en forside i content/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Skjermbilde av den nyopprettede siden

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

For å generere et nettsted, bare kjør:

hugo

Kataloginnhold offentlig/ og vil være din nettside.
Ja, forresten, la oss umiddelbart legge det til .gitignore:

echo /public > .gitignore

Ikke glem å forplikte endringene våre:

git add .
git commit -m "New site created"

2. Klargjør Dockerfilen

Det er på tide å definere strukturen til depotet vårt. Jeg bruker vanligvis noe sånt som:

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

  • dockerfiles/ — inneholder kataloger med Dockerfiler og alt nødvendig for å bygge våre Docker-bilder.
  • utplassere/ — inneholder kataloger for distribusjon av applikasjonene våre til Kubernetes

Dermed vil vi lage vår første Dockerfile langs stien 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, inneholder Dockerfilen to FRA, kalles denne funksjonen flertrinnsbygging og lar deg ekskludere alt unødvendig fra det endelige docker-bildet.
Dermed vil det endelige bildet bare inneholde mørkhttpd (lett HTTP-server) og offentlig/ — innholdet på vår statisk genererte nettside.

Ikke glem å forplikte endringene våre:

git add dockerfiles/website
git commit -m "Add Dockerfile for website"

3. Bli kjent med kaniko

Som en docker-bildebygger bestemte jeg meg for å bruke kaniko, siden operasjonen ikke krever en docker-demon, og selve byggingen kan utføres på hvilken som helst maskin og cachen kan lagres direkte i registeret, og dermed eliminere behovet for å ha en fullverdig vedvarende lagring.

For å bygge bildet, bare kjør beholderen med kaniko eksekutør og gi den gjeldende byggekontekst; dette kan også gjøres 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

hvor registry.gitlab.com/kvaps/docs.example.org/website - navnet på docker-bildet ditt; etter bygging vil det automatisk bli lansert i docker-registeret.

Parameter --cache lar deg bufre lag i docker-registeret; for eksempelet som er gitt, vil de bli lagret i registry.gitlab.com/kvaps/docs.example.org/website/cache, men du kan spesifisere en annen bane ved å bruke parameteren --cache-repo.

Skjermbilde av docker-register

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

4. Bli kjent med qbec

Qbec er et distribusjonsverktøy som lar deg beskrive applikasjonsmanifestene dine deklarativt og distribuere dem til Kubernetes. Ved å bruke Jsonnet som hovedsyntaks kan du i stor grad forenkle beskrivelsen av forskjeller på tvers av flere miljøer, og eliminerer også nesten fullstendig kodegjentakelse.

Dette kan spesielt gjelde i tilfeller der du trenger å distribuere en applikasjon til flere klynger med forskjellige parametere og ønsker å beskrive dem deklarativt i Git.

Qbec lar deg også gjengi Helm-diagrammer ved å gi dem de nødvendige parameterne og deretter betjene dem på samme måte som vanlige manifester, inkludert at du kan bruke forskjellige mutasjoner på dem, og dette lar deg i sin tur bli kvitt behovet for å bruk ChartMuseum. Det vil si at du kan lagre og gjengi diagrammer direkte fra git, der de hører hjemme.

Som jeg sa tidligere, vil vi lagre alle distribusjoner i en katalog utplassere/:

mkdir deploy
cd deploy

La oss initialisere vår første applikasjon:

qbec init website
cd website

Nå ser strukturen til søknaden vår slik ut:

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

la oss se 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: {}

Her er vi først og fremst interessert i spes.miljøer, qbec har allerede opprettet et standardmiljø for oss og tok serveradressen, samt navneområdet fra vår nåværende kubeconfig.
Nå når du distribuerer til standard~~POS=TRUNC miljø, vil qbec alltid bare distribuere til den spesifiserte Kubernetes-klyngen og til det angitte navneområdet, det vil si at du ikke lenger trenger å bytte mellom kontekster og navneområder for å utføre en distribusjon.
Om nødvendig kan du alltid oppdatere innstillingene i denne filen.

Alle miljøene dine er beskrevet i qbec.yaml, og i filen params.libsonnet, hvor det står hvor du kan hente parametrene for dem.

Deretter ser vi to kataloger:

  • komponenter / - alle manifester for applikasjonen vår vil bli lagret her; de kan beskrives både i jsonnet og vanlige yaml-filer
  • miljøer/ — her vil vi beskrive alle variablene (parametrene) for våre miljøer.

Som standard har vi to filer:

  • environments/base.libsonnet - den vil inneholde felles parametere for alle miljøer
  • environments/default.libsonnet — inneholder parametere som er overstyrt for miljøet standard~~POS=TRUNC

la oss åpne environments/base.libsonnet og legg til parametere for vår første komponent der:

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

La oss også lage vår første komponent komponenter/website.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 denne filen beskrev vi tre Kubernetes-enheter samtidig, disse er: Utplassering, Service и Ingress. Hvis vi ville, kunne vi sette dem inn i forskjellige komponenter, men på dette stadiet vil en være nok for oss.

syntaks jsonnet er veldig lik vanlig json, i prinsippet er vanlig json allerede gyldig jsonnet, så i begynnelsen kan det være lettere for deg å bruke nettjenester som yaml2json for å konvertere din vanlige yaml til json, eller, hvis komponentene dine ikke inneholder noen variabler, kan de beskrives i form av vanlig yaml.

Når du jobber med jsonnet Jeg anbefaler på det sterkeste å installere en plugin for redaktøren din

For eksempel er det en plugin for vim vim-jsonnet, som slår på syntaksutheving og kjøres automatisk jsonnet fmt hver gang du lagrer (krever at jsonnet er installert).

Alt er klart, nå kan vi begynne å distribuere:

For å se hva vi har, la oss kjøre:

qbec show default

Ved utgangen vil du se gjengitte yaml-manifester som vil bli brukt på standardklyngen.

Flott, søk nå:

qbec apply default

Ved utgangen vil du alltid se hva som vil bli gjort i klyngen din, qbec vil be deg om å godta endringene ved å skrive y du vil kunne bekrefte intensjonene dine.

Vår applikasjon er klar og distribuert!

Hvis du gjør endringer, kan du alltid gjøre:

qbec diff default

for å se hvordan disse endringene vil påvirke den nåværende distribusjonen

Ikke glem å forplikte endringene våre:

cd ../..
git add deploy/website
git commit -m "Add deploy for website"

5. Prøver Gitlab-runner med Kubernetes-executor

Inntil nylig brukte jeg bare vanlig gitlab-runner på en forhåndspreparert maskin (LXC container) med shell eller docker-executor. I utgangspunktet hadde vi flere slike løpere globalt definert i gitlaben vår. De samlet inn docker-bilder for alle prosjekter.

Men som praksis har vist, er ikke dette alternativet det mest ideelle, både når det gjelder praktisk og sikkerhet. Det er mye bedre og ideologisk mer riktig å ha separate løpere utplassert for hvert prosjekt, eller til og med for hvert miljø.

Heldigvis er ikke dette et problem i det hele tatt, siden vi nå vil distribuere gitlab-runner direkte som en del av vårt prosjekt rett i Kubernetes.

Gitlab gir et ferdig rordiagram for å distribuere gitlab-runner til Kubernetes. Så alt du trenger å gjøre er å finne ut registreringstoken for vårt prosjekt i Innstillinger -> CI / CD -> Løpere og gi den til roret:

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

Hvor:

  • https://gitlab.com — adressen til Gitlab-serveren din.
  • yga8y-jdCusVDn_t4Wxc — registreringstoken for prosjektet ditt.
  • rbac.create=true — gir løperen den nødvendige mengden privilegier for å kunne lage pods for å utføre oppgavene våre ved å bruke kubernetes-executor.

Hvis alt er gjort riktig, skal du se en registrert løper i seksjonen Løpere, i prosjektinnstillingene dine.

Skjermbilde av den lagte løperen

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Er det så enkelt? – Ja, så enkelt er det! Ikke mer problemer med å registrere løpere manuelt, fra nå av vil løpere opprettes og ødelegges automatisk.

6. Distribuer Helm-diagrammer med QBEC

Siden vi bestemte oss for å vurdere gitlab-runner en del av prosjektet vårt, er det på tide å beskrive det i Git-depotet vårt.

Vi kan beskrive det som en egen komponent nettsted, men i fremtiden planlegger vi å distribuere forskjellige kopier nettsted veldig ofte, i motsetning til gitlab-runner, som bare vil bli distribuert én gang per Kubernetes-klynge. Så la oss initialisere en egen applikasjon for det:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

Denne gangen vil vi ikke beskrive Kubernetes-entiteter manuelt, men vil ta et ferdig Helm-diagram. En av fordelene med qbec er muligheten til å gjengi Helm-diagrammer direkte fra et Git-depot.

La oss koble den ved hjelp av git-undermodulen:

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

Nå katalogen leverandør/gitlab-runner Vi har et depot med et diagram for gitlab-runner.

På lignende måte kan du koble andre depoter, for eksempel hele depotet med offisielle diagrammer https://github.com/helm/charts

La oss beskrive 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ørste argumentet til expandHelmTemplate vi passerer stien til kartet, da params.verdier, som vi tar fra miljøparameterne, kommer så objektet med

  • navnmal — utgivelsestittel
  • namespace — navneområde overført til roret
  • denne filen — en nødvendig parameter som sender banen til gjeldende fil
  • ordrik - viser kommandoen rormal med alle argumentene når du gjengir diagrammet

La oss nå beskrive parametrene for komponenten vår 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,
      },
    },
  },
}

Vær oppmerksom runnerRegistration Token vi tar fra en ekstern fil secrets/base.libsonnet, la oss lage det:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

La oss sjekke om alt fungerer:

qbec show default

hvis alt er i orden, kan vi slette vår tidligere distribuerte utgivelse via Helm:

helm uninstall gitlab-runner

og distribuer det på samme måte, men gjennom qbec:

qbec apply default

7. Introduksjon til git-crypt

Git-krypt er et verktøy som lar deg sette opp transparent kryptering for depotet ditt.

For øyeblikket ser katalogstrukturen vår for gitlab-runner slik ut:

.
├── components
│   ├── gitlab-runner.jsonnet
├── environments
│   ├── base.libsonnet
│   └── default.libsonnet
├── params.libsonnet
├── qbec.yaml
├── secrets
│   └── base.libsonnet
└── vendor
    └── gitlab-runner (submodule)

Men å lagre hemmeligheter i Git er vel ikke trygt? Så vi må kryptere dem riktig.

Vanligvis, for én variabels skyld, gir dette ikke alltid mening. Du kan overføre hemmeligheter til qbec og gjennom miljøvariablene til CI-systemet ditt.
Men det er verdt å merke seg at det også er mer komplekse prosjekter som kan inneholde mange flere hemmeligheter; å overføre dem alle gjennom miljøvariabler vil være ekstremt vanskelig.

Dessuten, i dette tilfellet ville jeg ikke kunne fortelle deg om et så fantastisk verktøy som git-krypt.

git-krypt Det er også praktisk ved at det lar deg lagre hele historien til hemmeligheter, samt sammenligne, slå sammen og løse konflikter på samme måte som vi er vant til å gjøre når det gjelder Git.

Det første etter installasjonen git-krypt vi må generere nøkler for vårt depot:

git crypt init

Hvis du har en PGP-nøkkel, kan du umiddelbart legge til deg selv som samarbeidspartner for dette prosjektet:

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

På denne måten kan du alltid dekryptere dette depotet ved å bruke din private nøkkel.

Hvis du ikke har en PGP-nøkkel og ikke forventer det, kan du gå den andre veien og eksportere prosjektnøkkelen:

git crypt export-key /path/to/keyfile

Dermed alle som har en eksportert nøkkelfil vil kunne dekryptere depotet ditt.

Det er på tide å sette opp vår første hemmelighet.
La meg minne deg på at vi fortsatt er i katalogen deploy/gitlab-runner/, hvor vi har en katalog hemmeligheter/, la oss kryptere alle filene i den, for dette vil vi lage en fil hemmeligheter/.gitattributes med følgende innhold:

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

Som det fremgår av innholdet, er alle filer maskert * vil bli kjørt gjennom git-krypt, bortsett fra de fleste .gitattributes

Vi kan sjekke dette ved å kjøre:

git crypt status -e

Utdataene vil være en liste over alle filene i depotet som kryptering er aktivert for

Det er alt, nå kan vi trygt gjennomføre endringene våre:

cd ../..
git add .
git commit -m "Add deploy for gitlab-runner"

For å blokkere et depot, bare kjør:

git crypt lock

og umiddelbart vil alle krypterte filer bli til binære noe, det vil være umulig å lese dem.
For å dekryptere depotet, kjør:

git crypt unlock

8. Lag et verktøykassebilde

Et verktøykassebilde er et bilde med alle verktøyene vi skal bruke for å distribuere prosjektet vårt. Den vil bli brukt av Gitlab-løperen til å utføre typiske distribusjonsoppgaver.

Alt er enkelt her, la oss lage en ny dockerfiles/toolbox/Dockerfile med følgende innhold:

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, i dette bildet installerer vi alle verktøyene vi brukte til å distribuere applikasjonen vår. Vi trenger det ikke her med mindre kubectl, men du vil kanskje leke med det under oppsettfasen av rørledningen.

For å kunne kommunisere med Kubernetes og distribuere til den, må vi også konfigurere en rolle for podene generert av gitlab-runner.

For å gjøre dette, la oss gå til katalogen med gitlab-runner:

cd deploy/gitlab-runner

og legg til 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 vil også beskrive de nye parameterne i environments/base.libsonnet, som nå ser slik ut:

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

Vær oppmerksom $.components.rbac.name refererer til navn for komponent rbac

La oss sjekke hva som har endret seg:

qbec diff default

og bruk endringene våre på Kubernetes:

qbec apply default

Ikke glem å forplikte endringene våre til 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ørste pipeline og sammenstilling av bilder etter tagger

I roten til prosjektet skal vi lage .gitlab-ci.yml med følgende innhold:

.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

Vær oppmerksom på at vi bruker GIT_SUBMODULE_STRATEGY: normal for de jobbene der du eksplisitt må initialisere undermoduler før kjøring.

Ikke glem å forplikte endringene våre:

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

Jeg tror vi trygt kan kalle dette en versjon v0.0.1 og legg til taggen:

git tag v0.0.1

Vi vil legge til tagger når vi trenger å gi ut en ny versjon. Tagger i Docker-bilder vil bli knyttet til Git-tagger. Hvert trykk med en ny tag vil initialisere oppbyggingen av bilder med denne taggen.

La oss gjøre det git push --tags, og la oss se på vår første pipeline:

Skjermbilde av den første rørledningen

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Det er verdt å trekke oppmerksomheten din til det faktum at montering av tagger er egnet for å bygge docker-bilder, men er ikke egnet for å distribuere en applikasjon til Kubernetes. Siden nye tagger kan tilordnes til gamle forpliktelser, vil initialisering av pipelinen for dem i dette tilfellet føre til distribusjon av den gamle versjonen.

For å løse dette problemet er byggingen av docker-bilder vanligvis knyttet til tagger, og distribusjonen av applikasjonen til en filial Master, der versjoner av de innsamlede bildene er hardkodet. Det er her du kan initialisere tilbakerulling med en enkel tilbakestilling Master-grener.

10. Automatisering av distribusjon

For at Gitlab-runner skal dekryptere hemmelighetene våre, må vi eksportere depotnøkkelen og legge den til CI-miljøvariablene våre:

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

Vi lagrer den resulterende linjen i Gitlab; for å gjøre dette, la oss gå til prosjektinnstillingene våre:
Innstillinger -> CI / CD -> Variabler

Og la oss lage en ny variabel:

typen
nøkkel
Verdi
beskyttet
Masked
Omfang

File
GITCRYPT_KEY
<your string>
true (under treningen kan du false)
true
All environments

Skjermbilde av den tilføyde variabelen

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

La oss nå oppdatere vår .gitlab-ci.yml legger til 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

Her har vi aktivert flere nye alternativer for qbec:

  • --root noen/app — lar deg bestemme katalogen til et bestemt program
  • --force:k8s-context __incluster__ - dette er en magisk variabel som sier at utrullingen vil skje i samme klynge som gtilab-runner kjører. Dette er nødvendig fordi ellers vil qbec prøve å finne en passende Kubernetes-server i kubeconfig
  • --vente — tvinger qbec til å vente til ressursene den oppretter går inn i Klar-tilstand og først deretter avslutte med en vellykket utgangskode.
  • -ja - deaktiverer ganske enkelt det interaktive skallet Er du sikker? når den er utplassert.

Ikke glem å forplikte endringene våre:

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

Og etter git push vi vil se hvordan applikasjonene våre har blitt distribuert:

Skjermbilde av den andre rørledningen

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

11. Artefakter og montering når du trykker for å mestre

Vanligvis er trinnene beskrevet ovenfor tilstrekkelige til å bygge og levere nesten alle mikrotjenester, men vi ønsker ikke å legge til en tag hver gang vi trenger å oppdatere nettstedet. Derfor vil vi ta en mer dynamisk rute og sette opp en sammendragsdistribusjon i mastergrenen.

Ideen er enkel: nå bildet av vår nettsted vil bli gjenoppbygd hver gang du trykker inn Master, og distribuer deretter automatisk til Kubernetes.

La oss oppdatere disse to jobbene 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"

Vær oppmerksom på at vi har lagt til en tråd Master к refs for jobber build_website og vi bruker nå $CI_COMMIT_REF_NAME i stedet for $CI_COMMIT_TAG, det vil si at vi er løst fra tagger i Git, og nå vil vi skyve et bilde med navnet på commit-grenen som initialiserte pipelinen. Det er verdt å merke seg at dette også vil fungere med tagger, som lar oss lagre øyeblikksbilder av et nettsted med en bestemt versjon i docker-registeret.

Når navnet på docker-taggen for en ny versjon av nettstedet kan være uendret, må vi fortsatt beskrive endringene i Kubernetes, ellers vil den ganske enkelt ikke omdistribuere applikasjonen fra det nye bildet, siden den ikke vil merke noen endringer i distribusjonsmanifest.

alternativ —vm:ext-str digest="$DIGEST" for qbec - lar deg sende en ekstern variabel til jsonnet. Vi vil at den skal omdistribueres i klyngen med hver utgivelse av applikasjonen vår. Vi kan ikke lenger bruke tagnavnet, som nå kan være uforanderlig, siden vi må være knyttet til en spesifikk versjon av bildet og utløse distribusjonen når den endres.

Her vil vi bli hjulpet av Kanikos evne til å lagre et sammendragsbilde til en fil (alternativ --digest-fil)
Deretter vil vi overføre denne filen og lese den på tidspunktet for distribusjon.

La oss oppdatere parametrene for vår deploy/website/environments/base.libsonnet som nå vil se slik ut:

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

Ferdig, nå forplikter du deg Master initialiserer byggingen av docker-bildet for nettsted, og distribuer den deretter til Kubernetes.

Ikke glem å forplikte endringene våre:

git add .
git commit -m "Configure dynamic build"

Vi sjekker senere git push vi burde se noe slikt:

Skjermbilde av rørledningen for master

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

I prinsippet trenger vi ikke å omdistribuere gitlab-runner med hvert trykk, med mindre selvfølgelig ingenting har endret seg i konfigurasjonen, la oss fikse 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/**/*

endringer lar deg overvåke endringer i deploy/gitlab-runner/ og vil utløse jobben vår bare hvis det er noen

Ikke glem å forplikte endringene våre:

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

git push, det er bedre:

Skjermbilde av den oppdaterte rørledningen

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

12. Dynamiske miljøer

Det er på tide å diversifisere rørledningen vår med dynamiske miljøer.

Først, la oss oppdatere jobben build_website i vår .gitlab-ci.yml, fjerner blokken fra den bare, som vil tvinge Gitlab til å utløse den på enhver commit til enhver filial:

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/

Oppdater deretter jobben deploy_website, legg til en blokk der 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"

Dette vil tillate Gitlab å knytte jobben til prod miljøet og vise riktig kobling til det.

La oss nå legge til to jobber til:

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 vil bli lansert ved push til alle grener unntatt master og vil distribuere forhåndsvisningsversjonen av nettstedet.

Vi ser et nytt alternativ for qbec: --app-tag — den lar deg merke distribuerte versjoner av applikasjonen og arbeide bare innenfor denne taggen; når du oppretter og ødelegger ressurser i Kubernetes, vil qbec kun operere med dem.
På denne måten kan vi ikke lage et eget miljø for hver anmeldelse, men ganske enkelt gjenbruke det samme.

Her bruker vi også qbec bruke vurdering, i stedet for qbec bruke standard - dette er akkurat det øyeblikket vi vil prøve å beskrive forskjellene for våre miljøer (gjennomgang og standard):

La oss legge til anmeldelse miljø i deploy/website/qbec.yaml

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

Da vil vi deklarere 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

Og skriv ned de egendefinerte parameterne for 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',
    },
  },
}

La oss også se nærmere på jobu stop_review, vil den utløses når filialen slettes og for at gitlab ikke skal prøve å sjekke ut blir den brukt GIT_STRATEGY: ingen, senere kloner vi Master- gren og slett anmeldelse gjennom den.
Det er litt forvirrende, men jeg har ikke funnet en vakrere måte ennå.
Et alternativt alternativ ville være å distribuere hver anmeldelse til et hotellnavnområde, som alltid kan rives helt.

Ikke glem å forplikte endringene våre:

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

git push, git checkout -b test, git push opprinnelsestest, Sjekk:

Skjermbilde av opprettede miljøer i Gitlab

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Alt fungerer? - flott, slett testgrenen vår: git checkout master, git push origin :test, sjekker vi at miljøslettejobbene fungerte uten feil.

Her vil jeg umiddelbart presisere at enhver utvikler i et prosjekt kan lage grener, han kan også endre .gitlab-ci.yml fil og få tilgang til hemmelige variabler.
Derfor anbefales det sterkt å tillate bruk kun for beskyttede grener, for eksempel i Master, eller lag et eget sett med variabler for hvert miljø.

13. Gjennomgå apper

Gjennomgå apper Dette er en GitLab-funksjon som lar deg legge til en knapp for hver fil i depotet for raskt å vise den i et distribuert miljø.

For at disse knappene skal vises, må du opprette en fil .gitlab/rute-kart.yml og beskriv alle banetransformasjoner i den; i vårt tilfelle vil det være veldig enkelt:

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

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

Ikke glem å forplikte endringene våre:

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

git push, og sjekk:

Skjermbilde av knappen Review App

Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Jobben er gjort!

Prosjektkilder:

Takk for oppmerksomheten, jeg håper du likte den Prøver nye verktøy for å bygge og automatisere distribusjon i Kubernetes

Kilde: www.habr.com

Legg til en kommentar