Handleiding - 20 min

Archestack hosten op AKS

Deze handleiding installeert een Archestack-instantie op Azure Kubernetes Service (AKS) en houdt haar daarna vanzelf up-to-date. De Helm-chart draait de API als meerdere identieke replica's, de builder en de gepubliceerde app, en Caddy als edge met automatische certificaten. PostgreSQL draait als Azure Database for PostgreSQL Flexible Server, en de images komen uit een eigen container registry. Een pipeline zoekt elke nacht de nieuwste Archestack-release op, brengt die naar ontwikkeling en test, en brengt diezelfde versie verder naar acceptatie en productie zodra iemand haar goedkeurt. Op één server is Hosten met Docker de eenvoudigere weg.

Heb je al een cluster en wil je er vandaag Archestack op, onder een pad van een hostnaam die je al bedient? Archestack op Kubernetes, de korte weg doet dat met vijf bestanden en één pipeline, en is de plek om te beginnen.

Je CI-systeem

Wat waar draait

                  LoadBalancer, static public IP (80, 443)
                                 |
                               Caddy ---- asks the API before every new certificate
         +-------------+---------+---------+------------------------------+
   editor.<zone>   api.<zone>    metrics.<zone>    <workspace>.<zone>, companies' own domains
         |             |              |                          |
   web-dashboard     api (N)       grafana                     web-app
                       |
     Azure Database for PostgreSQL 16, Flexible Server, private to the network
     Azure Files: /files and /backups, one share every API replica mounts
     omnicore-api and omnicore-web, pulled from your own registry (ACR)
  • Caddy is de edge, geen ingress controller. De adressen van werkruimtes en de eigen domeinen van bedrijven krijgen een certificaat de eerste keer dat iemand ze bezoekt, en Caddy vraagt de API of een hostnaam bij een werkruimte hoort voordat hij er een aanvraagt. cert-manager kan geen certificaten op aanvraag uitgeven, dus een ingress controller zou die adressen kwijtraken. Zonder die adressen kan Archestack ook onder een pad staan van een hostnaam die het cluster al bedient.
  • Het adres van de bezoeker blijft behouden (externalTrafficPolicy: Local). De rate limiter telt anonieme aanroepers per adres, en zonder deze instelling zou elke bezoeker het adres van een node delen.
  • Bestanden en back-ups staan op één share op Azure Files die elke API-replica koppelt, dus een logo of een back-up die via één replica is geschreven, staat op alle replica's. De NuGet-packages die scripts gebruiken zijn de uitzondering: elke pod bewaart zijn eigen op zijn node, zolang de pod bestaat (api.nugetCache).
  • De API maakt zijn eigen databases aan en migreert ze bij het starten, dus een lege server is alles wat hij nodig heeft. Er is geen aparte migratiejob.

Versies en tags

Elke Archestack-release heeft een versienummer, major.minor.build, zoals 1.4.23. Het buildnummer telt op bij elke release en begint weer bij 1 wanneer de major- of minorversie verandert, en er kan een nummer ontbreken wanneer een releasebuild niet is afgerond. Elke release publiceert twee images op Docker Hub, nickpeelman/omnicore-api en nickpeelman/omnicore-web, elk met drie tags: de versie, de commit waaruit ze is gebouwd, en latest.

Deploy het versienummer, nooit latest:

  • Er rolt niets uit. Een upgrade van latest naar latest laat de Deployment ongewijzigd, dus Kubernetes houdt de pods die er al zijn.
  • Replica's lopen uit elkaar. De chart haalt een image alleen op wanneer de node die nog niet heeft. Een node die vorige week latest ophaalde, blijft de versie van vorige week draaien, een nieuwe node draait die van deze week, en één instantie bedient twee versies.
  • Er is geen weg terug. De vorige versie heeft geen naam om naar terug te gaan.

De pipeline hieronder zet "nieuwste" daarom één keer om in een nummer, aan het begin van een run, en elke omgeving krijgt dat nummer. Hij kan ook één lijn volgen: 1.4 neemt de nieuwste 1.4.x en stapt nooit vanzelf over naar 1.5. In een nieuwe major- of minorversie komen de grotere wijzigingen terecht, en de VS Code-extensie waarschuwt wanneer ze verbinding maakt met een API van een andere major- of minorversie.

Voordat je begint

  • Een Azure-abonnement, en de az CLI, kubectl en Helm 3 op je machine.
  • Een project op Azure DevOps, GitHub of GitLab voor de pipeline, en daar een repository die van de starterkit is gemaakt.
  • De Helm-chart: de map infra/helm/archestack van de Archestack-repository, op hetzelfde pad in die repository gekopieerd.
  • Een DNS-zone waaraan je records kunt toevoegen, of een hostnaam die een cluster al via zijn ingress controller bedient (manier B).

De starterkit

infra/helm/archestack/       the chart, copied from the Archestack repository
setup/                       run once per environment, by a person
  1-cluster.sh               registry, network, Key Vault, database, cluster
  2-address.sh               way A: the public address for DNS
  3-pipeline.sh              the SMTP password, the namespace, the pipeline's identity
aks/
  settings.env               fill in: prefix, region, where the pipeline runs
  environment.sh             every Azure name, made from settings.env
  resolve-version.sh         newest, 1.4 or 1.4.23, made one version number
  deploy.sh                  import into your registry, then helm upgrade
  azure-pipelines.yml        the pipeline, on Azure DevOps
  environments/
    dev/ tst/ acc/ prd/      fill in: values.yaml, and names.env for a cluster you have
.github/workflows/           the pipeline, on GitHub Actions
.gitlab-ci.yml               the pipeline, on GitLab CI

Houd de pipeline van je CI-systeem en verwijder de andere twee. grep -rn CHANGE-ME . somt elke waarde op die je nog moet invullen, en deploy.sh weigert een omgeving die er nog een heeft. Daarna maken de drie setupscripts elke omgeving aan in Azure, één keer gedraaid door een persoon:

# Once per environment, after az login as someone who may create resources and assign roles
ENVIRONMENT=prd bash setup/1-cluster.sh       # registry, network, Key Vault, database, cluster
ENVIRONMENT=prd bash setup/2-address.sh       # way A only: the public address, then your DNS
ENVIRONMENT=prd bash setup/3-pipeline.sh      # asks the SMTP password; the pipeline's identity

Elk script maakt alleen aan wat ontbreekt, dus een script dat halverwege is gestopt, kun je gewoon opnieuw draaien, en elk script zegt aan het eind wat er daarna komt.

1. Vul de instellingen in

