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