docs: correct loader-network reachability claim; refresh HOWTO env vars
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 4m32s
CI / docker (push) Successful in 2m55s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 55s

Final-review must-fixes:
- HOWTO.md: drop CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME; add the three new
  artifact env vars (loaderimage / artifacttokenttlseconds / artifactbaseurl).
- DeploymentExecutor @PostConstruct WARN, handoff doc, and docker-orchestration
  rule no longer claim the loader uses cameleer-traefik. The loader runs on
  the PRIMARY Docker network only — additional networks are attached after
  startContainer returns, by which time the loader has exited. SaaS still
  works because the tenant's primary network hosts the tenant server.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-27 17:13:56 +02:00
parent c970120b9f
commit f772e868e6
4 changed files with 25 additions and 14 deletions

View File

@@ -48,13 +48,15 @@ When deployed via the cameleer-saas platform, this server orchestrates customer
`DeploymentExecutor` generates the signed URL via `ArtifactDownloadTokenSigner.sign(appVersion.id(), Duration.ofSeconds(artifactTokenTtlSeconds))` and passes `appVersion.id()`, the URL, `appVersion.jarSizeBytes()`, and the loader image into `ContainerRequest`. The host filesystem is no longer involved at deploy time.
**Loader → server reachability**: the loader container hits the Cameleer server over HTTP from inside its
own Docker network. The signed URL is built from `cameleer.server.runtime.artifactbaseurl` (preferred), falling
back to `cameleer.server.runtime.serverurl`, falling back to `http://cameleer-server:8081`. The default works
in SaaS mode because `DockerNetworkManager` adds `cameleer-traefik` as an additional network for tenant
containers, and the server is reachable on that network via the `cameleer-server` DNS alias. For non-SaaS
topologies (server on a different network than tenants), set `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL`
explicitly to a URL the loader can reach.
**Loader → server reachability**: the loader hits the Cameleer server from its **primary** Docker
network only (`request.network()`, set from `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK`). Additional networks
(`cameleer-traefik`, per-env) are attached by `DockerNetworkManager.connectContainer` AFTER `startContainer`
returns — by which time the loader has already exited. The loader cannot use them. The signed URL is built
from `cameleer.server.runtime.artifactbaseurl` (preferred), falling back to `cameleer.server.runtime.serverurl`,
falling back to `http://cameleer-server:8081`. The default works in SaaS mode because the tenant's primary
network (`cameleer-tenant-{slug}`) hosts the tenant's own server — same `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK`
on both. For non-SaaS topologies, set `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL` to a URL the loader can reach
on its primary network.
## DeploymentExecutor Details

View File

@@ -495,8 +495,10 @@ Key settings in `cameleer-server-app/src/main/resources/application.yml`. All cu
| `cameleer.server.runtime.baseimage` | `cameleer-runtime-base:latest` | `CAMELEER_SERVER_RUNTIME_BASEIMAGE` | Base Docker image for app containers |
| `cameleer.server.runtime.dockernetwork` | `cameleer` | `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK` | Primary Docker network |
| `cameleer.server.runtime.dockerruntime` | *(empty = auto)* | `CAMELEER_SERVER_RUNTIME_DOCKERRUNTIME` | Container runtime override. Empty auto-detects gVisor (`runsc`) when registered with the daemon and falls back to the daemon default. Set to e.g. `kata` to force a specific runtime, or `runc` to force the default even if `runsc` is installed. |
| `cameleer.server.runtime.jarstoragepath` | `/data/jars` | `CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH` | JAR file storage directory |
| `cameleer.server.runtime.jardockervolume` | *(empty)* | `CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME` | Docker volume for JAR sharing |
| `cameleer.server.runtime.jarstoragepath` | `/data/jars` | `CAMELEER_SERVER_RUNTIME_JARSTORAGEPATH` | JAR file storage directory (used by `FilesystemArtifactStore`) |
| `cameleer.server.runtime.loaderimage` | `gitea.siegeln.net/cameleer/cameleer-runtime-loader:latest` | `CAMELEER_SERVER_RUNTIME_LOADERIMAGE` | Init-container image that fetches the JAR via signed URL |
| `cameleer.server.runtime.artifacttokenttlseconds` | `600` | `CAMELEER_SERVER_RUNTIME_ARTIFACTTOKENTTLSECONDS` | TTL (seconds) for HMAC-signed artifact-download URLs |
| `cameleer.server.runtime.artifactbaseurl` | *(empty)* | `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL` | Base URL the loader uses to reach the server. Blank falls back to `serverurl`, then `http://cameleer-server:8081`. Must be reachable from the loader container's primary Docker network. |
| `cameleer.server.runtime.routingmode` | `path` | `CAMELEER_SERVER_RUNTIME_ROUTINGMODE` | `path` or `subdomain` Traefik routing |
| `cameleer.server.runtime.routingdomain` | `localhost` | `CAMELEER_SERVER_RUNTIME_ROUTINGDOMAIN` | Domain for Traefik routing labels |
| `cameleer.server.runtime.serverurl` | *(empty)* | `CAMELEER_SERVER_RUNTIME_SERVERURL` | Server URL injected into app containers |