aks/settings.env bevat wat alle omgevingen delen:

# aks/settings.env: fill in once. No passwords here: the setup asks for them and keeps them in Key Vault.
PREFIX=CHANGE-ME             # 2-10 lowercase letters or digits, unique to you, such as acme
LOCATION=westeurope          # the Azure region for every environment
PIPELINE=CHANGE-ME           # where the pipeline runs: azure-devops, github or gitlab
REPO=                        # github: owner/repo; gitlab: group/project; azure-devops: leave empty
GITLAB_URL=https://gitlab.com

Elke Azure-naam volgt daaruit in aks/environment.sh, dat zowel de setup als deploy.sh leest, dus ze komen altijd overeen. Namen die in Azure wereldwijd uniek moeten zijn (de registry, de vault en de databaseserver), eindigen op je prefix:

# aks/environment.sh: one environment's Azure names, made from settings.env, and how to sign in to
# its cluster. The setup scripts and deploy.sh both read it, so they always agree.
# shellcheck source=settings.env
source "$(dirname "${BASH_SOURCE[0]}")/settings.env"
: "${ENVIRONMENT:?set ENVIRONMENT to dev, tst, acc or prd}"
[[ $PREFIX =~ ^[a-z0-9]{2,10}$ ]] || { echo "Fill in PREFIX in aks/settings.env: 2-10 lowercase letters or digits" >&2; exit 1; }

RG=rg-archestack-$ENVIRONMENT
AKS_RG=$RG
AKS=aks-archestack-$ENVIRONMENT
VNET_RG=$RG
VNET=vnet-archestack-$ENVIRONMENT
PG_SUBNET=10.20.16.0/24
PIP=pip-archestack-$ENVIRONMENT
IDENTITY=id-archestack-$ENVIRONMENT
PG=pg-$PREFIX-$ENVIRONMENT          # <name>.postgres.database.azure.com, unique across Azure
KV=kv-$PREFIX-$ENVIRONMENT          # unique across Azure, at most 24 characters
ACR=acr$PREFIX                      # one registry for every environment, unique across Azure
ACR_RG=rg-archestack-shared
NAMESPACE=archestack

# A cluster you already have, such as one whose ingress already serves the hostname Archestack
# goes under: aks/environments/<env>/names.env changes the names above (see names.env.example).
OVERRIDES="$(dirname "${BASH_SOURCE[0]}")/environments/$ENVIRONMENT/names.env"
if [ -f "$OVERRIDES" ]; then source "$OVERRIDES"; fi

# Signs kubectl and helm in to the cluster as whoever az is signed in as. A cluster with Entra ID
# sign-in needs kubelogin for that, installed into ~/.local/bin when it is missing.
kube_login() {
  az aks get-credentials -g "$AKS_RG" -n "$AKS" --overwrite-existing -o none
  if [ "$(az aks show -g "$AKS_RG" -n "$AKS" --query 'aadProfile != null' -o tsv)" = true ]; then
    if ! command -v kubelogin > /dev/null; then
      mkdir -p "$HOME/.local/bin"
      az aks install-cli --install-location "$HOME/.local/bin/kubectl" \
        --kubelogin-install-location "$HOME/.local/bin/kubelogin" -o none
      export PATH="$HOME/.local/bin:$PATH"
    fi
    kubelogin convert-kubeconfig -l azurecli
  fi
}

De values.yaml van elke omgeving bevat haar waarden voor de chart en een van twee manieren om binnen te komen: eigen hostnamen (A), of een pad van een hostnaam die het cluster al bedient (B). Houd er één en verwijder de andere.

# aks/environments/prd/values.yaml: the chart values of this environment. The chart's own values.yaml
# lists every value. The images, the database server and the public address come from deploy.sh,
# the passwords from Key Vault.
#
# Two ways in. Keep one, delete the other, and fill in every CHANGE-ME that is left:
#   A. Hostnames of its own (editor., api., metrics., and <workspace>. per workspace) behind Caddy,
#      with certificates it gets by itself. Needs DNS records and setup/2-address.sh.
#   B. A path of a hostname the cluster's ingress already serves, such as https://apps.acme.com/archestack/.
#      No new DNS record, address or certificate. No workspace addresses, own domains or Grafana.

# --- A. Hostnames of its own ---
hosts:
  dashboard: editor.CHANGE-ME          # editor.<your zone>, such as editor.archestack.acme.com
  api: api.CHANGE-ME                   # api.<your zone>
  grafana: metrics.CHANGE-ME           # metrics.<your zone>
caddy:
  acmeEmail: CHANGE-ME                 # Let's Encrypt writes here about certificates
  persistence:
    storageClass: managed-csi

# --- B. A path of a hostname the cluster already serves ---
ingress:
  enabled: true
  className: nginx                     # the ingress class that serves the hostname
  host: CHANGE-ME                      # such as apps.acme.com
  basePath: /archestack                # the builder at https://<host>/archestack/
  tls:
    - secretName: CHANGE-ME            # the hostname's certificate, as a Secret in this namespace
      hosts: [CHANGE-ME]               # the host again

# --- Both ---
api:
  replicas: 3
  # A: your zone, for workspaces at <label>.<zone> and companies' own domains, or empty for neither.
  # B: empty.
  tenantHostSuffix: CHANGE-ME
  persistence:
    files: { storageClass: azurefile-csi, accessMode: ReadWriteMany, size: 100Gi }
    backups: { storageClass: azurefile-csi, accessMode: ReadWriteMany, size: 100Gi }

postgres:
  username: archestack
  sslMode: Require

# The mail server; its password is in Key Vault. Leave all three empty when the instance sends no mail.
smtp:
  host: CHANGE-ME
  username: CHANGE-ME
  from: CHANGE-ME

observability:
  enabled: true                        # B: Grafana is not routed; reach it with kubectl port-forward
  instance: prd
  storageClass: managed-csi
  • api.replicas: elk aantal, zonder sticky sessions. Hostinginfrastructuur somt op wat meerdere replica's vragen van het netwerk eromheen.
  • api.tenantHostSuffix: zet een adres per werkruimte en de eigen domeinen van bedrijven aan, alleen bij manier A.
  • api.persistence: moet ReadWriteMany zijn zodra replica's op meer dan één node draaien.
  • observability.enabled: Grafana, Prometheus, Tempo en Loki in het cluster. Hun panelen voor hosts en containers blijven leeg op AKS; Container Insights dekt de nodes en containers.

Onder een pad van een hostnaam die je al hebt

Manier B zet Archestack onder een pad van een hostnaam die de ingress controller van het cluster al bedient, naast de applicaties die er al op staan:

