From 652346dcd4a6750f8628a572de8f8dd793ad251c Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Thu, 23 Apr 2026 09:45:44 +0200 Subject: [PATCH] deploy: gen-suffixed container names + cameleer.generation label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Append an 8-char generation id (first 8 chars of deployment UUID) to: - container name: {tenant}-{env}-{app}-{replica}-{gen} - CAMELEER_AGENT_INSTANCEID (so old+new agents are distinct in the registry) - Traefik cameleer.instance-id label And emit a new standalone cameleer.generation label so dashboards (Prometheus/Grafana) can pin deploy boundaries without regex on instance-id. Strategy branching comes next — this commit is foundation only; the interim destroy-then-start flow still runs regardless of strategy. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../server/app/runtime/DeploymentExecutor.java | 14 +++++++++++--- .../server/app/runtime/TraefikLabelBuilder.java | 9 +++++++-- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/DeploymentExecutor.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/DeploymentExecutor.java index a6adfbc9..a39fbad7 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/DeploymentExecutor.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/DeploymentExecutor.java @@ -89,6 +89,13 @@ public class DeploymentExecutor { this.applicationConfigRepository = applicationConfigRepository; } + /** Deployment-scoped id suffix — distinguishes container names and + * CAMELEER_AGENT_INSTANCEID across redeploys so old + new replicas can + * coexist during a blue/green swap. First 8 chars of the deployment UUID. */ + static String generationOf(Deployment deployment) { + return deployment.id().toString().substring(0, 8); + } + @Async("deploymentTaskExecutor") public void executeAsync(Deployment deployment) { long deployStart = System.currentTimeMillis(); @@ -96,6 +103,7 @@ public class DeploymentExecutor { App app = appService.getById(deployment.appId()); Environment env = envService.getById(deployment.environmentId()); String jarPath = appService.resolveJarPath(deployment.appVersionId()); + String generation = generationOf(deployment); var globalDefaults = new ConfigMerger.GlobalRuntimeDefaults( parseMemoryLimitMb(globalMemoryLimit), @@ -192,11 +200,11 @@ public class DeploymentExecutor { List newContainerIds = new ArrayList<>(); for (int i = 0; i < config.replicas(); i++) { - String instanceId = env.slug() + "-" + app.slug() + "-" + i; + String instanceId = env.slug() + "-" + app.slug() + "-" + i + "-" + generation; String containerName = tenantId + "-" + instanceId; - // Per-replica labels (include replica index and instance-id) - Map labels = TraefikLabelBuilder.build(app.slug(), env.slug(), tenantId, config, i); + // Per-replica labels (include replica index, generation, and instance-id) + Map labels = TraefikLabelBuilder.build(app.slug(), env.slug(), tenantId, config, i, generation); labels.putAll(prometheusLabels); // Per-replica env vars (set agent instance ID to match container log identity) diff --git a/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/TraefikLabelBuilder.java b/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/TraefikLabelBuilder.java index df223813..6b1ae2c0 100644 --- a/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/TraefikLabelBuilder.java +++ b/cameleer-server-app/src/main/java/com/cameleer/server/app/runtime/TraefikLabelBuilder.java @@ -10,9 +10,13 @@ public final class TraefikLabelBuilder { private TraefikLabelBuilder() {} public static Map build(String appSlug, String envSlug, String tenantId, - ResolvedContainerConfig config, int replicaIndex) { + ResolvedContainerConfig config, int replicaIndex, + String generation) { + // Traefik router/service keys stay generation-agnostic so load balancing + // spans old + new replicas during a blue/green overlap. instance-id and + // the new generation label carry the per-deploy identity. String svc = envSlug + "-" + appSlug; - String instanceId = envSlug + "-" + appSlug + "-" + replicaIndex; + String instanceId = envSlug + "-" + appSlug + "-" + replicaIndex + "-" + generation; Map labels = new LinkedHashMap<>(); labels.put("traefik.enable", "true"); @@ -21,6 +25,7 @@ public final class TraefikLabelBuilder { labels.put("cameleer.app", appSlug); labels.put("cameleer.environment", envSlug); labels.put("cameleer.replica", String.valueOf(replicaIndex)); + labels.put("cameleer.generation", generation); labels.put("cameleer.instance-id", instanceId); labels.put("traefik.http.services." + svc + ".loadbalancer.server.port",