程序员求职经验分享与学习资料整理平台

网站首页 > 文章精选 正文

生产环境故障排查时,这10个K8s问题差点让我丢掉工作!

balukai 2025-03-20 12:21:17 文章精选 2 ℃

面试官问我,要是遇到了下面这些场景,该怎么处理:

"生产环境Pod疯狂重启!"

"服务发现异常,大量502!"

"配置更新后服务没响应!"

"日志突然采集不到了..."

让我们通过10个真实场景,来揭开K8s的神秘面纱。



问题1:调度的黑魔法


问题1


场景:

  • 生产环境2个节点
  • Node1上运行着3个Pod
  • Node2完全空闲
  • 新建Pod会被调度到哪里?

很多人想当然: "肯定调度到空闲的Node2啊!" 但真实情况是...

解决方案

理解调度原理



# 节点亲和性示例
apiVersion: v1
kind: Pod
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: zone
            operator: In
            values:
            - zone1

实际调度决策:

  • 首先评估节点资源充足度
  • 然后计算优先级得分: CPU/内存使用率 (占比30%) 节点亲和性 (占比40%) Pod分布均衡度 (占比30%)
  • 选择得分最高的节点

最佳实践:

合理使用节点标签

设置Pod亲和性/反亲和性

利用污点和容忍控制调度

配置资源请求和限制




问题2:OOM的命运


问题2


场景:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: memory-hog
    image: memory-app:v1
    resources:
      limits:
        memory: "256Mi"

当容器OOM时:

  • 是容器重启还是Pod重建?
  • 重启策略如何影响?
  • 如何避免雪崩效应?

解决方案



  1. 正确配置资源限制
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: memory-app
    resources:
      requests:
        memory: "128Mi"
      limits:
        memory: "256Mi"
    # 关键:设置恰当的重启策略
    restartPolicy: OnFailure
  1. 实现优雅终止
// 应用程序层面处理SIGTERM信号
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
    // 1. 停止接收新请求
    server.stopAcceptingNewRequests();
    
    // 2. 等待现有请求处理完成
    server.awaitTermination(30, TimeUnit.SECONDS);
    
    // 3. 清理资源
    cleanup();
}));
  1. 监控与告警
# Prometheus告警规则
groups:
- name: memory-alerts
  rules:
  - alert: ContainerMemoryUsage
    expr: container_memory_usage_bytes > threshold
    for: 5m
    labels:
      severity: warning

最佳实践:

设置合理的内存限制

实现优雅终止机制

配置监控和告警

使用内存自动伸缩



问题3:配置热更新之谜


问题3

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  APP_ENV: "prod"

关键疑问:

配置更新是否热生效?

Pod是否需要重建?

如何实现零停机更新?

解决方案



  1. 使用ConfigMap卷挂载
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    volumeMounts:
    - name: config-volume
      mountPath: /etc/config
  volumes:
  - name: config-volume
    configMap:
      name: app-config
  1. 实现配置热加载
@Configuration
public class ConfigReloader {
    @Autowired
    private ConfigMap configMap;
    
    // 监听配置变更
    @EventListener
    public void onConfigChange(ConfigChangeEvent event) {
        // 1. 验证新配置
        validateNewConfig(event.getNewConfig());
        
        // 2. 原子性更新
        atomicUpdateConfig(event.getNewConfig());
        
        // 3. 通知相关组件
        notifyComponents();
    }
}
  1. 优雅降级机制
public class ConfigManager {
    // 保存最后一个正常的配置
    private static Config lastGoodConfig;
    
    public void updateConfig(Config newConfig) {
        try {
            applyNewConfig(newConfig);
            lastGoodConfig = newConfig;
        } catch (Exception e) {
            // 发生错误时回滚
            rollbackToLastGoodConfig();
            throw e;
        }
    }
}

最佳实践:

使用卷挂载而非环境变量

实现配置热加载机制

添加配置验证逻辑

准备降级方案



问题4:Pod稳定性


问题4


场景:

  • Pod创建成功后
  • 无人为干预
  • 是否会自动迁移或重启?

解决方案



  1. Pod配置最佳实践
apiVersion: v1
kind: Pod
spec:
  # 1. 设置合适的QoS等级
  containers:
  - name: app
    resources:
      requests:
        memory: "128Mi"
        cpu: "100m"
      limits:
        memory: "256Mi"
        cpu: "200m"
  
  # 2. 配置优雅终止
  terminationGracePeriodSeconds: 30
  
  # 3. 设置Pod反亲和性避免单点
  affinity:
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          topologyKey: kubernetes.io/hostname
  1. 实现优雅终止