https://apps.acme.com/                       what is already there
https://apps.acme.com/archestack/            the builder
https://apps.acme.com/archestack/app/<page>  the published app
https://apps.acme.com/archestack/api         the API (and /hubs, live updates)

Er is geen nieuw DNS-record, geen openbaar adres en geen eigen certificaat voor nodig, en mensen blijven het adres gebruiken dat ze al kennen. De chart laat Caddy weg en rendert één Ingress voor de drie paden; de builder en de API krijgen hun prefix te horen, dus elke link die ze schrijven, bevat die prefix.

# --- B. A path of a hostname the cluster already serves ---
ingress:
  enabled: true
  className: nginx                     # the ingress class that serves the hostname
  host: CHANGE-ME                      # such as apps.acme.com
  basePath: /archestack                # the builder at https://<host>/archestack/
  tls:
    - secretName: CHANGE-ME            # the hostname's certificate, as a Secret in this namespace
      hosts: [CHANGE-ME]               # the host again
  • Een cluster dat je al hebt. De hostnaam staat op een cluster, meestal een dat er al vóór Archestack was. Kopieer names.env.example naar aks/environments/<env>/names.env, en de setup laat dat cluster zoals het is: hij geeft het cluster toestemming om uit de registry op te halen en voegt aan het netwerk ervan een subnet voor de database toe. Sla setup/2-address.sh over.
  • className is de ingress class die de hostnaam bedient. De chart zet de annotaties van ingress-nginx voor grote uploads en langlevende verbindingen; een andere controller heeft zijn eigen equivalenten nodig.
  • Het certificaat moet een Secret in de namespace archestack zijn, omdat een Ingress alleen Secrets in zijn eigen namespace gebruikt. Kopieer het certificaat van de hostnaam daarheen, of laat cert-manager het daar uitgeven.
  • Herschrijf het pad niet (geen rewrite-target). De API moet zijn prefix zien om zijn eigen adressen te schrijven.
  • Houd aanmeldproxy's erbuiten. Een proxy die de Authorization-header vervangt, breekt elke API-aanroep: de gepubliceerde app, Data API-sleutels, deploytokens, VS Code en MCP brengen allemaal hun eigen header mee.
  • De hostnaam is gedeeld. Aanmeldtokens staan in de opslag van de browser voor dat adres, die elke applicatie op dat adres kan lezen, dus een script dat in een van die applicaties wordt geïnjecteerd, bereikt Archestack-sessies. Twee Archestack-instanties op één hostnaam melden elkaars gebruikers af.
  • Niet beschikbaar op deze manier: adressen van werkruimtes en de eigen domeinen van bedrijven, die de certificaten op aanvraag van Caddy nodig hebben, en Grafana, dat de Ingress niet routeert. Bereik Grafana met kubectl port-forward.
# A cluster you already have, for one environment: copy this file to aks/environments/<env>/names.env
# and fill it in. The setup then leaves the cluster as it is, allows it to pull from the registry, and
# adds a subnet for the database to the cluster's network. Everything else is made as usual.

# The cluster, such as the one whose ingress already serves the hostname Archestack goes under.
AKS_RG=CHANGE-ME
AKS=CHANGE-ME

# Its network, which has to be one of your own (a cluster made with --vnet-subnet-id), and a free
# address range in it for the database's subnet.
VNET_RG=CHANGE-ME
VNET=CHANGE-ME
PG_SUBNET=CHANGE-ME                 # such as 10.30.16.0/24

2. Registry, cluster en database

#!/usr/bin/env bash
# setup/1-cluster.sh: one environment's registry, network, Key Vault, database and cluster.
# Each part is made only when it is missing, so a run that stopped halfway can simply be run again,
# and a cluster you already have (aks/environments/<env>/names.env) is left as it is.
#   ENVIRONMENT=prd bash setup/1-cluster.sh
# Smaller, for dev and test:
#   NODE_COUNT=2 ENVIRONMENT=dev bash setup/1-cluster.sh
source "$(dirname "$0")/common.sh"
NODE_COUNT=${NODE_COUNT:-3}
NODE_SIZE=${NODE_SIZE:-Standard_D4s_v5}
PG_SKU=${PG_SKU:-Standard_D2ds_v5}
exists() { "$@" -o none 2>/dev/null; }

# One registry for every environment: a version is imported once and promoted as it is.
az group create -n "$ACR_RG" -l "$LOCATION" -o none
exists az acr show -n "$ACR" || az acr create -g "$ACR_RG" -n "$ACR" --sku Standard -o none
ACR_ID=$(az acr show -n "$ACR" --query id -o tsv)

# The network: a subnet for the cluster, and one delegated to the database.
az group create -n "$RG" -l "$LOCATION" -o none
exists az network vnet show -g "$VNET_RG" -n "$VNET" || az network vnet create -g "$VNET_RG" -n "$VNET" \
  --address-prefixes 10.20.0.0/16 --subnet-name aks --subnet-prefixes 10.20.0.0/20 -o none
exists az network vnet subnet show -g "$VNET_RG" --vnet-name "$VNET" -n archestack-postgres \
  || az network vnet subnet create -g "$VNET_RG" --vnet-name "$VNET" -n archestack-postgres \
    --address-prefixes "$PG_SUBNET" --delegations Microsoft.DBforPostgreSQL/flexibleServers -o none

# The vault, and the right to write to it: a vault with RBAC gives its creator no access to secrets.
exists az keyvault show -n "$KV" || az keyvault create -g "$RG" -n "$KV" -l "$LOCATION" --enable-rbac-authorization true -o none
az role assignment create --assignee "$(az ad signed-in-user show --query id -o tsv)" \
  --role "Key Vault Secrets Officer" --scope "$(az keyvault show -n "$KV" --query id -o tsv)" -o none

# PostgreSQL 16, reachable from the network only. Its password is made here and goes straight
# into the vault, so nobody has to know it. No ';' in it: it goes into a connection string.
exists az postgres flexible-server show -g "$RG" -n "$PG" || {
  PG_PASSWORD=$(openssl rand -base64 36 | tr -d '/+=;')
  kv_set archestack-postgres-password "$PG_PASSWORD"
  az postgres flexible-server create -g "$RG" -n "$PG" -l "$LOCATION" --version 16 \
    --tier GeneralPurpose --sku-name "$PG_SKU" --storage-size 128 \
    --admin-user archestack --admin-password "$PG_PASSWORD" \
    --subnet "$(az network vnet subnet show -g "$VNET_RG" --vnet-name "$VNET" -n archestack-postgres --query id -o tsv)" \
    --private-dns-zone "$PG.private.postgres.database.azure.com" --yes -o none
}