View File

@@ -111,9 +111,12 @@ public class DeploymentExecutor {
if (artifactBaseUrl.isBlank() && globalServerUrl.isBlank()) {
log.warn("Neither cameleer.server.runtime.artifactbaseurl nor cameleer.server.runtime.serverurl is set. "
+ "Loader containers will fall back to http://cameleer-server:8081 — this requires the loader's "
+ "Docker network to resolve `cameleer-server`. In SaaS mode the server is on `cameleer-traefik` "
+ "which is added as an additional network for tenant containers, so this works. For other "
+ "deployment topologies, set CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL explicitly.");
+ "PRIMARY Docker network (CAMELEER_SERVER_RUNTIME_DOCKERNETWORK) to resolve `cameleer-server`. "
+ "Additional networks (e.g. cameleer-traefik) are attached AFTER startContainer returns, by "
+ "which time the loader has already exited — they are not available to the loader. In SaaS "
+ "mode the tenant primary network (cameleer-tenant-{slug}) hosts the tenant's server, so this "
+ "works. For other topologies, set CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL to a URL the loader "
+ "can reach over the primary network.");
}
}

View File

@@ -90,11 +90,15 @@ New env vars (`application.yml` defaults shown):
Removed: `CAMELEER_SERVER_RUNTIME_JARDOCKERVOLUME` — no longer needed (loader downloads via HTTP, not bind-mount).
`@PostConstruct` WARN logs at server startup if neither `artifactbaseurl` nor `serverurl` is set, pointing at the implicit `cameleer-server` Docker DNS dependency that only works on `cameleer-traefik`.
`@PostConstruct` WARN logs at server startup if neither `artifactbaseurl` nor `serverurl` is set, pointing at the implicit `cameleer-server` Docker DNS dependency on the loader's primary network.
## Network reachability requirement
The loader container must be able to reach the Cameleer server over HTTP. In SaaS mode this works because `DockerNetworkManager` adds `cameleer-traefik` as an additional network for tenant containers, and the server is reachable on that network via the `cameleer-server` DNS alias. For non-SaaS topologies, set `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL` to a URL the loader can reach.
The loader container reaches the server over the **primary** Docker network only — `request.network()` in `ContainerRequest`, set from `CAMELEER_SERVER_RUNTIME_DOCKERNETWORK`. Additional networks (`cameleer-traefik`, per-env, etc.) are attached by `DockerNetworkManager.connectContainer` AFTER `startContainer` returns, by which time the loader has already exited — they are NOT available to the loader.
In SaaS mode this works because the tenant's primary network is `cameleer-tenant-{slug}` and the tenant's own `cameleer-server` instance is configured to run on that same network (`CAMELEER_SERVER_RUNTIME_DOCKERNETWORK=cameleer-tenant-{slug}` on the server's compose/manifest). The loader resolves `cameleer-server` via Docker DNS on the primary network and pulls the artifact directly.
For non-SaaS topologies (e.g. server on a different network from tenant containers), set `CAMELEER_SERVER_RUNTIME_ARTIFACTBASEURL` to a URL the loader can reach over its primary network.
## Documented but skipped