A comprehensive Docker client library for Java applications providing programmatic Docker API access with container lifecycle management, image operations, and Docker Swarm support.
—
This document covers comprehensive Docker Swarm orchestration including swarm management, services, tasks, nodes, secrets, configs, and production deployment patterns.
import com.spotify.docker.client.DockerClient;
import com.spotify.docker.client.messages.swarm.*;
// Initialize new swarm cluster
SwarmInit swarmInit = SwarmInit.builder()
.listenAddr("0.0.0.0:2377")
.advertiseAddr("192.168.1.100:2377")
.build();
String nodeId = docker.initSwarm(swarmInit);
System.out.println("Swarm initialized. Node ID: " + nodeId);// Initialize swarm with custom specification
SwarmSpec swarmSpec = SwarmSpec.builder()
.name("production-swarm")
.labels(Map.of(
"environment", "production",
"datacenter", "us-east-1"
))
// Orchestration settings
.orchestration(OrchestrationConfig.builder()
.taskHistoryRetentionLimit(10)
.build())
// Raft consensus configuration
.raft(RaftConfig.builder()
.snapshotInterval(10000L)
.keepOldSnapshots(5L)
.logEntriesForSlowFollowers(500L)
.electionTick(10L)
.heartbeatTick(1L)
.build())
// Dispatcher configuration
.dispatcher(DispatcherConfig.builder()
.heartbeatPeriod(Duration.ofSeconds(5))
.build())
// Certificate authority configuration
.caConfig(CaConfig.builder()
.nodeCertExpiry(Duration.ofDays(90))
.build())
// Encryption configuration
.encryptionConfig(EncryptionConfig.builder()
.autoLockManagers(true)
.build())
.build();
SwarmInit advancedInit = SwarmInit.builder()
.listenAddr("0.0.0.0:2377")
.advertiseAddr("192.168.1.100:2377")
.forceNewCluster(false)
.spec(swarmSpec)
.build();
String nodeId = docker.initSwarm(advancedInit);// Get swarm information
Swarm swarm = docker.inspectSwarm();
System.out.println("Swarm ID: " + swarm.id());
System.out.println("Created At: " + swarm.createdAt());
System.out.println("Updated At: " + swarm.updatedAt());
// Swarm specification
SwarmSpec spec = swarm.spec();
System.out.println("Swarm Name: " + spec.name());
System.out.println("Labels: " + spec.labels());
// Join tokens for workers and managers
JoinTokens joinTokens = swarm.joinTokens();
System.out.println("Worker Join Token: " + joinTokens.worker());
System.out.println("Manager Join Token: " + joinTokens.manager());
// Remote managers
List<RemoteManager> managers = swarm.remoteManagers();
for (RemoteManager manager : managers) {
System.out.println("Manager: " + manager.nodeId() + " @ " + manager.addr());
}// Join swarm as worker
SwarmJoin workerJoin = SwarmJoin.builder()
.listenAddr("0.0.0.0:2377")
.advertiseAddr("192.168.1.101:2377")
.remoteAddrs("192.168.1.100:2377")
.joinToken("worker-token-here")
.build();
docker.joinSwarm(workerJoin);
// Join swarm as manager
SwarmJoin managerJoin = SwarmJoin.builder()
.listenAddr("0.0.0.0:2377")
.advertiseAddr("192.168.1.102:2377")
.remoteAddrs("192.168.1.100:2377")
.joinToken("manager-token-here")
.build();
docker.joinSwarm(managerJoin);
// Leave swarm
docker.leaveSwarm();
// Force leave (for managers)
docker.leaveSwarm(true);// Update swarm configuration
Swarm currentSwarm = docker.inspectSwarm();
Long version = currentSwarm.version().index();
SwarmSpec updatedSpec = currentSwarm.spec().toBuilder()
.name("updated-production-swarm")
.labels(Map.of(
"environment", "production",
"region", "us-east",
"updated", "true"
))
.build();
// Update swarm
docker.updateSwarm(version, updatedSpec);
// Update with token rotation
docker.updateSwarm(version, true, false, updatedSpec); // Rotate worker token
docker.updateSwarm(version, false, true, updatedSpec); // Rotate manager token// Get unlock key (for auto-lock enabled swarms)
UnlockKey unlockKey = docker.unlockKey();
System.out.println("Unlock Key: " + unlockKey.unlockKey());
// Unlock swarm
docker.unlock(unlockKey);import com.spotify.docker.client.messages.swarm.*;
// Basic service creation
ServiceSpec serviceSpec = ServiceSpec.builder()
.name("web-service")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("nginx:latest")
.build())
.build())
.build();
ServiceCreateResponse response = docker.createService(serviceSpec);
System.out.println("Created service ID: " + response.id());// Comprehensive service configuration
ServiceSpec advancedService = ServiceSpec.builder()
.name("production-api")
.labels(Map.of(
"environment", "production",
"tier", "backend",
"version", "1.0.0"
))
// Task template
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("myapp:latest")
.hostname("api-server")
.env("ENV=production", "PORT=8080")
.args("--config", "/app/config.yml")
.user("appuser")
.dir("/app")
// Resource limits
.resources(ResourceRequirements.builder()
.limits(Resources.builder()
.memoryBytes(512L * 1024 * 1024) // 512MB
.nanoCpus(500_000_000L) // 0.5 CPU
.build())
.reservations(Resources.builder()
.memoryBytes(256L * 1024 * 1024) // 256MB reserved
.nanoCpus(250_000_000L) // 0.25 CPU reserved
.build())
.build())
// Health check
.healthcheck(HealthConfig.builder()
.test("CMD curl -f http://localhost:8080/health || exit 1")
.interval(Duration.ofSeconds(30))
.timeout(Duration.ofSeconds(10))
.retries(3)
.startPeriod(Duration.ofSeconds(60))
.build())
// Secrets and configs
.secrets(SecretBind.builder()
.secretId("app-secret-id")
.secretName("database-password")
.file(SecretFile.builder()
.name("db-password")
.uid(1000L)
.gid(1000L)
.mode(0400L)
.build())
.build())
.configs(ConfigBind.builder()
.configId("app-config-id")
.configName("application-config")
.file(ConfigFile.builder()
.name("app-config.yml")
.uid(1000L)
.gid(1000L)
.mode(0644L)
.build())
.build())
.build())
// Placement constraints
.placement(Placement.builder()
.constraints("node.role==worker", "engine.labels.type==compute")
.preferences(PlacementPreference.builder()
.spread(SpreadDescriptor.builder()
.spreadDescriptor("node.labels.zone")
.build())
.build())
.maxReplicas(2L) // Max replicas per node
.build())
// Restart policy
.restartPolicy(RestartPolicy.builder()
.condition("on-failure")
.delay(Duration.ofSeconds(5))
.maxAttempts(3L)
.window(Duration.ofSeconds(120))
.build())
// Networks
.networks(NetworkAttachmentConfig.builder()
.target("production-network")
.aliases("api", "backend")
.build())
.build())
// Service mode (replicated vs global)
.mode(ServiceMode.builder()
.replicated(ReplicatedService.builder()
.replicas(3L)
.build())
.build())
// Update configuration
.updateConfig(UpdateConfig.builder()
.parallelism(1L)
.delay(Duration.ofSeconds(10))
.failureAction("rollback")
.monitor(Duration.ofSeconds(60))
.maxFailureRatio(0.1f)
.order("start-first")
.build())
// Rollback configuration
.rollbackConfig(UpdateConfig.builder()
.parallelism(1L)
.delay(Duration.ofSeconds(5))
.failureAction("pause")
.monitor(Duration.ofSeconds(30))
.maxFailureRatio(0.0f)
.build())
// Endpoint specification (for published ports)
.endpointSpec(EndpointSpec.builder()
.mode("vip")
.ports(PortConfig.builder()
.name("http")
.protocol("tcp")
.targetPort(8080L)
.publishedPort(80L)
.publishMode("ingress")
.build())
.build())
.build();
ServiceCreateResponse response = docker.createService(advancedService);// Create service that requires registry authentication
RegistryAuth auth = RegistryAuth.builder()
.username("myuser")
.password("mypass")
.serverAddress("https://my-registry.com")
.build();
ServiceSpec serviceSpec = ServiceSpec.builder()
.name("private-service")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("my-registry.com/myapp:latest")
.build())
.build())
.build();
ServiceCreateResponse response = docker.createService(serviceSpec, auth);// Inspect service
Service service = docker.inspectService("service-name-or-id");
System.out.println("Service ID: " + service.id());
System.out.println("Version: " + service.version().index());
System.out.println("Created: " + service.createdAt());
System.out.println("Updated: " + service.updatedAt());
// Service endpoint
Endpoint endpoint = service.endpoint();
if (endpoint != null) {
System.out.println("Endpoint Mode: " + endpoint.spec().mode());
for (PortConfig port : endpoint.spec().ports()) {
System.out.println("Port: " + port.publishedPort() + " -> " + port.targetPort());
}
// Virtual IPs
for (EndpointVirtualIp vip : endpoint.virtualIps()) {
System.out.println("VIP: " + vip.addr() + " (Network: " + vip.networkId() + ")");
}
}
// Update service
ServiceSpec updatedSpec = service.spec().toBuilder()
.taskTemplate(service.spec().taskTemplate().toBuilder()
.containerSpec(service.spec().taskTemplate().containerSpec().toBuilder()
.image("myapp:v2.0")
.env("VERSION=2.0", "UPDATED=true")
.build())
.build())
.build();
docker.updateService(service.id(), service.version().index(), updatedSpec);// List all services
List<Service> allServices = docker.listServices();
// List with criteria
Service.Criteria criteria = Service.Criteria.builder()
.serviceName("web-service")
.serviceId("service-id")
.serviceLabel("environment", "production")
.build();
List<Service> filteredServices = docker.listServices(criteria);
for (Service service : filteredServices) {
System.out.println("Service: " + service.spec().name());
System.out.println("Replicas: " + service.spec().mode().replicated().replicas());
System.out.println("Image: " + service.spec().taskTemplate().containerSpec().image());
}// Remove service
docker.removeService("service-name-or-id");
// Safe service removal with scaling down first
public void safeRemoveService(DockerClient docker, String serviceId)
throws DockerException, InterruptedException {
try {
// First scale service to 0
Service service = docker.inspectService(serviceId);
ServiceSpec scaledSpec = service.spec().toBuilder()
.mode(ServiceMode.builder()
.replicated(ReplicatedService.builder()
.replicas(0L)
.build())
.build())
.build();
docker.updateService(serviceId, service.version().index(), scaledSpec);
System.out.println("Scaled service to 0 replicas");
// Wait for tasks to stop
Thread.sleep(10000);
// Remove service
docker.removeService(serviceId);
System.out.println("Removed service: " + serviceId);
} catch (DockerException e) {
System.err.println("Error removing service: " + e.getMessage());
}
}// Get service logs
try (LogStream logs = docker.serviceLogs("service-name",
DockerClient.LogsParam.stdout(),
DockerClient.LogsParam.stderr(),
DockerClient.LogsParam.follow(),
DockerClient.LogsParam.timestamps(true))) {
logs.forEachRemaining(logMessage -> {
System.out.println("Service Log: " + logMessage.content().toStringUtf8());
});
}// List all tasks
List<Task> allTasks = docker.listTasks();
// List with criteria
Task.Criteria taskCriteria = Task.Criteria.builder()
.serviceName("web-service")
.nodeId("node-id")
.taskName("web-service.1")
.taskState("running")
.build();
List<Task> filteredTasks = docker.listTasks(taskCriteria);
for (Task task : filteredTasks) {
System.out.println("Task ID: " + task.id());
System.out.println("Service ID: " + task.serviceId());
System.out.println("Node ID: " + task.nodeId());
System.out.println("Slot: " + task.slot());
TaskStatus status = task.status();
System.out.println("State: " + status.state());
System.out.println("Message: " + status.message());
if (status.containerStatus() != null) {
System.out.println("Container ID: " + status.containerStatus().containerId());
System.out.println("PID: " + status.containerStatus().pid());
}
}// Inspect specific task
Task task = docker.inspectTask("task-id");
System.out.println("Task Details:");
System.out.println(" ID: " + task.id());
System.out.println(" Version: " + task.version().index());
System.out.println(" Created: " + task.createdAt());
System.out.println(" Updated: " + task.updatedAt());
System.out.println(" Service: " + task.serviceId());
System.out.println(" Node: " + task.nodeId());
System.out.println(" Slot: " + task.slot());
// Task specification
TaskSpec spec = task.spec();
ContainerSpec containerSpec = spec.containerSpec();
System.out.println(" Image: " + containerSpec.image());
System.out.println(" Command: " + containerSpec.command());
System.out.println(" Args: " + containerSpec.args());
// Task status and history
TaskStatus status = task.status();
System.out.println(" Current State: " + status.state());
System.out.println(" Message: " + status.message());
System.out.println(" Timestamp: " + status.timestamp());
// Container status (if running)
if (status.containerStatus() != null) {
ContainerStatus containerStatus = status.containerStatus();
System.out.println(" Container ID: " + containerStatus.containerId());
System.out.println(" PID: " + containerStatus.pid());
System.out.println(" Exit Code: " + containerStatus.exitCode());
}// List all nodes
List<Node> allNodes = docker.listNodes();
// List with criteria
Node.Criteria nodeCriteria = Node.Criteria.builder()
.nodeName("worker-1")
.nodeRole("worker")
.membershipAccepted()
.build();
List<Node> filteredNodes = docker.listNodes(nodeCriteria);
for (Node node : filteredNodes) {
System.out.println("Node ID: " + node.id());
System.out.println("Hostname: " + node.description().hostname());
System.out.println("Role: " + node.spec().role());
System.out.println("Availability: " + node.spec().availability());
System.out.println("State: " + node.status().state());
System.out.println("Address: " + node.status().addr());
// Manager status (if applicable)
ManagerStatus managerStatus = node.managerStatus();
if (managerStatus != null) {
System.out.println("Leader: " + managerStatus.leader());
System.out.println("Reachability: " + managerStatus.reachability());
System.out.println("Address: " + managerStatus.addr());
}
// Node resources
NodeDescription desc = node.description();
Resources resources = desc.resources();
System.out.println("Memory: " + resources.memoryBytes() + " bytes");
System.out.println("CPUs: " + resources.nanoCpus() / 1_000_000_000.0);
}// Inspect specific node (returns NodeInfo which is alias for Node)
NodeInfo nodeInfo = docker.inspectNode("node-id-or-hostname");
System.out.println("Node Information:");
System.out.println(" ID: " + nodeInfo.id());
System.out.println(" Version: " + nodeInfo.version().index());
System.out.println(" Created: " + nodeInfo.createdAt());
System.out.println(" Updated: " + nodeInfo.updatedAt());
// Node specification
NodeSpec spec = nodeInfo.spec();
System.out.println(" Name: " + spec.name());
System.out.println(" Labels: " + spec.labels());
System.out.println(" Role: " + spec.role());
System.out.println(" Availability: " + spec.availability());
// Node description (hardware/software info)
NodeDescription desc = nodeInfo.description();
System.out.println(" Hostname: " + desc.hostname());
Platform platform = desc.platform();
System.out.println(" Architecture: " + platform.architecture());
System.out.println(" OS: " + platform.os());
// Engine info
EngineDescription engine = desc.engine();
System.out.println(" Engine Version: " + engine.engineVersion());
System.out.println(" Plugins: " + engine.plugins());
System.out.println(" Labels: " + engine.labels());
// TLS info
TlsInfo tlsInfo = desc.tlsInfo();
System.out.println(" Certificate Subject: " + tlsInfo.certIssuerSubject());
System.out.println(" Certificate Issuer: " + tlsInfo.certIssuerPublicKey());// Update node configuration
Node node = docker.listNodes().get(0); // Get first node
Long version = node.version().index();
NodeSpec updatedSpec = node.spec().toBuilder()
.availability("drain") // Drain node for maintenance
.labels(Map.of(
"maintenance", "true",
"type", "compute",
"zone", "us-east-1a"
))
.build();
docker.updateNode(node.id(), version, updatedSpec);
// Common node operations
public void drainNode(DockerClient docker, String nodeId)
throws DockerException, InterruptedException {
Node node = docker.inspectNode(nodeId);
NodeSpec drainSpec = node.spec().toBuilder()
.availability("drain")
.build();
docker.updateNode(nodeId, node.version().index(), drainSpec);
System.out.println("Node " + nodeId + " drained");
}
public void activateNode(DockerClient docker, String nodeId)
throws DockerException, InterruptedException {
Node node = docker.inspectNode(nodeId);
NodeSpec activeSpec = node.spec().toBuilder()
.availability("active")
.build();
docker.updateNode(nodeId, node.version().index(), activeSpec);
System.out.println("Node " + nodeId + " activated");
}// Remove node from swarm
docker.deleteNode("node-id");
// Force remove (even if node is manager)
docker.deleteNode("node-id", true);
// Safe node removal process
public void safeRemoveNode(DockerClient docker, String nodeId)
throws DockerException, InterruptedException {
try {
// First drain the node
drainNode(docker, nodeId);
// Wait for tasks to migrate
System.out.println("Waiting for tasks to migrate...");
Thread.sleep(30000);
// Verify no tasks are running on the node
List<Task> nodeTasks = docker.listTasks(
Task.Criteria.builder()
.nodeId(nodeId)
.taskState("running")
.build()
);
if (!nodeTasks.isEmpty()) {
System.out.println("Warning: " + nodeTasks.size() + " tasks still running on node");
}
// Remove node
docker.deleteNode(nodeId);
System.out.println("Node " + nodeId + " removed successfully");
} catch (DockerException e) {
System.err.println("Error removing node: " + e.getMessage());
}
}import com.spotify.docker.client.messages.swarm.*;
import java.util.Base64;
// Create secret from string data
String secretData = "my-secret-password";
String encodedData = Base64.getEncoder().encodeToString(secretData.getBytes());
SecretSpec secretSpec = SecretSpec.builder()
.name("database-password")
.data(encodedData)
.labels(Map.of(
"environment", "production",
"type", "password"
))
.build();
SecretCreateResponse response = docker.createSecret(secretSpec);
System.out.println("Created secret ID: " + response.id());// Create secret with external driver
SecretSpec externalSecret = SecretSpec.builder()
.name("external-secret")
.labels(Map.of("provider", "vault"))
.driver(Driver.builder()
.name("vault")
.options(Map.of(
"path", "secret/myapp/db",
"key", "password"
))
.build())
.build();
// Create secret from file
byte[] fileContent = Files.readAllBytes(Paths.get("/path/to/secret/file"));
String fileData = Base64.getEncoder().encodeToString(fileContent);
SecretSpec fileSecret = SecretSpec.builder()
.name("ssl-certificate")
.data(fileData)
.labels(Map.of("type", "certificate"))
.build();
docker.createSecret(externalSecret);
docker.createSecret(fileSecret);// List all secrets
List<Secret> secrets = docker.listSecrets();
for (Secret secret : secrets) {
System.out.println("Secret ID: " + secret.id());
System.out.println("Name: " + secret.spec().name());
System.out.println("Created: " + secret.createdAt());
System.out.println("Updated: " + secret.updatedAt());
System.out.println("Labels: " + secret.spec().labels());
// Driver information
Driver driver = secret.spec().driver();
if (driver != null) {
System.out.println("Driver: " + driver.name());
System.out.println("Driver Options: " + driver.options());
}
}
// Inspect specific secret
Secret secret = docker.inspectSecret("secret-name-or-id");
System.out.println("Secret Details:");
System.out.println(" ID: " + secret.id());
System.out.println(" Version: " + secret.version().index());
System.out.println(" Name: " + secret.spec().name());
// Note: Secret data is not returned for security reasons// Service with secret bindings
ServiceSpec serviceWithSecrets = ServiceSpec.builder()
.name("secure-web-service")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("nginx:latest")
// Bind secrets as files
.secrets(
SecretBind.builder()
.secretId("database-password-id")
.secretName("database-password")
.file(SecretFile.builder()
.name("db_password")
.uid(33L) // www-data user
.gid(33L) // www-data group
.mode(0400L) // Read-only for owner
.build())
.build(),
SecretBind.builder()
.secretId("ssl-cert-id")
.secretName("ssl-certificate")
.file(SecretFile.builder()
.name("server.crt")
.uid(0L) // root
.gid(0L) // root
.mode(0444L) // Read-only for all
.build())
.build()
)
.build())
.build())
.build();
docker.createService(serviceWithSecrets);// Remove secret
docker.deleteSecret("secret-name-or-id");
// Safe secret removal (check usage first)
public void safeRemoveSecret(DockerClient docker, String secretId)
throws DockerException, InterruptedException {
try {
// Check which services use this secret
List<Service> services = docker.listServices();
List<String> usingServices = new ArrayList<>();
for (Service service : services) {
TaskSpec taskSpec = service.spec().taskTemplate();
if (taskSpec.containerSpec().secrets() != null) {
for (SecretBind secret : taskSpec.containerSpec().secrets()) {
if (secretId.equals(secret.secretId()) || secretId.equals(secret.secretName())) {
usingServices.add(service.spec().name());
break;
}
}
}
}
if (usingServices.isEmpty()) {
docker.deleteSecret(secretId);
System.out.println("Removed secret: " + secretId);
} else {
System.out.println("Secret is in use by services: " + usingServices);
}
} catch (DockerException e) {
System.err.println("Error removing secret: " + e.getMessage());
}
}import com.spotify.docker.client.messages.swarm.*;
// Create config from string data
String configData = "server {\n listen 80;\n server_name example.com;\n}";
String encodedConfig = Base64.getEncoder().encodeToString(configData.getBytes());
ConfigSpec configSpec = ConfigSpec.builder()
.name("nginx-config")
.data(encodedConfig)
.labels(Map.of(
"application", "nginx",
"version", "1.0"
))
.build();
ConfigCreateResponse response = docker.createConfig(configSpec);
System.out.println("Created config ID: " + response.id());// Create config from template file
String templateData = """
upstream backend {
server ${BACKEND_HOST}:${BACKEND_PORT};
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
""";
ConfigSpec templateConfig = ConfigSpec.builder()
.name("nginx-template")
.data(Base64.getEncoder().encodeToString(templateData.getBytes()))
.labels(Map.of(
"type", "template",
"service", "nginx"
))
.build();
docker.createConfig(templateConfig);// List all configs
List<Config> configs = docker.listConfigs();
// List with criteria
Config.Criteria configCriteria = Config.Criteria.builder()
.configName("nginx-config")
.configLabel("application", "nginx")
.build();
List<Config> filteredConfigs = docker.listConfigs(configCriteria);
for (Config config : filteredConfigs) {
System.out.println("Config ID: " + config.id());
System.out.println("Name: " + config.spec().name());
System.out.println("Created: " + config.createdAt());
System.out.println("Labels: " + config.spec().labels());
}
// Inspect specific config
Config config = docker.inspectConfig("config-name-or-id");
System.out.println("Config Details:");
System.out.println(" ID: " + config.id());
System.out.println(" Version: " + config.version().index());
System.out.println(" Name: " + config.spec().name());
// Config data is available (unlike secrets)
String data = new String(Base64.getDecoder().decode(config.spec().data()));
System.out.println(" Data: " + data);// Service with config bindings
ServiceSpec serviceWithConfigs = ServiceSpec.builder()
.name("configured-nginx")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("nginx:latest")
// Bind configs as files
.configs(
ConfigBind.builder()
.configId("nginx-config-id")
.configName("nginx-config")
.file(ConfigFile.builder()
.name("nginx.conf")
.uid(0L)
.gid(0L)
.mode(0644L)
.build())
.build(),
ConfigBind.builder()
.configId("ssl-config-id")
.configName("ssl-config")
.file(ConfigFile.builder()
.name("ssl.conf")
.uid(33L) // www-data
.gid(33L)
.mode(0644L)
.build())
.build()
)
.build())
.build())
.build();
docker.createService(serviceWithConfigs);// Update config (creates new version)
Config existingConfig = docker.inspectConfig("nginx-config");
String newConfigData = "server {\n listen 443 ssl;\n server_name example.com;\n}";
ConfigSpec updatedSpec = existingConfig.spec().toBuilder()
.data(Base64.getEncoder().encodeToString(newConfigData.getBytes()))
.labels(Map.of(
"application", "nginx",
"version", "2.0",
"ssl", "enabled"
))
.build();
docker.updateConfig(existingConfig.id(), existingConfig.version().index(), updatedSpec);// Remove config
docker.deleteConfig("config-name-or-id");
// Bulk config cleanup
public void cleanupOldConfigs(DockerClient docker, String namePrefix)
throws DockerException, InterruptedException {
List<Config> configs = docker.listConfigs();
for (Config config : configs) {
String configName = config.spec().name();
if (configName.startsWith(namePrefix)) {
// Check if config is in use
boolean inUse = isConfigInUse(docker, config.id());
if (!inUse) {
try {
docker.deleteConfig(config.id());
System.out.println("Removed unused config: " + configName);
} catch (DockerException e) {
System.err.println("Failed to remove config " + configName + ": " + e.getMessage());
}
} else {
System.out.println("Config in use, skipping: " + configName);
}
}
}
}
private boolean isConfigInUse(DockerClient docker, String configId)
throws DockerException, InterruptedException {
List<Service> services = docker.listServices();
for (Service service : services) {
TaskSpec taskSpec = service.spec().taskTemplate();
if (taskSpec.containerSpec().configs() != null) {
for (ConfigBind config : taskSpec.containerSpec().configs()) {
if (configId.equals(config.configId()) || configId.equals(config.configName())) {
return true;
}
}
}
}
return false;
}public class SwarmDeploymentExample {
public void deployCompleteApplication(DockerClient docker)
throws DockerException, InterruptedException {
System.out.println("Deploying complete multi-service application to swarm...");
// 1. Initialize swarm if needed
try {
docker.inspectSwarm();
System.out.println("Swarm already initialized");
} catch (DockerException e) {
initializeSwarm(docker);
}
// 2. Create application network
createApplicationNetwork(docker);
// 3. Create secrets and configs
createSecretsAndConfigs(docker);
// 4. Deploy database service
deployDatabaseService(docker);
// 5. Deploy application service
deployApplicationService(docker);
// 6. Deploy load balancer
deployLoadBalancer(docker);
// 7. Monitor deployment
monitorDeployment(docker);
System.out.println("Application deployment completed");
}
private void initializeSwarm(DockerClient docker)
throws DockerException, InterruptedException {
SwarmInit init = SwarmInit.builder()
.listenAddr("0.0.0.0:2377")
.build();
String nodeId = docker.initSwarm(init);
System.out.println("Initialized swarm with node: " + nodeId);
}
private void createApplicationNetwork(DockerClient docker)
throws DockerException, InterruptedException {
NetworkConfig networkConfig = NetworkConfig.builder()
.name("app-network")
.driver("overlay")
.attachable(true)
.labels(Map.of("purpose", "application"))
.build();
try {
docker.createNetwork(networkConfig);
System.out.println("Created application network");
} catch (DockerException e) {
System.out.println("Network already exists");
}
}
private void createSecretsAndConfigs(DockerClient docker)
throws DockerException, InterruptedException {
// Database password secret
SecretSpec dbSecret = SecretSpec.builder()
.name("db-password")
.data(Base64.getEncoder().encodeToString("secure-password".getBytes()))
.build();
// Application config
String appConfig = """
database:
host: postgres
port: 5432
name: myapp
server:
port: 8080
""";
ConfigSpec appConfigSpec = ConfigSpec.builder()
.name("app-config")
.data(Base64.getEncoder().encodeToString(appConfig.getBytes()))
.build();
try {
docker.createSecret(dbSecret);
docker.createConfig(appConfigSpec);
System.out.println("Created secrets and configs");
} catch (DockerException e) {
System.out.println("Secrets/configs already exist");
}
}
private void deployDatabaseService(DockerClient docker)
throws DockerException, InterruptedException {
ServiceSpec dbService = ServiceSpec.builder()
.name("postgres")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("postgres:13")
.env("POSTGRES_DB=myapp", "POSTGRES_USER=app")
.secrets(SecretBind.builder()
.secretName("db-password")
.file(SecretFile.builder()
.name("postgres_password")
.uid(999L) // postgres user
.gid(999L)
.mode(0400L)
.build())
.build())
.mounts(Mount.builder()
.type("volume")
.source("postgres-data")
.target("/var/lib/postgresql/data")
.build())
.build())
.networks(NetworkAttachmentConfig.builder()
.target("app-network")
.aliases("database")
.build())
.placement(Placement.builder()
.constraints("node.role==manager") // Database on manager for persistence
.build())
.build())
.mode(ServiceMode.builder()
.replicated(ReplicatedService.builder()
.replicas(1L)
.build())
.build())
.build();
docker.createService(dbService);
System.out.println("Deployed database service");
}
private void deployApplicationService(DockerClient docker)
throws DockerException, InterruptedException {
ServiceSpec appService = ServiceSpec.builder()
.name("webapp")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("myapp:latest")
.configs(ConfigBind.builder()
.configName("app-config")
.file(ConfigFile.builder()
.name("config.yml")
.uid(1000L)
.gid(1000L)
.mode(0644L)
.build())
.build())
.secrets(SecretBind.builder()
.secretName("db-password")
.file(SecretFile.builder()
.name("db_password")
.uid(1000L)
.gid(1000L)
.mode(0400L)
.build())
.build())
.healthcheck(HealthConfig.builder()
.test("CMD curl -f http://localhost:8080/health")
.interval(Duration.ofSeconds(30))
.timeout(Duration.ofSeconds(10))
.retries(3)
.build())
.build())
.networks(NetworkAttachmentConfig.builder()
.target("app-network")
.aliases("backend")
.build())
.placement(Placement.builder()
.constraints("node.role==worker")
.build())
.resources(ResourceRequirements.builder()
.limits(Resources.builder()
.memoryBytes(512L * 1024 * 1024)
.nanoCpus(500_000_000L)
.build())
.build())
.build())
.mode(ServiceMode.builder()
.replicated(ReplicatedService.builder()
.replicas(3L)
.build())
.build())
.updateConfig(UpdateConfig.builder()
.parallelism(1L)
.delay(Duration.ofSeconds(10))
.failureAction("rollback")
.build())
.build();
docker.createService(appService);
System.out.println("Deployed application service");
}
private void deployLoadBalancer(DockerClient docker)
throws DockerException, InterruptedException {
ServiceSpec lbService = ServiceSpec.builder()
.name("nginx-lb")
.taskTemplate(TaskSpec.builder()
.containerSpec(ContainerSpec.builder()
.image("nginx:latest")
.build())
.networks(NetworkAttachmentConfig.builder()
.target("app-network")
.build())
.build())
.mode(ServiceMode.builder()
.replicated(ReplicatedService.builder()
.replicas(2L)
.build())
.build())
.endpointSpec(EndpointSpec.builder()
.ports(PortConfig.builder()
.targetPort(80L)
.publishedPort(80L)
.protocol("tcp")
.publishMode("ingress")
.build())
.build())
.build();
docker.createService(lbService);
System.out.println("Deployed load balancer service");
}
private void monitorDeployment(DockerClient docker)
throws DockerException, InterruptedException {
System.out.println("Monitoring deployment status...");
String[] services = {"postgres", "webapp", "nginx-lb"};
for (int i = 0; i < 30; i++) { // Wait up to 5 minutes
boolean allReady = true;
for (String serviceName : services) {
Service service = docker.inspectService(serviceName);
List<Task> tasks = docker.listTasks(Task.Criteria.builder()
.serviceName(serviceName)
.taskState("running")
.build());
Long replicas = service.spec().mode().replicated().replicas();
System.out.printf("Service %s: %d/%d replicas running%n",
serviceName, tasks.size(), replicas);
if (tasks.size() < replicas) {
allReady = false;
}
}
if (allReady) {
System.out.println("All services are ready!");
return;
}
Thread.sleep(10000); // Wait 10 seconds
}
System.out.println("Deployment monitoring timeout");
}
}This comprehensive Docker Swarm system provides full orchestration capabilities with service management, security through secrets and configs, node management, and production deployment patterns for scalable containerized applications.
Install with Tessl CLI
npx tessl i tessl/maven-com-spotify--docker-client