Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Ahoj! V poslední době vyšlo mnoho skvělých nástrojů pro automatizaci jak pro sestavování Docker obrazů, tak pro nasazení do Kubernetes. Rozhodl jsem se proto si pohrát s GitLabem, pořádně prozkoumat jeho možnosti a samozřejmě nastavit pipeline.

Inspirací pro tuto práci byla stránka kubernetes.io, která se generuje z zdrojových kódů automaticky, a na každý zaslaný pull request robot automaticky generuje náhledovou verzi stránky s vašimi změnami a poskytuje odkaz pro zhlédnutí.

Snažil jsem se vybudovat podobný proces od základů, ale zcela postavený na GitLab CI a volných nástrojích, které jsem zvyklý používat pro nasazení aplikací do Kubernetes. Dnes vám konečně podrobněji představím tyto nástroje.

V článku budou uvedeny tyto nástroje jako:
Hugo, qbec, kaniko, git-crypt a GitLab CI s vytvářením dynamických prostředí.

Obsah

  1. Seznámení s Hugem
  2. Příprava Dockerfile
  3. Seznámení s kaniko
  4. Seznámení s qbec
  5. Zkoušíme GitLab-runner s Kubernetes-executorem
  6. Nasazení Helm chartů s qbec
  7. Seznámení s git-crypt
  8. Vytváříme toolbox obraz
  9. Naše první pipeline a sestavování obrazů podle tagů
  10. Automatizace nasazení
  11. Artefakty a sestavení při push do master
  12. Dynamická prostředí
  13. Review Apps

1. Seznámení s Hugem

Jako příklad našeho projektu vyzkoušíme vytvořit stránku pro publikaci dokumentace postavenou na Hugovi. Hugo je statický generátor obsahu.

Pro ty, kteří nejsou obeznámeni se statickými generátory, povím o nich trochu podrobněji. Na rozdíl od běžných redakčních systémů se databází a nějakým PHP, které při uživatelském dotazu generují stránky na míru, jsou statické generátory konstruovány trochu jinak. Umožňují vzít zdrojové soubory, zpravidla sadu souborů v Markdown formátu a šablony motivů, a poté je zkompilovat do zcela hotové stránky.

To znamená, že na výstupu získáte strukturu adresářů a sadu vygenerovaných html souborů, které lze jednoduše nahrát na jakýkoli levný hosting a dostat funkční web.

Hugo lze nainstalovat lokálně a vyzkoušet si ho v praxi:

Inicializujeme novou stránku:

hugo new site docs.example.org

A zároveň i git repozitorium:

cd docs.example.org
git init

Naše stránka je zatím naprosto čistá, a aby na ní něco bylo, nejprve musíme připojit téma, téma — to je pouhý soubor šablon a zadaných pravidel, podle kterých se generuje naše stránka.

Jako téma použijeme Learn, které se podle mě skvěle hodí pro dokumentační web.

Zvláštní pozornost je třeba věnovat tomu, že nemusíme uchovávat soubory tématu v repozitáři našeho projektu, místo toho můžeme jednoduše připojit téma pomocí git submodule:

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

Tímto způsobem budou v našem repozitáři pouze soubory přímo související s naším projektem, a připojené téma zůstane jako odkaz na konkrétní repozitář a jeho commit, což znamená, že ji vždy můžeme vytáhnout z původního zdroje a nebojíme se nekompatibilních změn.

Upravíme konfigurační soubor config.toml:

baseURL = "http://docs.example.org/"
languageCode = "en-us"
title = "Moje dokumentační stránka"
theme = "learn"

Už v této fázi můžeme spustit:

hugo server

A na adrese http://localhost:1313/ zkontrolovat právě vytvořenou stránku, všechny změny provedené v adresáři se automaticky aktualizují na otevřené stránce v prohlížeči, což je velmi pohodlné!

Zkusíme vytvořit titulní stránku v content/_index.md:

# My docs site

