学习目标

通过本章学习,您将能够:

  • 掌握 Docker Swarm 服务的创建和配置方法
  • 学会服务的更新、回滚和版本管理
  • 了解服务扩缩容的策略和实现
  • 掌握服务约束和调度策略
  • 学会服务监控和故障排除

1. 服务基础概念

1.1 服务与任务

服务(Service)

# 服务是 Swarm 中的核心概念
# 定义了容器的期望状态和运行规范

# 服务特性:
- 声明式配置
- 自动故障恢复
- 负载均衡
- 滚动更新
- 服务发现

任务(Task)

# 任务是服务的具体执行单元
# 每个任务对应一个容器实例

# 任务状态:
- NEW:新创建的任务
- PENDING:等待调度
- ASSIGNED:已分配到节点
- ACCEPTED:节点已接受任务
- PREPARING:准备启动容器
- STARTING:正在启动容器
- RUNNING:容器正在运行
- COMPLETE:任务完成
- FAILED:任务失败
- SHUTDOWN:任务关闭
- REJECTED:任务被拒绝

1.2 服务类型

复制服务(Replicated Service)

# 指定副本数量的服务
docker service create --name web --replicas 3 nginx

# 特点:
- 指定固定的副本数量
- 自动负载均衡
- 故障时自动重启
- 支持滚动更新

全局服务(Global Service)

# 在每个节点上运行一个实例
docker service create --name monitor --mode global alpine top

# 特点:
- 每个节点运行一个实例
- 新节点加入时自动部署
- 适用于监控、日志收集等场景
- 不支持副本数量设置

2. 服务创建与配置

2.1 基本服务创建

简单服务创建

# 创建基本服务
docker service create --name my-web nginx

# 指定副本数量
docker service create --name my-web --replicas 3 nginx

# 发布端口
docker service create --name my-web --replicas 3 --publish 80:80 nginx

# 查看服务列表
docker service ls

# 查看服务详情
docker service inspect my-web

# 查看服务任务
docker service ps my-web

服务配置选项

# 完整的服务创建命令
docker service create \
  --name web-app \
  --replicas 3 \
  --publish published=80,target=8080,protocol=tcp,mode=ingress \
  --env APP_ENV=production \
  --env DB_HOST=database \
  --mount type=volume,source=web-data,target=/app/data \
  --network my-overlay-network \
  --constraint 'node.role == worker' \
  --limit-cpu 0.5 \
  --limit-memory 512M \
  --reserve-cpu 0.25 \
  --reserve-memory 256M \
  --restart-condition on-failure \
  --restart-max-attempts 3 \
  --restart-delay 10s \
  --update-delay 30s \
  --update-parallelism 1 \
  --rollback-parallelism 1 \
  --health-cmd "curl -f http://localhost:8080/health || exit 1" \
  --health-interval 30s \
  --health-timeout 10s \
  --health-retries 3 \
  my-app:latest

2.2 环境变量配置

基本环境变量

# 单个环境变量
docker service create --name app --env NODE_ENV=production node:alpine

# 多个环境变量
docker service create --name app \
  --env NODE_ENV=production \
  --env PORT=3000 \
  --env DB_HOST=postgres \
  node:alpine

# 从文件读取环境变量
echo "NODE_ENV=production" > app.env
echo "PORT=3000" >> app.env
docker service create --name app --env-file app.env node:alpine

环境变量模板

# 使用模板变量
docker service create --name app \
  --env SERVICE_NAME={{.Service.Name}} \
  --env NODE_ID={{.Node.ID}} \
  --env TASK_ID={{.Task.ID}} \
  --env TASK_SLOT={{.Task.Slot}} \
  alpine printenv

# 可用的模板变量:
# {{.Service.ID}}     - 服务 ID
# {{.Service.Name}}   - 服务名称
# {{.Service.Labels}} - 服务标签
# {{.Node.ID}}        - 节点 ID
# {{.Node.Hostname}}  - 节点主机名
# {{.Task.ID}}        - 任务 ID
# {{.Task.Name}}      - 任务名称
# {{.Task.Slot}}      - 任务槽位