# The cluster, allowed to pull from the registry.
if exists az aks show -g "$AKS_RG" -n "$AKS"; then
  az aks update -g "$AKS_RG" -n "$AKS" --attach-acr "$ACR_ID" -o none
else
  az aks create -g "$AKS_RG" -n "$AKS" --tier standard \
    --node-count "$NODE_COUNT" --node-vm-size "$NODE_SIZE" \
    --network-plugin azure --network-plugin-mode overlay --pod-cidr 10.244.0.0/16 \
    --vnet-subnet-id "$(az network vnet subnet show -g "$VNET_RG" --vnet-name "$VNET" -n aks --query id -o tsv)" \
    --enable-managed-identity --generate-ssh-keys --attach-acr "$ACR_ID" -o none
fi

echo "$ENVIRONMENT: $AKS, $PG and $KV are ready."
echo "Next: ENVIRONMENT=$ENVIRONMENT bash setup/2-address.sh, or with a path of a hostname the"
echo "cluster already serves (way B in values.yaml): ENVIRONMENT=$ENVIRONMENT bash setup/3-pipeline.sh"

Eén registry bedient elke omgeving, dus een versie wordt één keer geïmporteerd en productie draait precies de image die acceptatie draaide. Het script maakt het databasewachtwoord aan en zet het meteen in Key Vault: niemand hoeft het te kennen. Capaciteit en dimensionering geeft de gemeten cijfers achter de groottes, die een startpunt voor productie zijn.

Elke werkruimte heeft twee databases. Een werkruimte die niet in gebruik is, houdt geen verbindingen open, maar een drukke server heeft ruimte nodig: de limiet van 50 verbindingen van de Burstable-tier is te laag. De API heeft geen extensies en geen superuser nodig, alleen een admingebruiker die databases kan aanmaken.

3. Openbaar adres en DNS

#!/usr/bin/env bash
# setup/2-address.sh: the environment's static public address, for Caddy and your DNS. Not needed
# with a path of a hostname the cluster already serves (way B in values.yaml).
#   ENVIRONMENT=prd bash setup/2-address.sh
source "$(dirname "$0")/common.sh"

# In the cluster's node resource group, because that is where its load balancer looks for it by name.
NODE_RG=$(az aks show -g "$AKS_RG" -n "$AKS" --query nodeResourceGroup -o tsv)
az network public-ip create -g "$NODE_RG" -n "$PIP" --sku Standard --allocation-method Static -o none
IP=$(az network public-ip show -g "$NODE_RG" -n "$PIP" --query ipAddress -o tsv)

echo "Point these DNS records at $IP, under the zone in aks/environments/$ENVIRONMENT/values.yaml:"
echo "  editor  api  metrics  *"
echo "Next: ENVIRONMENT=$ENVIRONMENT bash setup/3-pipeline.sh"

Alleen voor manier A. Laat editor, api en metrics onder je zone naar het adres wijzen, plus een wildcard * voor de eigen adressen van de werkruimtes. Caddy haalt zijn certificaten bij Let's Encrypt, dus poort 80 en 443 moeten vanaf internet bereikbaar zijn.

4. Wachtwoorden en de identiteit van de pipeline

#!/usr/bin/env bash
# setup/3-pipeline.sh: the SMTP password, the namespace, and the identity the pipeline deploys
# this environment as.
#   ENVIRONMENT=prd bash setup/3-pipeline.sh
source "$(dirname "$0")/common.sh"
case "$PIPELINE" in
  azure-devops) ;;
  github | gitlab) [ -n "$REPO" ] || { echo "Fill in REPO in aks/settings.env" >&2; exit 1; } ;;
  *) echo "Fill in PIPELINE in aks/settings.env: azure-devops, github or gitlab" >&2; exit 1 ;;
esac

# Asked for here, so it is never written to a file. Enter nothing to keep what the vault has,
# or when the instance sends no mail.
read -rsp "SMTP password for $ENVIRONMENT (Enter to skip): " SMTP_PASSWORD; echo
if [ -n "$SMTP_PASSWORD" ]; then kv_set archestack-smtp-password "$SMTP_PASSWORD"; fi

# The pipeline's identity, one per environment, and what it may do: sign in to the cluster,
# read the passwords, and import into the registry.
az identity create -g "$RG" -n "$IDENTITY" -l "$LOCATION" -o none
CLIENT_ID=$(az identity show -g "$RG" -n "$IDENTITY" --query clientId -o tsv)
PRINCIPAL_ID=$(az identity show -g "$RG" -n "$IDENTITY" --query principalId -o tsv)
AKS_ID=$(az aks show -g "$AKS_RG" -n "$AKS" --query id -o tsv)
grant() {
  az role assignment create --assignee-object-id "$PRINCIPAL_ID" --assignee-principal-type ServicePrincipal \
    --role "$1" --scope "$2" -o none
}
grant "Azure Kubernetes Service Cluster User Role" "$AKS_ID"
grant "Key Vault Secrets User" "$(az keyvault show -n "$KV" --query id -o tsv)"
grant Contributor "$(az acr show -n "$ACR" --query id -o tsv)"

# The namespace, made here by you, so the pipeline only ever needs rights inside it. A cluster
# with Entra ID sign-in checks those rights itself: Azure RBAC, or a Kubernetes RoleBinding.
kube_login
kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - > /dev/null
ENTRA=$(az aks show -g "$AKS_RG" -n "$AKS" --query 'aadProfile != null' -o tsv)
AZURE_RBAC=$(az aks show -g "$AKS_RG" -n "$AKS" --query 'aadProfile.enableAzureRbac == `true`' -o tsv)
if [ "$AZURE_RBAC" = true ]; then
  grant "Azure Kubernetes Service RBAC Writer" "$AKS_ID/namespaces/$NAMESPACE"
elif [ "$ENTRA" = true ]; then
  kubectl create rolebinding archestack-pipeline -n "$NAMESPACE" --clusterrole=edit --user="$PRINCIPAL_ID" \
    --dry-run=client -o yaml | kubectl apply -f - > /dev/null
fi

