Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Hallo! Onlangs zijn er veel coole automatiseringstools uitgebracht, zowel voor het bouwen van Docker-images als voor de implementatie in Kubernetes. In dit opzicht besloot ik om met GitLab te spelen, de mogelijkheden ervan grondig te bestuderen en natuurlijk de pijplijn op te zetten.

Dit werk is geïnspireerd op de website kubernetes.io, waaruit wordt gegenereerd broncodes automatisch, en voor elk verzonden poolverzoek genereert de robot automatisch een voorbeeldversie van de site met uw wijzigingen en biedt een link om deze te bekijken.

Ik heb geprobeerd een soortgelijk proces helemaal opnieuw te bouwen, maar volledig gebouwd op Gitlab CI en gratis tools die ik gewend ben te gebruiken om applicaties in Kubernetes te implementeren. Vandaag vertel ik je er eindelijk meer over.

In het artikel worden hulpmiddelen besproken zoals:
Hugo, qbec, Kaniko, git-crypt и GitLab-CI met het creëren van dynamische omgevingen.

Inhoud

  1. Maak kennis met Hugo
  2. Het Dockerbestand voorbereiden
  3. Kennismaken met Kaniko
  4. Kennismaken met Qbec
  5. Gitlab-runner proberen met Kubernetes-uitvoerder
  6. Helm-grafieken implementeren met qbec
  7. Introductie van git-crypt
  8. Een toolbox-afbeelding maken
  9. Onze eerste pijplijn en verzameling van afbeeldingen via tags
  10. Automatisering van de implementatie
  11. Artefacten en montage bij het pushen om onder de knie te krijgen
  12. Dynamische omgevingen
  13. Bekijk apps

1. Hugo leren kennen

Als voorbeeld van ons project zullen we proberen een site voor het publiceren van documentatie te maken, gebouwd op Hugo. Hugo is een statische contentgenerator.

Voor degenen die niet bekend zijn met statische generatoren, zal ik er wat meer over vertellen. In tegenstelling tot conventionele website-engines met een database en een beetje PHP, die, wanneer de gebruiker daarom vraagt, direct pagina's genereren, zijn statische generatoren iets anders ontworpen. Hiermee kunt u bronnen gebruiken, meestal een reeks bestanden in Markdown-opmaak en themasjablonen, en deze vervolgens compileren tot een volledig voltooide website.

Dat wil zeggen dat u als resultaat een directorystructuur en een reeks gegenereerde HTML-bestanden ontvangt, die u eenvoudig naar een goedkope hosting kunt uploaden en een werkende website kunt krijgen.

Je kunt Hugo lokaal installeren en uitproberen:

Een nieuwe site initialiseren:

hugo new site docs.example.org

En tegelijkertijd de git-repository:

cd docs.example.org
git init

Tot nu toe is onze site onberispelijk en voordat er iets op verschijnt, moeten we eerst een thema verbinden; een thema is slechts een reeks sjablonen en gespecificeerde regels waarmee onze site wordt gegenereerd.

Voor het thema zullen we gebruiken Leer, wat naar mijn mening perfect geschikt is voor een documentatiesite.

Ik zou speciale aandacht willen besteden aan het feit dat we de themabestanden niet in de repository van ons project hoeven op te slaan; in plaats daarvan kunnen we deze eenvoudigweg verbinden met behulp van git submodule:

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

Onze repository zal dus alleen bestanden bevatten die direct verband houden met ons project, en het verbonden thema zal een link blijven naar een specifieke repository en een commit daarin, dat wil zeggen dat het altijd uit de originele bron kan worden gehaald en er niet bang voor hoeft te zijn onverenigbare veranderingen.

Laten we de configuratie corrigeren config.toml:

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

Al in dit stadium kunt u het volgende uitvoeren:

hugo server

En op het adres http://localhost:1313/ bekijk onze nieuw gemaakte website, alle wijzigingen die in de map worden aangebracht, werken automatisch de geopende pagina in de browser bij, erg handig!

Laten we proberen een voorblad te maken inhoud/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Screenshot van de nieuw aangemaakte pagina

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Om een ​​site te genereren, voert u gewoon het volgende uit:

hugo