2.3 存储配置

卷挂载

# 命名卷挂载
docker volume create web-data
docker service create --name web \
  --mount type=volume,source=web-data,target=/var/www/html \
  nginx

# 绑定挂载
docker service create --name web \
  --mount type=bind,source=/host/path,target=/container/path \
  nginx

# 临时文件系统
docker service create --name web \
  --mount type=tmpfs,target=/tmp,tmpfs-size=100m \
  nginx

存储配置选项

# 卷挂载的详细配置
docker service create --name database \
  --mount type=volume,source=db-data,target=/var/lib/mysql,volume-driver=local,volume-opt=type=nfs,volume-opt=device=:/path/to/nfs \
  mysql:8.0

# 只读挂载
docker service create --name web \
  --mount type=volume,source=web-content,target=/usr/share/nginx/html,readonly \
  nginx

# 绑定挂载的详细配置
docker service create --name app \
  --mount type=bind,source=/host/config,target=/app/config,readonly,bind-propagation=rprivate \
  my-app:latest

2.4 网络配置

网络连接

# 创建自定义网络
docker network create --driver overlay my-network

# 连接服务到网络
docker service create --name web --network my-network nginx

# 连接到多个网络
docker service create --name app \
  --network frontend \
  --network backend \
  my-app:latest

# 网络别名
docker service create --name database \
  --network backend \
  --network-alias db \
  --network-alias mysql \
  mysql:8.0

端口发布

# 基本端口发布
docker service create --name web --publish 80:80 nginx

# 指定协议
docker service create --name web --publish 80:80/tcp nginx

# 发布到特定接口
docker service create --name web --publish 192.168.1.100:80:80 nginx

# 发布模式
# ingress 模式(默认)- 通过路由网格负载均衡
docker service create --name web --publish published=80,target=80,mode=ingress nginx

# host 模式 - 直接发布到主机端口
docker service create --name web --publish published=80,target=80,mode=host nginx

3. 服务更新与回滚

3.1 服务更新策略

滚动更新配置

# 创建带更新策略的服务
docker service create --name web \
  --replicas 6 \
  --update-delay 30s \
  --update-parallelism 2 \
  --update-failure-action rollback \
  --update-monitor 60s \
  --update-max-failure-ratio 0.3 \
  nginx:1.20

# 更新策略参数说明:
# --update-delay: 更新任务间的延迟
# --update-parallelism: 并行更新的任务数
# --update-failure-action: 失败时的动作(pause/continue/rollback)
# --update-monitor: 监控期间
# --update-max-failure-ratio: 最大失败比例

回滚策略配置

# 配置回滚策略
docker service create --name web \
  --replicas 4 \
  --rollback-delay 10s \
  --rollback-parallelism 1 \
  --rollback-failure-action pause \
  --rollback-monitor 30s \
  --rollback-max-failure-ratio 0.2 \
  nginx:1.20

3.2 执行服务更新

镜像更新

# 更新服务镜像
docker service update --image nginx:1.21 web

# 强制更新(即使镜像相同)
docker service update --force web

# 查看更新进度
docker service ps web

# 查看更新历史
docker service inspect web --format '{{json .UpdateStatus}}'

配置更新

# 更新副本数量
docker service update --replicas 8 web

# 更新环境变量
docker service update --env-add NEW_VAR=value web
docker service update --env-rm OLD_VAR web

# 更新端口发布
docker service update --publish-add 8080:80 web
docker service update --publish-rm 80:80 web

# 更新资源限制
docker service update --limit-cpu 1.0 --limit-memory 1G web

# 更新约束条件
docker service update --constraint-add 'node.labels.environment == production' web

批量更新脚本

#!/bin/bash
# update-services.sh

SERVICES=("web" "api" "worker")
NEW_IMAGE_TAG="v2.0"

