低成本运维之道:基于Kubernetes HPA与Spot实例的电商返利平台成本优化策略

大家好,我是高佣返利省赚客APP研发者阿宝!

在电商返利行业,利润微薄是常态,而基础设施成本往往占据运营支出的大头。面对“双11”、“618”等大促期间的流量洪峰,传统按量付费或预留实例模式要么导致资源闲置浪费,要么面临扩容不及导致的系统崩溃。如何在保证高可用的前提下极致压缩成本?省赚客APP研发团队给出了一套组合拳:利用Kubernetes HPA(水平自动伸缩)实现秒级弹性,结合云厂商的Spot实例(抢占式实例)构建低成本计算池,通过智能调度算法将综合算力成本降低了60%以上。

基于自定义指标的HPA动态伸缩策略

传统的HPA仅依赖CPU和内存利用率,但这对于IO密集型或业务逻辑复杂的返利系统往往滞后。我们引入了KEDA(Kubernetes Event-driven Autoscaling),将业务指标(如“待处理订单队列长度”、“佣金计算延迟”)直接作为伸缩触发器。当上游回调激增时,系统能在秒级内感知并扩容Pod,流量回落时迅速缩容,避免资源空转。

package juwatech.cn.scaling.metrics;

import io.keda.adapter.MetricProvider;
import juwatech.cn.repository.OrderQueueRepository;
import juwatech.cn.model.ScalingMetric;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;

@Component
public class BusinessMetricProvider implements MetricProvider {

    private final OrderQueueRepository queueRepository;

    public BusinessMetricProvider(OrderQueueRepository queueRepository) {
        this.queueRepository = queueRepository;
    }

    /**
     * 暴露自定义业务指标给KEDA
     * 指标名称:pending_order_count
     * 目标阈值:每个Pod处理500个待办订单
     */
    @Override
    public ScalingMetric getMetric(String metricName, String namespace) {
        if ("pending_order_count".equals(metricName)) {
            long count = queueRepository.getPendingOrderCount();
            return new ScalingMetric(
                "pending_order_count",
                BigDecimal.valueOf(count),
                "juwatech.cn/scaling"
            );
        }
        throw new IllegalArgumentException("Unknown metric: " + metricName);
    }
}

对应的HPA配置yaml片段(逻辑示意):

# juwatech.cn/k8s/hpa-commission-worker.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: commission-worker-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: commission-worker
  minReplicas: 2
  maxReplicas: 50
  metrics:
  - type: External
    external:
      metric:
        name: pending_order_count
        selector:
          matchLabels:
            app: shengzhuanke
      target:
        type: AverageValue
        averageValue: "500" # 每个Pod承载500单

Spot实例混合集群架构与容错设计

Spot实例价格仅为按量实例的10%-20%,但存在被云厂商随时回收的风险。为了利用这一成本优势,我们构建了“基础节点池(On-Demand)+ 弹性节点池(Spot)”的混合架构。核心状态服务(如数据库、Redis主节点)运行在稳定的On-Demand节点上,而无状态的佣金计算、日志处理、数据同步等服务则优先调度到Spot实例池。

package juwatech.cn.scheduler.spot;

import juwatech.cn.model.PodSpecBuilder;
import juwatech.cn.config.ClusterConfig;
import io.fabric8.kubernetes.api.model.Pod;
import io.fabric8.kubernetes.api.model.NodeAffinity;
import io.fabric8.kubernetes.api.model.NodeSelectorRequirement;
import java.util.Collections;

public class SpotAwareScheduler {

    /**
     * 为无状态服务构建带有Spot亲和性的Pod规格
     */
    public Pod buildSpotOptimizedPod(String serviceName, String image) {
        PodSpecBuilder builder = new PodSpecBuilder()
            .withServiceName(serviceName)
            .withImage(image)
            .withRestartPolicy("Always");

        // 添加节点亲和性:优先调度到 spot-instance=true 的节点
        NodeSelectorRequirement requirement = new NodeSelectorRequirement();
        requirement.setKey("node-type");
        requirement.setOperator("In");
        requirement.setValues(Collections.singletonList("spot"));

        NodeAffinity affinity = new NodeAffinity();
        // 设置偏好调度(PreferredDuringSchedulingIgnoredDuringExecution)
        // 如果Spot资源不足,允许回退到普通节点,保证可用性
        affinity.addPreferredTerm(100, requirement); 

        builder.withAffinity(affinity);
        
        // 添加容忍度:允许Pod被调度到带有污点的Spot节点
        builder.addToleration("spot-instance", "True", "NoSchedule");

        return builder.build();
    }
}