Directory-inhoud openbaar/ en zal uw website zijn.
Ja, laten we het trouwens meteen toevoegen .gitignore:

echo /public > .gitignore

Vergeet niet onze wijzigingen vast te leggen:

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

2. Het Dockerbestand voorbereiden

Het is tijd om de structuur van onze repository te definiëren. Ik gebruik meestal zoiets als:

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

  • dockerbestanden/ - bevatten mappen met Dockerfiles en alles wat nodig is voor het bouwen van onze Docker-images.
  • aanwenden/ — bevat mappen voor het implementeren van onze applicaties op Kubernetes

We zullen dus ons eerste Dockerbestand langs het pad maken 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" ]

Zoals u kunt zien, bevat het Dockerbestand er twee NU, deze mogelijkheid wordt genoemd meertraps opbouw en stelt u in staat om alles wat onnodig is uit te sluiten van de uiteindelijke docker-image.
De uiteindelijke afbeelding bevat dus alleen donkerhttpd (lichtgewicht HTTP-server) en openbaar/ — de inhoud van onze statisch gegenereerde website.

Vergeet niet onze wijzigingen vast te leggen:

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

3. Kaniko leren kennen

Als Docker-imagebouwer besloot ik te gebruiken Kaniko, omdat voor de werking ervan geen docker-daemon nodig is, en de build zelf op elke machine kan worden uitgevoerd en de cache direct in het register kan worden opgeslagen, waardoor de noodzaak van een volwaardige permanente opslag wordt geëlimineerd.

Om de afbeelding te bouwen, voert u gewoon de container uit met kaniko-uitvoerder en geef het de huidige build-context door; dit kan ook lokaal worden gedaan, 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

Где register.gitlab.com/kvaps/docs.example.org/website — de naam van uw docker-image; na het bouwen wordt deze automatisch gelanceerd in het docker-register.

Parameter --cache Hiermee kunt u lagen in het docker-register in de cache opslaan; in het gegeven voorbeeld worden ze opgeslagen in register.gitlab.com/kvaps/docs.example.org/website/cache, maar u kunt een ander pad opgeven met behulp van de parameter --cache-repository.

Schermafbeelding van docker-register

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

4. Kennismaken met qbec

Qbec is een implementatietool waarmee u uw applicatiemanifesten declaratief kunt beschrijven en deze kunt implementeren in Kubernetes. Door Jsonnet als hoofdsyntaxis te gebruiken, kunt u de beschrijving van verschillen in meerdere omgevingen aanzienlijk vereenvoudigen en wordt codeherhaling vrijwel volledig geëlimineerd.

Dit kan met name het geval zijn in gevallen waarin u een applicatie in verschillende clusters met verschillende parameters moet implementeren en deze declaratief in Git wilt beschrijven.

Met Qbec kunt u ook Helm-diagrammen weergeven door ze de noodzakelijke parameters door te geven en ze vervolgens op dezelfde manier te gebruiken als reguliere manifesten, waarbij u ook verschillende mutaties erop kunt toepassen, en dit stelt u op zijn beurt in staat de noodzaak om gebruik ChartMuseum. Dat wil zeggen dat je grafieken rechtstreeks vanuit git kunt opslaan en renderen, waar ze thuishoren.

Zoals ik al eerder zei, slaan we alle implementaties op in een map aanwenden/:

mkdir deploy
cd deploy

Laten we onze eerste applicatie initialiseren:

qbec init website
cd website

Nu ziet de structuur van onze applicatie er als volgt uit:

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

laten we het bestand bekijken qbec.yaml:

apiVersion: qbec.io/v1alpha1
kind: App
metadata:
  name: website
spec:
  environments:
    default:
      defaultNamespace: docs
      server: https://kubernetes.example.org:8443
  vars: {}

Hier zijn wij vooral in geïnteresseerd spec.omgevingen, heeft qbec al een standaardomgeving voor ons gemaakt en het serveradres en de naamruimte van onze huidige kubeconfig overgenomen.
Wanneer u nu implementeert naar verzuim omgeving zal qbec altijd alleen implementeren in het opgegeven Kubernetes-cluster en in de opgegeven naamruimte, dat wil zeggen dat u niet langer hoeft te schakelen tussen contexten en naamruimten om een ​​implementatie uit te voeren.
Indien nodig kunt u de instellingen in dit bestand altijd bijwerken.