for service in "${SERVICES[@]}"; do
    echo "Updating service: $service"
    
    # 获取当前镜像
    current_image=$(docker service inspect $service --format '{{.Spec.TaskTemplate.ContainerSpec.Image}}')
    base_image=$(echo $current_image | cut -d':' -f1)
    new_image="$base_image:$NEW_IMAGE_TAG"
    
    echo "Updating $service from $current_image to $new_image"
    
    # 执行更新
    docker service update --image $new_image $service
    
    # 等待更新完成
    while [ $(docker service ps $service --filter desired-state=running --filter current-state=running | wc -l) -lt $(docker service inspect $service --format '{{.Spec.Replicas}}') ]; do
        echo "Waiting for $service update to complete..."
        sleep 10
    done
    
    echo "Service $service updated successfully"
done

echo "All services updated"

3.3 服务回滚

手动回滚

# 回滚到上一个版本
docker service rollback web

# 查看回滚进度
docker service ps web

# 查看服务历史
docker service inspect web --format '{{json .PreviousSpec}}'

自动回滚

# 配置自动回滚的服务
docker service create --name web \
  --replicas 4 \
  --update-failure-action rollback \
  --update-monitor 60s \
  --update-max-failure-ratio 0.3 \
  --rollback-parallelism 2 \
  --rollback-delay 10s \
  nginx:1.20

# 触发失败更新(模拟)
docker service update --image nginx:invalid-tag web

# 观察自动回滚过程
docker service ps web

回滚验证脚本

#!/bin/bash
# rollback-verification.sh

SERVICE_NAME="$1"
HEALTH_CHECK_URL="$2"

if [ -z "$SERVICE_NAME" ] || [ -z "$HEALTH_CHECK_URL" ]; then
    echo "Usage: $0 <service-name> <health-check-url>"
    exit 1
fi

echo "Rolling back service: $SERVICE_NAME"

# 执行回滚
docker service rollback $SERVICE_NAME

# 等待回滚完成
echo "Waiting for rollback to complete..."
while [ $(docker service ps $SERVICE_NAME --filter desired-state=running --filter current-state=running | wc -l) -lt $(docker service inspect $SERVICE_NAME --format '{{.Spec.Replicas}}') ]; do
    sleep 5
done

# 健康检查
echo "Performing health check..."
for i in {1..10}; do
    if curl -f $HEALTH_CHECK_URL > /dev/null 2>&1; then
        echo "Health check passed"
        exit 0
    fi
    echo "Health check failed, retrying in 10 seconds..."
    sleep 10
done

echo "Health check failed after rollback"
exit 1

4. 服务扩缩容

4.1 手动扩缩容

基本扩缩容操作

# 扩容服务
docker service scale web=10

# 缩容服务
docker service scale web=3

# 批量扩缩容
docker service scale web=5 api=3 worker=8

# 使用 update 命令扩缩容
docker service update --replicas 6 web

# 查看扩缩容进度
docker service ps web
watch docker service ls

扩缩容验证

#!/bin/bash
# scale-verification.sh

SERVICE_NAME="$1"
TARGET_REPLICAS="$2"

if [ -z "$SERVICE_NAME" ] || [ -z "$TARGET_REPLICAS" ]; then
    echo "Usage: $0 <service-name> <target-replicas>"
    exit 1
fi

echo "Scaling $SERVICE_NAME to $TARGET_REPLICAS replicas"

# 执行扩缩容
docker service scale $SERVICE_NAME=$TARGET_REPLICAS

# 等待扩缩容完成
echo "Waiting for scaling to complete..."
while true; do
    current_replicas=$(docker service ps $SERVICE_NAME --filter desired-state=running --filter current-state=running | wc -l)
    echo "Current replicas: $current_replicas, Target: $TARGET_REPLICAS"
    
    if [ $current_replicas -eq $TARGET_REPLICAS ]; then
        echo "Scaling completed successfully"
        break
    fi
    
    sleep 5
done

# 显示最终状态
docker service ls --filter name=$SERVICE_NAME
docker service ps $SERVICE_NAME

4.2 自动扩缩容

基于 CPU 使用率的自动扩容

#!/bin/bash
# auto-scale-cpu.sh

