Next.js部署Kubernetes遇静态资源404,求零停机配置方案
解决Next.js应用K8s部署后静态资源404+零停机部署的配置方案
一、先搞定静态资源404的问题
1. 检查Dockerfile的构建逻辑
你的Dockerfile必须完整复制Next.js构建生成的所有静态资源,不能只挑部分复制。正确的多阶段构建示例:
FROM node:18-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM node:18-alpine AS runner WORKDIR /app ENV NODE_ENV production COPY --from=builder /app/package*.json ./ RUN npm ci --only=production # 必须完整复制.next目录,里面包含带哈希的静态资源 COPY --from=builder /app/.next ./.next COPY --from=builder /app/public ./public COPY --from=builder /app/next.config.js ./ EXPOSE 3000 CMD ["npm", "start"]
重点:.next/static下的哈希目录是构建时自动生成的,每个版本的哈希都不一样,必须完整复制整个.next目录,否则新Pod会找不到旧版本的静态资源,反之亦然。
2. 调整Ingress的路径转发规则
确保Ingress不会截断/_next/static/后面的哈希路径,比如用Nginx Ingress的话,配置要这样写:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: nextjs-app-ingress annotations: nginx.ingress.kubernetes.io/rewrite-target: /$1 spec: rules: - host: your-domain.com http: paths: - path: /(.*) pathType: Prefix backend: service: name: nextjs-app-service port: number: 3000
如果用Traefik之类的其他Ingress控制器,核心是保证/_next/static/xxx/xxx.js这种完整路径能被正确转发到Pod,别被规则吃掉后面的哈希部分。
3. 确认Pod的文件访问权限
有时候Pod启动后没权限读取静态资源目录,导致404。可以给容器加SecurityContext配置:
# 加到Deployment的template.spec.containers下面 securityContext: runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000
二、实现零停机部署的核心配置
1. 配置滚动更新策略
在Deployment里设置滚动更新的参数,保证更新过程中始终有可用的Pod:
apiVersion: apps/v1 kind: Deployment metadata: name: nextjs-app-deployment spec: replicas: 3 strategy: rollingUpdate: maxSurge: 25% # 每次最多新增1个Pod(按3副本算) maxUnavailable: 0 # 更新时绝不允许有Pod不可用,确保零停机 type: RollingUpdate
2. 必须加就绪探针(Readiness Probe)
这个是零停机的关键,只有当Pod完全启动、能正常返回静态资源时,才把它加入服务的端点列表,避免把请求打到还没准备好的Pod上:
# 加到Deployment的template.spec.containers下面 readinessProbe: httpGet: # 用public目录里的固定静态资源当探测路径,比根路径更可靠 path: /your-fixed-static-file.png port: 3000 initialDelaySeconds: 10 # 容器启动后等10秒再开始探测 periodSeconds: 5 # 每5秒测一次 failureThreshold: 3 # 连续3次失败就标记为未就绪
也可以用/_next/static/下的某个固定资源,只要是每个版本都存在的就行。
3. 可选:加PreStop钩子
让Pod在被终止前等10秒,处理完正在进行的请求再 shutdown,避免中途断开:
# 加到Deployment的template.spec.containers下面 lifecycle: preStop: exec: command: ["sleep", "10"]
三、额外优化点
- 如果应用有用户会话,可以给Service加
sessionAffinity: ClientIP,让同一用户的请求始终打到同一个Pod,避免跨版本的静态资源问题。 - 要是用了CDN,把
/_next/static/路径的资源缓存起来,因为哈希路径是唯一的,缓存时间可以设长一点,能减少Pod切换时的资源请求压力。
内容的提问来源于stack exchange,提问作者hoque
相关产品推荐
相关产品推荐