## Welcome to the docs!

You will be very smart :-)

Snímek obrazovky právě vytvořené stránky

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Pro generaci stránky stačí spustit:

hugo

Obsah adresáře public/ a bude představovat vaši stránku.
Ano, mimochodem, ihned ji přidáme do .gitignore:

echo /public > .gitignore

Nezapomínejte commitnout naše změny:

git add .
git commit -m "Nová stránka vytvořena"

2. Příprava Dockerfile

Nastal čas definovat strukturu našeho repozitáře. Obvykle používám něco jako:

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

  • dockerfiles/ — obsahují adresáře s Dockerfiles a vším potřebným pro sestavení našich docker obrazů.
  • deploy/ — obsahuje adresáře pro nasazení našich aplikací v Kubernetes.

Tímto způsobem vytvoříme náš první Dockerfile na cestě 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" ]

Jak vidíte, Dockerfile obsahuje dva FROM, tato možnost se nazývá multi-stage build a umožňuje vyloučit ze závěrečného docker obrazu vše nepotřebné.
Tím pádem finální obraz bude obsahovat pouze darkhttpd (lehký HTTP server) a public/ — obsah naší staticky vygenerované stránky.

Nezapomínejte commitnout naše změny:

git add dockerfiles/website
git commit -m "Přidání Dockerfile pro webové stránky"

3. Seznámení s kaniko

Jako nástroj pro vytváření dockerových obrazů jsem se rozhodl použít kaniko, protože ke své práci nepotřebuje docker démon a samotné sestavení lze provádět na libovolném stroji a uložit cache přímo do registru, čímž se zbavujeme potřeby mít plnohodnotné persistentní úložiště.

Pro sestavení obrazu stačí spustit kontejner s kaniko executor a předat mu aktuální kontext sestavení, což lze provést i lokálně pomocí dockeru:

docker run -ti --rm 
  -v $PWD:/$workspace 
  -v ~/$HOME/.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

Kde registry.gitlab.com/kvaps/docs.example.org/website — název vašeho docker obrazu, po sestavení bude automaticky nahrán do docker registru.

Parameter —cache umožňuje ukládat vrstvy do docker registru, v uvedeném příkladu budou uloženy do registry.gitlab.com/kvaps/docs.example.org/website/cache, ale můžete také specifikovat jinou cestu pomocí parametru —cache-repo.

Snímek obrazovky docker-registry

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

4. Seznámení s qbec

Qbec — je to nástroj pro nasazení, který umožňuje deklarativně popisovat manifesty vaší aplikace a nasazovat je do Kubernetes. Použití Jsonnet jako základní syntaxe velmi usnadňuje popis rozdílů pro více prostředí a téměř úplně se zbavuje opakování kódu.

To může být obzvláště aktuální v případech, kdy potřebujete nasadit aplikaci do několika clusterů s různými parametry a chcete je deklarativně popsat v Gitu.

Qbec také umožňuje vykreslovat Helm grafy předáváním potřebných parametrů a dále s nimi zacházet jako s běžnými manifesty, mj. je možné na ně aplikovat různé mutace, což zase umožňuje zbavit se potřeby používat ChartMuseum. To znamená, že můžete ukládat a vykreslovat grafy přímo z gitu, kde jim je nejlépe.

Jak jsem říkal dříve, všechny nasazení budeme ukládat do adresáře deploy/:

mkdir deploy
cd deploy

Pojďme inicializovat naši první aplikaci:

qbec init website
cd website

Nyní struktura naší aplikace vypadá takto:

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

podívejme se na soubor qbec.yaml:

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

Zde nás zajímá především spec.environments, qbec již vytvořil výchozí prostředí a vzal adresu serveru, stejně jako namespace z našeho aktuálního kubeconfigu.
Nyní při nasazení do default prostředí, qbec vždy nasadí pouze do uvedeného Kubernetes clusteru a do uvedeného namespace, což znamená, že se nemusíte více přepínat mezi kontexty a namespaces, abyste provedli nasazení.
V případě potřeby můžete vždy aktualizovat nastavení v tomto souboru.