SERVICE_NAME="web"
MIN_REPLICAS=2
MAX_REPLICAS=10
CPU_THRESHOLD_UP=80
CPU_THRESHOLD_DOWN=30
SCALE_UP_STEP=2
SCALE_DOWN_STEP=1

while true; do
    # 获取当前副本数
    current_replicas=$(docker service inspect $SERVICE_NAME --format '{{.Spec.Replicas}}')
    
    # 获取 CPU 使用率(需要监控系统支持)
    cpu_usage=$(docker stats --no-stream --format "{{.CPUPerc}}" | grep $SERVICE_NAME | sed 's/%//' | awk '{sum+=$1} END {print sum/NR}')
    
    echo "Current replicas: $current_replicas, CPU usage: $cpu_usage%"
    
    # 扩容条件
    if (( $(echo "$cpu_usage > $CPU_THRESHOLD_UP" | bc -l) )) && [ $current_replicas -lt $MAX_REPLICAS ]; then
        new_replicas=$((current_replicas + SCALE_UP_STEP))
        if [ $new_replicas -gt $MAX_REPLICAS ]; then
            new_replicas=$MAX_REPLICAS
        fi
        echo "Scaling up to $new_replicas replicas"
        docker service scale $SERVICE_NAME=$new_replicas
    
    # 缩容条件
    elif (( $(echo "$cpu_usage < $CPU_THRESHOLD_DOWN" | bc -l) )) && [ $current_replicas -gt $MIN_REPLICAS ]; then
        new_replicas=$((current_replicas - SCALE_DOWN_STEP))
        if [ $new_replicas -lt $MIN_REPLICAS ]; then
            new_replicas=$MIN_REPLICAS
        fi
        echo "Scaling down to $new_replicas replicas"
        docker service scale $SERVICE_NAME=$new_replicas
    fi
    
    sleep 60
done

基于请求队列的自动扩容

#!/bin/bash
# auto-scale-queue.sh

SERVICE_NAME="worker"
MIN_REPLICAS=1
MAX_REPLICAS=20
QUEUE_THRESHOLD_UP=100
QUEUE_THRESHOLD_DOWN=10
REDIS_HOST="redis"
QUEUE_NAME="task_queue"

get_queue_length() {
    docker run --rm --network backend redis:alpine redis-cli -h $REDIS_HOST llen $QUEUE_NAME
}

while true; do
    current_replicas=$(docker service inspect $SERVICE_NAME --format '{{.Spec.Replicas}}')
    queue_length=$(get_queue_length)
    
    echo "Current replicas: $current_replicas, Queue length: $queue_length"
    
    # 基于队列长度计算所需副本数
    needed_replicas=$(( (queue_length + 9) / 10 ))  # 每10个任务需要1个副本
    
    if [ $needed_replicas -lt $MIN_REPLICAS ]; then
        needed_replicas=$MIN_REPLICAS
    elif [ $needed_replicas -gt $MAX_REPLICAS ]; then
        needed_replicas=$MAX_REPLICAS
    fi
    
    if [ $needed_replicas -ne $current_replicas ]; then
        echo "Scaling to $needed_replicas replicas"
        docker service scale $SERVICE_NAME=$needed_replicas
    fi
    
    sleep 30
done

4.3 扩缩容策略

预测性扩容

#!/bin/bash
# predictive-scaling.sh

# 基于历史数据预测负载
analyze_historical_load() {
    local hour=$(date +%H)
    local day=$(date +%u)
    
    # 工作日高峰期(9-18点)
    if [ $day -le 5 ] && [ $hour -ge 9 ] && [ $hour -le 18 ]; then
        echo "peak"
    # 工作日非高峰期
    elif [ $day -le 5 ]; then
        echo "normal"
    # 周末
    else
        echo "low"
    fi
}

