学习目标

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

  • 掌握节点角色管理和转换方法
  • 学会集群扩容和缩容操作
  • 了解节点故障检测和处理机制
  • 掌握集群备份和恢复策略
  • 学会集群健康监控和维护

1. 节点角色管理

1.1 节点角色概述

管理节点(Manager Node)

# 管理节点职责
- 集群状态管理
- 服务编排和调度
- API 端点提供
- Raft 共识算法参与
- 工作节点管理

# 管理节点特点
- 默认也可运行容器任务
- 参与 Raft 共识决策
- 存储集群状态信息
- 提供 Swarm API 接口

工作节点(Worker Node)

# 工作节点职责
- 运行容器任务
- 接收管理节点指令
- 报告节点状态
- 执行健康检查

# 工作节点特点
- 专注于任务执行
- 不参与集群决策
- 资源消耗较低
- 可大规模扩展

1.2 节点角色转换

工作节点提升为管理节点

# 查看当前节点状态
docker node ls

# 提升工作节点为管理节点
docker node promote <worker-node-id>

# 批量提升多个节点
docker node promote node1 node2 node3

# 验证提升结果
docker node ls

管理节点降级为工作节点

# 降级管理节点为工作节点
docker node demote <manager-node-id>

# 批量降级多个节点
docker node demote node1 node2

# 注意:不能降级最后一个管理节点
# 确保至少保留一个管理节点

角色转换最佳实践

# 管理节点数量建议
# 1 个:仅测试环境
# 3 个:小型生产环境(推荐奇数)
# 5 个:中型生产环境
# 7 个:大型生产环境(最大推荐)

# 转换前检查
docker node ls --filter role=manager
docker info --format '{{.Swarm.Managers}}'

# 确保 Raft 仲裁
# 仲裁数 = (N/2) + 1
# 3 个管理节点:仲裁数 = 2
# 5 个管理节点:仲裁数 = 3

1.3 节点可用性管理

节点可用性状态

# Active:活跃状态,接受新任务
# Pause:暂停状态,不接受新任务但保持现有任务
# Drain:排空状态,不接受新任务且迁移现有任务

设置节点可用性

# 设置节点为活跃状态
docker node update --availability active <node-id>

# 设置节点为暂停状态
docker node update --availability pause <node-id>

# 设置节点为排空状态
docker node update --availability drain <node-id>

# 查看节点可用性
docker node ls --format "table {{.ID}}\t{{.Hostname}}\t{{.Status}}\t{{.Availability}}"

节点维护操作

# 维护前排空节点
docker node update --availability drain <node-id>

# 等待任务迁移完成
docker node ps <node-id>

# 执行维护操作(系统更新、硬件维护等)
# ...

# 维护完成后恢复节点
docker node update --availability active <node-id>

2. 集群扩容操作

2.1 水平扩容

添加工作节点

# 在管理节点获取加入令牌
docker swarm join-token worker

# 在新节点执行加入命令
docker swarm join --token <worker-token> <manager-ip>:2377

# 验证节点加入
docker node ls

添加管理节点

# 获取管理节点加入令牌
docker swarm join-token manager

# 在新节点执行加入命令
docker swarm join --token <manager-token> <manager-ip>:2377

# 验证管理节点加入
docker node ls --filter role=manager

批量节点添加脚本

#!/bin/bash
# add-workers.sh

# 配置参数
MANAGER_IP="192.168.1.100"
WORKER_IPS=("192.168.1.101" "192.168.1.102" "192.168.1.103")

# 获取工作节点令牌
WORKER_TOKEN=$(docker swarm join-token worker -q)

# 批量添加工作节点
for ip in "${WORKER_IPS[@]}"; do
    echo "Adding worker node: $ip"
    ssh root@$ip "docker swarm join --token $WORKER_TOKEN $MANAGER_IP:2377"
    if [ $? -eq 0 ]; then
        echo "Successfully added worker: $ip"
    else
        echo "Failed to add worker: $ip"
    fi
done

# 验证结果
docker node ls

2.2 垂直扩容

节点资源升级

# 1. 排空节点
docker node update --availability drain <node-id>