Al uw omgevingen worden beschreven in qbec.yamlen in het bestand params.libsonnet, waar staat waar de parameters ervoor kunnen worden opgehaald.

Vervolgens zien we twee mappen:

  • componenten / — alle manifesten voor onze applicatie worden hier opgeslagen; ze kunnen zowel in jsonnet- als in reguliere yaml-bestanden worden beschreven
  • omgevingen/ — hier zullen we alle variabelen (parameters) voor onze omgevingen beschrijven.

Standaard hebben we twee bestanden:

  • omgevingen/base.libsonnet - het zal gemeenschappelijke parameters bevatten voor alle omgevingen
  • omgevingen/default.libsonnet — bevat parameters die voor de omgeving worden overschreven verzuim

Laten we openen omgevingen/base.libsonnet en voeg daar parameters toe voor onze eerste component:

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

Laten we ook ons ​​eerste onderdeel maken componenten/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,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

In dit bestand hebben we drie Kubernetes-entiteiten tegelijk beschreven, dit zijn: Deployment, Service и Ingress. Als we zouden willen, zouden we ze in verschillende componenten kunnen stoppen, maar in dit stadium is één voldoende voor ons.

syntaxis jsonnet lijkt erg op reguliere json, in principe is reguliere json al geldig jsonnet, dus in eerste instantie kan het gemakkelijker voor u zijn om online diensten zoals te gebruiken yaml2json om uw gebruikelijke yaml naar json te converteren, of, als uw componenten geen variabelen bevatten, kunnen ze worden beschreven in de vorm van gewone yaml.

Bij het werken met jsonnet Ik raad ten zeerste aan om een ​​plug-in voor uw editor te installeren

Er is bijvoorbeeld een plug-in voor vim vim-jsonnet, waarmee syntaxisaccentuering wordt ingeschakeld en automatisch wordt uitgevoerd jsonnet fmt elke keer dat u opslaat (vereist jsonnet geïnstalleerd).

Alles is klaar, nu kunnen we beginnen met het inzetten van:

Om te zien wat we hebben, laten we rennen:

qbec show default

Bij de uitvoer ziet u gerenderde yaml-manifesten die worden toegepast op het standaardcluster.

Geweldig, solliciteer nu:

qbec apply default

Bij de uitvoer ziet u altijd wat er in uw cluster gaat gebeuren, qbec zal u vragen akkoord te gaan met de wijzigingen door te typen y u zult uw bedoelingen kunnen bevestigen.

Onze applicatie is klaar en geïmplementeerd!

Als u wijzigingen aanbrengt, kunt u altijd het volgende doen:

qbec diff default

om te zien hoe deze wijzigingen de huidige implementatie zullen beïnvloeden

Vergeet niet onze wijzigingen vast te leggen:

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

5. Gitlab-runner uitproberen met Kubernetes-uitvoerder

Tot voor kort gebruikte ik alleen regulier gitlab-runner op een vooraf voorbereide machine (LXC-container) met shell of docker-uitvoerder. Aanvankelijk hadden we wereldwijd verschillende van dergelijke hardlopers gedefinieerd in ons gitlab. Ze verzamelden docker-afbeeldingen voor alle projecten.

Maar zoals de praktijk heeft geleerd, is deze optie niet de meest ideale, zowel qua bruikbaarheid als qua veiligheid. Het is veel beter en ideologisch juister om voor elk project, of zelfs voor elke omgeving, aparte runners in te zetten.

Gelukkig is dit helemaal geen probleem, aangezien we nu gaan inzetten gitlab-runner direct als onderdeel van ons project in Kubernetes.

Gitlab biedt een kant-en-klaar roerdiagram voor het implementeren van gitlab-runner in Kubernetes. U hoeft het dus alleen maar uit te zoeken registratietoken voor ons project in Instellingen -> CI/CD -> Lopers en geef het door aan het roer:

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

Waar:

  • https://gitlab.com — het adres van uw Gitlab-server.
  • yga8y-jdCusVDn_t4Wxc — registratietoken voor uw project.
  • rbac.create=waar — biedt de hardloper de benodigde hoeveelheid rechten om pods te kunnen maken om onze taken uit te voeren met behulp van kubernetes-executor.