# 根据预测调整副本数
adjust_replicas_by_prediction() {
    local service=$1
    local load_pattern=$(analyze_historical_load)
    
    case $load_pattern in
        "peak")
            target_replicas=8
            ;;
        "normal")
            target_replicas=4
            ;;
        "low")
            target_replicas=2
            ;;
    esac
    
    current_replicas=$(docker service inspect $service --format '{{.Spec.Replicas}}')
    
    if [ $current_replicas -ne $target_replicas ]; then
        echo "Predictive scaling: $service from $current_replicas to $target_replicas (pattern: $load_pattern)"
        docker service scale $service=$target_replicas
    fi
}

# 每小时执行一次预测性扩容
while true; do
    adjust_replicas_by_prediction "web"
    adjust_replicas_by_prediction "api"
    sleep 3600
done

5. 服务约束与调度

5.1 节点约束

基于节点属性的约束

# 基于节点角色约束
docker service create --name web \
  --constraint 'node.role == worker' \
  nginx

# 基于节点 ID 约束
docker service create --name database \
  --constraint 'node.id == abc123def456' \
  mysql:8.0

# 基于节点主机名约束
docker service create --name cache \
  --constraint 'node.hostname == cache-server' \
  redis:alpine

# 基于节点标签约束
docker service create --name compute-task \
  --constraint 'node.labels.type == compute' \
  --constraint 'node.labels.environment == production' \
  my-compute-app:latest

排除约束

# 排除特定节点
docker service create --name web \
  --constraint 'node.hostname != maintenance-node' \
  nginx

# 排除特定标签的节点
docker service create --name production-app \
  --constraint 'node.labels.environment != development' \
  my-app:latest

5.2 放置偏好

分布策略

# 基于可用区分布
docker service create --name web \
  --replicas 6 \
  --placement-pref 'spread=node.labels.zone' \
  nginx

# 基于机架分布
docker service create --name database \
  --replicas 3 \
  --placement-pref 'spread=node.labels.rack' \
  mysql:8.0

# 多级分布策略
docker service create --name app \
  --replicas 9 \
  --placement-pref 'spread=node.labels.datacenter' \
  --placement-pref 'spread=node.labels.rack' \
  my-app:latest

节点标签管理

# 添加节点标签
docker node update --label-add zone=us-west-1a node1
docker node update --label-add zone=us-west-1b node2
docker node update --label-add zone=us-west-1c node3

docker node update --label-add rack=rack1 node1
docker node update --label-add rack=rack2 node2
docker node update --label-add rack=rack3 node3

docker node update --label-add type=compute node1
docker node update --label-add type=storage node2
docker node update --label-add type=network node3

# 查看节点标签
docker node inspect node1 --format '{{json .Spec.Labels}}'

# 删除节点标签
docker node update --label-rm zone node1

5.3 资源约束

CPU 和内存限制

# 设置资源限制和预留
docker service create --name web \
  --replicas 3 \
  --limit-cpu 1.0 \
  --limit-memory 1G \
  --reserve-cpu 0.5 \
  --reserve-memory 512M \
  nginx

# 查看资源使用情况
docker service ps web --format "table {{.Name}}\t{{.Node}}\t{{.CurrentState}}"
docker stats --no-stream

资源配额管理

#!/bin/bash
# resource-quota.sh

# 检查节点资源使用情况
check_node_resources() {
    echo "=== Node Resource Usage ==="
    for node in $(docker node ls --format '{{.Hostname}}'); do
        echo "Node: $node"
        
        # 获取节点总资源
        total_cpu=$(docker node inspect $node --format '{{.Description.Resources.NanoCPUs}}')
        total_memory=$(docker node inspect $node --format '{{.Description.Resources.MemoryBytes}}')
        
        # 转换单位
        total_cpu_cores=$(echo "scale=2; $total_cpu / 1000000000" | bc)
        total_memory_gb=$(echo "scale=2; $total_memory / 1073741824" | bc)
        
        echo "  Total CPU: ${total_cpu_cores} cores"
        echo "  Total Memory: ${total_memory_gb} GB"
        
        # 计算已使用资源(需要遍历该节点上的所有任务)
        echo "  Running tasks:"
        docker node ps $node --filter desired-state=running --format "    {{.Name}}"
        echo "---"
    done
}

