未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适合有Linux与Kubernetes基础的读者梳理集群规划、传统部署、容器编排、扩缩容及升级流程,不是可直接复制执行的生产方案。本次仅作全文结构审阅,未进行外部事实核验或部署测试;示例未明确PrestoDB、历史PrestoSQL或Trino的目标发行版,450/451与下载坐标、镜像、Java 17及配置项的兼容性不能视为已确认。当前仍有内存参数重复冲突、配置文件职责混放、Worker标识缺少渲染步骤、CPU扩缩容指标未归一化及缩容缺少可靠任务排空等问题;多Coordinator发现、路由和查询状态接续没有完整实现,滚动更新不保证查询连续性。Helm资源约定、监控采集接口与指标适配前提尚不完整,部署步骤引用的仪表盘和资源组配置也未提供。升级脚本原有条件判断、闭合结构及JSON数值问题已修正,不再作为现存错误。容量表仅为假设性规划,生产资源应以实际工作负载和测试结果确定,证书、认证环境及密钥须自行准备,示例口令不得用于生产。

Presto集群部署与管理 - 生产环境运维实战指南

abstract 📋 本节概览

  • 🎯 学习目标:掌握Presto集群的完整部署、配置、扩缩容和运维管理
  • 📚 前置知识:安全配置与权限 和 Linux系统管理、容器技术基础
  • ⏱️ 预计用时:160分钟

🏗️ 集群架构设计

📊 生产环境集群架构

%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TB
    subgraph "生产级Presto集群架构"
        subgraph "负载均衡层"
            A[负载均衡器<br/>⚖️ Load Balancer]
            B[高可用代理<br/>🔄 HA Proxy]
        end
        subgraph "Coordinator集群"
            C[主Coordinator<br/>🎯 Primary Coordinator]
            D[备Coordinator<br/>🔄 Standby Coordinator]
            E[发现服务<br/>🔍 Discovery Service]
        end
        subgraph "Worker节点池"
            F[数据处理节点池<br/>💻 Data Processing Pool]
            G[计算密集节点池<br/>🧮 Compute Intensive Pool]
            H[内存优化节点池<br/>🧠 Memory Optimized Pool]
        end
        subgraph "存储层"
            I[分布式文件系统<br/>📁 HDFS/S3]
            J[关系数据库<br/>🗄️ RDBMS]
            K[NoSQL数据库<br/>📊 NoSQL]
        end
        subgraph "监控管理层"
            L[监控系统<br/>📈 Monitoring]
            M[日志聚合<br/>📝 Log Aggregation]
            N[告警系统<br/>🚨 Alerting]
            O[配置管理<br/>⚙️ Configuration Management]
        end
        subgraph "容器编排层"
            P[Kubernetes集群<br/>☸️ K8s Cluster]
            Q[Helm Charts<br/>📦 Package Manager]
            R[CI/CD流水线<br/>🔄 Pipeline]
        end
    A --> B --> C
    B --> D
    C --> E
    D --> E --> F
    E --> G  
    E --> H
    F --> I
    G --> J
    H --> K
    C --> L
    D --> M
    F --> N
    G --> O
    P --> Q --> R --> C
    R --> D
    style A fill:#e3f2fd
    style C fill:#f3e5f5
    style F fill:#e8f5e8
    style I fill:#fff3e0
    style L fill:#fce4ec
    style P fill:#f3e5f5

    end

🎯 集群规模规划矩阵(假设性示例,非实测容量结论)

场景类型 用户规模 QPS Coordinator Worker节点 总内存 存储
小型团队 <50 <10 1-2台 3-5台 200-500GB 10-50TB
中型企业 50-200 10-50 2-3台 10-20台 1-2TB 50-200TB
大型企业 200-1000 50-200 3-5台 30-100台 3-10TB 200TB-1PB
超大规模 >1000 >200 5-10台 100-500台 >10TB >1PB

🚀 传统部署方式

📋 集群节点准备

系统环境准备脚本:

#!/bin/bash
# prepare_presto_cluster.sh - Presto集群环境准备脚本

set -e

# 配置变量
PRESTO_USER="presto"
PRESTO_VERSION="450"
JAVA_VERSION="17"
PRESTO_HOME="/opt/presto"
DATA_DIR="/data/presto"