@Component
public class GracefulShutdown {
    @PreDestroy
    public void shutdown() {
        log.info("Received shutdown signal");
        
        // 1. 停止接收新请求
        stopNewRequests();
        
        // 2. 等待现有请求完成
        awaitRequestsCompletion(30, TimeUnit.SECONDS);
        
        // 3. 关闭资源连接
        closeResources();
    }
}

最佳实践:

合理设置资源请求和限制

实现优雅终止机制

使用Pod反亲和性

配置存活和就绪探针



问题5:负载均衡的真相


问题5


apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  type: ClusterIP
  ports:
  - port: 80

核心问题:

  • TCP连接如何均衡?
  • 会话保持怎么处理?
  • 性能开销多大?

解决方案



  1. Service配置优化



apiVersion: v1
kind: Service
spec:
  type: ClusterIP
  # 1. 会话亲和性配置
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800
  
  # 2. 配置负载均衡策略
  externalTrafficPolicy: Local
  
  # 3. 设置合适的超时时间
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
  1. 应用层会话处理
@Configuration
public class SessionConfig {
    @Bean
    public HttpSessionIdResolver httpSessionIdResolver() {
        // 使用header传递会话ID
        return HeaderHttpSessionIdResolver
            .xAuthToken();
    }
    
    @Bean
    public RedisSessionRepository sessionRepository() {
        // 使用Redis存储会话
        return new RedisSessionRepository(
            redisTemplate);
    }
}

最佳实践:

使用分布式会话存储

配置合适的会话亲和性

监控连接分布情况

设置合理的超时时间



问题6:日志收集的艺术


问题6


场景:

apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    volumeMounts:
    - name: log-storage
      mountPath: /var/log

关键考量:

如何避免日志丢失?

性能影响如何权衡?

存储成本如何控制?

解决方案

  1. 日志收集架构
# Fluentd配置
apiVersion: v1
kind: ConfigMap
metadata:
  name: fluentd-config