Všechna vaše prostředí jsou popsána v qbec.yaml, a v souboru params.libsonnet, kde je uvedeno, odkud mají být brány parametry.

Dále vidíme dvě adresáře:

  • components/ — zde budou uchovávány všechny manifesty pro naši aplikaci, mohou být popsány jak v jsonnet, tak v běžných yaml souborech.
  • environments/ — zde budeme popisovat všechny proměnné (parametry) pro naše prostředí.

Ve výchozím nastavení máme dva soubory:

  • environments/base.libsonnet — ten bude obsahovat společné parametry pro všechna prostředí.
  • environments/default.libsonnet — obsahuje parametry přepsané pro prostředí. default

Pojďme otevřít environments/base.libsonnet a přidat tam parametry pro naši první komponentu:

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

Také vytvořme naši první komponentu components/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,
                },
              },
            ],
          },
        },
      ],
    },
  },
]

V tomto souboru jsme popsali hned tři entity Kubernetes, a to: Nasazení, Služba a Ingress. Pokud byste chtěli, mohli bychom je rozdělit do různých komponent, ale v tuto chvíli nám stačí jedna.

Syntaxe jsonnet je velmi podobná běžnému json, v podstatě obvyklý json již je platným jsonnet, takže v prvních chvílích pro vás může být snazší využít online služby jako yaml2json k převodu známého yaml na json, nebo pokud vaše komponenty neobsahují žádné proměnné, lze je celkem dobře popsat jako běžný yaml.

Při práci s jsonnet vřele doporučuji nainstalovat vám plugin pro váš editor

Například pro vim existuje plugin vim-jsonnet, který zahrnuje zvýraznění syntaxe a automaticky provádí jsonnet fmt při každém uložení (vyžaduje nainstalovaný jsonnet).

Vše je připraveno, nyní můžeme začít s nasazením:

Abychom viděli, co jsme vytvořili, provedeme:

qbec show default

Na výstupu uvidíte rendrované yaml manifesty, které budou aplikovány do clusteru default.

Skvěle, nyní aplikujeme:

qbec apply default

Na výstupu vždy uvidíte, co bude provedeno ve vašem clusteru, qbec vás požádá o potvrzení změn napsáním y potvrdíte své úmysly.

Hotovo, naše aplikace je nyní nasazena!

Pokud provedete změny, vždy je můžete spustit:

qbec diff default

abyste viděli, jak se tyto změny odrazí v aktuálním nasazení

Nezapomínejte commitnout naše změny:

cd ..\/..
git add deploy\/website
git commit -m "Přidat nasazení pro webovou stránku"

5. Zkoušíme Gitlab-runner s Kubernetes-executor

Ještě nedávno jsem používal pouze běžný gitlab-runner na předem připraveném stroji (LXC kontejner) s shell- nebo docker-executorem. Původně jsme měli několik takových runnerů globálně definovaných v našem Gitlabu. Tyto runnery sestavovaly dockerové obrazy pro všechny projekty.

Ale jak ukázala praxe — tato varianta není nejideálnější, jak z hlediska praktičnosti, tak bezpečnosti. Mnohem lepší a ideologicky správnější je mít samostatné runnery nasazené pro každý projekt, nebo dokonce pro každé prostředí.

Naštěstí to není problém, neboť nyní budeme nasazovat gitlab-runner přímo jako součást našeho projektu přímo do Kubernetes.

Gitlab poskytuje hotový helm chart pro nasazení gitlab-runneru do Kubernetes. Takže vše, co potřebujete, je zjistit registration token pro náš projekt v Nastavení —> CI \/ CD —> Runnery a předat ho helm:

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