# No stored secret: the CI system hands the pipeline a token that says who it is, and the
# identity trusts only tokens with this issuer and subject.
federate() {
  az identity federated-credential create -g "$RG" --identity-name "$IDENTITY" -n "$1" \
    --issuer "$2" --subject "$3" --audiences "$4" -o none
}
TENANT_ID=$(az account show --query tenantId -o tsv)
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
case "$PIPELINE" in
  github)
    federate "github-$ENVIRONMENT" https://token.actions.githubusercontent.com \
      "repo:$REPO:environment:archestack-$ENVIRONMENT" api://AzureADTokenExchange
    echo "Done. In GitHub, create the environment archestack-$ENVIRONMENT, then run:"
    echo "  gh variable set AZURE_CLIENT_ID --env archestack-$ENVIRONMENT --body $CLIENT_ID --repo $REPO"
    echo "  gh variable set AZURE_TENANT_ID --body $TENANT_ID --repo $REPO"
    echo "  gh variable set AZURE_SUBSCRIPTION_ID --body $SUBSCRIPTION_ID --repo $REPO"
    ;;
  gitlab)
    federate "gitlab-$ENVIRONMENT" "$GITLAB_URL" \
      "project_path:$REPO:ref_type:branch:ref:main" "$GITLAB_URL"
    echo "Done. In GitLab, Settings, CI/CD, Variables, add:"
    echo "  AZURE_CLIENT_ID = $CLIENT_ID    (environment scope: archestack-$ENVIRONMENT)"
    echo "  AZURE_TENANT_ID = $TENANT_ID    (all environments)"
    echo "  AZURE_SUBSCRIPTION_ID = $SUBSCRIPTION_ID    (all environments)"
    ;;
  azure-devops)
    if [ -n "${AZDO_ISSUER:-}" ]; then
      federate "azure-devops-$ENVIRONMENT" "$AZDO_ISSUER" "$AZDO_SUBJECT" api://AzureADTokenExchange
      echo "Done. sc-archestack-$ENVIRONMENT can deploy $ENVIRONMENT; verify it in Azure DevOps."
    else
      echo "In Azure DevOps, add a service connection sc-archestack-$ENVIRONMENT: Azure Resource Manager,"
      echo "workload identity federation, set up manually, with this identity:"
      echo "  tenant $TENANT_ID, subscription $SUBSCRIPTION_ID, client id $CLIENT_ID"
      echo "It then shows an issuer and a subject identifier. Run this again with them:"
      echo "  AZDO_ISSUER='<issuer>' AZDO_SUBJECT='<subject identifier>' ENVIRONMENT=$ENVIRONMENT bash setup/3-pipeline.sh"
    fi
    ;;
esac
  • Het SMTP-wachtwoord wordt gevraagd en gaat meteen naar Key Vault. Een instantie die geen mail verstuurt, slaat het over.
  • De pipeline meldt zich aan zonder opgeslagen secret. Elke omgeving heeft een eigen identiteit, die alleen het token vertrouwt dat het CI-systeem aan de jobs van die omgeving geeft. Die identiteit mag zich aanmelden bij het cluster, de wachtwoorden lezen en in de registry importeren, verder niets.
  • De namespace maak je zelf aan, dus de pipeline heeft alleen rechten daarbinnen nodig. Een cluster met aanmelden via Entra ID controleert die rechten zelf, via Azure RBAC of een Kubernetes-RoleBinding; het script kent ze toe via welke van de twee het cluster gebruikt.
  • Het script eindigt met wat je in je CI-systeem moet instellen: GitHub-variabelen, GitLab-variabelen of de Azure DevOps service connection.

5. De eerste installatie

De eerste installatie is het script dat de pipeline draait, met de hand gestart. Een eerste run van de pipeline doet hetzelfde.

# From the repository, after az login as the person who ran the setup.
bash aks/resolve-version.sh                          # writes the newest version to ./version
VERSION=$(cat version) ENVIRONMENT=prd bash aks/deploy.sh
kubectl -n archestack get deploy -o wide             # every Deployment ready, and its image

# Grafana's password, generated on the first install and kept from then on
kubectl -n archestack get secret archestack-secrets -o jsonpath='{.data.admin-password}' | base64 -d

Open meteen de builder en maak de eerste admin aan. Tot iemand dat doet, wordt wie de builder als eerste bereikt de admin. Om helemaal geen tijdvenster open te laten, maak je de eerste admin vooraf aan met api.initialAdmin (zie de values.yaml van de chart).

De nieuwste release, automatisch

resolve-version.sh vraagt de registry om elke tag van beide images en kiest de versie die deze run deployt:

#!/usr/bin/env bash
# aks/resolve-version.sh: turn "newest" into the version number this run deploys.
#   ARCHESTACK_VERSION=newest    the newest release
#   ARCHESTACK_VERSION=1.4       the newest 1.4.x
#   ARCHESTACK_VERSION=1.4.23    exactly that one
set -euo pipefail
export LC_ALL=C
SOURCE=${SOURCE_NAMESPACE:-nickpeelman}
WANT=${ARCHESTACK_VERSION:-newest}
OUT=${OUT_DIR:-.}

# Every major.minor.build tag of one image, from the registry API. The commit tags and
# "latest" do not match the pattern.
tags() {
  local repo="$SOURCE/$1" token
  token=$(curl -fsS "https://auth.docker.io/token?service=registry.docker.io&scope=repository:$repo:pull" \
    | sed -nE 's/.*"token":"([^"]+)".*/\1/p')
  curl -fsS -H "Authorization: Bearer $token" "https://registry-1.docker.io/v2/$repo/tags/list" \
    | grep -oE '"[0-9]+\.[0-9]+\.[0-9]+"' | tr -d '"' | sort -u
}

# A version counts only when both images carry it: a release build that stopped between
# the two pushes leaves one of them without it. Sorted as versions, so 1.0.10 beats 1.0.9.
versions=$(comm -12 <(tags omnicore-api) <(tags omnicore-web) | sort -V)

if [ "$WANT" = newest ]; then
  pick=$(tail -n 1 <<<"$versions")
