Writing Skills
writing-skills
效率与协作技能,来自 obra/superpowers,查看原文获取完整用途和操作说明。
deployment-orchestration【部署与运维】Orchestrates complex multi-stage deployments with dependency management, sequencing rules, and failure handling across interconnected services.
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
name: deployment-orchestration compatibility: opencode completeness: 95 content-types:
Orchestrates coordinated multi-service deployments with dependency-aware sequencing, failure containment, and automatic rollback across service boundaries. Ensures that when multiple services deploy together, their compatibility constraints are respected.
Use this skill when:
Avoid this skill for:
Document all inter-service dependencies: API contracts, shared data schemas, event streams, and configuration dependencies. Build a dependency graph.
Checkpoint: You have a complete dependency graph showing which services depend on which others.
Group services into batches based on dependency direction. Services with no mutual dependencies can deploy in parallel. Services that depend on each other must deploy sequentially.
Checkpoint: Each batch's internal order respects dependency constraints, and batches between each other can run in parallel.
For each dependency pair, define the compatibility window: what versions of each service can coexist during the transition.
Checkpoint: You know the exact version compatibility matrix for every dependency pair.
Deploy each batch, wait for health gates, then proceed to the next batch. Parallel batches execute simultaneously when their health gates pass.
Checkpoint: All services in the batch pass health checks before the next batch starts.
If any service in a batch fails its health check, stop all parallel batches, roll back the failed batch, and decide whether to retry or abort.
Checkpoint: Rollback order is the reverse of deployment order to maintain compatibility.
Build a dependency graph and compute deployment batches using topological sorting.
# ❌ BAD: Deploying services in arbitrary order ignores dependencies
# order-service depends on user-service API — deploying order first will fail
SERVICES=("payment-service" "user-service" "order-service" "notification-service")
for service in "${SERVICES[@]}"; do
kubectl set image deployment/$service app=app:latest
kubectl rollout status deployment/$service
done
# Problem: order-service might depend on user-service API
# Problem: notification-service might depend on order-service events
# Problem: payment-service might depend on both
# Deploying in this order risks incompatible versions running simultaneously
What's wrong:
# ✅ GOOD: Dependency-aware batch deployment with topological sort
# Services with no mutual dependencies deploy in parallel; dependent services deploy sequentially
# --- Dependency graph definition (JSON) ---
# {
# "dependencies": {
# "payment-service": ["user-service"],
# "order-service": ["user-service", "payment-service"],
# "notification-service": ["order-service"]
# }
# }
DEPS_FILE="/tmp/dependency-graph.json"
# --- Topological sort using Kahn's algorithm ---
compute_batches() {
local deps_file=$1
local batch_dir
batch_dir=$(mktemp -d)
# Build adjacency list and in-degree count
local all_services
all_services=$(jq -r 'keys[]' "$deps_file")
local service_count
service_count=$(echo "$all_services" | wc -l)
# Initialize in-degree for all services
declare -A in_degree
for svc in $all_services; do
in_degree[$svc]=0
done
# Count in-degrees
for svc in $all_services; do
local deps
deps=$(jq -r ".dependencies.\"$svc\" // [] | .[]" "$deps_file" 2>/dev/null)
if [[ -n "$deps" ]]; then
in_degree[$svc]=$(echo "$deps" | wc -l)
fi
done
# Find initial zero in-degree services
local batch_num=0
local processed=0
local remaining_services="$all_services"
while [[ $processed -lt $service_count ]]; do
local current_batch=()
# Find services with in-degree 0
for svc in $remaining_services; do
if [[ ${in_degree[$svc]} -eq 0 ]]; then
current_batch+=("$svc")
fi
done
if [[ ${#current_batch[@]} -eq 0 ]]; then
echo "❌ ERROR: Circular dependency detected among remaining services"
echo "Remaining: $remaining_services"
return 1
fi
# Record this batch
local batch_id="batch-$batch_num"
echo "${current_batch[*]}" > "$batch_dir/$batch_id"
echo " Batch $batch_num: ${current_batch[*]}"
# Update in-degrees for next round
for svc in "${current_batch[@]}"; do
# Find services that depend on this one
for other_svc in $remaining_services; do
local deps
deps=$(jq -r ".dependencies.\"$other_svc\" // [] | .[]" "$deps_file" 2>/dev/null)
if echo "$deps" | grep -qx "$svc"; then
in_degree[$other_svc]=$(( ${in_degree[$other_svc]} - 1 ))
fi
done
done
# Remove processed services
remaining_services=$(echo "$remaining_services" | grep -v -x "$(printf '%s\n' "${current_batch[@]}")")
processed=$((processed + ${#current_batch[@]}))
batch_num=$((batch_num + 1))
done
# Output batch files
echo "$batch_dir"
}
# --- Health check function ---
check_service_health() {
local service=$1
local http_code
http_code=$(curl -s -o /dev/null -w "%{http_code}" \
--max-time 5 "http://${service}:8080/health" 2>/dev/null)
[[ "$http_code" == "200" ]]
}
# --- Deploy a single service ---
deploy_service() {
local service=$1 version=$2
echo " Deploying $service → $version"
kubectl set image deployment/$service app=$version
kubectl rollout status deployment/$service --timeout=120s
}
# --- Rollback a single service ---
rollback_service() {
local service=$1
echo " Rolling back $service"
kubectl rollout undo deployment/$service
}
# --- Main orchestration ---
orchestrate_deployment() {
local version=$1
echo "=== Multi-Service Deployment Orchestration ==="
echo "Target version: $version"
echo "Computing deployment batches..."
echo ""
# Compute batches
local batch_dir
batch_dir=$(compute_batches "$DEPS_FILE")
echo ""
# Deploy each batch sequentially
local batch_num=0
local batch_file="$batch_dir/batch-$batch_num"
local deployment_results=()
while [[ -f "$batch_file" ]]; do
local batch_services
read -r -a batch_services < "$batch_file"
echo "--- Batch $batch_num: ${batch_services[*]} ---"
local batch_success=true
local failed_service=""
# Deploy all services in batch
for service in "${batch_services[@]}"; do
if ! deploy_service "$service" "$version"; then
batch_success=false
failed_service=$service
break
fi
done
if [[ "$batch_success" != true ]]; then
echo "❌ Deployment failed for $failed_service — rolling back"
for service in "${batch_services[@]}"; do
rollback_service "$service"
done
return 1
fi
# Health gate for all services in batch
for service in "${batch_services[@]}"; do
if ! check_service_health "$service"; then
echo "❌ Health check failed for $service — rolling back batch"
for svc in "${batch_services[@]}"; do
rollback_service "$svc"
done
return 1
fi
done
echo "✅ Batch $batch_num passed — all services healthy"
deployment_results+=("batch-$batch_num:PASS")
batch_num=$((batch_num + 1))
batch_file="$batch_dir/batch-$batch_num"
done
echo ""
echo "=== Deployment Complete ==="
for result in "${deployment_results[@]}"; do
echo " $result"
done
}
# --- Rollback in reverse order ---
rollback_deployment() {
local batch_dir=$1
echo "=== Rolling back deployment (reverse order) ==="
local batch_num=$(( $(ls "$batch_dir" | sort -t'-' -k2 -n | tail -1 | cut -d'-' -f2) - 1 ))
while [[ $batch_num -ge 0 ]]; do
local batch_file="$batch_dir/batch-$batch_num"
if [[ -f "$batch_file" ]]; then
read -r -a batch_services < "$batch_file"
echo "Rolling back batch $batch_num: ${batch_services[*]}"
for service in "${batch_services[@]}"; do
rollback_service "$service"
done
fi
batch_num=$((batch_num - 1))
done
}
# --- Usage ---
# orchestrate_deployment "app:v2.0.0"
Track version compatibility windows to ensure safe transitions during deployment.
# ✅ GOOD: Validate version compatibility before deploying each service
# Checks that target version is compatible with currently running dependencies
# --- Compatibility windows (JSON) ---
# Defines which version combinations are safe for each service dependency pair
COMPLIANCE_FILE="/tmp/compatibility-windows.json"
# [
# {
# "provider": "user-service",
# "consumer": "order-service",
# "provider_min": "1.0.0",
# "provider_max": "1.5.0",
# "consumer_min": "2.0.0",
# "consumer_max": "2.5.0"
# }
# ]
# --- Version comparison (simple semver string comparison) ---
version_in_range() {
local version=$1 min_ver=$2 max_ver=$3
[[ "$version" > "$min_ver" || "$version" == "$min_ver" ]] && \
[[ "$version" < "$max_ver" || "$version" == "$max_ver" ]]
}
# --- Validate a service deployment against compatibility windows ---
validate_deployment() {
local service=$1 target_version=$2 running_versions_file=$3
echo "Validating deployment: $service@$target_version"
local violations=()
# Check provider windows (service is the provider)
while IFS= read -r window; do
local provider consumer provider_min provider_max consumer_min consumer_max
provider=$(echo "$window" | jq -r '.provider')
consumer=$(echo "$window" | jq -r '.consumer')
provider_min=$(echo "$window" | jq -r '.provider_min')
provider_max=$(echo "$window" | jq -r '.provider_max')
consumer_min=$(echo "$window" | jq -r '.consumer_min')
consumer_max=$(echo "$window" | jq -r '.consumer_max')
if [[ "$provider" == "$service" ]]; then
local running_consumer
running_consumer=$(jq -r ".\"$consumer\" // empty" "$running_versions_file")
if [[ -n "$running_consumer" ]]; then
if ! version_in_range "$target_version" "$provider_min" "$provider_max"; then
violations+=("Deploying $service@$target_version incompatible with $consumer@$running_consumer (requires $consumer@$consumer_min-$consumer_max)")
fi
fi
fi
# Check consumer windows (service is the consumer)
if [[ "$consumer" == "$service" ]]; then
local running_provider
running_provider=$(jq -r ".\"$provider\" // empty" "$running_versions_file")
if [[ -n "$running_provider" ]]; then
if ! version_in_range "$target_version" "$consumer_min" "$consumer_max"; then
violations+=("Deploying $service@$target_version incompatible with $provider@$running_provider (requires $provider@$provider_min-$provider_max)")
fi
fi
fi
done < <(jq -c '.[]' "$COMPLIANCE_FILE")
if [[ ${#violations[@]} -gt 0 ]]; then
echo "❌ Compatibility violations:"
for v in "${violations[@]}"; do
echo " - $v"
done
return 1
else
echo "✅ Compatible with all running services"
return 0
fi
}
# --- Get running versions from cluster ---
get_running_versions() {
local services=$@
local versions_json="{}"
for service in $services; do
local version
version=$(kubectl get deployment "$service" -o jsonpath='{.spec.template.metadata.labels.version}' 2>/dev/null)
if [[ -n "$version" ]]; then
versions_json=$(echo "$versions_json" | jq --arg s "$service" --arg v "$version" '. + {($s): $v}')
fi
done
echo "$versions_json"
}
# --- Usage ---
# running=$(get_running_versions user-service order-service payment-service notification-service)
# echo "$running" > /tmp/running-versions.json
# validate_deployment order-service "2.1.0" /tmp/running-versions.json
Track the state of a multi-service deployment for observability and recovery.
# ✅ GOOD: Track multi-service deployment state for observability and recovery
# Uses file-based state machine for crash recovery and audit trail
# --- State file configuration ---
DEPLOYMENT_ID="deploy-$(date +%Y%m%d-%H%M%S)"
STATE_DIR="/tmp/deployment-state/$DEPLOYMENT_ID"
STATE_FILE="$STATE_DIR/state.json"
HISTORY_FILE="$STATE_DIR/history.json"
# --- Initialize state ---
init_state() {
local total_batches=$1
mkdir -p "$STATE_DIR"
jq -n \
--arg id "$DEPLOYMENT_ID" \
--arg phase "planning" \
--argjson total "$total_batches" \
--arg started "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
'{
deployment_id: $id,
phase: $phase,
current_batch: "",
total_batches: $total,
completed_batches: 0,
failed_service: "",
error: "",
started_at: $started,
completed_at: null
}' > "$STATE_FILE"
echo "[]" > "$HISTORY_FILE"
}
# --- Record state transition ---
record_state() {
local phase=$1 batch_id=${2:-}
local timestamp
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
# Update main state
local state
state=$(cat "$STATE_FILE")
state=$(echo "$state" | jq --arg p "$phase" --arg b "$batch_id" '.phase = $p | .current_batch = $b')
# Add to history
local history
history=$(cat "$HISTORY_FILE")
jq --arg ts "$timestamp" --argjson s "$state" \
'. + [{timestamp: $ts, state: $s}]' "$HISTORY_FILE" > "${HISTORY_FILE}.tmp"
mv "${HISTORY_FILE}.tmp" "$HISTORY_FILE"
echo "$state" > "$STATE_FILE"
}
# --- Advance batch ---
advance_batch() {
local batch_id=$1
local state
state=$(cat "$STATE_FILE")
local completed
completed=$(echo "$state" | jq -r '.completed_batches')
echo "Advancing batch: $batch_id (completed: $((completed + 1)))"
jq --arg b "$batch_id" '.current_batch = $b | .completed_batches += 1 | .phase = "verifying"' \
"$STATE_FILE" > "${STATE_FILE}.tmp"
mv "${STATE_FILE}.tmp" "$STATE_FILE"
record_state "verifying" "$batch_id"
}
# --- Verify complete ---
verify_complete() {
echo "Verification phase complete"
jq '.phase = "deploying"' "$STATE_FILE" > "${STATE_FILE}.tmp"
mv "${STATE_FILE}.tmp" "$STATE_FILE"
record_state "deploying"
}
# --- Mark failure ---
mark_failed() {
local service=$1 error=$2
local timestamp
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
jq --arg s "$service" --arg e "$error" --arg t "$timestamp" \
'.phase = "failed" | .failed_service = $s | .error = $e | .completed_at = $t' \
"$STATE_FILE" > "${STATE_FILE}.tmp"
mv "${STATE_FILE}.tmp" "$STATE_FILE"
record_state "failed"
}
# --- Mark rollback ---
mark_rollback() {
echo "Beginning rollback phase"
jq '.phase = "rolling_back"' "$STATE_FILE" > "${STATE_FILE}.tmp"
mv "${STATE_FILE}.tmp" "$STATE_FILE"
record_state "rolling_back"
}
# --- Mark complete ---
mark_complete() {
local timestamp
timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
jq --arg t "$timestamp" '.phase = "complete" | .completed_at = $t' \
"$STATE_FILE" > "${STATE_FILE}.tmp"
mv "${STATE_FILE}.tmp" "$STATE_FILE"
record_state "complete"
}
# --- Get state ---
get_state() {
cat "$STATE_FILE" 2>/dev/null || echo "{}"
}
get_progress() {
local state
state=$(get_state)
local total completed
total=$(echo "$state" | jq -r '.total_batches')
completed=$(echo "$state" | jq -r '.completed_batches')
if [[ "$total" -eq 0 ]]; then
echo "0%"
else
echo "$(( (completed * 100) / total ))%"
fi
}
# --- Usage ---
# init_state 3
# advance_batch "batch-0"
# verify_complete
# mark_complete
# get_state | jq . # View current state
# cat "$HISTORY_FILE" | jq . # View state history
deployment:
strategy: batch_with_gates
parallel_limit: 3 # Max services deploying simultaneously
health_gate:
min_healthy_instances: 0.8
error_rate_threshold: 0.01
latency_p99_threshold_ms: 500
rollback:
auto_rollback: true
on_failure: all_previous_batches
order: reverse_topological
compatibility:
require_validation: true
window_timeout_hours: 24
This skill adheres to the 5 Laws of Elegant Defense:
DependencyGraph, ServiceBatch, CompatibilityWindow — names describe domain conceptsrollback_order() vs compute_batches() — operations are self-documenting| Skill Name | When to Use | Relationship |
|---|---|---|
deployment-philosophy | Use before this skill to determine if multi-service orchestration is needed | Prerequisite: strategy assessment |
canary-deployment | Use alongside when individual services within a batch use canary | Complementary: canary within orchestration |
blue-green-deployment | Use alongside when services use blue-green for zero-downtime | Complementary: blue-green within orchestration |
state-management | Use alongside when database/schema changes coordinate with application deployment | Complementary: data + code coordination |
When applying this skill, your output should contain:
Skill Version: 1.0.0
Created: 2026-05-15
Maturity: stable
Completeness: 95%