Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Hej! En masse smarte automatiseringsværktøjer er blevet frigivet på det seneste til både opbygning af Docker-billeder og implementering til Kubernetes. I denne henseende besluttede jeg at lege med Gitlab, hvordan man studerer dets muligheder og selvfølgelig opsætter en pipeline.

Denne side er inspireret af kubernetes.io, som er genereret fra kildekoder automatisk, og for hver sendt pull-anmodning genererer robotten automatisk en forhåndsvisning af webstedet med dine ændringer og giver et link til visning.

Jeg forsøgte at bygge en lignende proces fra bunden, men helt bygget på Gitlab CI og gratis værktøjer, som jeg plejede at bruge til at implementere applikationer til Kubernetes. I dag vil jeg endelig fortælle mere om dem.

Artiklen vil dække værktøjer som:
Hugo, qbec, kaniko, git-krypt и GitLab CI med skabelsen af ​​dynamiske miljøer.

Indhold

  1. Lær Hugo at kende
  2. Forberedelse af Dockerfilen
  3. Lær kaniko at kende
  4. Introduktion til qbec
  5. Prøver Gitlab-runner med Kubernetes-executor
  6. Implementer Helm-diagrammer med qbec
  7. Introduktion til git-crypt
  8. Opret et værktøjskassebillede
  9. Vores første pipeline og samling af billeder efter tags
  10. Implementer automatisering
  11. Artefakter og samling, når der skubbes til master
  12. Dynamiske miljøer
  13. Gennemgå apps

1. Lær Hugo at kende

Som et eksempel på vores projekt vil vi forsøge at skabe en dokumentationspubliceringsside bygget på Hugo. Hugo er en statisk indholdsgenerator.

For dem, der ikke er bekendt med statiske generatorer, vil jeg fortælle dig lidt mere om dem. I modsætning til almindelige webstedsmotorer med en database og en slags php, som, når brugeren anmoder om det, genererer sider i farten, er statiske generatorer arrangeret lidt anderledes. De giver dig mulighed for at tage kildekoden, normalt et sæt filer i Markdown-markerings- og temaskabeloner, og derefter kompilere dem til et helt færdigt websted.

Det vil sige, ved udgangen vil du få en mappestruktur og et sæt af genererede html-filer, som du blot kan uploade til en hvilken som helst billig hosting og få et fungerende websted.

Du kan installere Hugo lokalt og prøve det:

Initialisering af det nye websted:

hugo new site docs.example.org

Og samtidig git-depotet:

cd docs.example.org
git init

Indtil videre er vores websted uberørt, og for at noget skal vises på det, skal vi først forbinde et tema, et tema er blot et sæt skabeloner og fastlagte regler, som vores websted genereres efter.

Som tema vil vi bruge Learn, som efter min mening er bedst egnet til en side med dokumentation.

Jeg vil gerne være særlig opmærksom på, at vi ikke behøver at gemme temafilerne i vores projekts lager, i stedet kan vi blot forbinde det vha. git undermodul:

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

Således vil kun filer, der er direkte relateret til vores projekt, være i vores repository, og det forbundne tema vil forblive som et link til et specifikt depot og en commit i det, det vil sige, det kan altid trækkes fra den originale kilde og ikke være bange. af uforenelige ændringer.

Lad os rette opsætningen config.toml:

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

Allerede på dette stadium kan du køre:

hugo server

Og på adressen http://localhost:1313/ tjek vores nyoprettede websted, alle ændringer foretaget i mappen opdaterer automatisk den åbne side i browseren, meget praktisk!

Lad os prøve at oprette en titelside i content/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Skærmbillede af den nyoprettede side

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

For at generere et websted skal du blot køre:

hugo

Katalogindhold offentlig/ og vil være dit websted.
Ja, forresten, lad os straks bringe det ind .gitignore:

echo /public > .gitignore

Glem ikke at foretage vores ændringer:

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

2. Forberedelse af Dockerfilen

Det er tid til at definere strukturen af ​​vores depot. Normalt bruger jeg noget som:

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

  • dockerfiler/ - indeholder mapper med Dockerfiler og alt det nødvendige for at bygge vores docker-billeder.
  • indsætte/ - indeholder mapper til implementering af vores applikationer i Kubernetes