Kde:

  • https://gitlab.com — adresa vašeho Gitlab serveru.
  • yga8y-jdCusVDn_t4Wxc — registration token pro váš projekt.
  • rbac.create=true — poskytuje runneru potřebné množství oprávnění, aby mohl vytvářet pody pro provádění našich úloh pomocí kubernetes-executor.

Pokud bylo vše uděláno správně, měli byste vidět zaregistrovaného runnery v sekci Runnery, v nastavení vašeho projektu.

Screenshot přidaného runnery

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Opravdu tak snadné? — ano, tak snadné! Žádné další trápení s manuální registrací runnerů, od této chvíle budou runnery vytvářeny a ničené automaticky.

6. Nasazení Helm chartů s QBEC

Jelikož jsme se rozhodli považovat gitlab-runner za součást našeho projektu, nastal čas jej popsat v našem Git repozitáři.

Mohli bychom jej popsat jako samostatnou komponentu website, ale v budoucnu plánujeme nasazovat různé kopie website velmi často, na rozdíl od gitlab-runner, který bude nasazen pouze jednou na každý Kubernetes cluster. Takže pojďme inicializovat samostatnou aplikaci pro něj:

cd deploy
qbec init gitlab-runner
cd gitlab-runner

Tentokrát nebudeme popisovat entity Kubernetes ručně, ale vezmeme hotový Helm graf. Jednou z výhod qbec je schopnost renderovat Helm grafy přímo z Git repozitáře.

Připojme jej pomocí git submodule:

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

Nyní adresář vendor/gitlab-runner obsahuje repozitář s grafem pro gitlab-runner.

Podobně je možné připojovat i další repozitáře, například celý repozitář s oficiálními grafy. https://github.com/helm/charts

Pojďme popsat komponent components/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,
  }
)

Prvním argumentem pro expandHelmTemplate předáváme cestu k grafu, poté params.values, které vezmeme z parametru prostředí, poté následuje objekt s

  • nameTemplate — název vydání
  • namespace — namespace předaný Helmu
  • thisFile — povinný parametr, který předává cestu k aktuálnímu souboru
  • verbose — zobrazuje příkaz helm template se všemi argumenty při renderování grafu.

Nyní popíšeme parametry pro naši komponentu v 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,
      },
    },
  },
}

Vezměte prosím na vědomí runnerRegistrationToken bereme z externího souboru secrets/base.libsonnet, pojďme jej vytvořit:

{
  runnerRegistrationToken: 'yga8y-jdCusVDn_t4Wxc',
}

Ověřme, zda vše funguje:

qbec show default

pokud je vše v pořádku, můžeme odstranit náš dříve nasazený release přes Helm:

helm uninstall gitlab-runner

a nasadit jej znovu, ale již přes qbec:

qbec apply default

7. Seznámení s git-crypt

Git-crypt je nástroj, který umožňuje nastavit transparentní šifrování pro váš repozitář.

V tuto chvíli vypadá struktura našeho adresáře pro gitlab-runner takto:

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

Ale uchovávat tajemství v Git není bezpečné, že? Takže je musíme správně zašifrovat.

Obvykle to pro jednu proměnnou nemá vždy smysl. Můžete předávat tajemství v qbec a přes proměnné prostředí vaší CI systémů.
Je třeba poznamenat, že existují také složitější projekty, které mohou obsahovat mnohem více tajemství, a předávat je všechny prostřednictvím proměnných prostředí by bylo velmi obtížné.

Kromě toho bych vám v takovém případě nemohl říct o skvělém nástroji, jako je git-crypt.

git-crypt také pohodlný, protože umožňuje uchovat veškerou historii tajemství, stejně jako porovnávat, sloučit a řešit konflikty tak, jak jsme zvyklí dělat to v případě Gitu.

Nejprve po instalaci git-crypt musíme vygenerovat klíče pro náš repozitář:

git crypt init

Pokud máte PGP klíč, můžete se hned přidat jako spolupracovník pro tento projekt:

git-crypt add-gpg-user kvapss@gmail.com