# 2. 等待任务迁移
while [ $(docker node ps <node-id> --filter desired-state=running -q | wc -l) -gt 0 ]; do
    echo "Waiting for tasks to migrate..."
    sleep 10
done

# 3. 停止节点服务
ssh <node-ip> "sudo systemctl stop docker"

# 4. 升级硬件资源(CPU、内存、存储)
# ...

# 5. 重启节点服务
ssh <node-ip> "sudo systemctl start docker"

# 6. 恢复节点可用性
docker node update --availability active <node-id>

存储扩容

# 检查当前存储使用情况
docker system df
docker system df -v

# 清理未使用的资源
docker system prune -a

# 扩展存储卷(具体方法依赖于存储类型)
# LVM 扩容示例
sudo lvextend -L +50G /dev/mapper/docker-pool
sudo resize2fs /dev/mapper/docker-pool

2.3 扩容策略规划

容量规划

# 监控指标收集
docker stats --no-stream
docker system events --filter type=container

# 资源使用分析
#!/bin/bash
# resource-analysis.sh

echo "=== Node Resource Usage ==="
docker node ls --format "table {{.Hostname}}\t{{.Status}}\t{{.Availability}}"

echo "\n=== Service Resource Usage ==="
docker service ls --format "table {{.Name}}\t{{.Replicas}}\t{{.Image}}"

echo "\n=== System Resource Usage ==="
docker system df

echo "\n=== Container Resource Usage ==="
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"

自动扩容触发器

# 基于 CPU 使用率的扩容脚本
#!/bin/bash
# auto-scale.sh

CPU_THRESHOLD=80
MEM_THRESHOLD=80

# 检查集群资源使用率
CPU_USAGE=$(docker stats --no-stream --format "{{.CPUPerc}}" | sed 's/%//' | awk '{sum+=$1} END {print sum/NR}')
MEM_USAGE=$(docker stats --no-stream --format "{{.MemPerc}}" | sed 's/%//' | awk '{sum+=$1} END {print sum/NR}')

if (( $(echo "$CPU_USAGE > $CPU_THRESHOLD" | bc -l) )); then
    echo "CPU usage ($CPU_USAGE%) exceeds threshold ($CPU_THRESHOLD%)"
    # 触发扩容操作
    ./add-worker-node.sh
fi

if (( $(echo "$MEM_USAGE > $MEM_THRESHOLD" | bc -l) )); then
    echo "Memory usage ($MEM_USAGE%) exceeds threshold ($MEM_THRESHOLD%)"
    # 触发扩容操作
    ./add-worker-node.sh
fi

3. 集群缩容操作

3.1 安全缩容流程

工作节点移除

# 1. 排空节点任务
docker node update --availability drain <node-id>

# 2. 等待任务迁移完成
docker node ps <node-id> --filter desired-state=running

# 3. 在目标节点离开集群
ssh <node-ip> "docker swarm leave"

# 4. 在管理节点移除节点
docker node rm <node-id>

# 5. 验证移除结果
docker node ls

管理节点移除

# 1. 确保有足够的管理节点维持仲裁
docker node ls --filter role=manager

# 2. 降级管理节点为工作节点
docker node demote <manager-node-id>

# 3. 按工作节点移除流程操作
docker node update --availability drain <node-id>
ssh <node-ip> "docker swarm leave"
docker node rm <node-id>

强制移除节点

# 当节点无法正常通信时使用
docker node rm --force <node-id>

# 注意:强制移除可能导致数据丢失
# 仅在节点永久故障时使用

3.2 批量缩容脚本

#!/bin/bash
# remove-workers.sh

# 要移除的节点列表
NODES_TO_REMOVE=("worker3" "worker4" "worker5")

