diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DeploymentExecutor.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DeploymentExecutor.java index 8481e5a6..d11c3b99 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DeploymentExecutor.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DeploymentExecutor.java @@ -18,6 +18,7 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Optional; @@ -99,11 +100,17 @@ public class DeploymentExecutor { baseImage, jarPath, dockerNetwork, + List.of(), envVars, labels, parseMemoryLimitBytes(containerMemoryLimit), + null, containerCpuShares, - agentHealthPort); + null, + List.of(), + agentHealthPort, + "on-failure", + 3); String containerId = orchestrator.startContainer(request); waitForHealthy(containerId, healthCheckTimeout); diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DockerRuntimeOrchestrator.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DockerRuntimeOrchestrator.java index d61bd217..0c28642a 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DockerRuntimeOrchestrator.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/runtime/DockerRuntimeOrchestrator.java @@ -10,6 +10,7 @@ import com.github.dockerjava.api.model.Bind; import com.github.dockerjava.api.model.Frame; import com.github.dockerjava.api.model.HealthCheck; import com.github.dockerjava.api.model.HostConfig; +import com.github.dockerjava.api.model.RestartPolicy; import com.github.dockerjava.api.model.Volume; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; @@ -59,7 +60,6 @@ public class DockerRuntimeOrchestrator implements RuntimeOrchestrator { List envList = request.envVars().entrySet().stream() .map(e -> e.getKey() + "=" + e.getValue()).toList(); - // Volume bind: mount JAR into container Bind jarBind = new Bind(request.jarPath(), new Volume("/app/app.jar"), AccessMode.ro); HostConfig hostConfig = HostConfig.newHostConfig() @@ -67,9 +67,17 @@ public class DockerRuntimeOrchestrator implements RuntimeOrchestrator { .withMemorySwap(request.memoryLimitBytes()) .withCpuShares(request.cpuShares()) .withNetworkMode(request.network()) - .withBinds(jarBind); + .withBinds(jarBind) + .withRestartPolicy(RestartPolicy.onFailureRestart(request.restartPolicyMaxRetries())); - var container = dockerClient.createContainerCmd(request.baseImage()) + if (request.memoryReserveBytes() != null) { + hostConfig.withMemoryReservation(request.memoryReserveBytes()); + } + if (request.cpuQuota() != null) { + hostConfig.withCpuQuota(request.cpuQuota()); + } + + var createCmd = dockerClient.createContainerCmd(request.baseImage()) .withName(request.containerName()) .withEnv(envList) .withLabels(request.labels() != null ? request.labels() : Map.of()) @@ -77,17 +85,38 @@ public class DockerRuntimeOrchestrator implements RuntimeOrchestrator { .withHealthcheck(new HealthCheck() .withTest(List.of("CMD-SHELL", "wget -qO- http://localhost:" + request.healthCheckPort() + "/cameleer/health || exit 1")) - .withInterval(10_000_000_000L) // 10s - .withTimeout(5_000_000_000L) // 5s + .withInterval(10_000_000_000L) + .withTimeout(5_000_000_000L) .withRetries(3) - .withStartPeriod(30_000_000_000L)) // 30s - .exec(); + .withStartPeriod(30_000_000_000L)); + if (request.exposedPorts() != null && !request.exposedPorts().isEmpty()) { + var ports = request.exposedPorts().stream() + .map(p -> com.github.dockerjava.api.model.ExposedPort.tcp(p)) + .toArray(com.github.dockerjava.api.model.ExposedPort[]::new); + createCmd.withExposedPorts(ports); + } + + var container = createCmd.exec(); dockerClient.startContainerCmd(container.getId()).exec(); + + if (request.additionalNetworks() != null) { + for (String net : request.additionalNetworks()) { + dockerClient.connectToNetworkCmd() + .withContainerId(container.getId()) + .withNetworkId(net) + .exec(); + } + } + log.info("Started container {} ({})", request.containerName(), container.getId()); return container.getId(); } + public DockerClient getDockerClient() { + return dockerClient; + } + @Override public void stopContainer(String containerId) { try { diff --git a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/runtime/ContainerRequest.java b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/runtime/ContainerRequest.java index 4e8a6c0e..d29079d0 100644 --- a/cameleer3-server-core/src/main/java/com/cameleer3/server/core/runtime/ContainerRequest.java +++ b/cameleer3-server-core/src/main/java/com/cameleer3/server/core/runtime/ContainerRequest.java @@ -1,5 +1,6 @@ package com.cameleer3.server.core.runtime; +import java.util.List; import java.util.Map; public record ContainerRequest( @@ -7,9 +8,15 @@ public record ContainerRequest( String baseImage, String jarPath, String network, + List additionalNetworks, Map envVars, Map labels, long memoryLimitBytes, + Long memoryReserveBytes, int cpuShares, - int healthCheckPort + Long cpuQuota, + List exposedPorts, + int healthCheckPort, + String restartPolicyName, + int restartPolicyMaxRetries ) {}