data:
  fluent.conf: |
    
      @type tail
      path /var/log/containers/*.log
      pos_file /var/log/fluentd-containers.log.pos
      tag kubernetes.*
      read_from_head true
      
        @type json
      
    
    
    
      @type elasticsearch
      host elasticsearch-logging
      port 9200
      logstash_format true
    
  1. 应用日志最佳实践
@Slf4j
public class LoggingService {
    // 1. 使用结构化日志
    private static final String LOG_PATTERN = 
        "{} | {} | {} | {} | {}";
    
    public void logBusinessEvent(String event) {
        log.info(LOG_PATTERN,
            LocalDateTime.now(),
            Thread.currentThread().getName(),
            "BUSINESS",
            event,
            TraceContext.getCurrentSpan());
    }
    
    // 2. 实现日志轮转
    @PostConstruct
    public void configureLogRotation() {
        LoggerContext context = 
            (LoggerContext) LoggerFactory.getILoggerFactory();
        
        RollingFileAppender appender = 
            new RollingFileAppender<>();
        appender.setContext(context);
        appender.setFile("/var/log/app.log");
        
        TimeBasedRollingPolicy policy = 
            new TimeBasedRollingPolicy<>();
        policy.setMaxHistory(7); // 保留7天
        appender.setRollingPolicy(policy);
    }
}

最佳实践:

使用结构化日志格式

实现日志轮转机制

配置集中式日志收集

设置日志保留策略



问题7:健康检查的误区


问题7


apiVersion: v1
kind: Pod
spec:
  containers:
  - name: web
    livenessProbe:
      httpGet:
        path: /health
        port: 8080

常见误解:

  • 存活探针等于应用健康?
  • 就绪探针可以省略?
  • 探针超时无所谓?

解决方案



  1. 多层次健康检查
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: web-app
    # 1. 存活探针 - 检查进程是否存活
    livenessProbe:
      httpGet:
        path: /health/liveness
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3
    
    # 2. 就绪探针 - 检查是否可以接收流量
    readinessProbe:
      httpGet:
        path: /health/readiness
        port: 8080
      initialDelaySeconds: 20
      periodSeconds: 5
    
    # 3. 启动探针 - 处理长启动时间
    startupProbe:
      httpGet:
        path: /health/startup
        port: 8080
      failureThreshold: 30
      periodSeconds: 10



  1. 健康检查端点实现
@RestController
@RequestMapping("/health")
public class HealthController {
    
    @Autowired
    private DatabaseHealthChecker dbChecker;
    @Autowired
    private CacheHealthChecker cacheChecker;
    
    // 1. 存活检查 - 只检查基础组件
    @GetMapping("/liveness")
    public ResponseEntity checkLiveness() {
        if (!isBasicComponentsHealthy()) {
            return ResponseEntity.status(500).build();
        }
        return ResponseEntity.ok("OK");
    }
    
    // 2. 就绪检查 - 全面检查
    @GetMapping("/readiness")
    public ResponseEntity checkReadiness() {
        HealthCheckResult result = HealthChecker.builder()
            .check(dbChecker)
            .check(cacheChecker)
            .check(this::checkExternalServices)
            .build()
            .check();
            
        return result.isHealthy() 
            ? ResponseEntity.ok("OK")
            : ResponseEntity.status(503).build();
    }
    
    // 3. 自定义健康检查逻辑
    private boolean checkExternalServices() {
        try {
            // 检查外部依赖服务
            return externalServiceChecker.isHealthy();
        } catch (Exception e) {
            log.error("Health check failed", e);
            return false;
        }
    }
}

最佳实践:

区分存活和就绪探针

实现细粒度健康检查

设置合理的超时时间

添加监控和告警



问题8:弹性伸缩的智慧


问题8


场景:

apiVersion: apps/v1
kind: Deployment
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: web
        resources:
          requests:
            cpu: "100m"

核心问题:

  • 如何应对流量突增?
  • 扩容时机如何把控?
  • 资源限制如何设置

解决方案

  1. HPA配置最佳实践


apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 3
  maxReplicas: 10
  metrics:
  # 1. CPU基准
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  # 2. 自定义指标
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: 1000
  # 3. 行为配置
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
      - type: Percent
        value: 100
        periodSeconds: 15
    scaleDown:
      stabilizationWindowSeconds: 300
  1. 应用层自适应



@Configuration
public class AdaptiveConfig {
    
    @Bean
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        // 1. 核心线程数动态调整
        executor.setCorePoolSize(
            calculateOptimalThreads());
            
        // 2. 队列大小自适应
        executor.setQueueCapacity(
            calculateQueueSize());
            
        // 3. 拒绝策略
        executor.setRejectedExecutionHandler(
            new CallerRunsPolicy());
            
        return executor;
    }
    
    private int calculateOptimalThreads() {
        int cpuCores = Runtime.getRuntime().availableProcessors();
        return Math.max(cpuCores * 2, 8);
    }
}

最佳实践:

设置合理的扩缩容阈值

使用多个扩缩容指标

配置稳定窗口期

实现应用层自适应



问题9:容器登录的真相


问题9


当执行:

kubectl exec -it  -- bash

你真的理解:

  • 是否真的登录了Pod?
  • 网络空间的隔离?
  • 权限边界在哪里?

解决方案



  1. 安全访问配置
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: app
    # 1. 安全上下文
    securityContext:
      runAsUser: 1000
      runAsGroup: 3000
      fsGroup: 2000
      allowPrivilegeEscalation: false
  
  # 2. 服务账号
  serviceAccountName: restricted-sa
  1. 调试工具集成
# 创建调试容器
kubectl debug -it pod/web-app --image=busybox

# 使用临时容器
kubectl alpha debug pod/web-app -it \
  --image=ubuntu \
  --target=app

最佳实践:

使用最小权限原则

配置安全上下文

利用临时调试容器

实施审计日志



问题10:故障排查之道


问题10


场景:Pod反复重启

kubectl describe pod 
Status: CrashLoopBackOff

解决方案



  1. 系统故障排查流程
# 1. 查看Pod状态
kubectl get pod web-app -o wide

# 2. 查看详细信息
kubectl describe pod web-app

# 3. 查看容器日志
kubectl logs web-app -c app --previous

# 4. 查看事件
kubectl get events --sort-by='.lastTimestamp'

# 5. 收集诊断信息
kubectl cluster-info dump
  1. 应用层诊断接口
@RestController
@RequestMapping("/debug")
public class DiagnosticController {
    
    @GetMapping("/thread-dump")
    public Map getThreadDump() {
        ThreadMXBean threadMXBean = 
            ManagementFactory.getThreadMXBean();
        return ThreadDumper.dump(threadMXBean);
    }
    
    @GetMapping("/heap-dump")
    public void triggerHeapDump() {
        String filename = String.format(
            "/tmp/heap_%d.hprof", 
            System.currentTimeMillis());
        HeapDumper.dumpHeap(filename, true);
    }
}

最佳实践:

建立故障排查流程

实现诊断接口

保留故障现场

设置监控告警



写在最后

Kubernetes的稳定运行需要:

  • 深入理解各组件工作原理
  • 建立完善的监控告警体系
  • 制定标准的故障排查流程
  • 持续优化和改进

记住:

没有完美的系统,只有不断进化的系统!


#Kubernetes #云原生 #容器化 #运维实战

最近发表
标签列表