# 系统优化参数
optimize_system() {
    echo "Optimizing system parameters..."
    
    # 内核参数优化
    cat >> /etc/sysctl.conf << EOF
# Presto优化参数
vm.max_map_count=262144
vm.swappiness=1
net.core.somaxconn=65535
net.core.netdev_max_backlog=5000
net.ipv4.tcp_keepalive_time=300
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=3
net.ipv4.tcp_fin_timeout=30
fs.file-max=1000000
EOF
    
    sysctl -p
    
    # 用户限制优化
    cat >> /etc/security/limits.conf << EOF
$PRESTO_USER soft nofile 1000000
$PRESTO_USER hard nofile 1000000
$PRESTO_USER soft nproc 32768
$PRESTO_USER hard nproc 32768
$PRESTO_USER soft memlock unlimited
$PRESTO_USER hard memlock unlimited
EOF
    
    # 禁用透明大页
    echo never > /sys/kernel/mm/transparent_hugepage/enabled
    echo never > /sys/kernel/mm/transparent_hugepage/defrag
}

# 安装Java
install_java() {
    echo "Installing Java $JAVA_VERSION..."
    
    # Amazon Corretto安装
    curl -L https://corretto.aws/downloads/latest/amazon-corretto-$JAVA_VERSION-x64-linux-jdk.tar.gz -o java.tar.gz
    tar -xzf java.tar.gz -C /opt/
    ln -sf /opt/amazon-corretto-$JAVA_VERSION.* /opt/java
    
    # 环境变量设置
    cat >> /etc/environment << EOF
JAVA_HOME=/opt/java
PATH=$PATH:/opt/java/bin
EOF
    
    source /etc/environment
}

# 创建Presto用户和目录
setup_user_and_dirs() {
    echo "Setting up Presto user and directories..."
    
    # 创建用户
    useradd -r -m -s /bin/bash $PRESTO_USER
    
    # 创建必要目录
    mkdir -p $PRESTO_HOME/{bin,lib,plugin,etc/catalog}
    mkdir -p $DATA_DIR/{data,logs,tmp}
    mkdir -p /var/log/presto
    
    # 设置权限
    chown -R $PRESTO_USER:$PRESTO_USER $PRESTO_HOME $DATA_DIR /var/log/presto
    chmod -R 755 $PRESTO_HOME
    chmod -R 750 $DATA_DIR
}