Således vil vi oprette vores første Dockerfile undervejs 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, indeholder Dockerfilen to FRA, kaldes denne mulighed byggeri i flere trin og giver dig mulighed for at udelukke alt unødvendigt fra det endelige docker-billede.
Det endelige billede vil således kun indeholde mørkhttpd (letvægts HTTP-server) og offentlig/ - indholdet af vores statisk genererede websted.

Glem ikke at foretage vores ændringer:

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

3. Lær kaniko at kende

Som opbygger af docker-billeder besluttede jeg at bruge kaniko, da det ikke kræver en docker-dæmon for at fungere, og selve samlingen kan udføres på enhver maskine og gemme cachen direkte i registreringsdatabasen, og derved slippe af med behovet for at have et fuldgyldigt vedvarende lager.

For at bygge billedet skal du bare køre beholderen med kaniko eksekutor og videregive den aktuelle byggekontekst til det, du kan gøre det 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å dit docker-billede, efter at det er bygget, vil det automatisk blive lanceret i docker-registret.

Parameter --cache giver dig mulighed for at cache lag i docker-registret, for det givne eksempel vil de blive gemt i registry.gitlab.com/kvaps/docs.example.org/website/cache, men du kan angive en anden sti med parameteren --cache-repo.

Skærmbillede af docker-registry

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

4. Introduktion til qbec

Qbec er et implementeringsværktøj, der giver dig mulighed for deklarativt at beskrive dine applikationsmanifester og implementere dem til Kubernetes. Brug af Jsonnet som hovedsyntaks gør det meget nemt at beskrive forskellene for flere miljøer og eliminerer også næsten fuldstændig kodegentagelse.

Dette kan især være tilfældet i tilfælde, hvor du har brug for at implementere en applikation i flere klynger med forskellige parametre og ønsker at beskrive dem deklarativt i Git.

Qbec giver dig også mulighed for at gengive Helm-diagrammer ved at give dem de nødvendige parametre og derefter operere på dem på samme måde som almindelige manifester, inklusive evnen til at anvende forskellige mutationer på dem, og dette eliminerer igen behovet for at bruge ChartMuseum. Det vil sige, at du kan gemme og gengive diagrammer direkte fra git, hvor de hører hjemme.

Som jeg sagde før, vil vi gemme alle implementeringer i mappen indsætte/:

mkdir deploy
cd deploy

Lad os initialisere vores første applikation:

qbec init website
cd website

Nu ser strukturen af ​​vores applikation sådan ud:

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

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 primært interesserede i spec.miljøer, qbec har allerede oprettet standardmiljøet for os og tog serveradressen og navneområdet fra vores nuværende kubeconfig.
Nu når de udrulles til standard miljø, vil qbec altid kun implementere til den angivne Kubernetes-klynge og til det angivne navneområde, dvs. du behøver ikke længere at skifte mellem kontekster og navnerum for at implementere.
Om nødvendigt kan du altid opdatere indstillingerne i denne fil.

Alle dine miljøer er beskrevet i qbec.yaml, og i filen params.libsonnet, som siger, hvor du skal tage parametrene for dem.

Dernæst ser vi to mapper:

  • komponenter / - alle manifester til vores applikation vil blive gemt her, de kan beskrives både i jsonnet og i almindelige yaml-filer
  • miljøer/ - her vil vi beskrive alle variabler (parametre) for vores miljøer.

Som standard har vi to filer:

  • environments/base.libsonnet - den vil indeholde fælles parametre for alle miljøer
  • environments/default.libsonnet - indeholder parametre omdefineret for miljøet standard

lad os åbne environments/base.libsonnet og tilføje parametre for vores 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',
    },
  },
}

Lad os også skabe vores 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 fil beskrev vi tre Kubernetes-enheder på én gang, disse er: Deployment, Service и Ingress. Hvis det ønskes, kunne vi flytte dem ind i forskellige komponenter, men på dette tidspunkt er én nok for os.

syntaks jsonnet meget lig almindelig json, i princippet er almindelig json allerede et gyldigt jsonnet, så i starten kan det være lettere for dig at bruge onlinetjenester som f.eks. yaml2json at konvertere din sædvanlige yaml til json, eller hvis dine komponenter ikke indeholder nogen variable, så kan de beskrives i form af almindelig yaml.