# 验证服务资源需求
validate_service_resources() {
    local service_name=$1
    local cpu_limit=$2
    local memory_limit=$3
    local replicas=$4
    
    echo "Validating resources for service: $service_name"
    echo "CPU limit per replica: $cpu_limit"
    echo "Memory limit per replica: $memory_limit"
    echo "Replicas: $replicas"
    
    # 计算总资源需求
    total_cpu_needed=$(echo "$cpu_limit * $replicas" | bc)
    total_memory_needed=$(echo "$memory_limit * $replicas" | bc)
    
    echo "Total CPU needed: $total_cpu_needed cores"
    echo "Total Memory needed: $total_memory_needed GB"
    
    # 检查集群是否有足够资源
    # 这里需要实现具体的资源检查逻辑
}

check_node_resources
validate_service_resources "web" "0.5" "1" "6"

6. 服务监控与故障排除

6.1 服务状态监控

基本监控命令

# 查看服务列表
docker service ls

# 查看服务详细信息
docker service inspect web

# 查看服务任务
docker service ps web

# 查看服务日志
docker service logs web
docker service logs --follow web
docker service logs --tail 100 web

# 实时监控服务状态
watch docker service ps web

服务健康监控脚本

#!/bin/bash
# service-health-monitor.sh

SERVICES=("web" "api" "database" "cache")
ALERT_EMAIL="admin@example.com"
SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