Als alles goed is gedaan, zou je een geregistreerde loper in de sectie moeten zien Runners, in uw projectinstellingen.

Schermafbeelding van de toegevoegde loper

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Is het zo eenvoudig? - ja, zo simpel is het! Geen gedoe meer met het handmatig registreren van lopers, lopers worden voortaan automatisch aangemaakt en vernietigd.

6. Implementeer Helm-diagrammen met QBEC

Omdat we besloten om te overwegen gitlab-runner onderdeel van ons project, is het tijd om het te beschrijven in onze Git-repository.

We zouden het als een afzonderlijk onderdeel kunnen omschrijven van de, maar in de toekomst zijn we van plan verschillende exemplaren te implementeren van de heel vaak, in tegenstelling tot gitlab-runner, die slechts één keer per Kubernetes-cluster wordt geïmplementeerd. Laten we er dus een aparte applicatie voor initialiseren:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

Deze keer beschrijven we Kubernetes-entiteiten niet handmatig, maar gebruiken we een kant-en-klaar Helm-diagram. Een van de voordelen van qbec is de mogelijkheid om Helm-diagrammen rechtstreeks vanuit een Git-repository weer te geven.

Laten we het verbinden met behulp van de git-submodule:

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

Nu de map leverancier/gitlab-runner We hebben een repository met een diagram voor gitlab-runner.

Op een vergelijkbare manier kunt u andere repository's verbinden, bijvoorbeeld de hele repository met officiële grafieken https://github.com/helm/charts

Laten we het onderdeel beschrijven componenten/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,
  }
)

Het eerste argument om expandHelmsjabloon we passeren dan het pad naar de kaart params.waarden, die we uit de omgevingsparameters halen, komt dan met het object

  • naamSjabloon - titel van uitgave
  • namespace — naamruimte overgebracht naar roer
  • dit bestand — een vereiste parameter die het pad naar het huidige bestand doorgeeft
  • breedsprakig - toont de opdracht roer sjabloon met alle argumenten bij het weergeven van het diagram

Laten we nu de parameters voor onze component in beschrijven omgevingen/base.libsonnet:

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

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

Noot runnerRegistratieToken we nemen uit een extern bestand geheimen/base.libsonnet, laten we het maken:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Laten we eens kijken of alles werkt:

qbec show default

als alles in orde is, kunnen we onze eerder ingezette release via Helm verwijderen:

helm uninstall gitlab-runner

en implementeer het op dezelfde manier, maar via qbec:

qbec apply default

7. Inleiding tot git-crypt

Git-crypt is een tool waarmee u transparante encryptie voor uw repository kunt instellen.

Op dit moment ziet onze directorystructuur voor gitlab-runner er als volgt uit:

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

Maar het opslaan van geheimen in Git is niet veilig, toch? We moeten ze dus op de juiste manier versleutelen.

Meestal is dit vanwege één variabele niet altijd logisch. U kunt geheimen overbrengen naar qbec en via de omgevingsvariabelen van uw CI-systeem.
Maar het is de moeite waard om op te merken dat er ook complexere projecten zijn die veel meer geheimen kunnen bevatten; het overbrengen ervan via omgevingsvariabelen zal uiterst moeilijk zijn.

Bovendien zou ik je in dit geval niet kunnen vertellen over zo'n geweldig hulpmiddel als git-crypt.

git-crypt Het is ook handig omdat je hiermee de hele geschiedenis van geheimen kunt opslaan, en conflicten kunt vergelijken, samenvoegen en oplossen op dezelfde manier als we gewend zijn in het geval van Git.

Eerste ding na installatie git-crypt we moeten sleutels genereren voor onze repository:

git crypt init

Als u een PGP-sleutel heeft, kunt u uzelf onmiddellijk toevoegen als medewerker voor dit project:

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

Zo kunt u deze repository altijd ontsleutelen met uw privésleutel.

Als u geen PGP-sleutel heeft en deze niet verwacht, kunt u de andere kant op gaan en de projectsleutel exporteren:

git crypt export-key /path/to/keyfile

Dus iedereen die een export heeft sleutelbestand in staat zal zijn om uw repository te decoderen.