Tímto způsobem budete vždy schopni dešifrovat tento repozitář pomocí svého soukromého klíče.

Pokud ale nemáte PGP klíč a neplánujete ho získat, můžete se vydat jinou cestou a exportovat klíč projektu:

git crypt export-key /path/to/keyfile

Tímto způsobem kdokoliv, kdo má exportovaný keyfile bude mít možnost dešifrovat váš repozitář.

Je čas nastavit naše první tajemství.
Připomínám, že se stále nacházíme v adresáři deploy/gitlab-runner/, kde máme adresář secrets/, pojďme zašifrovat všechny soubory v něm, proto vytvoříme soubor secrets/.gitattributes s tímto obsahem:

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

Jak je vidět z obsahu, všechny soubory podle masky * budou zpracovány přes git-crypt, kromě samotného .gitattributes

To můžeme zkontrolovat spuštěním:

git crypt status -e

Na výstupu dostaneme seznam všech souborů v repozitáři, pro které je zapnuto šifrování.

A je to, nyní můžeme bez obav zapsat naše změny:

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

Pro zablokování repozitáře stačí provést:

git crypt lock

a všechny zašifrované soubory se okamžitě promění v binární nic, nebude možné je přečíst.
Pro dešifrování repozitáře proveďte:

git crypt unlock

8. Vytváříme toolbox obraz

Toolbox obraz je obraz se všemi nástroji, které budeme používat pro nasazení našeho projektu. Bude používán gitlab runnerem pro provádění typických úloh nasazení.

Tady je to jednoduché, vytvoříme nový dockerfiles/toolbox/Dockerfile s tímto obsahem:

Z 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

Jak si můžete všimnout, v tomto obraze instalujeme všechny nástroje, které jsme použili pro nasazení naší aplikace. Tady nám nezbývá nic jiného než kubectl, ale možná budete chtít si s tím pohrát v nastavení pipeline.

Abychom mohli komunikovat s Kubernetes a provádět nasazení, musíme nastavit roli pro pod, které generuje gitlab-runner.

Proto přejdeme do adresáře s gitlab-runnerem:

cd deploy/gitlab-runner

a přidáme nový komponent components/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,
      },
    ],
  },
]

Také popíšeme nové parametry v environments/base.libsonnet, který nyní vypadá takto:

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

Vezměte prosím na vědomí $.components.rbac.name odkazuje na name pro komponentu rbac

Zkontrolujme, co se změnilo:

qbec diff default

a aplikujme naše změny v Kubernetes:

qbec apply default

Také nezapomínáme zcommittovat naše změny do gitu:

cd ../../
git add dockerfiles/toolbox
git commit -m "Přidat Dockerfile pro toolbox"
git add deploy/gitlab-runner
git commit -m "Nastavit gitlab-runner pro použití toolboxu"

9. Naše první pipeline a sestavení obrazů podle tagů

V kořenovém adresáři projektu vytvoříme .gitlab-ci.yml s tímto obsahem:

.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šimněte si, že používáme GIT_SUBMODULE_STRATEGY: normal pro ty úkoly, kde je nutné explicitně inicializovat podmoduly před provedením.

Nezapomínejte commitnout naše změny:

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

Myslím, že to můžeme s jistotou nazvat verzí v0.0.1 a přidat tag:

git tag v0.0.1

Tagy budeme přidávat vždy, když budeme potřebovat uvolnit novou verzi. Tagy v Docker obrazech budou spojeny s Git tagy. Každý push s novým tagem spustí sestavení obrazů s tímto tagem.

Provedeme git push —tags, a podíváme se na náš první pipeline:

Screenshot prvního pipeline

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Měli byste si uvědomit, že sestavení podle tagů je vhodné pro sestavení docker obrazů, ale není vhodné pro nasazení aplikace v Kubernetes. Nové tagy mohou být přiřazeny i ke starým commitům, v tomto případě inicializace pipeline pro ně povede k nasazení staré verze.

