chore(runtime): point shipped image defaults to registry.cameleer.io
Customers running this server with no overrides reach the public registry alias, not the internal hostname. registry.cameleer.io and gitea.siegeln.net resolve to the same registry — buildtime CI keeps pushing to gitea.siegeln.net, runtime defaults pull via the public alias. - application.yml: baseimage, loaderimage defaults - DeploymentExecutor.java: matching @Value defaults - docker-orchestration.md: updates the documented default and notes the buildtime/public split so future changes don't "fix" the asymmetry Out of scope (intentionally still on gitea.siegeln.net): - LoaderHardeningIT and the two DockerRuntimeOrchestrator unit tests. Tests are buildtime artifacts; LoaderHardeningIT pulls the real image via CI's pre-authenticated docker login to gitea.siegeln.net. - deploy/base/*.yaml and deploy/overlays/main/*.yaml (internal k3s, customers don't use these manifests). - pom.xml, .npmrc, ui/Dockerfile (build dependency sources). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -41,7 +41,7 @@ When deployed via the cameleer-saas platform, this server orchestrates customer
|
||||
`startContainer` is now a two-phase op per replica:
|
||||
|
||||
1. **Volume create** — `cameleer-jars-{containerName}` named volume (per-replica, deterministic so cleanup in `removeContainer` can derive it).
|
||||
2. **Loader container** — `loaderImage` (default `gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest`, **built and published by the cameleer-saas repo** at `docker/runtime-loader/`), name `{containerName}-loader`, mount the volume **RW at `/app/jars`**, env vars `ARTIFACT_URL` + `ARTIFACT_EXPECTED_SIZE`. Loader downloads the JAR from the signed URL into the volume and exits 0. Orchestrator blocks on `waitContainerCmd().exec(WaitContainerResultCallback).awaitStatusCode(120, SECONDS)`. Loader container is removed in a `finally` block; on non-zero exit the volume is also removed and `RuntimeException` propagates so `DeploymentExecutor` marks the deployment FAILED. **Loader logs are captured before removal** (`captureLoaderLogs` — `logContainerCmd` with `withTail(50)`, capped at 4096 chars, 5s timeout) and appended to the thrown `RuntimeException` message as `". loader output: <text>"`. Best-effort: log-capture failures are swallowed and don't mask the original exit. The loader image's Dockerfile pre-creates `/app/jars` owned by `loader:loader` (UID 1000) so the orchestrator's fresh named volume initialises with that ownership — without it the empty volume comes up as `root:root 0755` and wget exits 1 with "Permission denied". `LoaderHardeningIT` is the cross-repo contract test (pulls the published `:latest` and asserts exit 0 under the orchestrator's hardening shape).
|
||||
2. **Loader container** — `loaderImage` (default `registry.cameleer.io/cameleer/cameleer-runtime-loader:latest`, **built and published by the cameleer-saas repo** at `docker/runtime-loader/`; CI pushes to `gitea.siegeln.net` under the same path — both names resolve to the same registry), name `{containerName}-loader`, mount the volume **RW at `/app/jars`**, env vars `ARTIFACT_URL` + `ARTIFACT_EXPECTED_SIZE`. Loader downloads the JAR from the signed URL into the volume and exits 0. Orchestrator blocks on `waitContainerCmd().exec(WaitContainerResultCallback).awaitStatusCode(120, SECONDS)`. Loader container is removed in a `finally` block; on non-zero exit the volume is also removed and `RuntimeException` propagates so `DeploymentExecutor` marks the deployment FAILED. **Loader logs are captured before removal** (`captureLoaderLogs` — `logContainerCmd` with `withTail(50)`, capped at 4096 chars, 5s timeout) and appended to the thrown `RuntimeException` message as `". loader output: <text>"`. Best-effort: log-capture failures are swallowed and don't mask the original exit. The loader image's Dockerfile pre-creates `/app/jars` owned by `loader:loader` (UID 1000) so the orchestrator's fresh named volume initialises with that ownership — without it the empty volume comes up as `root:root 0755` and wget exits 1 with "Permission denied". `LoaderHardeningIT` is the cross-repo contract test (pulls the published `:latest` and asserts exit 0 under the orchestrator's hardening shape).
|
||||
3. **Main container** — same hardening contract, mount the same volume **RO at `/app/jars`**, entrypoint reads `/app/jars/app.jar` (Spring Boot/Quarkus: `-jar /app/jars/app.jar`; plain Java: `-cp /app/jars/app.jar <MainClass>`; native: `exec /app/jars/app.jar`).
|
||||
|
||||
`removeContainer(id)` derives the volume name from the inspected container name (Docker prefixes it with `/`) and removes the volume after the container removes — blue/green doesn't leak volumes.
|
||||
|
||||
@@ -36,7 +36,7 @@ public class DeploymentExecutor {
|
||||
@Autowired(required = false)
|
||||
private DockerNetworkManager networkManager;
|
||||
|
||||
@Value("${cameleer.server.runtime.baseimage:gitea.siegeln.net/cameleer/cameleer-runtime-base:latest}")
|
||||
@Value("${cameleer.server.runtime.baseimage:registry.cameleer.io/cameleer/cameleer-runtime-base:latest}")
|
||||
private String baseImage;
|
||||
|
||||
@Value("${cameleer.server.runtime.dockernetwork:cameleer}")
|
||||
@@ -69,7 +69,7 @@ public class DeploymentExecutor {
|
||||
@Value("${cameleer.server.runtime.certresolver:}")
|
||||
private String globalCertResolver;
|
||||
|
||||
@Value("${cameleer.server.runtime.loaderimage:gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest}")
|
||||
@Value("${cameleer.server.runtime.loaderimage:registry.cameleer.io/cameleer/cameleer-runtime-loader:latest}")
|
||||
private String loaderImage;
|
||||
|
||||
@Value("${cameleer.server.runtime.artifacttokenttlseconds:600}")
|
||||
|
||||
@@ -45,7 +45,7 @@ cameleer:
|
||||
runtime:
|
||||
enabled: ${CAMELEER_SERVER_RUNTIME_ENABLED:true}
|
||||
jarstoragepath: ${CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH:/data/jars}
|
||||
baseimage: ${CAMELEER_SERVER_RUNTIME_BASEIMAGE:gitea.siegeln.net/cameleer/cameleer-runtime-base:latest}
|
||||
baseimage: ${CAMELEER_SERVER_RUNTIME_BASEIMAGE:registry.cameleer.io/cameleer/cameleer-runtime-base:latest}
|
||||
dockernetwork: ${CAMELEER_SERVER_RUNTIME_DOCKERNETWORK:cameleer}
|
||||
# Container runtime override. Empty (default) auto-detects: uses runsc
|
||||
# (gVisor) if the daemon has it registered, otherwise the daemon default
|
||||
@@ -65,7 +65,7 @@ cameleer:
|
||||
# short-lived sidecar that downloads the JAR from a signed URL into a
|
||||
# per-replica named volume, which the main container then mounts RO at
|
||||
# /app/jars. See issue #152 close-out + .claude/rules/docker-orchestration.md.
|
||||
loaderimage: ${CAMELEER_SERVER_RUNTIME_LOADERIMAGE:gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest}
|
||||
loaderimage: ${CAMELEER_SERVER_RUNTIME_LOADERIMAGE:registry.cameleer.io/cameleer/cameleer-runtime-loader:latest}
|
||||
artifacttokenttlseconds: ${CAMELEER_SERVER_RUNTIME_ARTIFACTTOKENTTLSECONDS:600}
|
||||
artifactbaseurl: ${CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL:}
|
||||
indexer:
|
||||
|
||||
Reference in New Issue
Block a user