elif [[ $WANT =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
  pick=$(grep -xF "$WANT" <<<"$versions" || true)
elif [[ $WANT =~ ^[0-9]+\.[0-9]+$ ]]; then
  pick=$(grep -E "^${WANT//./\\.}\.[0-9]+$" <<<"$versions" | tail -n 1 || true)
else
  echo "Version must be newest, major.minor or major.minor.build, not '$WANT'" >&2
  exit 1
fi
[ -n "$pick" ] || { echo "No release of $SOURCE/omnicore-api and omnicore-web matches '$WANT'" >&2; exit 1; }

# The version for the next steps: a file, and whatever the CI system passes between jobs.
echo "Deploying Archestack $pick"
mkdir -p "$OUT"
echo "$pick" > "$OUT/version"
if [ -n "${TF_BUILD:-}" ]; then echo "##vso[build.updatebuildnumber]$pick"; fi
if [ -n "${GITHUB_OUTPUT:-}" ]; then
  echo "version=$pick" >> "$GITHUB_OUTPUT"
  echo "Deploying Archestack **$pick**" >> "$GITHUB_STEP_SUMMARY"
fi
if [ -n "${GITLAB_CI:-}" ]; then echo "VERSION=$pick" > "$OUT/version.env"; fi
  • Versies worden als getallen vergeleken, dus 1.0.10 is nieuwer dan 1.0.9, en een ontbrekend nummer wordt overgeslagen in plaats van erop te wachten.
  • Alleen een versie die beide images hebben, telt. Een releasebuild die stopte tussen het pushen van de API en het pushen van de web-image, wordt nooit half gedeployd.
  • Een exacte versie wordt ook gecontroleerd. Een versie die de images niet hebben, stopt de run voordat er iets wordt aangeraakt.
  • De versie reist mee zoals elk CI-systeem waarden doorgeeft: Azure DevOps noemt de run ernaar, GitHub Actions maakt er een joboutput van en toont hem in de samenvatting van de run, en GitLab CI geeft hem als $VERSION door aan de latere jobs.

deploy.sh brengt daarna één omgeving naar die versie:

#!/usr/bin/env bash
# aks/deploy.sh: copy one Archestack version into your registry and roll it out with Helm.
# Every pipeline runs it once per environment, signed in to Azure. By hand, after az login:
#   VERSION=1.4.23 ENVIRONMENT=prd bash aks/deploy.sh
set -euo pipefail
cd "$(dirname "$0")/.."
: "${VERSION:?}" "${ENVIRONMENT:?}"
source aks/environment.sh
SOURCE=${SOURCE_NAMESPACE:-nickpeelman}
VALUES="aks/environments/$ENVIRONMENT/values.yaml"
if grep -v '^[[:space:]]*#' "$VALUES" | grep -q CHANGE-ME; then echo "Fill in every CHANGE-ME in $VALUES first" >&2; exit 1; fi

# Keeps a value out of the pipeline's log. GitLab has no such command; nothing here prints one.
mask() {
  if [ -n "${TF_BUILD:-}" ]; then echo "##vso[task.setsecret]$1"; fi
  if [ -n "${GITHUB_ACTIONS:-}" ]; then echo "::add-mask::$1"; fi
}

# 1. The two images, imported from Docker Hub into your registry. --force makes a rerun harmless.
for image in omnicore-api omnicore-web; do
  az acr import --name "$ACR" --force \
    --source "docker.io/$SOURCE/$image:$VERSION" --image "$image:$VERSION"
done

# 2. The passwords, from Key Vault into files only this job can read. They go in on every
#    upgrade: the chart writes the SMTP password from the values each time.
secret() { az keyvault secret show --vault-name "$KV" -n "$1" --query value -o tsv 2>/dev/null || true; }
pg=$(secret archestack-postgres-password)
smtp=$(secret archestack-smtp-password)
[ -n "$pg" ] || { echo "Cannot read archestack-postgres-password from $KV (missing, or no Key Vault Secrets User)" >&2; exit 1; }
for value in "$pg" "$smtp"; do
  if [ -n "$value" ]; then mask "$value"; fi
done
umask 077
secrets=$(mktemp -d)
trap 'rm -rf "$secrets"' EXIT
printf '%s' "$pg" > "$secrets/postgres"
printf '%s' "$smtp" > "$secrets/smtp"

# 3. The rollout, into the namespace setup/3-pipeline.sh made.
kube_login
helm upgrade --install archestack infra/helm/archestack \
  --namespace "$NAMESPACE" \
  -f "$VALUES" \
  --set-file postgres.password="$secrets/postgres" \
  --set-file smtp.password="$secrets/smtp" \
  --set-string postgres.host="$PG.postgres.database.azure.com" \
  --set-string 'caddy.service.annotations.service\.beta\.kubernetes\.io/azure-pip-name'="$PIP" \
  --set-string api.image.repository="$ACR.azurecr.io/omnicore-api" \
  --set-string api.image.tag="$VERSION" \
  --set-string web.image.repository="$ACR.azurecr.io/omnicore-web" \
  --set-string web.image.tag="$VERSION" \
  --wait --timeout 30m
  • De images gaan eerst naar je registry. Nodes halen ze op uit Azure, binnen de regio, dus een node toevoegen of vervangen wacht nooit op Docker Hub of op de limieten daarvan voor anonieme pulls. Mocht een import vanwege die limieten geweigerd worden, geef az acr import dan een Docker Hub-account mee met --username en --password.
  • De wachtwoorden gaan bij elke upgrade mee. De chart schrijft het SMTP-wachtwoord elke keer uit de waarden, dus na een upgrade zonder dat wachtwoord kan de instantie geen mail meer versturen. Ze reizen in bestanden die alleen de job kan lezen, en Azure DevOps en GitHub Actions maskeren ze in het log.
  • --wait --timeout 30m wacht tot elke Deployment gereed is. De API migreert elke werkruimte voordat hij antwoordt, dus een versie met migraties duurt even op een instantie met veel werkruimtes.
  • Geen --atomic. Een mislukte uitrol blijft staan om te bekijken, in plaats van ongedaan gemaakt te worden, omdat de nieuwe versie de databases misschien al heeft gemigreerd. De oude pods blijven ondertussen verzoeken beantwoorden.

De pipeline

De kit bevat dezelfde pipeline voor drie CI-systemen. Elk ervan draait de twee scripts hierboven, in dezelfde volgorde:

Archestack publishes 1.4.23     omnicore-api and omnicore-web, tagged 1.4.23, <commit> and latest

03:00, the nightly run
  resolve    newest -> 1.4.23   the highest number both images carry
  dev        import 1.4.23 into your registry, helm upgrade, wait until ready
  tst        the same 1.4.23
             a scheduled run stops here

a run started by hand, version 1.4.23
  dev, tst   already on 1.4.23, nothing rolls out
  acc        waits for its approval, then 1.4.23
  prd        waits for its approval, then 1.4.23
  • Elke nacht brengt de geplande run de nieuwste release naar ontwikkeling en test.
  • Een geplande run stopt na test. Acceptatie en productie gaan alleen mee bij een run die met de hand is gestart. Geef die de versie die op test draait, zoals 1.4.23, en acceptatie en productie krijgen precies wat getest is. Laat je het op newest staan, dan neemt de run wat op dat moment het nieuwste is.
  • Eén versie per run. De versie wordt één keer bepaald, vóór de eerste omgeving, dus een release die verschijnt terwijl een run op een goedkeuring wacht, glipt niet mee naar productie.
  • Een omgeving die al op de versie staat, rolt niets uit. De specificatie van de pods is ongewijzigd, dus Kubernetes laat ze draaien. Een wijziging in de values.yaml van die omgeving wordt nog steeds doorgevoerd.
Je CI-systeem

Azure DevOps

# aks/azure-pipelines.yml: the newest Archestack release to dev and tst every night,
# to acc and prd on a run started by hand, once each is approved.
trigger: none

schedules:
  - cron: "0 3 * * *"              # 03:00 UTC
    displayName: Nightly, newest release to dev and tst
    branches:
      include: [main]
    always: true                    # a release is a new image, not a commit in this repository

parameters:
  - name: version
    displayName: Archestack version (newest, 1.4 for the newest 1.4.x, or 1.4.23)
    type: string
    default: newest
  - name: environments
    displayName: Environments, in order
    type: object
    default: [dev, tst, acc, prd]

pool:
  vmImage: ubuntu-latest

stages:
  # Once per run, so every environment gets the same number even when a release
  # is published while the run waits for an approval.
  - stage: resolve
    displayName: Resolve the version
    jobs:
      - job: resolve
        steps:
          - script: bash aks/resolve-version.sh
            displayName: Newest version both images carry
            env:
              ARCHESTACK_VERSION: ${{ parameters.version }}
              OUT_DIR: $(Build.ArtifactStagingDirectory)
          - publish: $(Build.ArtifactStagingDirectory)
            artifact: version

  - ${{ each env in parameters.environments }}:
    - stage: deploy_${{ env }}
      displayName: Deploy ${{ env }}
      ${{ if in(env, 'acc', 'prd') }}:
        # A scheduled run stops after tst.
        condition: and(succeeded(), ne(variables['Build.Reason'], 'Schedule'))
      jobs:
        - deployment: deploy
          environment: archestack-${{ env }}
          strategy:
            runOnce:
              deploy:
                steps:
                  - checkout: self
                  - download: current
                    artifact: version
                  - task: AzureCLI@2
                    displayName: Import the images, then helm upgrade
                    inputs:
                      azureSubscription: sc-archestack-${{ env }}
                      scriptType: bash
                      scriptLocation: inlineScript
                      workingDirectory: $(Build.SourcesDirectory)
                      inlineScript: |
                        export VERSION=$(cat "$PIPELINE_WORKSPACE/version/version")
                        ENVIRONMENT=${{ env }} bash aks/deploy.sh
  • Service connections heten sc-archestack-<env>, één per omgeving, elk met de identiteit van die omgeving. setup/3-pipeline.sh toont wat je moet invullen en neemt de issuer en het subject over die de verbinding toont.
  • Goedkeuringen. Voeg onder Pipelines, Environments een Approvals-check toe aan archestack-acc en archestack-prd, en een Exclusive lock-check aan alle vier, zodat twee runs nooit tegelijk naar één omgeving uitrollen.
  • always: true draait de geplande run ook zonder nieuwe commit, omdat een release een nieuwe image is, geen wijziging in deze repository.

GitHub Actions

# .github/workflows/archestack.yml: the newest Archestack release to dev and tst every night,
# to acc and prd on a run started by hand, once each is approved.
name: Archestack on AKS

on:
  schedule:
    - cron: "0 3 * * *"            # 03:00 UTC
  workflow_dispatch:
    inputs:
      version:
        description: Archestack version (newest, 1.4 for the newest 1.4.x, or 1.4.23)
        default: newest

permissions:
  id-token: write                  # sign in to Azure with a token, no stored secret
  contents: read

jobs:
  # Once per run, so every environment gets the same number even when a release
  # is published while the run waits for an approval.
  resolve:
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.pick.outputs.version }}
    steps:
      - uses: actions/checkout@v4
      - id: pick
        run: bash aks/resolve-version.sh
        env:
          ARCHESTACK_VERSION: ${{ inputs.version || 'newest' }}
          OUT_DIR: ${{ runner.temp }}

  dev:
    needs: resolve
    uses: ./.github/workflows/archestack-deploy.yml
    with: { environment: dev, version: "${{ needs.resolve.outputs.version }}" }

  tst:
    needs: [resolve, dev]
    uses: ./.github/workflows/archestack-deploy.yml
    with: { environment: tst, version: "${{ needs.resolve.outputs.version }}" }

  # A scheduled run stops after tst.
  acc:
    if: github.event_name != 'schedule'
    needs: [resolve, tst]
    uses: ./.github/workflows/archestack-deploy.yml
    with: { environment: acc, version: "${{ needs.resolve.outputs.version }}" }

  prd:
    if: github.event_name != 'schedule'
    needs: [resolve, acc]
    uses: ./.github/workflows/archestack-deploy.yml
    with: { environment: prd, version: "${{ needs.resolve.outputs.version }}" }

