Перейти к основному содержимому

Docker Target Overview

Implementation Reference

Source Files:

  • packages/core/src/adapters/docker-adapter.ts - Docker execution adapter
  • packages/core/src/docker/docker-client.ts - Docker API client
  • packages/core/src/docker/container.ts - Container management
  • packages/core/src/docker/compose.ts - Docker Compose operations
  • packages/core/src/docker/volume.ts - Volume management
  • apps/xec/src/config/types.ts - Docker target configuration (lines 76-95)

Key Classes:

  • DockerAdapter - Docker command execution adapter
  • DockerClient - Docker API wrapper
  • ContainerManager - Container lifecycle management
  • ComposeManager - 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:

OperationTimeNotes
Container exec50-100msExisting container
Container create200-500msFrom cached image
Container start100-200msAlready created
Image pullVariableNetwork dependent
Volume mount<10msLocal filesystem

Optimization Strategies

  1. 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`;
  1. 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`;
  1. 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

  1. Use specific image tags instead of latest
  2. Run containers as non-root users
  3. Set resource limits to prevent resource exhaustion
  4. Use health checks for production containers
  5. Clean up stopped containers and unused images
  6. Use multi-stage builds for smaller images
  7. Implement proper logging with log drivers
  8. Use secrets for sensitive data