for node in "${NODES_TO_REMOVE[@]}"; do
    echo "Removing node: $node"
    
    # 获取节点 ID
    NODE_ID=$(docker node ls --filter name=$node --format "{{.ID}}")
    
    if [ -z "$NODE_ID" ]; then
        echo "Node $node not found"
        continue
    fi
    
    # 排空节点
    echo "Draining node $node..."
    docker node update --availability drain $NODE_ID
    
    # 等待任务迁移
    while [ $(docker node ps $NODE_ID --filter desired-state=running -q | wc -l) -gt 0 ]; do
        echo "Waiting for tasks to migrate from $node..."
        sleep 10
    done
    
    # 获取节点 IP
    NODE_IP=$(docker node inspect $NODE_ID --format '{{.Status.Addr}}')
    
    # 节点离开集群
    echo "Node $node leaving swarm..."
    ssh root@$NODE_IP "docker swarm leave" || echo "Failed to leave swarm on $node"
    
    # 移除节点
    echo "Removing node $node from cluster..."
    docker node rm $NODE_ID
    
    echo "Successfully removed node: $node"
done

echo "Cluster scaling down completed"
docker node ls

3.3 缩容后优化

资源重新分配

# 检查服务分布
docker service ps <service-name>

# 重新平衡服务
docker service update --force <service-name>

# 检查网络配置
docker network ls
docker network inspect ingress

存储清理

# 清理未使用的卷
docker volume prune

# 清理未使用的网络
docker network prune

# 清理未使用的镜像
docker image prune -a

# 系统整体清理
docker system prune -a --volumes

4. 节点故障处理

4.1 故障检测机制

健康检查配置

# 节点健康检查间隔
docker swarm update --task-history-limit 5

# 查看节点状态
docker node ls
docker node inspect <node-id> --format '{{.Status}}'

故障检测脚本

#!/bin/bash
# health-check.sh

echo "=== Swarm Cluster Health Check ==="
echo "Timestamp: $(date)"

# 检查集群状态
echo "\n1. Cluster Status:"
docker info --format '{{.Swarm.LocalNodeState}}'

# 检查节点状态
echo "\n2. Node Status:"
docker node ls --format "table {{.Hostname}}\t{{.Status}}\t{{.Availability}}\t{{.ManagerStatus}}"

# 检查不健康的节点
echo "\n3. Unhealthy Nodes:"
docker node ls --filter status=down

# 检查服务状态
echo "\n4. Service Status:"
docker service ls --format "table {{.Name}}\t{{.Replicas}}\t{{.Image}}"

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

# 检查网络状态
echo "\n6. Network Status:"
docker network ls --filter driver=overlay

# 资源使用情况
echo "\n7. Resource Usage:"
docker system df

4.2 常见故障处理

节点离线处理

# 检查离线节点
docker node ls --filter status=down

# 尝试重新连接
# 在离线节点上执行
sudo systemctl restart docker

# 如果节点无法恢复,强制移除
docker node rm --force <offline-node-id>

管理节点故障

# 检查管理节点状态
docker node ls --filter role=manager

# 如果失去仲裁,在剩余管理节点上执行
docker swarm init --force-new-cluster

# 重新添加管理节点
docker swarm join-token manager
# 在新节点执行加入命令

网络故障处理

# 检查网络连通性
docker network ls
docker network inspect ingress

# 重建 ingress 网络
docker network rm ingress
docker network create \
  --driver overlay \
  --ingress \
  --subnet=10.255.0.0/16 \
  --gateway=10.255.0.1 \
  ingress

4.3 故障恢复策略

自动故障转移

# 服务自动重启配置
docker service create \
  --name web \
  --replicas 3 \
  --restart-condition on-failure \
  --restart-max-attempts 3 \
  --restart-delay 10s \
  nginx

数据恢复

# 备份集群状态
docker run --rm -v /var/lib/docker/swarm:/backup alpine tar czf /backup/swarm-backup.tar.gz /backup

# 恢复集群状态
docker swarm init --force-new-cluster
# 恢复备份数据到 /var/lib/docker/swarm/

5. 集群备份与恢复

5.1 备份策略

集群状态备份

#!/bin/bash
# backup-swarm.sh

BACKUP_DIR="/backup/swarm/$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR

echo "Starting Swarm cluster backup..."

# 1. 备份集群配置
echo "Backing up cluster configuration..."
docker info --format '{{json .Swarm}}' > $BACKUP_DIR/cluster-info.json

# 2. 备份节点信息
echo "Backing up node information..."
docker node ls --format '{{json .}}' > $BACKUP_DIR/nodes.json

