Héberger Archestack sur AKS
Ce guide installe une instance Archestack sur Azure Kubernetes Service (AKS), puis la tient à jour d'elle-même. Le chart Helm fait tourner l'API sous forme de plusieurs réplicas identiques, le builder et l'application publiée, ainsi que Caddy en frontal avec des certificats automatiques. PostgreSQL tourne en tant qu'Azure Database for PostgreSQL Flexible Server, et les images proviennent d'un registre de conteneurs qui vous est propre. Chaque nuit, un pipeline recherche la release d'Archestack la plus récente, l'amène en développement et en test, puis mène cette même version jusqu'en recette et en production une fois que quelqu'un l'a approuvée. Sur un seul serveur, Héberger avec Docker est la manière la plus simple.
Vous avez déjà un cluster et voulez y mettre Archestack dès aujourd'hui, sous un chemin d'un nom d'hôte que vous servez déjà ? Archestack sur Kubernetes, la manière courte le fait avec cinq fichiers et un pipeline, et c'est par là qu'il faut commencer.
Ce qui tourne où
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 est le frontal, pas un ingress controller. Les adresses des espaces de travail et les domaines propres des entreprises reçoivent un certificat la première fois que quelqu'un les visite, et Caddy demande à l'API si un nom d'hôte appartient à un espace de travail avant d'en demander un. cert-manager ne sait pas émettre de certificats à la demande, si bien qu'un ingress controller perdrait ces adresses. Sans ces adresses, Archestack peut aussi se placer sous un chemin d'un nom d'hôte que le cluster sert déjà.
- L'adresse du visiteur est conservée (
externalTrafficPolicy: Local). Le limiteur de débit compte les appelants anonymes par adresse, et sans cela tous les visiteurs partageraient l'adresse d'un nœud. - Les fichiers et les sauvegardes forment un seul partage sur Azure Files, que
chaque réplica de l'API monte, si bien qu'un logo ou une sauvegarde écrits via un réplica sont
présents sur tous. Les paquets NuGet qu'utilisent les scripts font exception : chaque pod garde
les siens sur son nœud, aussi longtemps que le pod existe (
api.nugetCache). - L'API crée ses propres bases de données et les migre à son démarrage, si bien qu'un serveur vide est tout ce dont elle a besoin. Il n'existe pas de tâche de migration distincte.
Versions et tags
Chaque release d'Archestack porte un numéro de version, major.minor.build, comme
1.4.23. Le numéro de build augmente à chaque release et repart à 1 quand la version
majeure ou mineure change, et un numéro peut manquer lorsque le build d'une release ne s'est pas
terminé. Chaque release publie deux images sur Docker Hub, nickpeelman/omnicore-api
et nickpeelman/omnicore-web, chacune avec trois tags : la version, le commit à
partir duquel elle a été construite, et latest.
Déployez le numéro de version, jamais latest :
- Rien ne se déploie. Une mise à niveau de
latestverslatestlaisse le Deployment inchangé, si bien que Kubernetes garde les pods qu'il a. - Les réplicas divergent. Le chart ne récupère une image que lorsque le nœud ne
l'a pas encore. Un nœud qui a récupéré
latestla semaine dernière continue de faire tourner la version de la semaine dernière, un nouveau nœud fait tourner celle de cette semaine, et une même instance sert deux versions. - Pas de retour possible. La version précédente n'a pas de nom auquel revenir.
Le pipeline ci-dessous transforme donc "la plus récente" en un numéro une seule fois, au début
d'une exécution, et chaque environnement reçoit ce numéro. Il peut aussi suivre une seule ligne de
versions : 1.4 prend la 1.4.x la plus récente et ne passe jamais à
1.5 de lui-même. C'est dans une nouvelle version majeure ou mineure qu'arrivent les
changements les plus importants, et l'extension VS Code avertit quand
elle se connecte à une API d'une autre version majeure ou mineure.
Avant de commencer
- Un abonnement Azure, ainsi que la CLI
az,kubectlet Helm 3 sur votre machine. - Un projet sur Azure DevOps, GitHub ou GitLab pour le pipeline, et un dépôt sur cette même plateforme, créé à partir du kit de démarrage.
-
Le chart Helm : le dossier
infra/helm/archestackdu dépôt Archestack, copié dans ce dépôt au même chemin. - Une zone DNS dans laquelle vous pouvez ajouter des enregistrements, ou un nom d'hôte qu'un cluster sert déjà via son ingress controller (voie B).
Le kit de démarrage
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
Gardez le pipeline de votre système de CI et supprimez les deux autres.
grep -rn CHANGE-ME . liste chaque valeur qui reste à renseigner, et
deploy.sh refuse un environnement qui en contient encore une. Les trois scripts de
mise en place, qu'une personne exécute une seule fois, créent ensuite chaque environnement dans
Azure :
# 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 Chaque script ne crée que ce qui manque, si bien qu'un script arrêté à mi-chemin peut simplement être relancé, et chacun se termine en indiquant la suite.
1. Renseigner les paramètres
aks/settings.env contient ce que tous les environnements partagent :
# 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
Chaque nom Azure en découle dans aks/environment.sh, que la mise en place et
deploy.sh lisent tous les deux, si bien qu'ils concordent toujours. Les noms
qu'Azure exige uniques au niveau mondial (le registre, le coffre de clés et le serveur de base de
données) se terminent par votre préfixe :
# 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
}
Le values.yaml de chaque environnement contient ses valeurs du chart et l'une des
deux voies d'accès : des noms d'hôte qui lui sont propres (A), ou un chemin d'un nom d'hôte que le
cluster sert déjà (B). Gardez-en une, supprimez l'autre.
# 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: n'importe quel nombre, sans sessions persistantes. Infrastructure d'hébergement liste ce que plusieurs réplicas demandent au réseau qui les entoure. -
api.tenantHostSuffix: active une adresse par espace de travail et les domaines propres des entreprises, avec la voie A uniquement. -
api.persistence: doit êtreReadWriteManydès que des réplicas tournent sur plus d'un nœud. -
observability.enabled: Grafana, Prometheus, Tempo et Loki à l'intérieur du cluster. Leurs panneaux d'hôtes et de conteneurs restent vides sur AKS ; Container Insights couvre les nœuds et les conteneurs.
Sous un chemin d'un nom d'hôte que vous avez déjà
La voie B place Archestack sous un chemin d'un nom d'hôte que l'ingress controller du cluster sert déjà, à côté des applications qui s'y trouvent déjà :
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) Elle ne demande ni nouvel enregistrement DNS, ni adresse publique, ni certificat propre, et les utilisateurs continuent d'utiliser l'adresse qu'ils connaissent déjà. Le chart laisse Caddy de côté et génère un seul Ingress pour les trois chemins ; le builder et l'API reçoivent leur préfixe, si bien que chaque lien qu'ils écrivent le porte.
# --- 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 - Un cluster que vous avez déjà. Le nom d'hôte se trouve sur un cluster,
généralement un cluster qui existait avant Archestack. Copiez
names.env.exampleversaks/environments/<env>/names.env, et la mise en place laisse ce cluster tel quel : elle autorise le cluster à récupérer les images du registre et ajoute à son réseau un sous-réseau pour la base de données. Ignorezsetup/2-address.sh. -
classNameest la classe d'ingress qui sert le nom d'hôte. Le chart définit les annotations d'ingress-nginx pour les envois de fichiers volumineux et les connexions de longue durée ; un autre ingress controller a besoin de ses propres équivalents. - Le certificat doit être un Secret dans le namespace
archestack, car un Ingress n'utilise que les Secrets de son propre namespace. Copiez-y le certificat du nom d'hôte, ou demandez à cert-manager de l'y émettre. - Ne réécrivez pas le chemin (pas de
rewrite-target). L'API doit voir son préfixe pour écrire ses propres adresses. - Tenez les proxys de connexion à l'écart. Un proxy qui remplace l'en-tête
Authorizationcasse chaque appel à l'API : l'application publiée, les clés de la Data API, les jetons de déploiement, VS Code et MCP apportent tous le leur. - Le nom d'hôte est partagé. Les jetons de connexion sont conservés dans le stockage du navigateur pour cette adresse, que chaque application qui s'y trouve peut lire, si bien qu'un script injecté dans l'une d'elles atteint les sessions Archestack. Deux instances Archestack sur un même nom d'hôte déconnectent mutuellement leurs utilisateurs.
- Non disponibles par cette voie : les adresses des espaces de travail et les
domaines propres des entreprises, qui ont besoin des certificats à la demande de Caddy, ainsi que
Grafana, que l'Ingress ne route pas. Accédez à Grafana avec
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. Registre, cluster et base de données
#!/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" Un seul registre sert tous les environnements, si bien qu'une version n'est importée qu'une fois et que la production fait tourner exactement l'image que la recette a fait tourner. Le mot de passe de la base de données est généré par le script et va directement dans Key Vault : personne n'a besoin de le connaître. Capacité et dimensionnement donne les chiffres mesurés derrière les tailles, qui sont un point de départ pour la production.
Chaque espace de travail possède deux bases de données. Un espace de travail inactif n'occupe aucune connexion, mais un serveur chargé a besoin de marge : la limite de 50 connexions du niveau Burstable est trop basse. L'API n'a besoin ni d'extensions ni de superutilisateur, seulement d'un utilisateur admin capable de créer des bases de données.
3. Adresse publique et 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"
Voie A uniquement. Faites pointer editor, api et metrics
sous votre zone vers l'adresse, ainsi qu'un enregistrement générique * pour les
adresses propres des espaces de travail. Caddy obtient ses certificats auprès de Let's Encrypt,
donc les ports 80 et 443 doivent être joignables depuis internet.
4. Mots de passe et identité du 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 - Le mot de passe SMTP est demandé et va directement dans Key Vault. Une instance qui n'envoie pas de mails s'en passe.
- Le pipeline se connecte sans secret stocké. Chaque environnement a sa propre identité, qui ne fait confiance qu'au jeton que son système de CI remet aux jobs de cet environnement. Elle peut se connecter au cluster, lire les mots de passe et importer dans le registre, rien d'autre.
- C'est vous qui créez le namespace, si bien que le pipeline n'a besoin de droits qu'à l'intérieur de celui-ci. Un cluster avec la connexion Entra ID vérifie lui-même ces droits, via Azure RBAC ou un RoleBinding Kubernetes ; le script les accorde par celui des deux que le cluster utilise.
- Le script se termine par ce qu'il faut configurer dans votre système de CI : variables GitHub, variables GitLab ou connexion de service Azure DevOps.
5. La première installation
La première installation, c'est le script que le pipeline exécute, lancé à la main. Une première exécution du pipeline fait la même chose.
# 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 Ouvrez immédiatement le builder et créez le premier admin. Tant que personne ne
l'a fait, la première personne qui y accède devient l'admin. Pour ne laisser aucune fenêtre
ouverte, définissez d'avance le premier admin avec api.initialAdmin (voir le
values.yaml du chart).
La release la plus récente, automatiquement
resolve-version.sh demande au registre tous les tags des deux images et choisit la
version que cette exécution déploie :
#!/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 - Les versions sont comparées comme des nombres, si bien que
1.0.10est plus récente que1.0.9, et qu'un numéro manquant est ignoré plutôt qu'attendu. - Seule compte une version que portent les deux images. Un build de release qui s'est arrêté entre l'envoi de l'image de l'API et celui de l'image web n'est jamais déployé à moitié.
- Une version exacte est vérifiée elle aussi. Une version que les images n'ont pas arrête l'exécution avant que quoi que ce soit ne soit modifié.
- La version circule de la manière dont chaque système de CI transmet les valeurs :
Azure DevOps nomme l'exécution d'après elle, GitHub Actions en fait une sortie de job et
l'affiche dans le résumé de l'exécution, et GitLab CI la transmet aux jobs suivants sous la forme
de
$VERSION.
deploy.sh amène ensuite un environnement à cette version :
#!/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 - Les images vont d'abord dans votre registre. Les nœuds récupèrent les images
depuis Azure, au sein de la région, si bien qu'ajouter ou remplacer un nœud n'attend jamais
Docker Hub ni ses limites sur les téléchargements anonymes. Si un import est refusé à cause de
ces limites, donnez à
az acr importun compte Docker Hub avec--usernameet--password. - Les mots de passe sont transmis à chaque mise à niveau. Le chart écrit à chaque fois le mot de passe SMTP à partir des valeurs, si bien qu'une mise à niveau sans lui laisse l'instance incapable d'envoyer des mails. Ils transitent par des fichiers que seul le job peut lire, et Azure DevOps et GitHub Actions les masquent dans le journal.
-
--wait --timeout 30mattend que chaque Deployment soit prêt. L'API migre chaque espace de travail avant de répondre, si bien qu'une version avec des migrations prend un certain temps sur une instance qui compte de nombreux espaces de travail. - Pas de
--atomic. Un déploiement en échec est laissé tel quel pour être examiné plutôt qu'annulé, car la nouvelle version a peut-être déjà migré les bases de données. Les anciens pods continuent de servir entre-temps.
Le pipeline
Le kit contient le même pipeline pour trois systèmes de CI. Chacun exécute les deux scripts ci-dessus, dans le même ordre :
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 - Chaque nuit, la planification amène la release la plus récente en développement et en test.
- Une exécution planifiée s'arrête après le test. La recette et la production ne
changent de version que lors d'une exécution lancée à la main. Donnez-lui la version qui tourne
en test, comme
1.4.23, et la recette et la production reçoivent exactement ce qui a été testé. Laissée surnewest, elle prend ce qui est le plus récent à ce moment-là. - Une version par exécution. La version est résolue une seule fois, avant le premier environnement, si bien qu'une release publiée pendant qu'une exécution attend une approbation ne se glisse pas en production.
- Un environnement déjà sur cette version ne redéploie rien. La spécification des
pods est inchangée, si bien que Kubernetes les laisse tourner. Une modification de son
values.yamlest quand même appliquée.
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 - Les connexions de service s'appellent
sc-archestack-<env>, une par environnement, chacune avec l'identité de cet environnement.setup/3-pipeline.shaffiche ce qu'il faut renseigner et prend en entrée l'émetteur et le sujet qu'affiche la connexion. - Approbations. Sous Pipelines, Environments, ajoutez une vérification Approvals
à
archestack-accetarchestack-prd, et une vérification Exclusive lock aux quatre, pour que deux exécutions ne déploient jamais dans un même environnement au même moment. -
always: truedéclenche l'exécution planifiée sans nouveau commit, car une release est une nouvelle image, pas une modification de ce dépôt.
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 }}" } Chaque environnement est déployé par le même workflow réutilisable :
# .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 }} - Environnements. Créez
archestack-dev,-tst,-accet-prdsous Settings, Environments, et ajoutez des relecteurs requis à la recette et à la production. Sur un dépôt privé, les environnements nécessitent GitHub Pro, Team ou Enterprise. - Connexion.
azure/loginéchange le jeton du job contre l'identité de l'environnement, dont l'identifiant client est une variable de cet environnement pour laquellesetup/3-pipeline.shaffiche les commandesgh. L'identité ne fait confiance qu'aux jobs de son propre environnement, si bien qu'un job destiné au test ne peut jamais déployer en production. -
concurrencyempêche deux exécutions de déployer dans un même environnement au même moment. - La planification ne s'exécute qu'à partir de la branche par défaut, et sur un dépôt public, GitHub la désactive après 60 jours sans activité dans le dépôt.
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 } - La planification se trouve dans GitLab plutôt que dans le fichier : sous Build,
Pipeline schedules, ajoutez-en une pour
mainà0 3 * * *. Un push ne lance rien. - La recette et la production sont des jobs manuels qui bloquent le pipeline : la production ne peut être lancée qu'une fois la recette exécutée. Sur GitLab Premium, protégez aussi ces environnements et exigez des approbations ; un job approuvé se lance quand même à la main.
- Connexion. Le jeton d'ID du job est échangé contre l'identité de
l'environnement, dont l'identifiant client est une variable limitée à cet environnement. Le
jeton de GitLab nomme la branche, pas l'environnement, donc protégez
main. Sur une instance autogérée, définissez son adresse commeGITLAB_URLet commeauddu jeton. -
resource_groupempêche deux pipelines de déployer dans un même environnement au même moment. - La version de l'image est fixée, et Helm est téléchargé dans une version fixée. Faites-les évoluer délibérément.
Mises à niveau sans interruption de service
- Un nouveau pod d'API à la fois. Le chart ajoute un pod avant d'en retirer un
(
maxSurge: 1,maxUnavailable: 0), et un ancien pod ne part qu'une fois qu'un nouveau est prêt. - Le démarrage se fait à tour de rôle. Les réplicas qui démarrent ensemble préparent les bases de données l'un après l'autre sous un verrou, si bien que deux d'entre eux ne migrent jamais en même temps. Une startup probe laisse à chaque pod 15 minutes avant que Kubernetes ne l'abandonne.
- Les mises à niveau des nœuds laissent l'API disponible. Un PodDisruptionBudget maintient au moins un pod d'API en fonctionnement pendant qu'AKS draine un nœud.
- Caddy ne redémarre que lorsque sa propre configuration change, par exemple pour un nouveau nom d'hôte. C'est un seul pod, avec ses certificats sur un disque qui lui est propre, donc ce redémarrage n'est qu'une courte interruption. Une mise à niveau d'Archestack n'y touche pas.
Revenir en arrière
Exécutez le pipeline avec la version précédente, comme 1.4.22, ou faites un retour
arrière de la release avec Helm :
helm history archestack -n archestack # the revisions, newest last
helm rollback archestack -n archestack --wait --timeout 30m # back to the one before Dans les deux cas, les bases de données restent telles que la version plus récente les a laissées : elle les a migrées à son démarrage, et une version plus ancienne ne défait pas cela. Revenir en arrière fait tourner du code plus ancien sur un schéma plus récent ; préférez donc la prochaine release qui corrige le problème. Ramener aussi le schéma en arrière implique de restaurer le serveur avec la restauration à un point dans le temps de Flexible Server.
Pièges
- Le chart vient de votre dépôt, les images du registre. Le pipeline prend de lui-même les images les plus récentes, jamais un chart plus récent. Quand vous passez à une nouvelle version majeure ou mineure, reprenez le chart dans le dépôt Archestack.
- Les images d'observabilité ne portent pas de numéro de version. Elles ont pour
tags un commit et
latest, viennent de Docker Hub et suiventobservability.imageTag. Réglez-le sur l'un de leurs tags de commit pour les figer. - Une release par namespace. Grafana et Tempo trouvent Prometheus, Tempo et Loki par des noms de service fixes.
- Derrière PgBouncer (le port 6432 de Flexible Server), réglez
postgres.clusterConnectionStringsur une connexion directe sur le port 5432. Les réplicas communiquent entre eux par une connexion PostgreSQL qui doit rester ouverte. - Les volumes et le Secret propre au chart survivent à
helm uninstall. Supprimez-les à la main si c'est bien ce que vous voulez.
Pour aller plus loin
Capacité et dimensionnement donne les chiffres mesurés pour dimensionner le cluster et la base de données. Espaces de travail et hébergement explique comment une instance sert de nombreux espaces de travail. Une fois l'instance en service, Stratégie de déploiement explique comment déployer votre application dans ses espaces de travail à partir d'un dépôt.