今天要學習如何將我們的交易系統容器化,這就像爸爸把傳統農場改造成現代化的工廠一樣。每個功能都被包裝在標準化的「容器」裡,可以在任何地方快速部署,維護更簡單,擴展更容易!
# 交易引擎 Dockerfile
FROM python:3.11-slim
# 設置工作目錄
WORKDIR /app
# 安裝系統依賴
RUN apt-get update && apt-get install -y \
gcc \
g++ \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 複製需求文件
COPY requirements.txt .
# 安裝 Python 依賴
RUN pip install --no-cache-dir -r requirements.txt
# 複製應用程式碼
COPY src/ ./src/
COPY config/ ./config/
# 創建非 root 用戶
RUN useradd -m -s /bin/bash trader && \
chown -R trader:trader /app
USER trader
# 設置環境變數
ENV PYTHONPATH=/app/src
ENV PYTHONUNBUFFERED=1
# 健康檢查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD python src/health_check.py
# 暴露端口
EXPOSE 8080
# 啟動命令
CMD ["python", "src/trading_engine.py"]
# 多階段構建 Dockerfile
# 第一階段:構建環境
FROM python:3.11 AS builder
WORKDIR /app
# 安裝構建依賴
RUN apt-get update && apt-get install -y \
gcc \
g++ \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# 安裝 Python 依賴
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# 第二階段:運行環境
FROM python:3.11-slim AS runtime
WORKDIR /app
# 安裝運行時依賴
RUN apt-get update && apt-get install -y \
libpq5 \
&& rm -rf /var/lib/apt/lists/* \
&& apt-get clean
# 從構建階段複製 Python 包
COPY --from=builder /root/.local /root/.local
# 複製應用程式碼
COPY src/ ./src/
COPY config/ ./config/
# 創建非 root 用戶
RUN useradd -m -s /bin/bash trader && \
chown -R trader:trader /app
USER trader
# 更新 PATH
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONPATH=/app/src
ENV PYTHONUNBUFFERED=1
# 健康檢查
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD python src/health_check.py
EXPOSE 8080
CMD ["python", "src/trading_engine.py"]
# src/trading_engine.py
import asyncio
import logging
import signal
import sys
from datetime import datetime
from typing import Dict, List, Optional
import aiohttp
from aiohttp import web
import json
import os
class TradingEngineService:
"""容器化交易引擎服務"""
def __init__(self):
self.app = web.Application()
self.setup_routes()
self.setup_middleware()
# 服務狀態
self.is_healthy = False
self.start_time = datetime.utcnow()
self.processed_signals = 0
self.active_positions = {}
# 配置
self.config = self.load_config()
# 設置日誌
self.setup_logging()
# 註冊信號處理器
self.setup_signal_handlers()
def load_config(self) -> Dict:
"""載入配置"""
config = {
'host': os.getenv('HOST', '0.0.0.0'),
'port': int(os.getenv('PORT', 8080)),
'debug': os.getenv('DEBUG', 'false').lower() == 'true',
'redis_url': os.getenv('REDIS_URL', 'redis://localhost:6379'),
'database_url': os.getenv('DATABASE_URL', 'postgresql://localhost/trading'),
'binance_api_key': os.getenv('BINANCE_API_KEY'),
'binance_secret_key': os.getenv('BINANCE_SECRET_KEY'),
's3_config_bucket': os.getenv('S3_CONFIG_BUCKET'),
'telegram_bot_token': os.getenv('TELEGRAM_BOT_TOKEN'),
'telegram_chat_id': os.getenv('TELEGRAM_CHAT_ID')
}
return config
def setup_logging(self):
"""設置日誌"""
log_level = logging.DEBUG if self.config['debug'] else logging.INFO
logging.basicConfig(
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout),
logging.FileHandler('/app/logs/trading_engine.log') if os.path.exists('/app/logs') else logging.NullHandler()
]
)
self.logger = logging.getLogger(__name__)
def setup_routes(self):
"""設置路由"""
self.app.router.add_get('/health', self.health_check)
self.app.router.add_get('/status', self.status_check)
self.app.router.add_post('/signal', self.process_signal)
self.app.router.add_get('/positions', self.get_positions)
self.app.router.add_post('/positions/{symbol}/close', self.close_position)
self.app.router.add_get('/metrics', self.get_metrics)
def setup_middleware(self):
"""設置中間件"""
@web.middleware
async def logging_middleware(request, handler):
start_time = datetime.utcnow()
try:
response = await handler(request)
duration = (datetime.utcnow() - start_time).total_seconds()
self.logger.info(f"{request.method} {request.path} - {response.status} - {duration:.3f}s")
return response
except Exception as e:
duration = (datetime.utcnow() - start_time).total_seconds()
self.logger.error(f"{request.method} {request.path} - ERROR: {e} - {duration:.3f}s")
raise
@web.middleware
async def cors_middleware(request, handler):
response = await handler(request)
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'GET, POST, PUT, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Content-Type, Authorization'
return response
self.app.middlewares.append(logging_middleware)
self.app.middlewares.append(cors_middleware)
def setup_signal_handlers(self):
"""設置信號處理器"""
def signal_handler(signum, frame):
self.logger.info(f"收到信號 {signum},開始優雅關閉...")
asyncio.create_task(self.graceful_shutdown())
signal.signal(signal.SIGTERM, signal_handler)
signal.signal(signal.SIGINT, signal_handler)
async def health_check(self, request):
"""健康檢查端點"""
# 檢查各種依賴服務
checks = {
'service': True,
'database': await self.check_database(),
'redis': await self.check_redis(),
's3': await self.check_s3(),
'binance_api': await self.check_binance_api()
}
all_healthy = all(checks.values())
status_code = 200 if all_healthy else 503
response_data = {
'status': 'healthy' if all_healthy else 'unhealthy',
'timestamp': datetime.utcnow().isoformat(),
'uptime_seconds': (datetime.utcnow() - self.start_time).total_seconds(),
'checks': checks
}
return web.json_response(response_data, status=status_code)
async def status_check(self, request):
"""狀態檢查端點"""
status_data = {
'service_name': 'trading-engine',
'version': '1.0.0',
'start_time': self.start_time.isoformat(),
'uptime_seconds': (datetime.utcnow() - self.start_time).total_seconds(),
'processed_signals': self.processed_signals,
'active_positions_count': len(self.active_positions),
'memory_usage': self.get_memory_usage(),
'config': {
'debug': self.config['debug'],
'port': self.config['port']
}
}
return web.json_response(status_data)
async def process_signal(self, request):
"""處理交易信號"""
try:
data = await request.json()
# 驗證信號格式
required_fields = ['symbol', 'action', 'price', 'timestamp']
if not all(field in data for field in required_fields):
return web.json_response(
{'error': 'Missing required fields'},
status=400
)
# 處理信號
result = await self.execute_trading_signal(data)
self.processed_signals += 1
return web.json_response({
'status': 'success',
'signal_id': result.get('signal_id'),
'processed_at': datetime.utcnow().isoformat()
})
except json.JSONDecodeError:
return web.json_response({'error': 'Invalid JSON'}, status=400)
except Exception as e:
self.logger.error(f"處理信號時發生錯誤: {e}")
return web.json_response(
{'error': 'Internal server error'},
status=500
)
async def get_positions(self, request):
"""獲取持倉資訊"""
positions = []
for symbol, position_data in self.active_positions.items():
positions.append({
'symbol': symbol,
'quantity': position_data['quantity'],
'entry_price': position_data['entry_price'],
'current_price': await self.get_current_price(symbol),
'unrealized_pnl': position_data.get('unrealized_pnl', 0),
'entry_time': position_data['entry_time']
})
return web.json_response({
'positions': positions,
'total_positions': len(positions),
'timestamp': datetime.utcnow().isoformat()
})
async def close_position(self, request):
"""關閉特定持倉"""
symbol = request.match_info['symbol']
if symbol not in self.active_positions:
return web.json_response(
{'error': f'No active position for {symbol}'},
status=404
)
try:
# 執行平倉
result = await self.execute_close_position(symbol)
return web.json_response({
'status': 'success',
'closed_position': result,
'timestamp': datetime.utcnow().isoformat()
})
except Exception as e:
self.logger.error(f"平倉時發生錯誤: {e}")
return web.json_response(
{'error': 'Failed to close position'},
status=500
)
async def get_metrics(self, request):
"""獲取服務指標(Prometheus 格式)"""
metrics = [
f"# HELP trading_engine_uptime_seconds Service uptime in seconds",
f"# TYPE trading_engine_uptime_seconds gauge",
f"trading_engine_uptime_seconds {(datetime.utcnow() - self.start_time).total_seconds()}",
f"",
f"# HELP trading_engine_processed_signals_total Total number of processed signals",
f"# TYPE trading_engine_processed_signals_total counter",
f"trading_engine_processed_signals_total {self.processed_signals}",
f"",
f"# HELP trading_engine_active_positions Current number of active positions",
f"# TYPE trading_engine_active_positions gauge",
f"trading_engine_active_positions {len(self.active_positions)}",
f"",
f"# HELP trading_engine_memory_usage_bytes Memory usage in bytes",
f"# TYPE trading_engine_memory_usage_bytes gauge",
f"trading_engine_memory_usage_bytes {self.get_memory_usage()}"
]
return web.Response(
text="\n".join(metrics),
content_type="text/plain"
)
async def execute_trading_signal(self, signal_data: Dict) -> Dict:
"""執行交易信號"""
# 這裡實作實際的交易邏輯
self.logger.info(f"執行交易信號: {signal_data}")
# 模擬信號處理
signal_id = f"signal_{int(datetime.utcnow().timestamp())}"
return {
'signal_id': signal_id,
'status': 'executed',
'timestamp': datetime.utcnow().isoformat()
}
async def execute_close_position(self, symbol: str) -> Dict:
"""執行平倉"""
position = self.active_positions.get(symbol)
if not position:
raise ValueError(f"No position found for {symbol}")
# 實作平倉邏輯
self.logger.info(f"平倉 {symbol}")
# 從活躍持倉中移除
del self.active_positions[symbol]
return {
'symbol': symbol,
'closed_quantity': position['quantity'],
'pnl': position.get('unrealized_pnl', 0)
}
async def check_database(self) -> bool:
"""檢查數據庫連接"""
try:
# 實作數據庫健康檢查
return True
except:
return False
async def check_redis(self) -> bool:
"""檢查 Redis 連接"""
try:
# 實作 Redis 健康檢查
return True
except:
return False
async def check_s3(self) -> bool:
"""檢查 S3 連接"""
try:
# 實作 S3 健康檢查
return True
except:
return False
async def check_binance_api(self) -> bool:
"""檢查 Binance API 連接"""
try:
# 實作 Binance API 健康檢查
return True
except:
return False
async def get_current_price(self, symbol: str) -> float:
"""獲取當前價格"""
# 實作價格獲取邏輯
return 50000.0 # 模擬價格
def get_memory_usage(self) -> int:
"""獲取記憶體使用量"""
import psutil
process = psutil.Process()
return process.memory_info().rss
async def graceful_shutdown(self):
"""優雅關閉"""
self.logger.info("開始優雅關閉流程...")
# 1. 停止接受新請求
self.is_healthy = False
# 2. 完成正在處理的請求
await asyncio.sleep(2)
# 3. 關閉所有持倉(可選)
if self.active_positions:
self.logger.info("關閉所有活躍持倉...")
for symbol in list(self.active_positions.keys()):
try:
await self.execute_close_position(symbol)
except Exception as e:
self.logger.error(f"關閉持倉 {symbol} 時發生錯誤: {e}")
# 4. 關閉數據庫連接
# await self.close_database_connections()
self.logger.info("優雅關閉完成")
sys.exit(0)
async def start_server(self):
"""啟動服務器"""
runner = web.AppRunner(self.app)
await runner.setup()
site = web.TCPSite(
runner,
self.config['host'],
self.config['port']
)
await site.start()
self.is_healthy = True
self.logger.info(f"交易引擎服務已啟動,監聽 {self.config['host']}:{self.config['port']}")
# 保持服務運行
try:
while True:
await asyncio.sleep(1)
except asyncio.CancelledError:
await runner.cleanup()
# src/health_check.py
import asyncio
import aiohttp
import sys
import os
async def health_check():
"""健康檢查腳本"""
port = os.getenv('PORT', 8080)
try:
async with aiohttp.ClientSession() as session:
async with session.get(f'http://localhost:{port}/health', timeout=5) as response:
if response.status == 200:
print("健康檢查通過")
sys.exit(0)
else:
print(f"健康檢查失敗,狀態碼: {response.status}")
sys.exit(1)
except Exception as e:
print(f"健康檢查錯誤: {e}")
sys.exit(1)
if __name__ == "__main__":
asyncio.run(health_check())
# 主程式入口
if __name__ == "__main__":
service = TradingEngineService()
asyncio.run(service.start_server())
# docker-compose.yml
version: '3.8'
services:
# 交易引擎服務
trading-engine:
build:
context: .
dockerfile: Dockerfile.trading-engine
container_name: trading-engine
ports:
- "8080:8080"
environment:
- DEBUG=false
- REDIS_URL=redis://redis:6379
- DATABASE_URL=postgresql://postgres:password@postgres:5432/trading
- S3_CONFIG_BUCKET=${S3_CONFIG_BUCKET}
- BINANCE_API_KEY=${BINANCE_API_KEY}
- BINANCE_SECRET_KEY=${BINANCE_SECRET_KEY}
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
depends_on:
- postgres
- redis
volumes:
- ./logs:/app/logs
restart: unless-stopped
networks:
- trading-network
healthcheck:
test: ["CMD", "python", "src/health_check.py"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
# 市場數據服務
market-data:
build:
context: .
dockerfile: Dockerfile.market-data
container_name: market-data
ports:
- "8081:8080"
environment:
- DEBUG=false
- REDIS_URL=redis://redis:6379
- BINANCE_API_KEY=${BINANCE_API_KEY}
- BINANCE_SECRET_KEY=${BINANCE_SECRET_KEY}
depends_on:
- redis
volumes:
- ./logs:/app/logs
restart: unless-stopped
networks:
- trading-network
# 風險管理服務
risk-management:
build:
context: .
dockerfile: Dockerfile.risk-management
container_name: risk-management
ports:
- "8082:8080"
environment:
- DEBUG=false
- DATABASE_URL=postgresql://postgres:password@postgres:5432/trading
- S3_CONFIG_BUCKET=${S3_CONFIG_BUCKET}
depends_on:
- postgres
volumes:
- ./logs:/app/logs
restart: unless-stopped
networks:
- trading-network
# 通知服務
notification:
build:
context: .
dockerfile: Dockerfile.notification
container_name: notification
ports:
- "8083:8080"
environment:
- DEBUG=false
- TELEGRAM_BOT_TOKEN=${TELEGRAM_BOT_TOKEN}
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
- SMTP_SERVER=${SMTP_SERVER}
- SMTP_PORT=${SMTP_PORT}
- SENDER_EMAIL=${SENDER_EMAIL}
restart: unless-stopped
networks:
- trading-network
# PostgreSQL 數據庫
postgres:
image: postgres:15
container_name: postgres-db
environment:
- POSTGRES_DB=trading
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init_db.sql:/docker-entrypoint-initdb.d/init_db.sql
restart: unless-stopped
networks:
- trading-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
# Redis 緩存
redis:
image: redis:7-alpine
container_name: redis-cache
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- trading-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
# Nginx 負載平衡器
nginx:
image: nginx:alpine
container_name: nginx-lb
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- trading-engine
- market-data
- risk-management
- notification
restart: unless-stopped
networks:
- trading-network
# Prometheus 監控
prometheus:
image: prom/prometheus:latest
container_name: prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
restart: unless-stopped
networks:
- trading-network
# Grafana 儀表板
grafana:
image: grafana/grafana:latest
container_name: grafana
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
depends_on:
- prometheus
restart: unless-stopped
networks:
- trading-network
volumes:
postgres_data:
redis_data:
prometheus_data:
grafana_data:
networks:
trading-network:
driver: bridge
# nginx.conf
events {
worker_connections 1024;
}
http {
upstream trading_engine {
server trading-engine:8080;
}
upstream market_data {
server market-data:8080;
}
upstream risk_management {
server risk-management:8080;
}
upstream notification {
server notification:8080;
}
# 日誌格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time" '
'uht="$upstream_header_time" urt="$upstream_response_time"';
access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log;
# API Gateway 配置
server {
listen 80;
server_name localhost;
# 健康檢查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
# 交易引擎 API
location /api/trading/ {
proxy_pass http://trading_engine/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超時設置
proxy_connect_timeout 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# 市場數據 API
location /api/market/ {
proxy_pass http://market_data/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# 風險管理 API
location /api/risk/ {
proxy_pass http://risk_management/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 通知服務 API
location /api/notification/ {
proxy_pass http://notification/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 靜態文件
location /static/ {
alias /var/www/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}
# HTTPS 配置 (可選)
server {
listen 443 ssl http2;
server_name localhost;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 其他配置與 HTTP 相同...
}
}
#!/bin/bash
# scripts/deploy.sh
set -e
echo "🚀 開始部署交易系統容器..."
# 檢查環境變數
required_vars=("BINANCE_API_KEY" "BINANCE_SECRET_KEY" "S3_CONFIG_BUCKET")
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "❌ 缺少環境變數: $var"
exit 1
fi
done
# 創建必要目錄
mkdir -p logs
mkdir -p ssl
# 構建映像
echo "🔨 構建 Docker 映像..."
docker-compose build --no-cache
# 啟動服務
echo "🚀 啟動服務..."
docker-compose up -d
# 等待服務啟動
echo "⏳ 等待服務啟動..."
sleep 30
# 檢查服務狀態
echo "🔍 檢查服務狀態..."
docker-compose ps
# 健康檢查
echo "🏥 執行健康檢查..."
services=("trading-engine" "market-data" "risk-management" "notification")
for service in "${services[@]}"; do
echo "檢查 $service..."
if docker-compose exec -T $service python src/health_check.py; then
echo "✅ $service 健康檢查通過"
else
echo "❌ $service 健康檢查失敗"
docker-compose logs $service
exit 1
fi
done
echo "🎉 部署完成!"
echo "📊 監控面板: http://localhost:3000 (admin/admin)"
echo "📈 Prometheus: http://localhost:9090"
echo "🔗 API Gateway: http://localhost:80"
#!/bin/bash
# scripts/stop.sh
echo "🛑 停止交易系統..."
# 優雅停止
docker-compose down --timeout 30
echo "✅ 交易系統已停止"
#!/bin/bash
# scripts/logs.sh
if [ -z "$1" ]; then
echo "顯示所有服務日誌:"
docker-compose logs -f
else
echo "顯示 $1 服務日誌:"
docker-compose logs -f $1
fi
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
# - "first_rules.yml"
# - "second_rules.yml"
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'trading-engine'
static_configs:
- targets: ['trading-engine:8080']
metrics_path: '/metrics'
scrape_interval: 10s
- job_name: 'market-data'
static_configs:
- targets: ['market-data:8080']
metrics_path: '/metrics'
scrape_interval: 10s
- job_name: 'risk-management'
static_configs:
- targets: ['risk-management:8080']
metrics_path: '/metrics'
scrape_interval: 10s
- job_name: 'notification'
static_configs:
- targets: ['notification:8080']
metrics_path: '/metrics'
scrape_interval: 10s
- job_name: 'nginx'
static_configs:
- targets: ['nginx:80']
metrics_path: '/metrics'
scrape_interval: 30s
- job_name: 'postgres'
static_configs:
- targets: ['postgres:5432']
scrape_interval: 30s
- job_name: 'redis'
static_configs:
- targets: ['redis:6379']
scrape_interval: 30s
# security_config.py
class ContainerSecurityConfig:
"""容器安全配置"""
@staticmethod
def get_security_best_practices():
return {
'dockerfile': [
'使用非 root 用戶運行應用',
'最小化映像層數',
'不在映像中包含敏感資訊',
'使用 .dockerignore 排除不必要文件',
'定期更新基礎映像',
'使用官方映像',
'掃描映像漏洞'
],
'runtime': [
'限制容器資源使用',
'設置只讀檔案系統',
'禁用特權模式',
'使用安全上下文',
'網路隔離',
'日誌監控',
'定期備份'
],
'secrets_management': [
'使用環境變數傳遞敏感資訊',
'避免在映像中硬編碼秘密',
'使用 Docker Secrets 或外部秘密管理',
'加密傳輸通道',
'定期輪換憑證',
'最小權限原則'
]
}
@staticmethod
def generate_secure_compose_config():
"""生成安全的 Compose 配置"""
security_config = {
'security_opt': [
'no-new-privileges:true'
],
'cap_drop': ['ALL'],
'cap_add': ['CHOWN', 'SETGID', 'SETUID'],
'read_only': True,
'tmpfs': ['/tmp', '/var/run'],
'user': '1000:1000'
}
return security_config
# docker-compose.override.yml (生產環境)
version: '3.8'
services:
trading-engine:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '1.0'
memory: 1G
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- CHOWN
- SETGID
- SETUID
market-data:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
postgres:
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
command: postgres -c shared_preload_libraries=pg_stat_statements
environment:
- POSTGRES_INITDB_ARGS=--data-checksums
redis:
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
今天我們完成了交易系統的容器化,就像把農場的每個功能都包裝成可隨時部署的標準化模組。重要概念包括:
容器化優勢:
Docker 最佳實踐:
服務編排:
安全性考量:
記住爸爸說的:「好的工廠要有標準化的生產線,每個環節都能獨立運作,出問題時能快速定位和修復」。容器化讓我們的系統更加穩健和可維護!
經過 29 天的學習,我們已經建立了一個完整的雲端量化交易系統,從 AWS 基礎設施到容器化部署,涵蓋了現代金融科技的各個重要環節。
系列完結,感謝您的學習之旅!