Het is tijd om ons eerste geheim op te zetten.
Laat me je eraan herinneren dat we nog steeds in de directory staan implementeren/gitlab-runner/, waar we een map hebben geheimen/, laten we alle bestanden erin coderen, hiervoor zullen we een bestand maken geheimen/.gitattributen met de volgende inhoud:

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

Zoals uit de inhoud blijkt, zijn alle bestanden gemaskeerd * zal doorgereden worden git-crypt, behalve het meeste .gitattributen

We kunnen dit controleren door het volgende uit te voeren:

git crypt status -e

De uitvoer is een lijst met alle bestanden in de repository waarvoor codering is ingeschakeld

Dat is alles, nu kunnen we onze wijzigingen veilig doorvoeren:

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

Om een ​​repository te blokkeren, voert u gewoon het volgende uit:

git crypt lock

en onmiddellijk zullen alle gecodeerde bestanden in binair iets veranderen, het zal onmogelijk zijn om ze te lezen.
Om de repository te decoderen, voer je het volgende uit:

git crypt unlock

8. Maak een toolbox-afbeelding

Een toolbox-afbeelding is een afbeelding met alle tools die we zullen gebruiken om ons project te implementeren. Het zal door de Gitlab-runner worden gebruikt om typische implementatietaken uit te voeren.

Alles is hier eenvoudig, laten we een nieuwe maken dockerfiles/toolbox/Dockerfile met de volgende inhoud:

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

Zoals u kunt zien, installeren we in deze afbeelding alle hulpprogramma's die we hebben gebruikt om onze applicatie te implementeren. We hebben het hier niet nodig, tenzij kubectl, maar misschien wil je er mee spelen tijdens de fase van het instellen van de pijplijn.

Om met Kubernetes te kunnen communiceren en ernaar te kunnen implementeren, moeten we ook een rol configureren voor de pods die door gitlab-runner zijn gegenereerd.

Om dit te doen, gaan we naar de directory met gitlab-runner:

cd deploy/gitlab-runner

en voeg een nieuw onderdeel toe componenten/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,
      },
    ],
  },
]

We zullen de nieuwe parameters ook beschrijven in omgevingen/base.libsonnet, dat er nu zo uitziet:

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

Noot $.components.rbac.naam verwijst naar naam voor onderdeel RBAC

Laten we eens kijken wat er is veranderd:

qbec diff default

en pas onze wijzigingen toe op Kubernetes:

qbec apply default

Vergeet ook niet om onze wijzigingen door te voeren naar 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. Onze eerste pijplijn en verzameling van afbeeldingen via tags

Aan de basis van het project zullen we creëren .gitlab-ci.yml met de volgende inhoud:

.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

Let op: wij gebruiken GIT_SUBMODULE_STRATEGY: normaal voor die taken waarbij u submodules expliciet moet initialiseren voordat u deze uitvoert.

Vergeet niet onze wijzigingen vast te leggen:

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

Ik denk dat we dit gerust een versie kunnen noemen v0.0.1 en voeg de tag toe:

git tag v0.0.1

We zullen tags toevoegen wanneer we een nieuwe versie moeten uitbrengen. Tags in Docker-images worden gekoppeld aan Git-tags. Elke push met een nieuwe tag initialiseert de opbouw van afbeeldingen met deze tag.

Laten we het doen git push --tags, en laten we eens kijken naar onze eerste pijplijn:

Screenshot van de eerste pijplijn

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Het is de moeite waard om uw aandacht te vestigen op het feit dat assemblage door tags geschikt is voor het bouwen van docker-images, maar niet geschikt is voor het implementeren van een applicatie op Kubernetes. Omdat nieuwe tags aan oude commits kunnen worden toegewezen, zal het initialiseren van de pijplijn daarvoor in dit geval leiden tot de implementatie van de oude versie.

Om dit probleem op te lossen, wordt meestal het bouwen van docker-images gekoppeld aan tags, en de implementatie van de applicatie in een branch meester, waarin versies van de verzamelde afbeeldingen hardgecodeerd zijn. Hier kunt u het terugdraaien initialiseren met een eenvoudige terugkeer meester-takken.

10. Automatisering van de implementatie

