学习目标
通过本章学习,您将能够:
- 掌握 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. 本章总结
关键要点
服务管理
- 理解服务和任务的概念
- 掌握服务创建和配置方法
- 学会环境变量、存储和网络配置
更新回滚
- 配置滚动更新策略
- 实现零停机更新
- 掌握自动和手动回滚
扩缩容
- 手动扩缩容操作
- 自动扩缩容策略
- 预测性扩容
调度策略
- 节点约束和放置偏好
- 资源限制和预留
- 高可用部署策略
监控维护
- 服务健康监控
- 故障诊断和排除
- 自动修复机制
最佳实践
- 服务设计:遵循微服务设计原则,保持服务的单一职责
- 资源规划:合理设置资源限制和预留,避免资源争用
- 更新策略:配置合适的更新参数,确保服务稳定性
- 监控告警:建立完善的监控和告警机制
- 自动化:使用脚本自动化常见的运维操作
下一步学习
在下一章中,我们将学习网络管理,包括:
- Overlay 网络原理
- 服务发现和负载均衡
- 网络安全和隔离
- 自定义网络配置
检查清单: - [ ] 掌握服务创建和配置 - [ ] 学会服务更新和回滚 - [ ] 理解扩缩容策略 - [ ] 掌握调度约束配置 - [ ] 建立监控和故障排除机制