# 3. 备份服务配置
echo "Backing up service configurations..."
mkdir -p $BACKUP_DIR/services
for service in $(docker service ls --format '{{.Name}}'); do
    docker service inspect $service > $BACKUP_DIR/services/$service.json
done

# 4. 备份网络配置
echo "Backing up network configurations..."
mkdir -p $BACKUP_DIR/networks
for network in $(docker network ls --filter driver=overlay --format '{{.Name}}'); do
    docker network inspect $network > $BACKUP_DIR/networks/$network.json
done

# 5. 备份密钥和配置
echo "Backing up secrets and configs..."
mkdir -p $BACKUP_DIR/secrets $BACKUP_DIR/configs
docker secret ls --format '{{.Name}}' | xargs -I {} docker secret inspect {} > $BACKUP_DIR/secrets/{}.json 2>/dev/null
docker config ls --format '{{.Name}}' | xargs -I {} docker config inspect {} > $BACKUP_DIR/configs/{}.json 2>/dev/null

# 6. 备份 Swarm 数据目录
echo "Backing up Swarm data directory..."
sudo tar czf $BACKUP_DIR/swarm-data.tar.gz -C /var/lib/docker swarm

echo "Backup completed: $BACKUP_DIR"

定期备份配置

# 添加到 crontab
# 每天凌晨 2 点执行备份
0 2 * * * /opt/scripts/backup-swarm.sh

# 每周日凌晨 1 点清理旧备份(保留 30 天)
0 1 * * 0 find /backup/swarm -type d -mtime +30 -exec rm -rf {} \;

5.2 恢复策略

完整集群恢复

#!/bin/bash
# restore-swarm.sh

BACKUP_DIR="$1"

if [ -z "$BACKUP_DIR" ]; then
    echo "Usage: $0 <backup-directory>"
    exit 1
fi

echo "Starting Swarm cluster restore from: $BACKUP_DIR"

# 1. 停止 Docker 服务
echo "Stopping Docker service..."
sudo systemctl stop docker

# 2. 清理现有 Swarm 数据
echo "Cleaning existing Swarm data..."
sudo rm -rf /var/lib/docker/swarm

# 3. 恢复 Swarm 数据
echo "Restoring Swarm data..."
sudo tar xzf $BACKUP_DIR/swarm-data.tar.gz -C /var/lib/docker/

# 4. 启动 Docker 服务
echo "Starting Docker service..."
sudo systemctl start docker

# 5. 强制重新初始化集群
echo "Force reinitializing cluster..."
docker swarm init --force-new-cluster

