Docker Target Overview
Implementation Referenceβ
Source Files:
packages/core/src/adapters/docker-adapter.ts
- Docker execution adapterpackages/core/src/docker/docker-client.ts
- Docker API clientpackages/core/src/docker/container.ts
- Container managementpackages/core/src/docker/compose.ts
- Docker Compose operationspackages/core/src/docker/volume.ts
- Volume managementapps/xec/src/config/types.ts
- Docker target configuration (lines 76-95)
Key Classes:
DockerAdapter
- Docker command execution adapterDockerClient
- Docker API wrapperContainerManager
- Container lifecycle managementComposeManager
- Docker Compose operations
Key Functions:
DockerAdapter.execute()
- Execute commands in containers (lines 30-120)DockerClient.exec()
- Docker exec implementation (lines 45-95)ContainerManager.create()
- Create containers (lines 25-80)ContainerManager.start()
- Start containers (lines 85-110)ComposeManager.up()
- Start compose services (lines 20-65)
Overviewβ
Docker targets enable command execution inside Docker containers. Xec provides comprehensive Docker support including container lifecycle management, Docker Compose integration, volume management, and network operations.
Target Configurationβ
Basic Docker Targetβ
# .xec/config.yaml
targets:
app:
type: docker
container: my-app # Container name or ID
database:
type: docker
container: postgres-db
user: postgres # Execute as specific user
Advanced Configurationβ
targets:
web-app:
type: docker
container: web-app
# Execution options
user: www-data
workdir: /app
env:
NODE_ENV: production
PORT: 3000
# Container options
privileged: false
tty: true
interactive: true
# Network
network: app-network
# Auto-create if not exists
image: node:18-alpine
create: true
createOptions:
ports:
- "3000:3000"
volumes:
- ./app:/app
restart: always
Container Executionβ
Basic Executionβ
// Execute in existing container
await $.docker('my-app')`ls -la`;
// Execute with options
await $.docker({
container: 'my-app',
user: 'node',
workdir: '/app'
})`npm install`;
// Using configured target
await $.target('app')`node server.js`;
Advanced Executionβ
// With environment variables
await $.docker('my-app').env({
NODE_ENV: 'production',
DEBUG: 'app:*'
})`npm start`;
// Interactive execution
await $.docker('my-app').interactive()`/bin/bash`;
// Execute as root
await $.docker('my-app').user('root')`apt-get update`;
// With working directory
await $.docker('my-app').cwd('/app')`npm test`;
Stream Processingβ
// Stream output
await $.docker('my-app')`tail -f /var/log/app.log`
.pipe(process.stdout);
// Process output line by line
await $.docker('my-app')`cat /data/large-file.csv`
.lines(async (line) => {
await processLine(line);
});
// Pipe between containers
await $.docker('source')`cat data.sql`
.pipe($.docker('postgres')`psql -U postgres`);
Container Lifecycleβ
Creating Containersβ
// Create container from image
const container = await $.docker.create({
name: 'my-app',
image: 'node:18-alpine',
command: 'node server.js',
ports: {
'3000': '3000'
},
volumes: {
'./app': '/app'
},
env: {
NODE_ENV: 'production'
}
});
// Start the container
await container.start();
// Execute commands
await $.docker(container.id)`npm install`;
Managing Containersβ
// Start container
await $.docker('my-app').start();
// Stop container
await $.docker('my-app').stop();
// Restart container
await $.docker('my-app').restart();
// Remove container
await $.docker('my-app').remove();
// Container status
const status = await $.docker('my-app').status();
console.log(`Container is ${status.State.Status}`);
Container Inspectionβ
// Get container info
const info = await $.docker('my-app').inspect();
console.log('Container IP:', info.NetworkSettings.IPAddress);
console.log('Ports:', info.NetworkSettings.Ports);
// List containers
const containers = await $.docker.list({
all: true, // Include stopped
filters: {
label: 'app=myapp'
}
});
// Container logs
const logs = await $.docker('my-app').logs({
stdout: true,
stderr: true,
tail: 100,
follow: false
});
Docker Compose Integrationβ
Compose Operationsβ
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "3000:3000"
db:
image: postgres:14
environment:
POSTGRES_PASSWORD: secret
// Start compose services
await $.compose.up({
file: 'docker-compose.yml',
detach: true,
build: true
});
// Execute in compose service
await $.compose('web')`npm test`;
// Stop compose services
await $.compose.down({
volumes: true, // Remove volumes
removeOrphans: true
});
// Scale services
await $.compose.scale({
web: 3,
worker: 5
});
Service Managementβ
// Start specific service
await $.compose.start('web');
// Restart service
await $.compose.restart('web');
// View service logs
await $.compose.logs('web', {
follow: true,
tail: 100
});
// Execute in service
await $.compose.exec('web', 'npm run migrate');
Volume Managementβ
Working with Volumesβ
// Create volume
await $.docker.volume.create({
name: 'app-data',
driver: 'local',
labels: {
app: 'myapp'
}
});
// List volumes
const volumes = await $.docker.volume.list({
filters: {
label: 'app=myapp'
}
});
// Copy to/from volume
await $.docker('my-app').copy('./data', '/app/data');
await $.docker('my-app').copyFrom('/app/logs', './logs');
// Remove volume
await $.docker.volume.remove('app-data');
Bind Mountsβ
// Create container with bind mount
await $.docker.create({
name: 'dev-app',
image: 'node:18',
volumes: {
// Bind mount (host:container)
'./src': '/app/src',
// Named volume
'app-data': '/data',
// Anonymous volume
'/tmp'
}
});
// Mount with options
await $.docker.create({
name: 'app',
image: 'node:18',
mounts: [{
type: 'bind',
source: './src',
target: '/app/src',
readonly: false,
consistency: 'delegated' // macOS optimization
}]
});
Network Operationsβ
Network Managementβ
// Create network
await $.docker.network.create({
name: 'app-network',
driver: 'bridge',
ipam: {
config: [{
subnet: '172.20.0.0/16',
gateway: '172.20.0.1'
}]
}
});
// Connect container to network
await $.docker('my-app').connect('app-network', {
aliases: ['app', 'web']
});
// Disconnect from network
await $.docker('my-app').disconnect('app-network');
// List networks
const networks = await $.docker.network.list();
Inter-Container Communicationβ
// Create containers on same network
const network = 'app-network';
// Create database
await $.docker.create({
name: 'db',
image: 'postgres:14',
network,
networkAliases: ['database']
});
// Create app that connects to db
await $.docker.create({
name: 'app',
image: 'node:18',
network,
env: {
DB_HOST: 'database', // Use network alias
DB_PORT: '5432'
}
});
// Test connection
await $.docker('app')`ping database`;
Image Managementβ
Working with Imagesβ
// Pull image
await $.docker.image.pull('node:18-alpine');
// Build image
await $.docker.image.build({
context: '.',
dockerfile: 'Dockerfile',
tag: 'my-app:latest',
buildArgs: {
NODE_VERSION: '18'
}
});
// Push image
await $.docker.image.push('my-app:latest');
// List images
const images = await $.docker.image.list({
filters: {
label: 'app=myapp'
}
});
// Remove image
await $.docker.image.remove('my-app:old');
Performance Characteristicsβ
Execution Overheadβ
Based on implementation measurements:
Operation | Time | Notes |
---|---|---|
Container exec | 50-100ms | Existing container |
Container create | 200-500ms | From cached image |
Container start | 100-200ms | Already created |
Image pull | Variable | Network dependent |
Volume mount | <10ms | Local filesystem |
Optimization Strategiesβ
- Keep Containers Running:
// Reuse long-running containers
const container = await $.docker.ensure('dev-env', {
image: 'node:18',
command: 'tail -f /dev/null' // Keep alive
});
// Execute multiple commands
await $.docker(container)`npm install`;
await $.docker(container)`npm test`;
- Use Exec Instead of Run:
// Slower - creates new container
await $.docker.run('node:18', 'npm install');
// Faster - uses existing container
await $.docker('existing-container')`npm install`;
- Cache Images Locally:
// Pre-pull images
const images = ['node:18', 'postgres:14', 'redis:7'];
await Promise.all(
images.map(image => $.docker.image.pull(image))
);
Security Considerationsβ
Container Securityβ
// Run with security options
await $.docker.create({
name: 'secure-app',
image: 'node:18',
// Security options
user: '1000:1000', // Non-root user
readOnly: true, // Read-only root filesystem
privileged: false, // No privileged mode
// Capabilities
capAdd: [],
capDrop: ['ALL'],
// Security opt
securityOpt: [
'no-new-privileges:true',
'seccomp=default.json'
],
// Resource limits
memory: '512m',
cpus: '0.5'
});
Secret Managementβ
// Use Docker secrets (Swarm mode)
await $.docker.secret.create({
name: 'db-password',
data: Buffer.from('secret-password').toString('base64')
});
// Mount secret in container
await $.docker.create({
name: 'app',
image: 'node:18',
secrets: [{
name: 'db-password',
target: '/run/secrets/db-password'
}]
});
// Read secret in container
await $.docker('app')`cat /run/secrets/db-password`;
Health Checksβ
Container Healthβ
// Define health check
await $.docker.create({
name: 'app',
image: 'node:18',
healthcheck: {
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health'],
interval: '30s',
timeout: '3s',
retries: 3,
startPeriod: '40s'
}
});
// Check health status
const health = await $.docker('app').health();
if (health.Status === 'healthy') {
console.log('Container is healthy');
}
// Wait for healthy
await $.docker('app').waitHealthy({
timeout: 60000
});
Error Handlingβ
Common Docker Errorsβ
try {
await $.docker('my-app')`command`;
} catch (error) {
if (error.code === 'CONTAINER_NOT_FOUND') {
console.error('Container does not exist');
} else if (error.code === 'CONTAINER_NOT_RUNNING') {
console.error('Container is not running');
await $.docker('my-app').start();
} else if (error.code === 'DOCKER_NOT_AVAILABLE') {
console.error('Docker daemon not running');
}
}
Automatic Recoveryβ
// Auto-restart on failure
async function ensureContainer(name: string, options: ContainerOptions) {
try {
const status = await $.docker(name).status();
if (status.State.Status !== 'running') {
await $.docker(name).start();
}
} catch (error) {
if (error.code === 'CONTAINER_NOT_FOUND') {
await $.docker.create({ name, ...options });
await $.docker(name).start();
}
}
}
Best Practicesβ
- Use specific image tags instead of
latest
- Run containers as non-root users
- Set resource limits to prevent resource exhaustion
- Use health checks for production containers
- Clean up stopped containers and unused images
- Use multi-stage builds for smaller images
- Implement proper logging with log drivers
- Use secrets for sensitive data
Related Documentationβ
- Container Lifecycle - Detailed lifecycle management
- Compose Integration - Docker Compose usage
- Volume Management - Volume operations
- Networking - Docker networking
- Docker Adapter API - API reference