# 安装Presto
install_presto() {
    echo "Installing Presto $PRESTO_VERSION..."
    
    # 下载Presto
    cd /tmp
    curl -L https://repo1.maven.org/maven2/io/prestosql/presto-server/$PRESTO_VERSION/presto-server-$PRESTO_VERSION.tar.gz -o presto-server.tar.gz
    
    # 解压安装
    tar -xzf presto-server.tar.gz
    cp -r presto-server-$PRESTO_VERSION/* $PRESTO_HOME/
    
    # 下载CLI
    curl -L https://repo1.maven.org/maven2/io/prestosql/presto-cli/$PRESTO_VERSION/presto-cli-$PRESTO_VERSION-executable.jar -o $PRESTO_HOME/bin/presto-cli
    chmod +x $PRESTO_HOME/bin/presto-cli
    
    # 设置权限
    chown -R $PRESTO_USER:$PRESTO_USER $PRESTO_HOME
}

# 主执行流程
main() {
    echo "Starting Presto cluster preparation..."
    
    optimize_system
    install_java
    setup_user_and_dirs
    install_presto
    
    echo "Presto cluster preparation completed!"
    echo "Next steps:"
    echo "1. Configure coordinator and workers"
    echo "2. Set up catalogs"
    echo "3. Start the services"
}

# 检查是否为root用户
if [ "$EUID" -ne 0 ]; then
    echo "Please run as root"
    exit 1
fi

main

⚙️ Coordinator配置

Coordinator配置文件 (etc/config.properties):

# 协调器基础配置
coordinator=true
node-scheduler.include-coordinator=false
http-server.http.port=8080
query.max-memory=50GB
query.max-memory-per-node=1GB
query.max-total-memory=50GB
discovery-server.enabled=true
discovery.uri=http://coordinator1.company.com:8080

# 高可用配置
http-server.https.enabled=true
http-server.https.port=8443
http-server.https.keystore.path=/etc/presto/keystore.jks
http-server.https.keystore.key=keystore_password

# 集群发现配置
node.environment=production
node.id=coordinator-1
node.data-dir=/data/presto/data

# 内存和并发配置
query.max-memory=100GB
query.max-memory-per-node=8GB
query.max-total-memory=500GB
memory.heap-headroom-per-node=2GB

# 资源组配置
resource-groups.configuration-manager=file
resource-groups.config-file=etc/resource-groups.json

# 事件监听器
event-listener.name=audit-logger
audit.log-path=/var/log/presto/audit.log

# 会话属性
session-property-managers=system

# 安全配置
http-server.authentication.type=KERBEROS
http.authentication.krb5.config=/etc/krb5.conf

🖥️ Worker配置

Worker配置文件 (etc/config.properties):

# Worker基础配置
coordinator=false
http-server.http.port=8080
query.max-memory-per-node=8GB
discovery.uri=http://coordinator1.company.com:8080,http://coordinator2.company.com:8080

# 节点标识
node.environment=production
node.id=worker-${HOSTNAME}
node.data-dir=/data/presto/data

# 性能配置
task.max-worker-threads=200
task.min-drivers=50
task.max-drivers-per-task=20

# 内存配置
memory.heap-headroom-per-node=2GB
resources.reserved-system-memory=4GB

# Spill配置
spill-enabled=true
spiller-spill-path=/data/presto/spill
spiller-max-used-space-threshold=0.8

# 网络配置
task.http-response-threads=100
task.http-timeout-threads=10

# 连接器线程配置
task.writer-count=4
task.task-notification-threads=10

# 健康检查
http-server.log.enabled=true
http-server.log.path=/var/log/presto/http-request.log

🔄 服务管理

Systemd服务配置 (/etc/systemd/system/presto.service):

[Unit]
Description=Presto Distributed SQL Query Engine
After=network.target
Wants=network.target

[Service]
Type=forking
User=presto
Group=presto
ExecStart=/opt/presto/bin/launcher start
ExecStop=/opt/presto/bin/launcher stop
ExecReload=/bin/kill -HUP $MAINPID
PIDFile=/data/presto/data/var/run/launcher.pid
Restart=always
RestartSec=10
LimitNOFILE=1000000
LimitNPROC=32768

# 环境变量
Environment=JAVA_HOME=/opt/java
Environment=PATH=/opt/java/bin:/usr/local/bin:/usr/bin:/bin

# 工作目录
WorkingDirectory=/opt/presto

# 安全设置
NoNewPrivileges=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=/data/presto /var/log/presto

[Install]
WantedBy=multi-user.target

集群启动脚本:

#!/bin/bash
# start_presto_cluster.sh - Presto集群启动脚本

COORDINATORS=("coordinator1.company.com" "coordinator2.company.com")
WORKERS=("worker1.company.com" "worker2.company.com" "worker3.company.com" "worker4.company.com" "worker5.company.com")

# 启动Coordinator
start_coordinators() {
    echo "Starting Presto coordinators..."
    
    for coordinator in "${COORDINATORS[@]}"; do
        echo "Starting coordinator on $coordinator..."
        ssh $coordinator "sudo systemctl start presto"
        
        # 等待启动完成
        sleep 30
        
        # 检查状态
        if ssh $coordinator "curl -f http://localhost:8080/v1/info" > /dev/null 2>&1; then
            echo "✓ Coordinator $coordinator started successfully"
        else
            echo "✗ Failed to start coordinator $coordinator"
        fi
    done
}

# 启动Workers
start_workers() {
    echo "Starting Presto workers..."
    
    for worker in "${WORKERS[@]}"; do
        echo "Starting worker on $worker..."
        ssh $worker "sudo systemctl start presto" &
    done
    
    # 等待所有worker启动
    wait
    sleep 60
    
    # 检查worker状态
    for worker in "${WORKERS[@]}"; do
        if ssh $worker "curl -f http://localhost:8080/v1/info" > /dev/null 2>&1; then
            echo "✓ Worker $worker started successfully"
        else
            echo "✗ Failed to start worker $worker"
        fi
    done
}

# 验证集群状态
verify_cluster() {
    echo "Verifying cluster status..."
    
    # 检查节点发现
    active_nodes=$(curl -s http://coordinator1.company.com:8080/v1/node | jq '. | length')
    expected_nodes=$((${#COORDINATORS[@]} + ${#WORKERS[@]}))
    
    echo "Active nodes: $active_nodes / $expected_nodes"
    
    if [ "$active_nodes" -eq "$expected_nodes" ]; then
        echo "✓ All nodes are active"
    else
        echo "⚠ Some nodes may not be active"
    fi
    
    # 运行测试查询
    echo "Running test query..."
    /opt/presto/bin/presto-cli --server coordinator1.company.com:8080 --execute "SELECT 'Cluster is ready!' as status"
}

# 主执行流程
main() {
    echo "Starting Presto cluster deployment..."
    
    start_coordinators
    start_workers
    verify_cluster
    
    echo "Presto cluster started successfully!"
    echo "Coordinator URLs:"
    for coordinator in "${COORDINATORS[@]}"; do
        echo "  - http://$coordinator:8080"
    done
}

main

☸️ Kubernetes部署

📦 Helm Chart部署

Presto Helm Values (values.yaml):

# Presto Helm配置
image:
  repository: prestosql/presto
  tag: "450"
  pullPolicy: IfNotPresent

# Coordinator配置
coordinator:
  enabled: true
  replicas: 2
  
  resources:
    requests:
      memory: "8Gi"
      cpu: "2"
    limits:
      memory: "16Gi"
      cpu: "4"
  
  nodeSelector:
    node-type: coordinator
  
  tolerations:
    - key: "coordinator"
      operator: "Equal"
      value: "true"
      effect: "NoSchedule"
  
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchExpressions:
              - key: "app"
                operator: "In"
                values: ["presto-coordinator"]
          topologyKey: "kubernetes.io/hostname"

# Worker配置
worker:
  enabled: true
  replicas: 10
  
  resources:
    requests:
      memory: "16Gi"
      cpu: "4"
    limits:
      memory: "32Gi"
      cpu: "8"
  
  nodeSelector:
    node-type: worker
  
  tolerations:
    - key: "worker"
      operator: "Equal"
      value: "true"
      effect: "NoSchedule"
  
  # 水平自动扩缩容
  autoscaling:
    enabled: true
    minReplicas: 5
    maxReplicas: 50
    targetCPUUtilizationPercentage: 70
    targetMemoryUtilizationPercentage: 80

# 服务配置
service:
  type: LoadBalancer
  port: 8080
  httpsPort: 8443
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"

# Ingress配置
ingress:
  enabled: true
  className: "nginx"
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
  
  hosts:
    - host: presto.company.com
      paths:
        - path: /
          pathType: Prefix
  
  tls:
    - secretName: presto-tls
      hosts:
        - presto.company.com

# 存储配置
persistence:
  enabled: true
  storageClass: "fast-ssd"
  size: 100Gi
  accessMode: ReadWriteOnce

# ConfigMap配置
config:
  coordinator:
    query.max-memory: "50GB"
    query.max-memory-per-node: "8GB"
    query.max-total-memory: "500GB"
    discovery-server.enabled: "true"
    http-server.http.port: "8080"
    
  worker:
    query.max-memory-per-node: "16GB"
    task.max-worker-threads: "200"
    task.min-drivers: "50"

# 监控配置
monitoring:
  enabled: true
  serviceMonitor:
    enabled: true
    interval: "30s"
    path: "/v1/status"
  
  prometheusRule:
    enabled: true
    rules:
      - alert: PrestoCoordinatorDown
        expr: up{job="presto-coordinator"} == 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "Presto coordinator is down"
      
      - alert: PrestoWorkerDown
        expr: up{job="presto-worker"} == 0
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "Presto worker is down"

# 安全配置
security:
  enabled: true
  authentication:
    type: "oauth2"
    oauth2:
      clientId: "presto-client"
      clientSecret: "presto-secret"
      issuerUrl: "https://auth.company.com"

Kubernetes部署脚本:

#!/bin/bash
# deploy_presto_k8s.sh - Kubernetes上部署Presto集群

set -e

NAMESPACE="presto"
RELEASE_NAME="presto-cluster"
CHART_VERSION="1.0.0"

# 创建命名空间
create_namespace() {
    echo "Creating namespace $NAMESPACE..."
    
    kubectl create namespace $NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
    
    # 设置资源配额
    cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ResourceQuota
metadata:
  name: presto-quota
  namespace: $NAMESPACE
spec:
  hard:
    requests.cpu: "100"
    requests.memory: "500Gi"
    limits.cpu: "200"
    limits.memory: "1000Gi"
    persistentvolumeclaims: "50"
EOF
}

# 创建密钥
create_secrets() {
    echo "Creating secrets..."
    
    # Presto内部通信密钥
    kubectl create secret generic presto-internal-secret \
        --from-literal=internal-shared-secret=$(openssl rand -hex 32) \
        --namespace=$NAMESPACE \
        --dry-run=client -o yaml | kubectl apply -f -
    
    # SSL证书密钥
    kubectl create secret tls presto-tls \
        --cert=presto.crt \
        --key=presto.key \
        --namespace=$NAMESPACE \
        --dry-run=client -o yaml | kubectl apply -f -
}

# 配置节点标签
label_nodes() {
    echo "Labeling nodes..."
    
    # 标记coordinator节点
    kubectl label nodes node1 node2 node-type=coordinator --overwrite
    kubectl taint nodes node1 node2 coordinator=true:NoSchedule --overwrite
    
    # 标记worker节点
    kubectl label nodes node3 node4 node5 node6 node7 node-type=worker --overwrite
    kubectl taint nodes node3 node4 node5 node6 node7 worker=true:NoSchedule --overwrite
}

# 部署Presto
deploy_presto() {
    echo "Deploying Presto with Helm..."
    
    # 添加Helm仓库
    helm repo add presto https://prestosql.github.io/presto-helm-charts
    helm repo update
    
    # 安装/升级Presto
    helm upgrade --install $RELEASE_NAME presto/presto \
        --namespace=$NAMESPACE \
        --version=$CHART_VERSION \
        --values=values.yaml \
        --wait \
        --timeout=20m
}

# 验证部署
verify_deployment() {
    echo "Verifying deployment..."
    
    # 检查Pod状态
    kubectl get pods -n $NAMESPACE
    
    # 检查服务状态
    kubectl get svc -n $NAMESPACE
    
    # 等待所有Pod就绪
    kubectl wait --for=condition=ready pod -l app=presto-coordinator -n $NAMESPACE --timeout=600s
    kubectl wait --for=condition=ready pod -l app=presto-worker -n $NAMESPACE --timeout=600s
    
    # 运行测试查询
    coordinator_pod=$(kubectl get pods -n $NAMESPACE -l app=presto-coordinator -o jsonpath='{.items[0].metadata.name}')
    kubectl exec -n $NAMESPACE $coordinator_pod -- presto-cli --execute "SELECT 'Kubernetes deployment successful!' as status"
}

# 设置监控
setup_monitoring() {
    echo "Setting up monitoring..."
    
    # 部署Prometheus ServiceMonitor
    cat <<EOF | kubectl apply -f -
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: presto-monitor
  namespace: $NAMESPACE
spec:
  selector:
    matchLabels:
      app: presto
  endpoints:
  - port: http
    path: /v1/status
    interval: 30s
EOF
    
    # 部署Grafana Dashboard ConfigMap
    kubectl create configmap presto-dashboard \
        --from-file=presto-dashboard.json \
        --namespace=$NAMESPACE \
        --dry-run=client -o yaml | kubectl apply -f -
}

# 主执行流程
main() {
    echo "Starting Presto Kubernetes deployment..."
    
    create_namespace
    create_secrets
    label_nodes
    deploy_presto
    verify_deployment
    setup_monitoring
    
    echo "Presto Kubernetes deployment completed successfully!"
    echo "Access URLs:"
    kubectl get ingress -n $NAMESPACE
}

main

🔄 集群扩缩容管理

📈 自动扩缩容配置

Horizontal Pod Autoscaler:

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: presto-worker-hpa
  namespace: presto
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: presto-worker
  minReplicas: 10
  maxReplicas: 100
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80
  - type: Pods
    pods:
      metric:
        name: presto_active_queries_per_node
      target:
        type: AverageValue
        averageValue: "5"
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 300
      policies:
      - type: Percent
        value: 50
        periodSeconds: 60
      - type: Pods
        value: 5
        periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 600
      policies:
      - type: Percent
        value: 10
        periodSeconds: 60
      - type: Pods
        value: 2
        periodSeconds: 60

基于负载的扩缩容脚本:

#!/bin/bash
# autoscale_presto.sh - Presto集群自动扩缩容脚本

NAMESPACE="presto"
MIN_WORKERS=10
MAX_WORKERS=50
SCALE_UP_THRESHOLD=80    # CPU使用率阈值
SCALE_DOWN_THRESHOLD=30  # CPU使用率阈值
CHECK_INTERVAL=60        # 检查间隔(秒)

# 获取当前worker数量
get_current_workers() {
    kubectl get deployment presto-worker -n $NAMESPACE -o jsonpath='{.spec.replicas}'
}

# 获取平均CPU使用率
get_cpu_usage() {
    kubectl top pods -n $NAMESPACE -l app=presto-worker --no-headers | \
    awk '{cpu+=$2} END {if(NR>0) print cpu/NR; else print 0}' | \
    sed 's/m$//'
}

# 获取当前查询负载
get_query_load() {
    # 通过Presto API获取当前运行的查询数
    coordinator_service=$(kubectl get svc presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
    curl -s "http://$coordinator_service:8080/v1/cluster" | jq '.runningQueries // 0'
}

# 扩容操作
scale_up() {
    local current_workers=$1
    local new_workers=$(( current_workers + 2 ))
    
    if [ $new_workers -le $MAX_WORKERS ]; then
        echo "$(date): Scaling up from $current_workers to $new_workers workers"
        kubectl scale deployment presto-worker --replicas=$new_workers -n $NAMESPACE
        
        # 等待新Pod就绪
        kubectl wait --for=condition=ready pod -l app=presto-worker -n $NAMESPACE --timeout=600s
        echo "$(date): Scale up completed"
    else
        echo "$(date): Maximum workers ($MAX_WORKERS) reached, cannot scale up"
    fi
}

# 缩容操作
scale_down() {
    local current_workers=$1
    local new_workers=$(( current_workers - 1 ))
    
    if [ $new_workers -ge $MIN_WORKERS ]; then
        echo "$(date): Scaling down from $current_workers to $new_workers workers"
        
        # 示例延迟:等待30秒不代表任务已排空,不能据此保证优雅缩容
        sleep 30
        
        kubectl scale deployment presto-worker --replicas=$new_workers -n $NAMESPACE
        echo "$(date): Scale down completed"
    else
        echo "$(date): Minimum workers ($MIN_WORKERS) reached, cannot scale down"
    fi
}

# 主监控循环
monitor_and_scale() {
    while true; do
        current_workers=$(get_current_workers)
        cpu_usage=$(get_cpu_usage)
        query_load=$(get_query_load)
        
        echo "$(date): Workers: $current_workers, CPU: ${cpu_usage}%, Queries: $query_load"
        
        # 扩容条件:CPU使用率高或查询数量多
        if [ "$cpu_usage" -gt "$SCALE_UP_THRESHOLD" ] || [ "$query_load" -gt 20 ]; then
            scale_up $current_workers
        # 缩容条件:CPU使用率低且查询数量少
        elif [ "$cpu_usage" -lt "$SCALE_DOWN_THRESHOLD" ] && [ "$query_load" -lt 5 ]; then
            scale_down $current_workers
        else
            echo "$(date): No scaling action needed"
        fi
        
        sleep $CHECK_INTERVAL
    done
}

# 启动监控
echo "Starting Presto autoscaling monitor..."
echo "Scale up threshold: ${SCALE_UP_THRESHOLD}% CPU"
echo "Scale down threshold: ${SCALE_DOWN_THRESHOLD}% CPU"
echo "Worker range: $MIN_WORKERS - $MAX_WORKERS"

monitor_and_scale

🔧 手动扩缩容操作

#!/bin/bash
# manual_scale_presto.sh - Presto集群手动扩缩容

NAMESPACE="presto"

# 添加Worker节点
add_workers() {
    local count=$1
    local current_workers=$(kubectl get deployment presto-worker -n $NAMESPACE -o jsonpath='{.spec.replicas}')
    local new_workers=$(( current_workers + count ))
    
    echo "Adding $count workers (current: $current_workers, new: $new_workers)"
    
    kubectl scale deployment presto-worker --replicas=$new_workers -n $NAMESPACE
    
    # 等待新节点就绪
    echo "Waiting for new workers to be ready..."
    kubectl wait --for=condition=ready pod -l app=presto-worker -n $NAMESPACE --timeout=600s
    
    # 验证节点已加入集群
    coordinator_service=$(kubectl get svc presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
    active_nodes=$(curl -s "http://$coordinator_service:8080/v1/node" | jq '. | length')
    
    echo "Active nodes in cluster: $active_nodes"
    echo "Workers added successfully"
}

# 移除Worker节点
remove_workers() {
    local count=$1
    local current_workers=$(kubectl get deployment presto-worker -n $NAMESPACE -o jsonpath='{.spec.replicas}')
    local new_workers=$(( current_workers - count ))
    
    if [ $new_workers -lt 1 ]; then
        echo "Error: Cannot remove all workers"
        exit 1
    fi
    
    echo "Removing $count workers (current: $current_workers, new: $new_workers)"
    
    # 优雅关闭:等待当前查询完成
    echo "Waiting for running queries to complete..."
    coordinator_service=$(kubectl get svc presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
    
    while true; do
        running_queries=$(curl -s "http://$coordinator_service:8080/v1/cluster" | jq '.runningQueries // 0')
        if [ "$running_queries" -eq 0 ]; then
            break
        fi
        echo "Waiting for $running_queries queries to complete..."
        sleep 10
    done
    
    kubectl scale deployment presto-worker --replicas=$new_workers -n $NAMESPACE
    echo "Workers removed successfully"
}

# 使用方法
case "$1" in
    add)
        if [ -z "$2" ]; then
            echo "Usage: $0 add <count>"
            exit 1
        fi
        add_workers $2
        ;;
    remove)
        if [ -z "$2" ]; then
            echo "Usage: $0 remove <count>"
            exit 1
        fi
        remove_workers $2
        ;;
    *)
        echo "Usage: $0 {add|remove} <count>"
        echo "Examples:"
        echo "  $0 add 5      # Add 5 workers"
        echo "  $0 remove 2   # Remove 2 workers"
        exit 1
        ;;
esac

🔄 集群升级管理

📦 滚动升级策略

#!/bin/bash
# rolling_upgrade_presto.sh - Presto集群滚动升级脚本

set -e

OLD_VERSION="450"
NEW_VERSION="451"
NAMESPACE="presto"
UPGRADE_TIMEOUT="20m"

# 预升级检查
pre_upgrade_checks() {
    echo "Performing pre-upgrade checks..."
    
    # 检查集群健康状态
    coordinator_service=$(kubectl get svc presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
    if ! curl -f "http://$coordinator_service:8080/v1/info" > /dev/null 2>&1; then
        echo "Error: Coordinator is not healthy"
        exit 1
    fi
    
    # 检查运行中的查询
    running_queries=$(curl -s "http://$coordinator_service:8080/v1/cluster" | jq '.runningQueries // 0')
    if [ "$running_queries" -gt 0 ]; then
        echo "Warning: $running_queries queries are currently running"
        read -p "Continue with upgrade? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            echo "Upgrade cancelled"
            exit 1
        fi
    fi
    
    # 备份配置
    echo "Backing up current configuration..."
    kubectl get configmap -n $NAMESPACE -o yaml > "presto-config-backup-$(date +%Y%m%d-%H%M%S).yaml"
    
    echo "Pre-upgrade checks completed"
}

# 升级Coordinator
upgrade_coordinators() {
    echo "Upgrading coordinators..."
    
    # 设置新镜像
    kubectl set image deployment/presto-coordinator \
        presto=prestosql/presto:$NEW_VERSION \
        -n $NAMESPACE
    
    # 等待滚动更新完成
    kubectl rollout status deployment/presto-coordinator \
        -n $NAMESPACE \
        --timeout=$UPGRADE_TIMEOUT
    
    # 验证coordinator健康状态
    sleep 30
    coordinator_service=$(kubectl get svc presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
    if curl -f "http://$coordinator_service:8080/v1/info" > /dev/null 2>&1; then
        echo "✓ Coordinators upgraded successfully"
    else
        echo "✗ Coordinator upgrade failed"
        rollback_upgrade "coordinator"
        exit 1
    fi
}

# 升级Workers
upgrade_workers() {
    echo "Upgrading workers..."
    
    # 分批升级workers以保持服务可用性
    local total_workers=$(kubectl get deployment presto-worker -n $NAMESPACE -o jsonpath='{.spec.replicas}')
    local batch_size=$(( total_workers / 3 + 1 ))  # 每批升级1/3的节点
    
    # 设置升级策略
    kubectl patch deployment presto-worker -n $NAMESPACE -p '{
        "spec": {
            "strategy": {
                "type": "RollingUpdate",
                "rollingUpdate": {
                    "maxUnavailable": '$batch_size',
                    "maxSurge": 0
                }
            }
        }
    }'
    
    # 设置新镜像
    kubectl set image deployment/presto-worker \
        presto=prestosql/presto:$NEW_VERSION \
        -n $NAMESPACE
    
    # 等待滚动更新完成
    kubectl rollout status deployment/presto-worker \
        -n $NAMESPACE \
        --timeout=$UPGRADE_TIMEOUT
    
    # 验证所有workers已加入集群
    sleep 60
    coordinator_service=$(kubectl get svc presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.clusterIP}')
    active_nodes=$(curl -s "http://$coordinator_service:8080/v1/node" | jq '. | length')
    expected_nodes=$(( $(kubectl get deployment presto-coordinator -n $NAMESPACE -o jsonpath='{.spec.replicas}') + total_workers ))
    
    if [ "$active_nodes" -eq "$expected_nodes" ]; then
        echo "✓ Workers upgraded successfully"
    else
        echo "✗ Worker upgrade failed (expected: $expected_nodes, actual: $active_nodes)"
        rollback_upgrade "worker"
        exit 1
    fi
}

# 回滚升级
rollback_upgrade() {
    local component=$1
    echo "Rolling back $component upgrade..."
    
    case $component in
        "coordinator")
            kubectl rollout undo deployment/presto-coordinator -n $NAMESPACE
            kubectl rollout status deployment/presto-coordinator -n $NAMESPACE
            ;;
        "worker")
            kubectl rollout undo deployment/presto-worker -n $NAMESPACE
            kubectl rollout status deployment/presto-worker -n $NAMESPACE
            ;;
        *)
            kubectl rollout undo deployment/presto-coordinator -n $NAMESPACE
            kubectl rollout undo deployment/presto-worker -n $NAMESPACE
            ;;
    esac
    
    echo "Rollback completed"
}

# 升级后验证
post_upgrade_verification() {
    echo "Performing post-upgrade verification..."
    
    # 检查版本
    coordinator_pod=$(kubectl get pods -n $NAMESPACE -l app=presto-coordinator -o jsonpath='{.items[0].metadata.name}')
    actual_version=$(kubectl exec -n $NAMESPACE $coordinator_pod -- java -cp /usr/lib/presto/lib/presto-main-*.jar io.prestosql.server.PrestoServer --version 2>/dev/null | grep -o '[0-9]\+' | head -1)
    
    if [ "$actual_version" = "$NEW_VERSION" ]; then
        echo "✓ Version verification passed: $NEW_VERSION"
    else
        echo "✗ Version verification failed: expected $NEW_VERSION, got $actual_version"
        return 1
    fi
    
    # 运行测试查询
    echo "Running test queries..."
    if kubectl exec -n $NAMESPACE $coordinator_pod -- presto-cli --execute "SELECT 'Upgrade verification successful!' as status" > /dev/null 2>&1; then
        echo "✓ Test query passed"
    else
        echo "✗ Test query failed"
        return 1
    fi
    
    echo "Post-upgrade verification completed successfully"
}

# 主升级流程
main() {
    echo "Starting Presto cluster upgrade from $OLD_VERSION to $NEW_VERSION..."
    
    pre_upgrade_checks
    upgrade_coordinators
    upgrade_workers
    post_upgrade_verification
    
    echo "Presto cluster upgrade completed successfully!"
    echo "All nodes are now running version $NEW_VERSION"
}

# 错误处理
trap 'echo "Upgrade failed! Check logs and consider rollback."; exit 1' ERR

main

🔗 学习连接

📚 本节核心技能

  • 集群架构设计:掌握生产级Presto集群的架构规划和节点配置
  • 传统部署:完整的物理机/虚拟机部署流程和系统优化
  • 容器化部署:Kubernetes环境下的Helm部署和管理
  • 扩缩容管理:自动和手动扩缩容策略的实现
  • 滚动升级:集群升级流程示例;能否降低停机时间取决于版本兼容、任务排空和路由机制,不保证零停机
  • 运维自动化:生产环境的自动化运维脚本和监控

🎯 下一步学习

集群管理检查:

  • ✅ 能够设计和部署生产级Presto集群?
  • ✅ 掌握Kubernetes环境下的容器化部署?
  • ✅ 理解集群扩缩容和升级的最佳实践?

继续学习路径:


tip 💡 集群部署精髓

🏗️ 生产部署核心原则:

  • 📊 合理规划 - 根据业务需求设计集群规模和节点配置
  • 🔄 高可用设计 - 多Coordinator架构规划示例;自动故障切换依赖目标发行版及额外机制,本文未提供完整实现
  • ☸️ 容器化优势 - 利用Kubernetes的自动化管理能力
  • 📈 弹性扩缩容 - 基于负载自动调整集群规模

🚀 运维监控:集群部署完成后,进入 14-监控与告警|监控与告警 学习完整的监控告警体系!