优雅中断处理与状态迁移机制

当云厂商发出Spot实例回收通知(通常提前2分钟)时,Kubernetes会发送SIGTERM信号。为了防止正在计算的佣金数据丢失,我们在应用中实现了优雅中断逻辑:监听终止信号,立即停止接收新任务,将内存中的未持久化数据刷写到持久存储(如RabbitMQ或Redis),并等待当前任务完成后再退出。

package juwatech.cn.lifecycle;

import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;
import juwatech.cn.service.TaskProcessor;
import juwatech.cn.repository.CheckpointRepository;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class GracefulShutdownHandler {

    private final AtomicBoolean isShuttingDown = new AtomicBoolean(false);
    private final TaskProcessor taskProcessor;
    private final CheckpointRepository checkpointRepo;

    public GracefulShutdownHandler(TaskProcessor taskProcessor, CheckpointRepository checkpointRepo) {
        this.taskProcessor = taskProcessor;
        this.checkpointRepo = checkpointRepo;
        
        // 注册JVM钩子或监听Spring关闭事件
        // 此处简化为逻辑描述,实际通过Spring Event监听
    }

    public void onShutdownSignal() {
        if (isShuttingDown.compareAndSet(false, true)) {
            log.warn("Spot instance termination signal received. Starting graceful shutdown...");
            
            // 1. 停止拉取新任务
            taskProcessor.stopPolling();
            
            // 2. 检查是否有正在处理的任务
            int activeTasks = taskProcessor.getActiveTaskCount();
            if (activeTasks > 0) {
                log.info("Waiting for {} active tasks to complete...", activeTasks);
                // 3. 将内存状态持久化到检查点
                checkpointRepo.saveCheckpoint(taskProcessor.getCurrentState());
                
                // 4. 等待任务完成(最多等待90秒,预留30秒给K8s强制kill)
                try {
                    taskProcessor.awaitTermination(90, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    log.error("Shutdown interrupted, forcing exit");
                }
            }
            
            log.info("Graceful shutdown completed. Safe to terminate.");
        }
    }
}

成本监控与自动化反馈闭环

为了持续优化成本,我们建立了实时的成本监控看板。通过采集每个Namespace、每个Deployment的资源消耗与对应的账单数据,计算出“单次订单处理成本”。当发现Spot实例回收频率过高导致重试成本上升,或者On-Demand实例利用率过低时,系统会自动调整HPA的最小副本数或Spot/On-Demand的比例配置。

package juwatech.cn.cost.optimizer;

import juwatech.cn.model.CostReport;
import juwatech.cn.repository.ResourceUsageRepository;
import juwatech.cn.client.CloudBillingClient;
import juwatech.cn.config.AutoScalingConfig;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class CostOptimizationLoop {

    private final CloudBillingClient billingClient;
    private final ResourceUsageRepository usageRepo;
    private final AutoScalingConfig config;

    public void runDailyOptimization() {
        CostReport report = billingClient.generateDailyReport("shengzhuanke-prod");
        
        double spotSavings = report.getOnDemandCost() - report.getActualCost();
        double interruptionRate = usageRepo.getSpotInterruptionRate();

        log.info("Daily Savings: ${}, Interruption Rate: {}", spotSavings, interruptionRate);

        // 动态调整策略
        if (interruptionRate > 0.15) {
            // 如果中断率过高,适当增加On-Demand基础水位
            config.increaseOnDemandBaseCapacity(10);
            log.warn("High interruption rate detected. Increasing On-Demand base capacity.");
        } else if (report.getCpuUtilizationAvg() < 0.3) {
            // 如果利用率过低,降低HPA最小副本数
            config.decreaseMinReplicas(1);
            log.info("Low utilization detected. Reducing min replicas.");
        }
    }
}

通过这套基于Kubernetes HPA与Spot实例的低成本运维体系,省赚客APP在应对大促流量波动的同时,成功将基础设施成本控制在极低水平,实现了技术效能与商业价值的双重提升。这不仅是一套技术方案,更是我们在激烈市场竞争中生存与发展的核心护城河。

本文著作权归 省赚客app 研发团队,转载请注明出处!

Logo

电商企业物流数字化转型必备!快递鸟 API 接口,72 小时快速完成物流系统集成。全流程实战1V1指导,营造开放的API技术生态圈。

更多推荐