Om ervoor te zorgen dat Gitlab-runner onze geheimen kan ontsleutelen, moeten we de repositorysleutel exporteren en toevoegen aan onze CI-omgevingsvariabelen:

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

We slaan de resulterende regel op in Gitlab; laten we hiervoor naar onze projectinstellingen gaan:
Instellingen -> CI/CD -> Variabelen

En laten we een nieuwe variabele maken:

Type
sleutel
Waarde
Beschermd
gemaskerd
strekking

File
GITCRYPT_KEY
<your string>
true (tijdens de training kan dat false)
true
All environments

Schermafbeelding van de toegevoegde variabele

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Laten we nu onze .gitlab-ci.yml eraan toevoegen:

.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

Hier hebben we verschillende nieuwe opties voor qbec ingeschakeld:

  • --root een aantal/app — hiermee kunt u de map van een specifieke toepassing bepalen
  • --force:k8s-context __incluster__ - dit is een magische variabele die zegt dat de implementatie zal plaatsvinden in hetzelfde cluster waarin gtilab-runner draait. Dit is nodig omdat qbec anders zal proberen een geschikte Kubernetes-server te vinden in uw kubeconfig
  • --wachten — dwingt qbec te wachten tot de bronnen die het creëert naar de Ready-status gaan en pas dan afsluiten met een succesvolle exit-code.
  • -Ja - schakelt eenvoudigweg de interactieve shell uit Weet je het zeker? wanneer ingezet.

Vergeet niet onze wijzigingen vast te leggen:

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

En daarna git push we zullen zien hoe onze applicaties zijn ingezet:

Screenshot van de tweede pijpleiding

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

11. Artefacten en montage bij het pushen om onder de knie te krijgen

Normaal gesproken zijn de hierboven beschreven stappen voldoende om vrijwel elke microservice te bouwen en te leveren, maar we willen niet elke keer dat we de site moeten bijwerken een tag toevoegen. Daarom zullen we een meer dynamische route volgen en een digest-implementatie in de master-branch opzetten.

Het idee is simpel: nu het beeld van onze van de wordt elke keer dat je erin duwt, opnieuw opgebouwd meesteren vervolgens automatisch implementeren in Kubernetes.

Laten we deze twee banen bijwerken in onze .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"

Let op: we hebben een draad toegevoegd meester к scheidsrechters voor banen bouw_website en wij gebruiken nu $CI_COMMIT_REF_NAME in plaats van $CI_COMMIT_TAG, dat wil zeggen, we zijn losgekoppeld van tags in Git en nu zullen we een image pushen met de naam van de commit branch die de pijplijn heeft geïnitialiseerd. Het is vermeldenswaard dat dit ook werkt met tags, waardoor we momentopnamen van een site met een specifieke versie in het docker-register kunnen opslaan.

Wanneer de naam van de docker-tag voor een nieuwe versie van de site ongewijzigd kan blijven, moeten we nog steeds de wijzigingen in Kubernetes beschrijven, anders zal de applicatie eenvoudigweg niet opnieuw worden geïmplementeerd vanuit de nieuwe afbeelding, omdat deze geen wijzigingen in de weergave zal opmerken. implementatie manifest.

optie —vm:ext-str digest=”$DIGEST” voor qbec - hiermee kunt u een externe variabele doorgeven aan jsonnet. We willen dat deze bij elke release van onze applicatie opnieuw in het cluster wordt geïmplementeerd. We kunnen de tagnaam niet langer gebruiken, die nu onveranderlijk kan zijn, omdat we aan een specifieke versie van de afbeelding moeten worden gekoppeld en de implementatie moeten activeren wanneer deze verandert.

Hier worden we geholpen door de mogelijkheid van Kaniko om een ​​samenvattingsafbeelding op te slaan in een bestand (optie --digest-bestand)
Vervolgens zullen we dit bestand overbrengen en lezen op het moment van implementatie.

Laten we de parameters voor onze updaten deploy/website/environments/base.libsonnet die er nu zo uit zal zien:

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

Klaar, nu is er een commit meester initialiseert de build van de docker-image voor van deen implementeer het vervolgens in Kubernetes.

Vergeet niet onze wijzigingen vast te leggen:

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

We zullen het later controleren git push we zouden zoiets als dit moeten zien:

Schermafbeelding van de pijplijn voor master

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

In principe hoeven we gitlab-runner niet bij elke druk opnieuw in te zetten, tenzij er natuurlijk niets is veranderd in de configuratie. Laten we dit oplossen .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/**/*

veranderingen kunt u veranderingen in de omgeving monitoren implementeren/gitlab-runner/ en zal onze taak alleen activeren als die er is

Vergeet niet onze wijzigingen vast te leggen:

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

git push, dat is beter:

Schermafbeelding van de bijgewerkte pijplijn

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

12. Dynamische omgevingen

Het is tijd om onze pijplijn te diversifiëren met dynamische omgevingen.

Laten we eerst de taak bijwerken bouw_website in onze .gitlab-ci.yml, verwijder het blok ervan Slechts, wat Gitlab zal dwingen om het te activeren bij elke commit naar elke branch:

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/

Werk vervolgens de taak bij implement_website, voeg daar een blok toe milieu:

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"

Hierdoor kan Gitlab de taak koppelen aan por omgeving en geef de juiste link ernaar weer.

Laten we nu nog twee banen toevoegen:

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

Ze worden na push gelanceerd naar alle vestigingen behalve master en zullen de preview-versie van de site implementeren.

We zien een nieuwe optie voor qbec: --app-tag — het stelt u in staat om geïmplementeerde versies van de applicatie te taggen en alleen binnen deze tag te werken; bij het maken en vernietigen van bronnen in Kubernetes zal qbec alleen daarmee werken.
Zo kunnen wij niet voor elke review een aparte omgeving creëren, maar gewoon dezelfde omgeving hergebruiken.

Hier gebruiken wij ook qbec beoordeling toepassen, in plaats van qbec standaard toepassen - dit is precies het moment waarop we zullen proberen de verschillen voor onze omgevingen te beschrijven (review en standaard):

Laten we toevoegen beoordelen omgeving erin implementeren/website/qbec.yaml

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

Dan declareren wij het 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

En noteer de aangepaste parameters ervoor in 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',
    },
  },
}

Laten we ook Jobu eens nader bekijken stop_beoordeling, het zal worden geactiveerd wanneer de branch wordt verwijderd en zodat gitlab niet probeert uit te checken wordt het gebruikt GIT_STRATEGY: geen, later klonen we meester-Vertak en verwijder de recensie er doorheen.
Het is een beetje verwarrend, maar een mooiere manier heb ik nog niet gevonden.
Een alternatieve optie zou zijn om elke recensie in een hotelnaamruimte te plaatsen, die altijd volledig kan worden gesloopt.

Vergeet niet onze wijzigingen vast te leggen:

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

git push, git checkout -b-test, git push oorsprongstest, rekening:

Schermafbeelding van gemaakte omgevingen in Gitlab

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Alles werkt? - geweldig, verwijder onze testvertakking: git checkout master, git push oorsprong:test, controleren we of de verwijderingstaken voor de omgeving zonder fouten hebben gewerkt.

Hier zou ik meteen willen verduidelijken dat elke ontwikkelaar in een project branches kan maken, hij kan ook veranderen .gitlab-ci.yml bestand en toegang tot geheime variabelen.
Daarom wordt sterk aanbevolen om het gebruik ervan alleen toe te staan ​​voor beschermde takken, bijvoorbeeld in meesterof maak voor elke omgeving een afzonderlijke set variabelen.

13. Bekijk apps

Bekijk apps Dit is een GitLab-functie waarmee u voor elk bestand in de repository een knop kunt toevoegen, zodat u deze snel kunt bekijken in een geïmplementeerde omgeving.

Om deze knoppen te laten verschijnen, moet u een bestand maken .gitlab/route-map.yml en beschrijf alle padtransformaties daarin; in ons geval zal het heel eenvoudig zijn:

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

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

Vergeet niet onze wijzigingen vast te leggen:

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

git push, en check:

Screenshot van de Review App-knop

Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

De klus is geklaard!

Projectbronnen:

Bedankt voor uw aandacht, ik hoop dat u het leuk vond Nieuwe tools uitproberen voor het bouwen en automatiseren van de implementatie in Kubernetes

Bron: www.habr.com

Voeg een reactie