Elke omgeving wordt gedeployd door dezelfde herbruikbare workflow:

# .github/workflows/archestack-deploy.yml: one environment to one version, called by archestack.yml.
name: Archestack deploy

on:
  workflow_call:
    inputs:
      environment: { type: string, required: true }
      version: { type: string, required: true }

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: archestack-${{ inputs.environment }}
    concurrency: archestack-${{ inputs.environment }}    # one rollout per environment at a time
    steps:
      - uses: actions/checkout@v4
      - uses: azure/login@v2
        with:
          client-id: ${{ vars.AZURE_CLIENT_ID }}       # this environment's identity
          tenant-id: ${{ vars.AZURE_TENANT_ID }}
          subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }}
      - run: bash aks/deploy.sh
        env:
          VERSION: ${{ inputs.version }}
          ENVIRONMENT: ${{ inputs.environment }}
  • Omgevingen. Maak onder Settings, Environments archestack-dev, -tst, -acc en -prd aan, en voeg aan acceptatie en productie required reviewers toe. Op een privérepository hebben omgevingen GitHub Pro, Team of Enterprise nodig.
  • Aanmelden. azure/login ruilt het token van de job in voor de identiteit van de omgeving, waarvan de client-id een omgevingsvariabele is waarvoor setup/3-pipeline.sh de gh-commando's toont. De identiteit vertrouwt alleen jobs in haar eigen omgeving, dus een job voor test kan nooit productie deployen.
  • concurrency voorkomt dat twee runs tegelijk naar één omgeving uitrollen.
  • De schedule draait alleen vanaf de standaardbranch, en op een openbare repository zet GitHub hem uit na 60 dagen zonder activiteit in de repository.