Abychom tento problém vyřešili, obvykle se sestavení docker obrazů vazují na tagy a nasazení aplikace na větev master, ve které jsou zafixované verze sestavených obrazů. Pouze v tomto případě budete moct inicializovat rollback jednoduchým revert master-větve.

10. Automatizace nasazení

Aby Gitlab-runner mohl dešifrovat naše tajemství, budeme potřebovat exportovat klíč repozitáře a přidat ho do proměnných prostředí našeho CI:

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

získaný řetězec uložíme do Gitlabu, k tomu přejdeme do nastavení našeho projektu:
Nastavení —> CI / CD —> Proměnné

A vytvoříme novou proměnnou:

Type
Klíč
Hodnota
Ochráněné
Maskované
Rozsah

Soubor
GITCRYPT_KEY
<your string>
true (na dobu školení může být i false)
true
Všechny prostředí

Screenshot přidané proměnné

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Nyní aktualizujeme náš .gitlab-ci.yml přidáním do něj:

.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

Zde jsme použili několik nových možností pro qbec:

  • —root some/app — umožňuje určit adresář konkrétní aplikace
  • —force:k8s-context __incluster__ — to je magická proměnná, která říká, že nasazení bude probíhat ve stejném clusteru, ve kterém běží gitlab-runner. Je to nutné, protože jinak se qbec pokusí najít vhodný Kubernetes server ve vašem kubeconfig.
  • —wait — způsobí, že qbec počká, až vytvořené zdroje přejdou do stavu Ready, a teprve poté úspěšně ukončí.
  • —yes — jednoduše deaktivuje interaktivní shell Jste si jistý? při nasazení.

Nezapomínejte commitnout naše změny:

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

A po tom git push uvidíme, jak byly naše aplikace nasazeny:

Screenshot druhého pipeline

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

11. Artefakty a sestavení při push do masteru

Obvykle výše uvedených kroků stačí k sestavení a nasazení téměř jakéhokoli mikroslužby, ale nechceme přidávat tag pokaždé, když potřebujeme aktualizovat web. Proto zvolíme dynamičtější cestu a nastavíme nasazení podle digestu v master větvi.

Myšlenka je jednoduchá: nyní se obraz našeho website bude znovu sestavovat pokaždé při push do master, a poté se automaticky nasadí do Kubernetes.

Aktualizujme tyto dvě úlohy v našem .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šimněte si, že jsme přidali větev master k refs pro úlohu build_website a nyní používáme $CI_COMMIT_REF_NAME místo $CI_COMMIT_TAG, což znamená, že se odpojíme od tagů v Git a nyní budeme pushovat obraz s názvem větve commitu, který inicializoval pipeline. Je dobré poznamenat, že to bude fungovat i s tagy, což nám umožní ukládat snímky webu s konkrétní verzí v docker-registry.

I když může být jméno docker-tag pro novou verzi webu neměnné, stále musíme popsat změny pro Kubernetes, jinak jednoduše nepřevede aplikaci z nového obrazu, protože si nevšimne žádných změn v manifestu nasazení.

Volba —vm:ext-str digest="%DIGEST%" pro qbec — umožňuje předat externí proměnnou do jsonnet. Chceme, aby se naše aplikace s každým vydáním znovu nasadila v klastru. Používat jméno tagu, které nyní může být neměnné, již nemůžeme, protože se musíme vázat na konkrétní verzi obrazu a vyvolat nasazení při jejím změně.

Zde nám pomůže možnost Kaniko uložit digest obrazu do souboru (možnost —digest-file)
Poté tento soubor předáme a přečteme v okamžiku nasazení.

Aktualizujeme parametry pro náš deploy/website/environments/base.libsonnet který nyní bude vypadat takto:

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

Hotovo, nyní každý commit v master inicializuje vytváření docker-obrazu pro website, a poté jeho nasazení do Kubernetes.

Nezapomínejte commitnout naše změny:

git add .
git commit -m "Konfigurovat dynamické nasazení"

Zkontrolujeme, po git push měli bychom vidět něco podobného:

Screenshot pipeline pro master

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

V zásadě není potřeba znovu nasazovat gitlab-runner při každém push, pokud, samozřejmě, nedošlo k žádným změnám v jeho konfiguraci, pojďme to opravit v .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/**/*

changes umožní sledovat změny v deploy/gitlab-runner/ a vyvolá naši práci pouze při jejich přítomnosti

Nezapomínejte commitnout naše změny:

git add .gitlab-ci.yml
git commit -m "Snížit nasazení gitlab-runner"

git push, to je lepší:

Screenshot aktualizovaného pipeline

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

12. Dynamická prostředí

Nastala doba obohatit naši pipeline dynamickými prostředími.

Nejprve aktualizujeme úlohu build_website v našem .gitlab-ci.yml, odstraníme z ní blok only, což způsobí, že Gitlab ji vyvolá při každém commitu do jakékoliv větve:

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/

Poté aktualizujeme úlohu deploy_website, přidáme tam blok environment:

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"

To umožní Gitlabu přiřadit úlohu k prod prostředí a zobrazit na něj správný odkaz.

Teď přidáme ještě dvě úlohy:

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

Tyto úlohy se spustí při push na jakékoli větve kromě master a budou nasazovat preview verzi webu.

Vidíme novou možnost pro qbec: —app-tag — umožňuje označit nasazené verze aplikace a pracovat pouze v rámci tohoto tagu, při vytváření a ničení zdrojů v Kubernetes bude qbec operovat pouze s nimi.
Takže nemusíme vytvořit samostatné prostředí pro každé review, ale jednoduše znovu použijeme to samé.

Zde také používáme qbec apply review, místo qbec apply default — právě teď se snažíme popsat rozdíly mezi našimi prostředími (review a default):

Přidáme review prostředí do deploy/website/qbec.yaml

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

Pak jej deklarujeme v 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

A zapíšeme vlastní parametry pro něj do 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',
    },
  },
}

Podívejme se také podrobněji na úlohu stop_review, která bude spouštěna při odstranění větve a aby se gitlab pokusil o checkout na ní, používá se GIT_STRATEGY: none, později klonujeme master-větev a odstraníme review přes ni.
Trochu složité, ale zatím jsem nenašel hezčí způsob.
Alternativní variantou by mohl být deployment každého review do samostatného namespace, který lze vždy kompletně odstranit.

Nezapomínejte commitnout naše změny:

git add .
git commit -m "Povolit automatické review"

git push, git checkout -b test, git push origin test, kontrolujeme:

Screenshot vytvořených environments v Gitlabu

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Všechno funguje? — skvěle, odstraníme naši testovací větev: git checkout master, git push origin :test, kontrolujeme, že joby na odstranění environmentu proběhly bez chyb.

Zde bych rád upřesnil, že větve může vytvářet každý vývojář v projektu, může také změnit .gitlab-ci.yml soubor a získat přístup k tajným proměnným.
Proto se důrazně doporučuje povolit jejich použití pouze pro protected-branch, například v master, nebo vytvořit oddělenou sadu proměnných pro každé prostředí.

13. Review Apps

Review Apps to je taková možnost GitLabu, která umožňuje přidat tlačítko pro rychlý náhled každého souboru v repozitáři do nasazeného prostředí.

Aby se tato tlačítka objevila, je třeba vytvořit soubor .gitlab/route-map.yml a popsat v něm všechny transformace cest, v našem případě to bude velmi jednoduché:

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

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

Nezapomínejte commitnout naše změny:

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

git push, a kontrolujeme:

Screenshot tlačítka Review App

Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Job je hotov!

Zdrojové kódy projektu:

Děkuji za pozornost, doufám, že se vám to líbilo Zkoušíme nové nástroje pro sestavování a automatizaci nasazení v Kubernetes

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster