Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Buna ziua! Recent, multe instrumente interesante de automatizare au fost lansate atât pentru construirea de imagini Docker, cât și pentru implementarea în Kubernetes. În acest sens, am decis să mă joc cu GitLab, să-i studiez amănunțit capacitățile și, bineînțeles, să configurez conducta.

Această lucrare a fost inspirată de site-ul web kubernetes.io, care este generat din codurile sursă automat și pentru fiecare cerere de grup trimisă, robotul generează automat o versiune de previzualizare a site-ului cu modificările dvs. și oferă un link pentru vizualizare.

Am încercat să construiesc un proces similar de la zero, dar construit în întregime pe Gitlab CI și instrumente gratuite pe care sunt obișnuit să le folosesc pentru a implementa aplicații în Kubernetes. Astăzi, în sfârșit, vă voi spune mai multe despre ele.

Articolul va discuta despre instrumente precum:
Hugo, qbec, kaniko, git-crypt и GitLab CI cu crearea unor medii dinamice.

Conţinut

  1. Faceți cunoștință cu Hugo
  2. Pregătirea fișierului Docker
  3. A face cunoștință cu Kaniko
  4. Cunoașterea qbec
  5. Încercați Gitlab-runner cu Kubernetes-executor
  6. Implementarea graficelor Helm cu qbec
  7. Vă prezentăm git-crypt
  8. Crearea unei imagini cu instrumente
  9. Prima noastră conductă și asamblare de imagini prin etichete
  10. Automatizarea implementării
  11. Artefacte și asamblare la împingere pentru a stăpâni
  12. Medii dinamice
  13. Examinați aplicațiile

1. Cunoașterea lui Hugo

Ca exemplu al proiectului nostru, vom încerca să creăm un site de publicare a documentației construit pe Hugo. Hugo este un generator de conținut static.

Pentru cei care nu sunt familiarizați cu generatoarele statice, vă voi spune puțin mai multe despre ele. Spre deosebire de motoarele convenționale de site-uri cu o bază de date și ceva PHP, care, atunci când sunt solicitate de un utilizator, generează pagini din mers, generatoarele statice sunt proiectate puțin diferit. Acestea vă permit să luați surse, de obicei un set de fișiere în marcaj Markdown și șabloane de teme, apoi să le compilați într-un site web complet terminat.

Adică, ca rezultat, veți primi o structură de directoare și un set de fișiere HTML generate, pe care pur și simplu le puteți încărca în orice găzduire ieftină și puteți obține un site web funcțional.

Puteți instala Hugo local și îl puteți încerca:

Inițializarea unui nou site:

hugo new site docs.example.org

Și, în același timp, depozitul git:

cd docs.example.org
git init

Până acum, site-ul nostru este impecabil și pentru ca ceva să apară pe el, trebuie mai întâi să conectăm o temă; o temă este doar un set de șabloane și reguli specificate prin care este generat site-ul nostru.

Pentru tema pe care o vom folosi Învață, care, după părerea mea, se pretează perfect pentru un site de documentare.

Aș dori să acord o atenție deosebită faptului că nu trebuie să salvăm fișierele cu tema în depozitul nostru de proiect; în schimb, le putem conecta pur și simplu folosind submodulul git:

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

Astfel, depozitul nostru va conține numai fișiere legate direct de proiectul nostru, iar tema conectată va rămâne ca o legătură către un anumit depozit și un commit în el, adică poate fi întotdeauna scoasă din sursa originală și să nu vă fie frică de modificări incompatibile.

Să corectăm configurația config.toml:

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

Deja în această etapă puteți rula:

hugo server

Si la adresa http://localhost:1313/ verificați site-ul nostru nou creat, toate modificările făcute în director actualizează automat pagina deschisă în browser, foarte convenabil!

Să încercăm să creăm o pagină de copertă în content/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Captură de ecran a paginii nou create

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Pentru a genera un site, rulați:

hugo

Conținutul directorului public/ și va fi site-ul tău.
Da, apropo, să-l adăugăm imediat .gitignore:

echo /public > .gitignore

Nu uitați să faceți modificările noastre:

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

2. Pregătirea fișierului Docker

Este timpul să definim structura depozitului nostru. De obicei folosesc ceva de genul:

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

  • dockerfiles/ — conține directoare cu fișiere Docker și tot ceea ce este necesar pentru a construi imaginile noastre Docker.
  • desfasura/ — conține directoare pentru implementarea aplicațiilor noastre pe Kubernetes

Astfel, vom crea primul nostru Dockerfile de-a lungul căii 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" ]

După cum puteți vedea, Dockerfile conține două DIN, această caracteristică se numește construcție în mai multe etape și vă permite să excludeți tot ce nu este necesar din imaginea finală a dockerului.
Astfel, imaginea finală va conține doar întunerichttpd (server HTTP ușor) și public/ — conținutul site-ului nostru web generat static.

Nu uitați să faceți modificările noastre:

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

3. Cunoașterea kaniko

În calitate de generator de imagini docker, am decis să folosesc kaniko, deoarece funcționarea sa nu necesită un daemon docker, iar construcția în sine poate fi efectuată pe orice mașină, iar memoria cache poate fi stocată direct în registru, eliminând astfel necesitatea de a avea o stocare persistentă cu drepturi depline.

Pentru a construi imaginea, rulați containerul cu executor kaniko și transmiteți-i contextul de construcție curent; acest lucru se poate face și local, prin 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

unde registry.gitlab.com/kvaps/docs.example.org/website — numele imaginii dvs. docker; după creare, aceasta va fi lansată automat în registrul docker.

Parametru --cache vă permite să stocați în cache straturi în registrul docker; pentru exemplul dat, acestea vor fi salvate în registry.gitlab.com/kvaps/docs.example.org/website/cache, dar puteți specifica o altă cale folosind parametrul --cache-repo.

Captură de ecran a docker-registry

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

4. Cunoașterea qbec

Qbec este un instrument de implementare care vă permite să descrieți în mod declarativ manifestele aplicației și să le implementați în Kubernetes. Utilizarea Jsonnet ca sintaxă principală vă permite să simplificați foarte mult descrierea diferențelor în mai multe medii și, de asemenea, elimină aproape complet repetarea codului.

Acest lucru poate fi valabil mai ales în cazurile în care trebuie să implementați o aplicație în mai multe clustere cu parametri diferiți și doriți să le descrieți declarativ în Git.

Qbec vă permite, de asemenea, să redați diagramele Helm, transmițându-le parametrii necesari și apoi să le operați în același mod ca manifestele obișnuite, inclusiv le puteți aplica diverse mutații, iar acest lucru, la rândul său, vă permite să scăpați de nevoia de a utilizați ChartMuseum. Adică, puteți stoca și reda diagrame direct din git, unde le aparțin.

După cum am spus mai devreme, vom stoca toate implementările într-un director desfasura/:

mkdir deploy
cd deploy

Să inițializam prima noastră aplicație:

qbec init website
cd website

Acum structura aplicației noastre arată astfel:

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

să ne uităm la dosar qbec.yaml:

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

Aici ne interesează în primul rând spec.medii, qbec a creat deja un mediu implicit pentru noi și a luat adresa serverului, precum și spațiul de nume din kubeconfig-ul nostru curent.
Acum, când se desfășoară în lipsă mediu, qbec va implementa întotdeauna numai în cluster-ul Kubernetes specificat și în spațiul de nume specificat, adică nu mai trebuie să comutați între contexte și spații de nume pentru a efectua o implementare.
Dacă este necesar, puteți actualiza întotdeauna setările din acest fișier.

Toate mediile dvs. sunt descrise în qbec.yaml, și în dosar params.libsonnet, unde scrie de unde să obțineți parametrii pentru ei.

În continuare vedem două directoare:

  • componente/ — toate manifestele pentru aplicația noastră vor fi stocate aici; ele pot fi descrise atât în ​​fișierele jsonnet, cât și în fișierele yaml obișnuite
  • medii/ — aici vom descrie toate variabilele (parametrii) pentru mediile noastre.

În mod implicit avem două fișiere:

  • medii/bază.libsonnet - va contine parametri comuni pentru toate mediile
  • medii/default.libsonnet — conține parametri anulați pentru mediu lipsă

hai sa deschidem medii/bază.libsonnet și adăugați acolo parametrii pentru prima noastră 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',
    },
  },
}

Să creăm și prima noastră componentă componente/site-ul web.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,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

În acest fișier am descris trei entități Kubernetes simultan, acestea sunt: Implementare, serviciu и Pătrundere. Dacă am dori, le-am putea pune în diferite componente, dar în această etapă una ne va fi suficientă.

sintaxă jsonnet este foarte asemănător cu json obișnuit, în principiu, json obișnuit este deja valid jsonnet, așa că la început poate fi mai ușor pentru tine să folosești servicii online precum yaml2json pentru a vă converti yaml obișnuit în json sau, dacă componentele dvs. nu conțin nicio variabilă, atunci acestea pot fi descrise sub formă de yaml obișnuit.

Când lucrați cu jsonnet Recomand cu căldură să instalați un plugin pentru editorul dvs

De exemplu, există un plugin pentru vim vim-jsonnet, care activează evidențierea sintaxei și se execută automat jsonnet fmt de fiecare dată când salvați (necesită instalarea jsonnet).

Totul este gata, acum putem începe implementarea:

Pentru a vedea ce avem, hai să alergăm:

qbec show default

La ieșire, veți vedea manifeste yaml redate care vor fi aplicate clusterului implicit.

Grozav, aplica acum:

qbec apply default

La ieșire veți vedea întotdeauna ce se va face în clusterul dvs., qbec vă va cere să fiți de acord cu modificările tastând y vei putea să-ți confirmi intențiile.

Aplicația noastră este gata și implementată!

Dacă faceți modificări, puteți face oricând:

qbec diff default

pentru a vedea cum aceste modificări vor afecta implementarea curentă

Nu uitați să faceți modificările noastre:

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

5. Încercarea Gitlab-runner cu Kubernetes-executor

Până de curând am folosit doar obișnuit gitlab-runner pe o mașină pre-preparată (container LXC) cu shell sau docker-executor. Inițial, am avut mai mulți astfel de alergători definiți la nivel global în gitlab-ul nostru. Au colectat imagini docker pentru toate proiectele.

Dar, după cum a arătat practica, această opțiune nu este cea mai ideală, atât din punct de vedere practic, cât și din punct de vedere al siguranței. Este mult mai bine și mai corect din punct de vedere ideologic să avem alergători separați pentru fiecare proiect sau chiar pentru fiecare mediu.

Din fericire, aceasta nu este deloc o problemă, deoarece acum vom implementa gitlab-runner direct ca parte a proiectului nostru chiar în Kubernetes.

Gitlab oferă o diagramă helm gata făcută pentru implementarea gitlab-runner pe Kubernetes. Deci tot ce trebuie să faci este să afli jeton de înregistrare pentru proiectul nostru în Setări -> CI / CD -> Runners și dă-l la cârmă:

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

În cazul în care:

  • https://gitlab.com — adresa serverului dumneavoastră Gitlab.
  • yga8y-jdCusVDn_t4Wxc — jeton de înregistrare pentru proiectul dvs.
  • rbac.create=true — oferă alergătorului cantitatea necesară de privilegii pentru a putea crea poduri pentru a ne îndeplini sarcinile folosind kubernetes-executor.

Dacă totul este făcut corect, ar trebui să vedeți un alergător înregistrat în secțiune Runners, în setările proiectului.

Captură de ecran a alergătorului adăugat

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Este atât de simplu? - Da, atât de simplu! Nu mai aveți probleme cu înregistrarea manuală a alergătorilor, de acum înainte alergătorii vor fi creați și distruși automat.

6. Implementați diagramele Helm cu QBEC

Din moment ce am decis să luăm în considerare gitlab-runner parte a proiectului nostru, este timpul să o descriem în depozitul nostru Git.

L-am putea descrie ca pe o componentă separată , dar pe viitor intenționăm să implementăm diferite copii foarte des, spre deosebire de gitlab-runner, care va fi implementat o singură dată pe cluster Kubernetes. Deci, să inițializam o aplicație separată pentru aceasta:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

De data aceasta nu vom descrie manual entitățile Kubernetes, ci vom lua o diagramă Helm gata făcută. Unul dintre avantajele qbec este capacitatea de a reda diagramele Helm direct dintr-un depozit Git.

Să-l conectăm folosind submodulul git:

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

Acum directorul furnizor/gitlab-runner Avem un depozit cu o diagramă pentru gitlab-runner.

Într-un mod similar, puteți conecta alte depozite, de exemplu, întregul depozit cu diagrame oficiale https://github.com/helm/charts

Să descriem componenta componente/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,
  }
)

Primul argument la expandHelmTemplate trecem calea către diagramă, atunci parametri.valori, pe care îl luăm din parametrii mediului, apoi vine obiectul cu

  • Șablon de nume — numele lansării
  • Spațiu de nume — spațiu de nume transferat la cârmă
  • acest fișier — un parametru necesar care transmite calea către fișierul curent
  • prolix - arată comanda șablon de cârmă cu toate argumentele la redarea diagramei

Acum să descriem parametrii pentru componenta noastră în medii/bază.libsonnet:

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

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

Nota runnerRegistrationToken luăm dintr-un fișier extern secrete/base.libsonnet, hai să-l creăm:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Să verificăm dacă totul funcționează:

qbec show default

dacă totul este în ordine, atunci putem șterge versiunea implementată anterior prin Helm:

helm uninstall gitlab-runner

și implementați-l în același mod, dar prin qbec:

qbec apply default

7. Introducere în git-crypt

Git-crypt este un instrument care vă permite să configurați o criptare transparentă pentru depozitul dvs.

În acest moment, structura noastră de directoare pentru gitlab-runner arată astfel:

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

Dar stocarea secretelor în Git nu este sigură, nu-i așa? Deci trebuie să le criptăm corect.

De obicei, de dragul unei variabile, acest lucru nu are întotdeauna sens. Puteți transfera secrete către qbec și prin variabilele de mediu ale sistemului dumneavoastră CI.
Dar este de remarcat faptul că există și proiecte mai complexe care pot conține mult mai multe secrete; transferul lor pe toate prin variabilele de mediu va fi extrem de dificil.

Mai mult, în acest caz nu aș putea să vă vorbesc despre un instrument atât de minunat ca git-crypt.

git-crypt De asemenea, este convenabil prin faptul că vă permite să salvați întregul istoric al secretelor, precum și să comparați, să îmbinați și să rezolvați conflictele în același mod în care suntem obișnuiți să facem în cazul Git.

Primul lucru după instalare git-crypt trebuie să generăm chei pentru depozitul nostru:

git crypt init

Dacă aveți o cheie PGP, atunci vă puteți adăuga imediat ca colaborator pentru acest proiect:

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

În acest fel, puteți decripta oricând acest depozit folosind cheia privată.

Dacă nu aveți o cheie PGP și nu vă așteptați la ea, atunci puteți merge invers și exportați cheia de proiect:

git crypt export-key /path/to/keyfile

Astfel, oricine are un exportat fişier cheie va putea să vă decripteze depozitul.

Este timpul să stabilim primul nostru secret.
Permiteți-mi să vă reamintesc că suntem încă în director deploy/gitlab-runner/, unde avem un director secrete/, să criptăm toate fișierele din el, pentru aceasta vom crea un fișier secrete/.gitattributes cu urmatorul continut:

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

După cum se poate vedea din conținut, toate fișierele sunt mascate * va fi condus prin git-crypt, cu excepția celor mai multe .gitattributes

Putem verifica acest lucru rulând:

git crypt status -e

Rezultatul va fi o listă cu toate fișierele din depozit pentru care este activată criptarea

Asta e tot, acum ne putem angaja modificările în siguranță:

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

Pentru a bloca un depozit, rulați:

git crypt lock

și imediat toate fișierele criptate se vor transforma în ceva binar, va fi imposibil să le citești.
Pentru a decripta depozitul, rulați:

git crypt unlock

8. Creați o imagine cu instrumente

O imagine cu instrumente este o imagine cu toate instrumentele pe care le vom folosi pentru implementarea proiectului nostru. Acesta va fi folosit de către rulerul Gitlab pentru a efectua sarcini tipice de implementare.

Totul este simplu aici, haideți să creăm unul nou dockerfiles/toolbox/Dockerfile cu urmatorul continut:

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

După cum puteți vedea, în această imagine instalăm toate utilitățile pe care le-am folosit pentru a implementa aplicația noastră. Nu avem nevoie de el aici decât dacă kubectl, dar poate doriți să vă jucați cu el în timpul fazei de configurare a conductei.

De asemenea, pentru a putea comunica cu Kubernetes și implementa în el, trebuie să configuram un rol pentru pod-urile generate de gitlab-runner.

Pentru a face acest lucru, să mergem la directorul cu gitlab-runner:

cd deploy/gitlab-runner

și adăugați o nouă componentă componente/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,
      },
    ],
  },
]

Vom descrie, de asemenea, noii parametri în medii/bază.libsonnet, care acum arată astfel:

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

Nota $.components.rbac.name se refera la nume pentru componentă rbac

Să verificăm ce s-a schimbat:

qbec diff default

și aplică modificările noastre la Kubernetes:

qbec apply default

De asemenea, nu uitați să trimiteți modificările noastre în 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. Prima noastră conductă și asamblare de imagini prin etichete

La rădăcina proiectului vom crea .gitlab-ci.yml cu urmatorul continut:

.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ă rugăm să rețineți că folosim GIT_SUBMODULE_STRATEGY: normal pentru acele joburi în care trebuie să inițializați în mod explicit submodulele înainte de execuție.

Nu uitați să faceți modificările noastre:

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

Cred că putem numi asta o versiune v0.0.1 și adăugați eticheta:

git tag v0.0.1

Vom adăuga etichete ori de câte ori trebuie să lansăm o nouă versiune. Etichetele din imaginile Docker vor fi legate de etichetele Git. Fiecare apăsare cu o nouă etichetă va inițializa construirea de imagini cu această etichetă.

Hai să o facem git push --tags, și să ne uităm la prima noastră conductă:

Captură de ecran a primei conducte

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Merită să vă atrageți atenția asupra faptului că asamblarea prin etichete este potrivită pentru construirea de imagini docker, dar nu este potrivită pentru implementarea unei aplicații în Kubernetes. Deoarece noile etichete pot fi atribuite vechilor comiteri, în acest caz, inițializarea conductei pentru acestea va duce la implementarea versiunii vechi.

Pentru a rezolva această problemă, de obicei construirea imaginilor docker este legată de etichete, iar implementarea aplicației într-o ramură maestru, în care versiunile imaginilor colectate sunt codificate. Aici puteți inițializa rollback-ul cu o simplă revenire maestru-ramuri.

10. Automatizarea implementării

Pentru ca Gitlab-runner să ne decripteze secretele, va trebui să exportăm cheia de depozit și să o adăugăm la variabilele noastre de mediu CI:

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

Vom salva linia rezultată în Gitlab; pentru a face acest lucru, să mergem la setările proiectului nostru:
Setări -> CI / CD -> Variabile

Și să creăm o nouă variabilă:

Tip
Cheie
Valoare
Protejat
Mascat
domeniu

File
GITCRYPT_KEY
<your string>
true (în timpul antrenamentului poți false)
true
All environments

Captură de ecran a variabilei adăugate

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Acum haideți să ne actualizăm .gitlab-ci.yml adăugând la acesta:

.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

Aici am activat câteva opțiuni noi pentru qbec:

  • --root some/app — vă permite să determinați directorul unei anumite aplicații
  • --force:k8s-context __incluster__ - aceasta este o variabilă magică care spune că implementarea va avea loc în același cluster în care rulează gtilab-runner. Acest lucru este necesar, deoarece altfel qbec va încerca să găsească un server Kubernetes potrivit în kubeconfig dvs.
  • --aștepta — obligă qbec să aștepte până când resursele pe care le creează intră în starea Gata și abia apoi să iasă cu un cod de ieșire reușit.
  • -da - pur și simplu dezactivează shell-ul interactiv Esti sigur? când este desfășurat.

Nu uitați să faceți modificările noastre:

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

Si dupa git push vom vedea cum au fost implementate aplicațiile noastre:

Captură de ecran a celei de-a doua conducte

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

11. Artefacte și asamblare la împingere pentru a stăpâni

De obicei, pașii descriși mai sus sunt suficienți pentru a construi și livra aproape orice microserviciu, dar nu dorim să adăugăm o etichetă de fiecare dată când trebuie să actualizăm site-ul. Prin urmare, vom lua o rută mai dinamică și vom configura o implementare digest în ramura principală.

Ideea este simplă: acum imaginea noastră va fi reconstruit de fiecare dată când împingeți maestru, apoi implementați automat în Kubernetes.

Să actualizăm aceste două locuri de muncă în nostru .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ă rugăm să rețineți că am adăugat un thread maestru к ref pentru locuri de muncă build_site iar acum folosim $CI_COMMIT_REF_NAME în loc de $CI_COMMIT_TAG, adică suntem dezlegați de etichete în Git și acum vom împinge o imagine cu numele ramurii de comitere care a inițializat pipeline-ul. Merită remarcat faptul că acest lucru va funcționa și cu etichete, ceea ce ne va permite să salvăm instantanee ale unui site cu o anumită versiune în docker-registry.

Când numele etichetei docker pentru o nouă versiune a site-ului poate fi neschimbat, mai trebuie să descriem modificările aduse Kubernetes, altfel pur și simplu nu va redistribui aplicația din noua imagine, deoarece nu va observa nicio modificare în manifest de implementare.

Opțiune —vm:ext-str digest="$DIGEST" pentru qbec - vă permite să treceți o variabilă externă la jsonnet. Dorim ca acesta să fie redistribuit în cluster cu fiecare lansare a aplicației noastre. Nu mai putem folosi numele etichetei, care acum poate fi neschimbat, deoarece trebuie să fim legați de o anumită versiune a imaginii și să declanșăm implementarea atunci când aceasta se schimbă.

Aici vom fi ajutați de capacitatea lui Kaniko de a salva o imagine rezumată într-un fișier (opțiune --digest-file)
Apoi vom transfera acest fișier și îl vom citi în momentul implementării.

Să actualizăm parametrii pentru noștri deploy/website/environments/base.libsonnet care acum va arăta astfel:

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

Gata, acum orice se angajează maestru inițializează construcția imaginii docker pentru , apoi implementați-l în Kubernetes.

Nu uitați să faceți modificările noastre:

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

Vom verifica mai târziu git push ar trebui sa vedem asa ceva:

Captură de ecran a conductei pentru master

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

În principiu, nu trebuie să redistribuim gitlab-runner cu fiecare apăsare, cu excepția cazului în care, desigur, nimic nu s-a schimbat în configurația sa, hai să o reparăm în .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/**/*

schimbari vă va permite să monitorizați modificările în deploy/gitlab-runner/ și ne va declanșa slujba numai dacă există

Nu uitați să faceți modificările noastre:

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

git push, asa e mai bine:

Captură de ecran a conductei actualizate

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

12. Medii dinamice

Este timpul să ne diversificăm conducta cu medii dinamice.

Mai întâi, să actualizăm jobul build_site în nostru .gitlab-ci.yml, îndepărtând blocul din acesta afară , ceea ce va forța Gitlab să-l declanșeze la orice comitere către orice ramură:

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/

Apoi actualizați jobul deploy_website, adăugați un bloc acolo mediu inconjurator:

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"

Acest lucru va permite Gitlab să asocieze jobul cu prod mediu și afișați linkul corect către acesta.

Acum să mai adăugăm două locuri de muncă:

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

Acestea vor fi lansate la push către orice ramuri, cu excepția masterului și vor implementa versiunea de previzualizare a site-ului.

Vedem o nouă opțiune pentru qbec: --app-tag — vă permite să etichetați versiunile implementate ale aplicației și să lucrați numai în cadrul acestei etichete; atunci când creați și distrugeți resurse în Kubernetes, qbec va funcționa numai cu acestea.
Astfel, nu putem crea un mediu separat pentru fiecare recenzie, ci pur și simplu îl putem reutiliza pe același.

Aici folosim si noi qbec aplica recenzie, în loc de qbec se aplică implicit - acesta este exact momentul în care vom încerca să descriem diferențele pentru mediile noastre (revizuire și implicită):

Să adăugăm revizuiască mediu în deploy/website/qbec.yaml

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

Atunci o vom declara în 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

Și notează parametrii personalizați pentru el 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',
    },
  },
}

Să aruncăm o privire mai atentă la jobu stop_review, va fi declanșat când ramura este ștearsă și pentru ca gitlab să nu încerce să verifice, este folosit GIT_STRATEGY: niciuna, mai târziu clonăm maestru-branchează și șterge recenzia prin ea.
Este puțin confuz, dar nu am găsit încă o modalitate mai frumoasă.
O opțiune alternativă ar fi implementarea fiecărei recenzii într-un spațiu de nume de hotel, care poate fi întotdeauna demolat în întregime.

Nu uitați să faceți modificările noastre:

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

git push, git checkout -b test, testul de origine git push, Verifica:

Captură de ecran a mediilor create în Gitlab

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Totul merge? - grozav, ștergeți ramura noastră de testare: git checkout master, git push origin :test, verificăm dacă joburile de ștergere a mediului au funcționat fără erori.

Aici aș dori să clarific imediat că orice dezvoltator dintr-un proiect poate crea ramuri, se poate schimba și el .gitlab-ci.yml fișier și acces la variabile secrete.
Prin urmare, se recomandă insistent să se permită utilizarea lor numai pentru ramurile protejate, de exemplu în maestrusau creați un set separat de variabile pentru fiecare mediu.

13. Examinați aplicațiile

Examinați aplicațiile Aceasta este o caracteristică GitLab care vă permite să adăugați un buton pentru fiecare fișier din depozit pentru a-l vizualiza rapid într-un mediu implementat.

Pentru ca aceste butoane să apară, trebuie să creați un fișier .gitlab/route-map.yml și descrieți toate transformările căii din ea; în cazul nostru va fi foarte simplu:

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

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

Nu uitați să faceți modificările noastre:

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

git pushși verificați:

Captură de ecran a butonului Review App

Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Treaba este gata!

Surse de proiect:

Vă mulțumesc pentru atenție, sper că v-a plăcut Încercarea de noi instrumente pentru construirea și automatizarea implementării în Kubernetes

Sursa: www.habr.com

Adauga un comentariu