Container Lifecycle Management
The Docker adapter provides comprehensive container lifecycle management, including creation, startup, health monitoring, and cleanup operations. This enables full control over Docker containers programmatically.
Container Creation​
Basic Container Creation​
Create containers with various configurations:
import { DockerAdapter } from '@xec-sh/core';
const docker = new DockerAdapter();
// Create a basic container
await docker.createContainer({
name: 'web-app',
image: 'nginx:alpine',
ports: ['80:80', '443:443']
});
// Create container with environment variables
await docker.createContainer({
name: 'api-server',
image: 'node:18-alpine',
env: {
NODE_ENV: 'production',
PORT: '3000',
DATABASE_URL: 'postgresql://localhost/myapp'
},
volumes: [
'./app:/usr/src/app',
'./logs:/var/log/app'
]
});
Advanced Container Creation​
Configure containers with advanced options:
// Create container with health check
await docker.createContainer({
name: 'health-monitored',
image: 'myapp:latest',
ports: ['8080:8080'],
env: {
HEALTH_CHECK_URL: 'http://localhost:8080/health'
}
});
// Create privileged container
await docker.createContainer({
name: 'system-container',
image: 'ubuntu:22.04',
volumes: ['/proc:/host/proc:ro'],
// Note: Privileged mode handled via runContainer method
});
Container Startup​
Starting Containers​
Start created containers:
// Start a single container
await docker.startContainer('web-app');
// Start multiple containers
const containers = ['web-app', 'api-server', 'database'];
await Promise.all(
containers.map(name => docker.startContainer(name))
);
// Start with error handling
for (const container of containers) {
try {
await docker.startContainer(container);
console.log(`Started: ${container}`);
} catch (error) {
console.error(`Failed to start ${container}:`, error.message);
}
}
Run Containers (Create + Start)​
Create and start containers in one operation:
// Basic run with automatic removal
await docker.runContainer({
name: 'temp-processor',
image: 'alpine:latest',
command: ['sh', '-c', 'echo "Processing..." && sleep 5'],
volumes: ['./data:/data'],
workdir: '/data'
});
// Production service container
await docker.runContainer({
name: 'web-service',
image: 'nginx:alpine',
ports: ['80:80', '443:443'],
volumes: [
'./nginx.conf:/etc/nginx/nginx.conf:ro',
'./ssl:/etc/ssl/certs:ro',
'./logs:/var/log/nginx'
],
network: 'web-network',
restart: 'unless-stopped',
labels: {
'traefik.enable': 'true',
'traefik.http.routers.web.rule': 'Host(`example.com`)'
}
});
Container with Health Checks​
Define health monitoring for containers:
// Container with comprehensive health check
await docker.runContainer({
name: 'monitored-app',
image: 'myapp:latest',
ports: ['3000:3000'],
healthcheck: {
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health'],
interval: '30s',
timeout: '10s',
retries: 3,
startPeriod: '60s'
},
env: {
HEALTH_CHECK_ENDPOINT: '/health'
}
});
// Alternative health check formats
await docker.runContainer({
name: 'http-service',
image: 'webapp:latest',
healthcheck: {
test: 'curl -f http://localhost:8080/ping || exit 1',
interval: '15s',
timeout: '5s',
retries: 2
}
});
Health Monitoring​
Wait for Healthy Status​
Monitor container health before proceeding:
// Start container and wait for healthy status
await docker.runContainer({
name: 'slow-startup',
image: 'complex-app:latest',
healthcheck: {
test: ['CMD', 'health-check-script.sh'],
interval: '10s',
timeout: '5s',
retries: 5,
startPeriod: '30s'
}
});
// Wait for container to be healthy (30 second timeout)
try {
await docker.waitForHealthy('slow-startup', 30000);
console.log('Container is healthy and ready');
} catch (error) {
console.error('Container failed to become healthy:', error.message);
}
// Custom health check loop
const checkHealth = async (container, maxAttempts = 10) => {
for (let i = 0; i < maxAttempts; i++) {
try {
const info = await docker.inspectContainer(container);
const health = info.State?.Health?.Status;
console.log(`Health check ${i + 1}/${maxAttempts}: ${health}`);
if (health === 'healthy') {
return true;
} else if (health === 'unhealthy') {
throw new Error('Container is unhealthy');
}
// Wait before next check
await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
console.error(`Health check failed: ${error.message}`);
}
}
throw new Error('Timeout waiting for container to be healthy');
};
await checkHealth('slow-startup');
Container Inspection​
Get detailed container information:
// Get container details
const info = await docker.inspectContainer('web-app');
console.log('Container info:');
console.log('- State:', info.State.Status);
console.log('- Health:', info.State.Health?.Status);
console.log('- Started:', info.State.StartedAt);
console.log('- Image:', info.Config.Image);
console.log('- Ports:', info.NetworkSettings.Ports);
// Check if container is running
const isRunning = info.State.Running;
const isHealthy = info.State.Health?.Status === 'healthy';
if (isRunning && isHealthy) {
console.log('Container is ready for traffic');
}
// Extract specific information
const extractContainerInfo = async (containerName) => {
const info = await docker.inspectContainer(containerName);
return {
name: info.Name.replace('/', ''),
status: info.State.Status,
health: info.State.Health?.Status,
uptime: Date.now() - new Date(info.State.StartedAt).getTime(),
image: info.Config.Image,
ports: Object.keys(info.NetworkSettings.Ports || {}),
mounts: info.Mounts.map(m => `${m.Source}:${m.Destination}`),
networks: Object.keys(info.NetworkSettings.Networks || {})
};
};
const containerInfo = await extractContainerInfo('web-app');
console.log(JSON.stringify(containerInfo, null, 2));
Container Statistics​
Performance Monitoring​
Monitor container resource usage:
// Get current container stats
const stats = await docker.getStats('web-app');
console.log('Container statistics:');
console.log('- CPU Usage:', stats.cpu_stats.cpu_usage.total_usage);
console.log('- Memory Usage:', stats.memory_stats.usage);
console.log('- Memory Limit:', stats.memory_stats.limit);
console.log('- Network RX:', stats.networks.eth0.rx_bytes);
console.log('- Network TX:', stats.networks.eth0.tx_bytes);
// Calculate usage percentages
const calculateUsage = (stats) => {
const cpuPercent = ((stats.cpu_stats.cpu_usage.total_usage - stats.precpu_stats.cpu_usage.total_usage) /
(stats.cpu_stats.system_cpu_usage - stats.precpu_stats.system_cpu_usage)) *
stats.cpu_stats.online_cpus * 100;
const memoryPercent = (stats.memory_stats.usage / stats.memory_stats.limit) * 100;
return {
cpu: cpuPercent.toFixed(2) + '%',
memory: memoryPercent.toFixed(2) + '%',
memoryUsed: (stats.memory_stats.usage / 1024 / 1024).toFixed(2) + ' MB',
memoryLimit: (stats.memory_stats.limit / 1024 / 1024).toFixed(2) + ' MB'
};
};
const usage = calculateUsage(stats);
console.log('Resource usage:', usage);
Continuous Monitoring​
Monitor container performance over time:
// Monitor container stats continuously
const monitorContainer = async (containerName, duration = 60000) => {
const startTime = Date.now();
const samples = [];
console.log(`Monitoring ${containerName} for ${duration/1000} seconds...`);
const monitor = setInterval(async () => {
try {
const stats = await docker.getStats(containerName);
const usage = calculateUsage(stats);
samples.push({
timestamp: Date.now(),
...usage
});
console.log(`[${new Date().toISOString()}] CPU: ${usage.cpu}, Memory: ${usage.memory}`);
if (Date.now() - startTime >= duration) {
clearInterval(monitor);
// Calculate averages
const avgCpu = samples.reduce((sum, s) => sum + parseFloat(s.cpu), 0) / samples.length;
const avgMemory = samples.reduce((sum, s) => sum + parseFloat(s.memory), 0) / samples.length;
console.log(`\nMonitoring complete. Averages:`);
console.log(`- CPU: ${avgCpu.toFixed(2)}%`);
console.log(`- Memory: ${avgMemory.toFixed(2)}%`);
}
} catch (error) {
clearInterval(monitor);
console.error('Monitoring failed:', error.message);
}
}, 5000);
};
await monitorContainer('web-app', 60000);
Container Stopping​
Graceful Shutdown​
Stop containers gracefully:
// Stop single container
await docker.stopContainer('web-app');
// Stop multiple containers gracefully
const activeContainers = await docker.listContainers();
console.log('Stopping containers:', activeContainers);
for (const container of activeContainers) {
try {
console.log(`Stopping ${container}...`);
await docker.stopContainer(container);
console.log(`Stopped ${container}`);
} catch (error) {
console.error(`Failed to stop ${container}:`, error.message);
}
}
// Stop with custom timeout (Docker default is 10 seconds)
const stopWithTimeout = async (containerName, timeout = 30) => {
console.log(`Stopping ${containerName} with ${timeout}s timeout...`);
// Docker stop command with timeout
try {
await docker.executeDockerCommand(['stop', '-t', timeout.toString(), containerName], {});
console.log(`Gracefully stopped ${containerName}`);
} catch (error) {
console.error(`Failed to stop ${containerName}:`, error.message);
// Force kill if graceful stop fails
try {
await docker.executeDockerCommand(['kill', containerName], {});
console.log(`Force killed ${containerName}`);
} catch (killError) {
console.error(`Failed to kill ${containerName}:`, killError.message);
}
}
};
await stopWithTimeout('slow-shutdown-app', 60);
Container Removal​
Clean Removal​
Remove containers after stopping:
// Remove stopped container
await docker.removeContainer('web-app');
// Force remove running container
await docker.removeContainer('stubborn-container', true);
// Remove multiple containers
const containersToRemove = ['temp-1', 'temp-2', 'temp-3'];
await Promise.all(
containersToRemove.map(async (container) => {
try {
// Stop first, then remove
await docker.stopContainer(container);
await docker.removeContainer(container);
console.log(`Removed ${container}`);
} catch (error) {
if (error.message.includes('No such container')) {
console.log(`Container ${container} already removed`);
} else {
console.error(`Failed to remove ${container}:`, error.message);
}
}
})
);
Cleanup Patterns​
Implement common cleanup patterns:
// Clean up all containers with specific label
const cleanupByLabel = async (label, value) => {
const containers = await docker.listContainers(true); // Include stopped
for (const container of containers) {
try {
const info = await docker.inspectContainer(container);
const labels = info.Config.Labels || {};
if (labels[label] === value) {
console.log(`Cleaning up ${container} (${label}=${value})`);
await docker.stopContainer(container);
await docker.removeContainer(container, true);
}
} catch (error) {
console.error(`Failed to cleanup ${container}:`, error.message);
}
}
};
await cleanupByLabel('app', 'test-suite');
// Clean up containers older than specified time
const cleanupOldContainers = async (maxAge = 24 * 60 * 60 * 1000) => { // 24 hours
const containers = await docker.listContainers(true);
const cutoff = Date.now() - maxAge;
for (const container of containers) {
try {
const info = await docker.inspectContainer(container);
const created = new Date(info.Created).getTime();
if (created < cutoff) {
console.log(`Removing old container: ${container}`);
await docker.removeContainer(container, true);
}
} catch (error) {
console.error(`Failed to remove old container ${container}:`, error.message);
}
}
};
await cleanupOldContainers();
Auto-Creation and Cleanup​
Temporary Container Management​
Handle ephemeral containers automatically:
// Configure auto-creation with cleanup
const docker = new DockerAdapter({
autoCreate: {
enabled: true,
image: 'alpine:latest',
autoRemove: true,
volumes: ['/tmp:/tmp:rw']
}
});
// Temporary containers are automatically created and cleaned up
const result = await docker.execute({
command: 'echo',
args: ['Temporary processing'],
adapterOptions: {
type: 'docker',
container: 'auto-created-temp'
}
});
console.log(result.stdout); // "Temporary processing"
// Container is automatically removed after execution
// Manual cleanup of all temporary containers
await docker.dispose();
Lifecycle Event Monitoring​
Monitor container lifecycle events:
// Set up event listeners
docker.on('docker:run', (event) => {
console.log(`Container started: ${event.container} (${event.image})`);
});
docker.on('docker:exec', (event) => {
console.log(`Command executed in ${event.container}: ${event.command}`);
});
docker.on('temp:cleanup', (event) => {
console.log(`Cleaning up temporary resource: ${event.path}`);
});
// Execute operations with monitoring
await docker.runContainer({
name: 'monitored-service',
image: 'nginx:alpine',
ports: ['80:80']
});
await docker.execute({
command: 'nginx',
args: ['-t'],
adapterOptions: {
type: 'docker',
container: 'monitored-service'
}
});
await docker.stopContainer('monitored-service');
await docker.removeContainer('monitored-service');
Complete Lifecycle Example​
Full Application Deployment​
Complete example showing full container lifecycle:
import { DockerAdapter } from '@xec-sh/core';
class ApplicationDeployment {
constructor() {
this.docker = new DockerAdapter();
this.containers = [];
}
async deploy() {
try {
// 1. Create and start database
await this.deployDatabase();
// 2. Create and start application
await this.deployApplication();
// 3. Wait for health checks
await this.waitForServices();
// 4. Run smoke tests
await this.runSmokeTests();
console.log('Deployment completed successfully');
} catch (error) {
console.error('Deployment failed:', error.message);
await this.cleanup();
throw error;
}
}
async deployDatabase() {
console.log('Deploying database...');
await this.docker.runContainer({
name: 'app-database',
image: 'postgres:15-alpine',
env: {
POSTGRES_DB: 'myapp',
POSTGRES_USER: 'appuser',
POSTGRES_PASSWORD: 'securepassword'
},
volumes: ['db-data:/var/lib/postgresql/data'],
ports: ['5432:5432'],
healthcheck: {
test: ['CMD-SHELL', 'pg_isready -U appuser -d myapp'],
interval: '10s',
timeout: '5s',
retries: 5
}
});
this.containers.push('app-database');
}
async deployApplication() {
console.log('Deploying application...');
await this.docker.runContainer({
name: 'app-server',
image: 'myapp:latest',
env: {
DATABASE_URL: 'postgresql://appuser:securepassword@app-database:5432/myapp',
NODE_ENV: 'production',
PORT: '3000'
},
ports: ['3000:3000'],
volumes: ['./logs:/app/logs'],
healthcheck: {
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health'],
interval: '15s',
timeout: '10s',
retries: 3,
startPeriod: '30s'
}
});
this.containers.push('app-server');
}
async waitForServices() {
console.log('Waiting for services to be healthy...');
for (const container of this.containers) {
await this.docker.waitForHealthy(container, 60000);
console.log(`${container} is healthy`);
}
}
async runSmokeTests() {
console.log('Running smoke tests...');
// Test database connectivity
const dbTest = await this.docker.execute({
command: 'pg_isready',
args: ['-h', 'app-database', '-U', 'appuser'],
adapterOptions: {
type: 'docker',
image: 'postgres:15-alpine',
runMode: 'run'
}
});
if (dbTest.exitCode !== 0) {
throw new Error('Database smoke test failed');
}
// Test application endpoint
const appTest = await this.docker.execute({
command: 'curl',
args: ['-f', 'http://app-server:3000/health'],
adapterOptions: {
type: 'docker',
image: 'curlimages/curl:latest',
runMode: 'run'
}
});
if (appTest.exitCode !== 0) {
throw new Error('Application smoke test failed');
}
console.log('All smoke tests passed');
}
async cleanup() {
console.log('Cleaning up containers...');
for (const container of this.containers.reverse()) {
try {
await this.docker.stopContainer(container);
await this.docker.removeContainer(container, true);
console.log(`Cleaned up ${container}`);
} catch (error) {
console.error(`Failed to cleanup ${container}:`, error.message);
}
}
await this.docker.dispose();
}
async getStatus() {
const status = {};
for (const container of this.containers) {
try {
const info = await this.docker.inspectContainer(container);
status[container] = {
running: info.State.Running,
health: info.State.Health?.Status,
uptime: Date.now() - new Date(info.State.StartedAt).getTime()
};
} catch (error) {
status[container] = { error: error.message };
}
}
return status;
}
}
// Usage
const deployment = new ApplicationDeployment();
try {
await deployment.deploy();
// Monitor deployment
setInterval(async () => {
const status = await deployment.getStatus();
console.log('Service status:', JSON.stringify(status, null, 2));
}, 30000);
} catch (error) {
console.error('Deployment failed:', error.message);
process.exit(1);
}
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Received SIGINT, cleaning up...');
await deployment.cleanup();
process.exit(0);
});
This comprehensive lifecycle management enables full control over Docker containers, from creation through monitoring to cleanup, providing the foundation for robust containerized applications.