GitLab CI

# .gitlab-ci.yml: the newest Archestack release to dev and tst on the nightly schedule,
# to acc and prd from a pipeline started by hand, each when a person runs its job.
variables:
  ARCHESTACK_VERSION:
    value: newest
    description: Archestack version (newest, 1.4 for the newest 1.4.x, or 1.4.23)

# Pipelines run on the schedule (Build, Pipeline schedules) and when started by hand, never on a push.
workflow:
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule" || $CI_PIPELINE_SOURCE == "web"

stages: [resolve, dev, tst, acc, prd]

default:
  image: mcr.microsoft.com/azure-cli:2.90.0
  before_script:
    - tdnf install -y curl tar gzip > /dev/null

# Once per pipeline, so every environment gets the same number. Later jobs read it as $VERSION.
resolve:
  stage: resolve
  script:
    - bash aks/resolve-version.sh
  artifacts:
    reports:
      dotenv: version.env

.deploy:
  id_tokens:
    AZURE_TOKEN:
      aud: https://gitlab.com          # GITLAB_URL from aks/settings.env, on a self-managed instance
  before_script:
    - tdnf install -y curl tar gzip > /dev/null
    - curl -fsSL https://get.helm.sh/helm-v3.16.2-linux-amd64.tar.gz | tar -xz -C /tmp
    - mv /tmp/linux-amd64/helm /usr/local/bin/helm
    - az login --service-principal -u "$AZURE_CLIENT_ID" -t "$AZURE_TENANT_ID" --federated-token "$AZURE_TOKEN" -o none
    - az account set -s "$AZURE_SUBSCRIPTION_ID"
  script:
    - bash aks/deploy.sh

dev:
  extends: .deploy
  stage: dev
  environment: archestack-dev
  resource_group: archestack-dev      # one rollout per environment at a time
  variables: { ENVIRONMENT: dev }

tst:
  extends: .deploy
  stage: tst
  environment: archestack-tst
  resource_group: archestack-tst
  variables: { ENVIRONMENT: tst }

# A scheduled pipeline stops after tst. acc and prd wait until a person runs them, in order.
.by-hand:
  rules:
    - if: $CI_PIPELINE_SOURCE == "schedule"
      when: never
    - when: manual
  allow_failure: false                # prd stays blocked until acc has run

acc:
  extends: [.deploy, .by-hand]
  stage: acc
  environment: archestack-acc
  resource_group: archestack-acc
  variables: { ENVIRONMENT: acc }

prd:
  extends: [.deploy, .by-hand]
  stage: prd
  environment: archestack-prd
  resource_group: archestack-prd
  variables: { ENVIRONMENT: prd }
  • De schedule staat in GitLab in plaats van in het bestand: voeg onder Build, Pipeline schedules er een toe voor main op 0 3 * * *. Een push start niets.
  • Acceptatie en productie zijn handmatige jobs die de pipeline blokkeren: productie kan pas gestart worden wanneer acceptatie heeft gedraaid. Bescherm op GitLab Premium ook die omgevingen en vereis goedkeuringen; een goedgekeurde job wordt nog steeds met de hand gestart.
  • Aanmelden. Het ID-token van de job wordt ingeruild voor de identiteit van de omgeving, waarvan de client-id een variabele is die tot die omgeving beperkt is. Het token van GitLab noemt de branch, niet de omgeving, dus bescherm main. Zet op een self-managed instantie het adres van die instantie als GITLAB_URL en als de aud van het token.
  • resource_group voorkomt dat twee pipelines tegelijk naar één omgeving uitrollen.
  • De image is vastgezet, en Helm wordt in een vastgezette versie gedownload. Werk ze bewust bij.

Upgrades zonder downtime

  • Eén nieuwe API-pod tegelijk. De chart voegt een pod toe voordat hij er een weghaalt (maxSurge: 1, maxUnavailable: 0), en een oude pod verdwijnt pas wanneer een nieuwe gereed is.
  • Opstarten gaat om beurten. Replica's die tegelijk starten, bereiden de databases na elkaar voor onder een lock, zodat er nooit twee tegelijk migreren. Een startup probe geeft elke pod 15 minuten voordat Kubernetes hem opgeeft.
  • Node-upgrades houden de API in de lucht. Een PodDisruptionBudget houdt minstens één API-pod draaiende terwijl AKS een node leegmaakt.
  • Caddy herstart alleen wanneer zijn eigen configuratie verandert, zoals bij een nieuwe hostnaam. Het is één pod met zijn certificaten op een eigen schijf, dus die herstart is een korte onderbreking. Een Archestack-upgrade komt er niet aan.

Teruggaan

Draai de pipeline met de vorige versie, zoals 1.4.22, of zet de release terug met Helm:

helm history archestack -n archestack                          # the revisions, newest last
helm rollback archestack -n archestack --wait --timeout 30m     # back to the one before

Hoe dan ook blijven de databases zoals de nieuwere versie ze heeft achtergelaten: die heeft ze bij het starten gemigreerd, en een oudere versie maakt dat niet ongedaan. Teruggaan draait oudere code op een nieuwer schema, dus kies liever de volgende release die het probleem oplost. Om ook het schema terug te zetten, herstel je de server met de point-in-time restore van Flexible Server.

Valkuilen

  • De chart komt uit je repository, de images uit de registry. De pipeline neemt vanzelf de nieuwste images, nooit een nieuwere chart. Wanneer je overstapt op een nieuwe major- of minorversie, haal je de chart opnieuw uit de Archestack-repository.
  • De observability-images hebben geen versienummer. Ze zijn getagd met een commit en latest, komen van Docker Hub en volgen observability.imageTag. Zet die op een van hun committags om ze vast te zetten.
  • Eén release per namespace. Grafana en Tempo vinden Prometheus, Tempo en Loki via vaste servicenamen.
  • Achter PgBouncer (poort 6432 van Flexible Server) zet je postgres.clusterConnectionString op een directe verbinding op 5432. De replica's praten met elkaar over een PostgreSQL-verbinding die open moet blijven.
  • Volumes en het eigen Secret van de chart overleven helm uninstall. Verwijder ze met de hand wanneer je dat echt wilt.

Waar dit naartoe gaat

Capaciteit en dimensionering geeft de gemeten cijfers om het cluster en de database te dimensioneren. Werkruimtes en hosting legt uit hoe één instantie veel werkruimtes bedient. Zodra de instantie draait, behandelt Deploymentstrategie hoe je je app vanuit een repository naar haar werkruimtes deployt.