check_service_health() {
    local service=$1
    local issues=()
    
    # 检查服务是否存在
    if ! docker service inspect $service > /dev/null 2>&1; then
        issues+=("Service $service does not exist")
        return
    fi
    
    # 获取期望和实际副本数
    desired_replicas=$(docker service inspect $service --format '{{.Spec.Replicas}}')
    running_replicas=$(docker service ps $service --filter desired-state=running --filter current-state=running | wc -l)
    
    if [ $running_replicas -lt $desired_replicas ]; then
        issues+=("Service $service: $running_replicas/$desired_replicas replicas running")
    fi
    
    # 检查失败的任务
    failed_tasks=$(docker service ps $service --filter desired-state=running --filter current-state=failed | wc -l)
    if [ $failed_tasks -gt 0 ]; then
        issues+=("Service $service has $failed_tasks failed tasks")
    fi
    
    # 检查任务重启次数
    restart_count=$(docker service ps $service --no-trunc | grep -c "Shutdown")
    if [ $restart_count -gt 5 ]; then
        issues+=("Service $service has high restart count: $restart_count")
    fi
    
    # 发送告警
    if [ ${#issues[@]} -gt 0 ]; then
        alert_message="Service Health Issues:\n$(printf '%s\n' "${issues[@]}")"
        
        # 发送邮件告警
        echo -e "$alert_message" | mail -s "Service Health Alert" $ALERT_EMAIL
        
        # 发送 Slack 告警
        curl -X POST -H 'Content-type: application/json' \
            --data "{\"text\":\"$alert_message\"}" \
            $SLACK_WEBHOOK
        
        echo "Alert sent for service: $service"
    else
        echo "Service $service is healthy"
    fi
}

# 监控所有服务
while true; do
    echo "=== Service Health Check - $(date) ==="
    for service in "${SERVICES[@]}"; do
        check_service_health $service
    done
    echo "---"
    sleep 300  # 每5分钟检查一次
done

6.2 故障排除

常见问题诊断

#!/bin/bash
# service-troubleshoot.sh

SERVICE_NAME="$1"

if [ -z "$SERVICE_NAME" ]; then
    echo "Usage: $0 <service-name>"
    exit 1
fi

echo "=== Troubleshooting Service: $SERVICE_NAME ==="

# 1. 检查服务基本信息
echo "1. Service Basic Info:"
docker service inspect $SERVICE_NAME --format '
Name: {{.Spec.Name}}
Image: {{.Spec.TaskTemplate.ContainerSpec.Image}}
Replicas: {{.Spec.Replicas}}
Update Status: {{.UpdateStatus.State}}
Created: {{.CreatedAt}}
Updated: {{.UpdatedAt}}'

# 2. 检查任务状态
echo "\n2. Task Status:"
docker service ps $SERVICE_NAME --no-trunc

# 3. 检查失败的任务
echo "\n3. Failed Tasks:"
docker service ps $SERVICE_NAME --filter desired-state=running --filter current-state=failed --no-trunc

# 4. 检查服务日志
echo "\n4. Recent Service Logs:"
docker service logs --tail 50 $SERVICE_NAME

# 5. 检查网络连接
echo "\n5. Network Information:"
docker service inspect $SERVICE_NAME --format '{{range .Spec.TaskTemplate.Networks}}Network: {{.Target}}{{end}}'

# 6. 检查资源约束
echo "\n6. Resource Constraints:"
docker service inspect $SERVICE_NAME --format '
CPU Limit: {{.Spec.TaskTemplate.Resources.Limits.NanoCPUs}}
Memory Limit: {{.Spec.TaskTemplate.Resources.Limits.MemoryBytes}}
CPU Reservation: {{.Spec.TaskTemplate.Resources.Reservations.NanoCPUs}}
Memory Reservation: {{.Spec.TaskTemplate.Resources.Reservations.MemoryBytes}}'

# 7. 检查放置约束
echo "\n7. Placement Constraints:"
docker service inspect $SERVICE_NAME --format '{{range .Spec.TaskTemplate.Placement.Constraints}}Constraint: {{.}}{{end}}'

# 8. 检查节点可用性
echo "\n8. Node Availability:"
docker node ls

# 9. 检查集群资源
echo "\n9. Cluster Resources:"
docker system df

# 10. 建议的修复操作
echo "\n10. Suggested Actions:"
echo "- Check if the image exists and is accessible"
echo "- Verify network connectivity between nodes"
echo "- Check if nodes have sufficient resources"
echo "- Review placement constraints and node labels"
echo "- Check service logs for application-specific errors"
echo "- Consider rolling back if this is an update issue"

自动修复脚本

#!/bin/bash
# auto-heal-service.sh

SERVICE_NAME="$1"
MAX_FAILED_TASKS=3
MAX_RESTART_ATTEMPTS=5

auto_heal_service() {
    local service=$1
    
    echo "Checking service health: $service"
    
    # 检查失败任务数量
    failed_tasks=$(docker service ps $service --filter current-state=failed | wc -l)
    
    if [ $failed_tasks -gt $MAX_FAILED_TASKS ]; then
        echo "Service $service has $failed_tasks failed tasks, attempting auto-heal"
        
        # 尝试强制更新服务
        echo "Performing force update..."
        docker service update --force $service
        
        # 等待更新完成
        sleep 30
        
        # 再次检查
        new_failed_tasks=$(docker service ps $service --filter current-state=failed | wc -l)
        
        if [ $new_failed_tasks -lt $failed_tasks ]; then
            echo "Auto-heal successful for service: $service"
        else
            echo "Auto-heal failed for service: $service, manual intervention required"
            # 发送告警
            echo "Service $service auto-heal failed" | mail -s "Auto-heal Failed" admin@example.com
        fi
    fi
}

# 检查重启次数
check_restart_count() {
    local service=$1
    
    restart_count=$(docker service ps $service --no-trunc | grep -c "Shutdown")
    
    if [ $restart_count -gt $MAX_RESTART_ATTEMPTS ]; then
        echo "Service $service has excessive restarts ($restart_count), investigating..."
        
        # 获取最新的错误日志
        docker service logs --tail 20 $service
        
        # 检查资源使用情况
        docker stats --no-stream | grep $service
        
        echo "Manual investigation required for service: $service"
    fi
}

if [ -z "$SERVICE_NAME" ]; then
    # 检查所有服务
    for service in $(docker service ls --format '{{.Name}}'); do
        auto_heal_service $service
        check_restart_count $service
    done
else
    # 检查指定服务
    auto_heal_service $SERVICE_NAME
    check_restart_count $SERVICE_NAME
fi

7. 实践练习

练习 1:Web 应用部署

目标:部署一个完整的 Web 应用栈

# 1. 创建网络
docker network create --driver overlay web-network

# 2. 部署数据库服务
docker service create --name database \
  --network web-network \
  --env MYSQL_ROOT_PASSWORD=rootpass \
  --env MYSQL_DATABASE=webapp \
  --env MYSQL_USER=webuser \
  --env MYSQL_PASSWORD=webpass \
  --mount type=volume,source=db-data,target=/var/lib/mysql \
  --constraint 'node.labels.type == storage' \
  mysql:8.0

# 3. 部署 Web 应用
docker service create --name webapp \
  --network web-network \
  --replicas 3 \
  --publish 80:80 \
  --env DB_HOST=database \
  --env DB_USER=webuser \
  --env DB_PASSWORD=webpass \
  --env DB_NAME=webapp \
  --constraint 'node.role == worker' \
  --update-delay 30s \
  --update-parallelism 1 \
  --update-failure-action rollback \
  nginx:alpine

# 4. 验证部署
docker service ls
docker service ps webapp
docker service ps database

# 5. 测试应用
curl http://localhost

练习 2:服务更新与回滚

目标:练习服务的滚动更新和回滚操作

# 1. 创建初始服务
docker service create --name update-demo \
  --replicas 4 \
  --publish 8080:80 \
  --update-delay 10s \
  --update-parallelism 2 \
  --update-failure-action rollback \
  nginx:1.20

# 2. 监控更新过程
watch docker service ps update-demo

# 3. 执行更新
docker service update --image nginx:1.21 update-demo

# 4. 观察更新进度
docker service ps update-demo

# 5. 模拟失败更新
docker service update --image nginx:invalid-tag update-demo

# 6. 观察自动回滚
docker service ps update-demo

# 7. 手动回滚
docker service rollback update-demo

# 8. 清理
docker service rm update-demo

练习 3:自动扩缩容

目标:实现基于负载的自动扩缩容

# 1. 创建测试服务
docker service create --name load-test \
  --replicas 2 \
  --publish 8081:80 \
  --limit-cpu 0.5 \
  --limit-memory 256M \
  nginx:alpine

# 2. 创建负载生成器
docker service create --name load-generator \
  --mode global \
  alpine sh -c 'while true; do wget -q -O- http://load-test:80; sleep 1; done'

# 3. 监控资源使用
watch docker stats

# 4. 手动扩容测试
docker service scale load-test=6

# 5. 观察负载分布
docker service ps load-test

# 6. 缩容测试
docker service scale load-test=2

# 7. 清理
docker service rm load-test load-generator

8. 本章总结

关键要点

  1. 服务管理

    • 理解服务和任务的概念
    • 掌握服务创建和配置方法
    • 学会环境变量、存储和网络配置
  2. 更新回滚

    • 配置滚动更新策略
    • 实现零停机更新
    • 掌握自动和手动回滚
  3. 扩缩容

    • 手动扩缩容操作
    • 自动扩缩容策略
    • 预测性扩容
  4. 调度策略

    • 节点约束和放置偏好
    • 资源限制和预留
    • 高可用部署策略
  5. 监控维护

    • 服务健康监控
    • 故障诊断和排除
    • 自动修复机制

最佳实践

  1. 服务设计:遵循微服务设计原则,保持服务的单一职责
  2. 资源规划:合理设置资源限制和预留,避免资源争用
  3. 更新策略:配置合适的更新参数,确保服务稳定性
  4. 监控告警:建立完善的监控和告警机制
  5. 自动化:使用脚本自动化常见的运维操作

下一步学习

在下一章中,我们将学习网络管理,包括:

  • Overlay 网络原理
  • 服务发现和负载均衡
  • 网络安全和隔离
  • 自定义网络配置

检查清单: - [ ] 掌握服务创建和配置 - [ ] 学会服务更新和回滚 - [ ] 理解扩缩容策略 - [ ] 掌握调度约束配置 - [ ] 建立监控和故障排除机制