# 6. 恢复服务
echo "Restoring services..."
for service_file in $BACKUP_DIR/services/*.json; do
    if [ -f "$service_file" ]; then
        service_name=$(basename "$service_file" .json)
        echo "Restoring service: $service_name"
        # 这里需要根据备份的服务配置重新创建服务
        # 具体实现依赖于服务配置格式
    fi
done

echo "Restore completed"
echo "Please verify cluster status and re-add nodes if necessary"

部分恢复操作

# 恢复单个服务
docker service create --name restored-service \
  $(cat backup/services/service-name.json | jq -r '.Spec')

# 恢复网络配置
docker network create \
  --driver overlay \
  $(cat backup/networks/network-name.json | jq -r '.Options')

# 恢复密钥
echo "secret-content" | docker secret create restored-secret -

5.3 灾难恢复计划

恢复优先级

# 1. 核心基础设施服务(优先级:高)
- 服务发现
- 负载均衡
- 数据库服务

# 2. 应用服务(优先级:中)
- Web 应用
- API 服务
- 缓存服务

# 3. 辅助服务(优先级:低)
- 监控服务
- 日志服务
- 备份服务

恢复时间目标(RTO)

# 定义恢复时间目标
RTO_CRITICAL=15min    # 关键服务
RTO_IMPORTANT=1hour   # 重要服务
RTO_NORMAL=4hours     # 普通服务

# 恢复点目标(RPO)
RPO_CRITICAL=5min     # 关键数据
RPO_IMPORTANT=1hour   # 重要数据
RPO_NORMAL=24hours    # 普通数据

6. 集群监控与维护

6.1 监控指标

集群级别监控

#!/bin/bash
# cluster-metrics.sh

echo "=== Cluster Metrics ==="
echo "Timestamp: $(date)"

# 节点数量统计
echo "\nNode Count:"
echo "Total: $(docker node ls | wc -l)"
echo "Managers: $(docker node ls --filter role=manager | wc -l)"
echo "Workers: $(docker node ls --filter role=worker | wc -l)"
echo "Active: $(docker node ls --filter status=ready | wc -l)"
echo "Down: $(docker node ls --filter status=down | wc -l)"

# 服务统计
echo "\nService Count:"
echo "Total: $(docker service ls | wc -l)"
echo "Running: $(docker service ls --filter mode=replicated | wc -l)"

# 任务统计
echo "\nTask Statistics:"
echo "Running: $(docker service ps --filter desired-state=running | wc -l)"
echo "Failed: $(docker service ps --filter desired-state=failed | wc -l)"

# 资源使用
echo "\nResource Usage:"
docker system df --format "table {{.Type}}\t{{.Total}}\t{{.Active}}\t{{.Size}}\t{{.Reclaimable}}"

节点级别监控

#!/bin/bash
# node-metrics.sh

for node in $(docker node ls --format '{{.Hostname}}'); do
    echo "=== Node: $node ==="
    
    # 节点基本信息
    docker node inspect $node --format '
    Status: {{.Status.State}}
    Availability: {{.Spec.Availability}}
    Role: {{.Spec.Role}}
    Engine Version: {{.Description.Engine.EngineVersion}}
    OS: {{.Description.Platform.OS}}
    Architecture: {{.Description.Platform.Architecture}}
    CPUs: {{.Description.Resources.NanoCPUs}}
    Memory: {{.Description.Resources.MemoryBytes}}'
    
    # 运行的任务数量
    echo "Running Tasks: $(docker node ps $node --filter desired-state=running | wc -l)"
    
    echo "---"
done

6.2 自动化维护

定期清理脚本

#!/bin/bash
# maintenance.sh

echo "Starting automated maintenance..."

# 1. 清理未使用的资源
echo "Cleaning unused resources..."
docker system prune -f

# 2. 清理旧的任务历史
echo "Cleaning old task history..."
docker swarm update --task-history-limit 3

# 3. 检查服务健康状态
echo "Checking service health..."
for service in $(docker service ls --format '{{.Name}}'); do
    replicas=$(docker service ls --filter name=$service --format '{{.Replicas}}')
    if [[ $replicas == *"0/"* ]]; then
        echo "WARNING: Service $service has no running replicas"
    fi
done

# 4. 检查节点状态
echo "Checking node status..."
down_nodes=$(docker node ls --filter status=down --format '{{.Hostname}}')
if [ ! -z "$down_nodes" ]; then
    echo "WARNING: Down nodes detected: $down_nodes"
fi

# 5. 生成维护报告
echo "Generating maintenance report..."
REPORT_FILE="/var/log/swarm-maintenance-$(date +%Y%m%d).log"
{
    echo "Maintenance Report - $(date)"
    echo "================================"
    docker node ls
    echo ""
    docker service ls
    echo ""
    docker system df
} > $REPORT_FILE

echo "Maintenance completed. Report saved to: $REPORT_FILE"

健康检查自动化

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

SLACK_WEBHOOK="https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"
EMAIL_RECIPIENT="admin@example.com"

# 检查集群健康状态
check_cluster_health() {
    local issues=()
    
    # 检查管理节点数量
    manager_count=$(docker node ls --filter role=manager --filter status=ready | wc -l)
    if [ $manager_count -lt 3 ]; then
        issues+=("Insufficient manager nodes: $manager_count")
    fi
    
    # 检查离线节点
    down_nodes=$(docker node ls --filter status=down --format '{{.Hostname}}')
    if [ ! -z "$down_nodes" ]; then
        issues+=("Down nodes: $down_nodes")
    fi
    
    # 检查失败的服务
    failed_services=$(docker service ps --filter desired-state=running --filter current-state=failed --format '{{.Name}}')
    if [ ! -z "$failed_services" ]; then
        issues+=("Failed services: $failed_services")
    fi
    
    # 发送告警
    if [ ${#issues[@]} -gt 0 ]; then
        alert_message="Swarm Cluster Issues Detected:\n$(printf '%s\n' "${issues[@]}")"
        
        # 发送 Slack 通知
        curl -X POST -H 'Content-type: application/json' \
            --data "{\"text\":\"$alert_message\"}" \
            $SLACK_WEBHOOK
        
        # 发送邮件通知
        echo -e "$alert_message" | mail -s "Swarm Cluster Alert" $EMAIL_RECIPIENT
    fi
}

# 每 5 分钟检查一次
while true; do
    check_cluster_health
    sleep 300
done

7. 实践练习

练习 1:节点角色管理

目标:练习节点角色转换和可用性管理

# 1. 查看当前集群状态
docker node ls

# 2. 提升一个工作节点为管理节点
docker node promote <worker-node>

# 3. 设置一个节点为维护模式
docker node update --availability drain <node-id>

# 4. 观察任务迁移过程
docker service ps <service-name>

# 5. 恢复节点可用性
docker node update --availability active <node-id>

# 6. 降级管理节点
docker node demote <manager-node>

练习 2:集群扩容缩容

目标:练习集群的动态扩容和缩容

# 1. 准备扩容脚本
cat > add-node.sh << 'EOF'
#!/bin/bash
NEW_NODE_IP="$1"
WORKER_TOKEN=$(docker swarm join-token worker -q)
MANAGER_IP=$(docker info --format '{{.Swarm.NodeAddr}}')

ssh root@$NEW_NODE_IP "docker swarm join --token $WORKER_TOKEN $MANAGER_IP:2377"
EOF

chmod +x add-node.sh

# 2. 添加新节点
./add-node.sh 192.168.1.104

# 3. 验证节点加入
docker node ls

# 4. 创建测试服务验证负载分布
docker service create --name test-lb --replicas 4 --publish 8080:80 nginx
docker service ps test-lb

# 5. 移除节点
docker node update --availability drain <node-id>
docker node rm <node-id>

# 6. 清理测试服务
docker service rm test-lb

练习 3:故障模拟与恢复

目标:模拟节点故障并练习恢复操作

# 1. 创建测试服务
docker service create --name fault-test --replicas 3 --publish 8081:80 nginx

# 2. 模拟节点故障(在工作节点上执行)
sudo systemctl stop docker

# 3. 观察服务自动恢复
docker service ps fault-test

# 4. 恢复故障节点
sudo systemctl start docker

# 5. 验证节点重新加入
docker node ls

# 6. 清理测试环境
docker service rm fault-test

8. 本章总结

关键要点

  1. 节点管理

    • 理解管理节点和工作节点的角色差异
    • 掌握节点角色转换和可用性控制
    • 合理规划管理节点数量保证高可用
  2. 集群扩容

    • 支持水平扩容(添加节点)和垂直扩容(升级资源)
    • 制定合理的扩容策略和触发条件
    • 实现自动化扩容脚本
  3. 集群缩容

    • 遵循安全缩容流程避免服务中断
    • 正确处理管理节点的移除
    • 缩容后进行资源优化
  4. 故障处理

    • 建立完善的故障检测机制
    • 掌握常见故障的处理方法
    • 制定故障恢复策略
  5. 备份恢复

    • 定期备份集群状态和配置
    • 制定灾难恢复计划
    • 验证备份的有效性

最佳实践

  1. 预防为主:建立完善的监控和告警机制
  2. 自动化运维:使用脚本自动化常见的运维操作
  3. 文档记录:详细记录集群配置和操作流程
  4. 定期演练:定期进行故障恢复演练
  5. 渐进式操作:重要操作先在测试环境验证

下一步学习

在下一章中,我们将学习服务部署与管理,包括:

  • 服务创建和配置
  • 服务更新和回滚
  • 服务扩缩容
  • 服务约束和调度策略

检查清单: - [ ] 理解节点角色和管理方法 - [ ] 掌握集群扩容缩容操作 - [ ] 学会故障检测和处理 - [ ] 建立备份恢复机制 - [ ] 实现自动化监控维护