Når man arbejder med jsonnet Jeg anbefaler dig kraftigt at installere et plugin til din editor

For eksempel er der et plugin til vim vim-jsonnet, som aktiverer syntaksfremhævning og udføres automatisk jsonnet fmt ved hver lagring (kræver, at jsonnet er installeret).

Alt er klar, nu kan vi starte implementeringen:

For at se, hvad vi har, lad os køre:

qbec show default

Ved udgangen vil du se de gengivede yaml-manifester, der vil blive anvendt på standardklyngen.

Godt, ansøg nu:

qbec apply default

På outputtet vil du altid se, hvad der vil blive gjort i din klynge, qbec vil bede dig om at acceptere ændringerne ved at skrive y du kan bekræfte dine hensigter.

Færdig nu er vores app implementeret!

Hvis der foretages ændringer, kan du altid køre:

qbec diff default

for at se, hvordan disse ændringer vil påvirke den aktuelle implementering

Glem ikke at foretage vores ændringer:

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

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

Indtil for nylig har jeg kun brugt almindelig gitlab-runner på en præpareret maskine (LXC container) med shell- eller docker-executor. I starten havde vi flere af disse løbere defineret globalt i vores gitlab. De byggede docker-billeder til alle projekter.

Men som praksis har vist, er denne mulighed ikke den mest ideelle, både med hensyn til praktisk og med hensyn til sikkerhed. Det er meget bedre og ideologisk korrekt at have separate løbere indsat for hvert projekt, og endda for hvert miljø.

Heldigvis er dette slet ikke et problem, da vi nu vil implementere gitlab-runner direkte som en del af vores projekt lige i Kubernetes.

Gitlab leverer et færdigt rordiagram til implementering af gitlab-runner til Kubernetes. Så alt hvad du behøver at vide er registreringstoken til vores projekt i Indstillinger -> CI / CD -> Løbere og send det 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 er adressen på din Gitlab-server.
  • yga8y-jdCusVDn_t4Wxc - registreringstoken til dit projekt.
  • rbac.create=sand - giver løberen det nødvendige antal privilegier for at kunne oprette pods til at udføre vores opgaver ved hjælp af kubernetes-executor.

Hvis alt er udført korrekt, bør du se den tilmeldte løber i afsnittet Løbere, i dine projektindstillinger.

Skærmbillede af den tilføjede løber

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Er det så simpelt? – ja, så enkelt er det! Ikke mere besvær med manuelt at registrere løbere, fra nu af vil løbere blive oprettet og destrueret automatisk.

6. Implementer Helm-diagrammer med QBEC

Da vi har besluttet at overveje gitlab-runner en del af vores projekt, er det tid til at beskrive det i vores Git-lager.

Vi kunne beskrive det som en separat komponent hjemmeside, men i fremtiden planlægger vi at implementere forskellige kopier hjemmeside meget ofte, i modsætning til gitlab-runner, som kun vil blive implementeret én gang pr. Kubernetes-klynge. Så lad os initialisere en separat applikation til det:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

Denne gang vil vi ikke beskrive Kubernetes-enheder manuelt, men tage et færdigt Helm-diagram. En af fordelene ved qbec er evnen til at gengive Helm-diagrammer direkte fra et Git-lager.

Lad os aktivere det ved hjælp af git undermodul:

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

Nu mappen leverandør/gitlab-runner indeholder vores repository med et diagram for gitlab-runner.

Andre depoter kan forbindes på lignende måde, for eksempel hele depotet med officielle diagrammer https://github.com/helm/charts

Lad os 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 argument til expandHelmTemplate så passerer vi stien til kortet params.værdier, som vi tager fra miljøparametrene, så kommer objektet med

  • navnSkabelon - udgivelsesnavn
  • navnerum — navneområde overført til roret
  • denne fil - en påkrævet parameter, der sender stien til den aktuelle fil
  • ordrig - viser kommando styreskabelon med alle argumenter ved gengivelse af et diagram

Lad os nu beskrive parametrene for vores 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,
      },
    },
  },
}

Bemærk venligst runnerRegistrationToken vi henter fra ekstern fil secrets/base.libsonnet, lad os skabe det:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Lad os tjekke om alt virker:

qbec show default

hvis alt er i orden, så kan vi fjerne vores tidligere installerede via Helm-udgivelse:

helm uninstall gitlab-runner

og implementer det, men gennem qbec:

qbec apply default

7. Introduktion til git-crypt

git-krypt er et værktøj, der giver dig mulighed for at opsætte gennemsigtig kryptering til dit lager.

I øjeblikket ser vores mappestruktur for gitlab-runner sådan ud:

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

Men at gemme hemmeligheder i Git er ikke sikkert, er det? Så vi skal kryptere dem ordentligt.

Normalt af hensyn til én variabel giver det ikke altid mening. Du kan overføre hemmeligheder til qbec og gennem miljøvariablerne i dit CI-system.
Men det er værd at bemærke, at der også er mere komplekse projekter, der kan indeholde meget flere hemmeligheder, det vil være ekstremt svært at føre dem alle gennem miljøvariabler.

Derudover ville jeg i dette tilfælde ikke være i stand til at fortælle dig om et så vidunderligt værktøj som git-krypt.

git-krypt Det er også praktisk, fordi det giver dig mulighed for at gemme hele historien om hemmeligheder, samt sammenligne, flette og løse konflikter på samme måde, som vi plejede at gøre det i tilfældet med Git.

Det første efter installationen git-krypt vi skal generere nøgler til vores lager:

git crypt init

Hvis du har en PGP-nøgle, kan du straks tilføje dig selv som samarbejdspartner for dette projekt:

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

På denne måde kan du altid dekryptere dette lager ved hjælp af din private nøgle.

Hvis du ikke har en PGP-nøgle og ikke forventes at have, så kan du gå den anden vej og eksportere projektnøglen:

git crypt export-key /path/to/keyfile

Således kan enhver, der besidder en eksporteret nøglefil vil være i stand til at dekryptere dit lager.

Det er tid til at oprette vores første hemmelighed.
Lad mig minde dig om, at vi stadig er i mappen deploy/gitlab-runner/hvor vi har en mappe hemmeligheder/, lad os kryptere alle filerne i den, for dette vil vi oprette en fil hemmeligheder/.gitattributes med indhold som dette:

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

Som det kan ses af indholdet, er alle filer efter maske * vil løbe igennem git-krypt, med undtagelse af .gitattributes

Vi kan tjekke dette ved at køre:

git crypt status -e

Ved udgangen får vi en liste over alle filer i depotet, for hvilke kryptering er aktiveret

Det er det, nu kan vi trygt foretage vores ændringer:

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

For at blokere depotet er det nok at udføre:

git crypt lock

og straks vil alle krypterede filer blive til et binært noget, det vil være umuligt at læse dem.
For at dekryptere depotet skal du køre:

git crypt unlock

8. Opret et værktøjskassebillede

Et værktøjskassebillede er et billede med alle de værktøjer, vi vil bruge til at implementere vores projekt. Det vil blive brugt af gitlab runner til at udføre typiske implementeringsopgaver.

Alt er enkelt her, vi skaber en ny dockerfiler/værktøjskasse/Dockerfil med indhold som dette:

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, installerer vi i dette billede alle de værktøjer, vi brugte til at implementere vores applikation. Vi har ikke brug for her, medmindre kubectl, men du vil måske lege med det, når du opsætter pipelinen.

For at kunne kommunikere med Kubernetes og implementere til det, er vi også nødt til at konfigurere en rolle for pods genereret af gitlab-runner.

For at gøre dette skal du gå til mappen med gitlab-runner'om:

cd deploy/gitlab-runner

og tilføje 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 beskriver også de nye parametre i environments/base.libsonnet, som nu ser sådan ud:

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

Bemærk venligst $.components.rbac.name hentyder til navn for komponent RBAC

Lad os tjekke, hvad der er ændret:

qbec diff default

og anvende vores ændringer på Kubernetes:

qbec apply default

Glem heller ikke at forpligte vores ændringer 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. Vores første pipeline og samling af billeder efter tags

I roden af ​​projektet vil vi skabe .gitlab-ci.yml med indhold som dette:

.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

Bemærk venligst, at vi bruger GIT_SUBMODULE_STRATEGY: normal til de job, hvor du eksplicit skal initialisere undermoduler før udførelse.

Glem ikke at foretage vores ændringer:

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

Jeg tror, ​​man roligt kan kalde det en version v0.0.1 og tilføj et tag:

git tag v0.0.1

Vi vil hænge tags, når vi skal udgive en ny version. Tags i Docker-billeder vil blive knyttet til Git-tags. Hvert tryk med et nyt tag vil initialisere en billedopbygning med det tag.

Lad os gøre det git push --tags, og se på vores første pipeline:

Skærmbillede af den første pipeline

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Det er værd at påpege, at tag-baserede builds er gode til at bygge docker-billeder, men ikke til at implementere en applikation til Kubernetes. Da nye tags også kan tildeles til gamle commits, vil initialisering af pipelinen for dem i dette tilfælde føre til implementeringen af ​​den gamle version.

For at løse dette problem er opbygningen af ​​docker-billeder normalt knyttet til tags og implementeringen af ​​applikationen til filialen Master, hvor versionerne af de indsamlede billeder er hardkodet. Det er i dette tilfælde, at du kan initialisere rollback med en simpel tilbagevending Master-grene.

10. Implementer automatisering

For at Gitlab-runner kan dekryptere vores hemmeligheder, skal vi eksportere lagernøglen og tilføje den til vores CI-miljøvariabler:

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

vi gemmer den resulterende streng i Gitlab, for dette vil vi gå til indstillingerne for vores projekt:
Indstillinger —> CI / CD —> Variabler

Og opret en ny variabel:

Type
Nøgle
Værdi
Beskyttet
maskeret
Anvendelsesområde

File
GITCRYPT_KEY
<your string>
true (på træningstidspunktet kan du false)
true
All environments

Skærmbillede af den tilføjede variabel

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Lad os nu opdatere vores .gitlab-ci.yml tilføjer 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 aktiveret nogle nye muligheder for qbec:

  • --root nogle/app - giver dig mulighed for at definere biblioteket for en specifik applikation
  • --force:k8s-context __incluster__ - dette er en magisk variabel, der siger, at implementeringen vil ske i den samme klynge, som gtilab-runner kører i. Dette er nødvendigt, ellers vil qbec forsøge at finde en passende Kubernetes-server i din kubeconfig
  • -vente - tvinger qbec til at vente, indtil de ressourcer, den skaber, går ind i tilstanden Klar og først derefter fuldføre med en vellykket exit-kode.
  • -Ja - deaktiverer bare den interaktive shell Er du sikker? under udsendelsen.

Glem ikke at foretage vores ændringer:

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

Og efter git push vi vil se, hvordan vores applikationer blev implementeret:

Skærmbillede af den anden pipeline

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

11. Artefakter og samling ved skubbe til master

Normalt er ovenstående trin nok til at bygge og levere næsten enhver mikroservice, men vi ønsker ikke at tilføje et tag, hver gang vi skal opdatere webstedet. Derfor vil vi gå på en mere dynamisk måde og opsætte en digest-implementering i mastergrenen.

Ideen er enkel: nu billedet af vores hjemmeside vil blive genopbygget hver gang du trykker på Master, og implementer derefter automatisk til Kubernetes.

Lad os opdatere disse to jobs i vores .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"

Bemærk, at vi har tilføjet en filial Master к ref til job build_website og vi bruger nu $CI_COMMIT_REF_NAME i stedet for $CI_COMMIT_TAG, det vil sige, vi slipper af med tags i Git, og nu vil vi skubbe billedet med navnet på den commit-gren, der initialiserede din pipeline. Det er værd at bemærke, at dette også vil fungere med tags, som giver os mulighed for at gemme snapshots af webstedet med en specifik version i docker-registret.

Når navnet på docker-tagget for den nye version af webstedet kan være uændret, er vi stadig nødt til at beskrive ændringerne for Kubernetes, ellers vil den ganske enkelt ikke geninstallere applikationen fra det nye billede, da den ikke vil bemærke nogen ændringer i implementeringsmanifest.

valgmulighed --vm:ext-str digest="$DIGEST" for qbec - giver dig mulighed for at sende en ekstern variabel til jsonnet. Vi ønsker, at vores applikation skal omplaceres i klyngen med hver udgivelse. Vi kan ikke længere bruge navnet på tagget, som nu kan være uændret, da vi skal binde til en specifik version af billedet og udløse implementeringen, når den ændres.

Her vil Kanikos evne til at gemme sammendraget af billedet til en fil hjælpe os (option --digest-fil)
Så overfører vi denne fil og læser den på tidspunktet for implementeringen.

Lad os opdatere parametrene for vores deploy/website/environments/base.libsonnet som nu kommer til at se sådan ud:

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

Færdig, nu skal enhver forpligte sig Master initialiserer opbygningen af ​​docker-billedet til hjemmeside, og derefter implementere det til Kubernetes.

Glem ikke at foretage vores ændringer:

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

Tjek det ud bagefter git push vi burde se noget som dette:

Pipeline screenshot til master

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

I princippet behøver vi ikke at ominstallere gitlab-runner med hvert tryk, medmindre selvfølgelig intet er ændret i dens konfiguration, lad os rette dette 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/**/*

ændringer vil holde styr på ændringer i deploy/gitlab-runner/ og vil kun udløse vores job, hvis der er nogen

Glem ikke at foretage vores ændringer:

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

git push, det er bedre:

Skærmbillede af den opdaterede pipeline

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

12. Dynamiske miljøer

Det er tid til at diversificere vores pipeline med dynamiske miljøer.

Lad os først opdatere jobbet build_website i vores .gitlab-ci.yml, ved at fjerne blokken fra den kun, hvilket vil tvinge Gitlab til at udløse det 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/

Opdater derefter jobbet deploy_website, tilføje en blok 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 give Gitlab mulighed for at knytte jobbet til prod miljøet og vise det korrekte link til det.

Lad os nu tilføje to job mere:

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 blive udløst ved push til alle grene undtagen master og vil implementere en forhåndsvisningsversion af webstedet.

Vi ser en ny mulighed for qbec: --app-tag - det giver dig mulighed for at tagge installerede versioner af applikationen og kun arbejde inden for dette tag; når du opretter og ødelægger ressourcer i Kubernetes, vil qbec kun fungere på dem.
Vi kan således ikke skabe et separat miljø for hver anmeldelse, men blot genbruge det samme.

Her bruger vi også qbec anvende anmeldelse, i stedet for qbec anvender standard - dette er præcis det øjeblik, hvor vi vil forsøge at beskrive forskellene for vores miljøer (gennemgang og standard):

Lad os tilføje gennemgå miljø i deploy/website/qbec.yaml

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

Så erklærer 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

Og skriv tilpassede parametre for det 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',
    },
  },
}

Lad os også se nærmere på jobbet stop_anmeldelse, vil den blive udløst, når grenen fjernes, og så gitlab ikke forsøger at betale på den, bruges GIT_STRATEGY: ingen, senere kloner vi Master-forgrene og slette anmeldelse gennem det.
Lidt forvirrende, men jeg har endnu ikke fundet en smukkere måde.
En alternativ mulighed ville være at implementere hver anmeldelse til et hotelnavneområde, som altid kan rives ned i sin helhed.

Glem ikke at foretage vores ændringer:

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

git push, git checkout -b test, git push oprindelsestest, kontrollere:

Skærmbillede af oprettede miljøer i Gitlab

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Fungerer alt? - super, slet vores testgren: git checkout mester, git push oprindelse :test, kontrollerer vi, at jobs til sletning af miljøet fungerede uden fejl.

Her vil jeg straks præcisere, at enhver udvikler i projektet kan oprette grene, han kan også ændre .gitlab-ci.yml fil og få adgang til hemmelige variabler.
Derfor anbefales det kraftigt kun at tillade deres anvendelse til beskyttede grene, for eksempel i Master, eller opret et separat sæt variabler for hvert miljø.

13 Gennemgå apps

Gennemgå apps dette er en gitlab-funktion, der giver dig mulighed for at tilføje en knap for hver fil i depotet for hurtigt at se den i det installerede miljø.

For at disse knapper skal vises, skal du oprette en fil .gitlab/rute-map.yml og beskriv i det alle transformationerne af stierne, i vores tilfælde vil det være meget enkelt:

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

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

Glem ikke at foretage vores ændringer:

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

git push, og tjek:

Skærmbillede af knappen Review App

Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Job er gjort!

Projektkilder:

Tak for din opmærksomhed, jeg håber du kunne lide den Prøver nye værktøjer til opbygning og automatisering af implementering i Kubernetes

Kilde: www.habr.com

Tilføj en kommentar