共计 6108 个字符,预计需要花费 16 分钟才能阅读完成。
在 [[Kubernetes]] 上部署 [[Next.js]] 项目是一种常见的生产级实践。与直接部署到 [[Vercel]] 不同,Kubernetes 方案让你对网络、证书、扩缩容和运行时有完全的控制权,适合对安全合规、私有化部署或成本管控有要求的场景。
本文将按照「构建镜像 → 编写 Manifest → 部署上线 → 运维优化」的顺序,完整介绍将 Next.js 应用部署到 Kubernetes 集群的全过程。
前置条件
在开始之前,需要准备以下环境和工具
- 一个可运行的 Kubernetes 集群([[minikube]]、[[K3s]]、[[Kind]] 或云厂商托管集群均可)
- 安装 [[Docker]] 并可正常构建镜像
- 安装 kubectl 并已配置集群访问
- 一个已完成开发的 Next.js 项目
- 一个容器镜像仓库([[Docker Hub]]、[[Harbor]]、GitHub Container Registry 等)
开启 Standalone 输出模式
Next.js 提供了 standalone 输出模式,它会在构建时自动追踪项目实际使用的依赖,生成一个精简的独立目录,只包含运行所需的最小文件集合。这对容器化部署至关重要,可以将最终镜像体积从 1GB 以上压缩到 150-250MB 左右。
在 next.config.js 中启用
/** @type {import('next').NextConfig} */
const nextConfig = {
output: 'standalone',
poweredByHeader: false,
};
module.exports = nextConfig;
启用后执行 npm run build,Next.js 会在 .next/standalone 目录下生成一个包含 server.js 和精简 node_modules 的独立运行包。需要注意,静态资源和 public 目录不会自动复制到 standalone 输出中,需要在 Dockerfile 里手动处理。
编写 Dockerfile
采用多阶段构建(Multi-stage Build)可以将构建依赖与运行时环境分离,进一步减小镜像体积并提升安全性。
# 阶段 1:安装依赖
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
# 阶段 2:构建应用
FROM node:20-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build
# 阶段 3:生产运行时
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# 复制 standalone 输出
COPY --from=builder /app/.next/standalone ./
# 复制静态资源
COPY --from=builder /app/.next/static ./.next/static
COPY --from=builder /app/public ./public
# 设置文件所有权
RUN chown -R nextjs:nodejs /app
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]
关键设计说明
- deps 阶段:仅复制
package.json和 lock 文件,利用 Docker 层缓存加速后续构建 - builder 阶段:执行
npm run build,生成 standalone 输出 - runner 阶段:仅包含运行时所需文件,使用非 root 用户运行以增强安全性
- 使用
node:20-alpine作为基础镜像,体积更小
创建 .dockerignore 文件避免将不必要的文件复制到构建上下文
node_modules
.next
.git
.env*.local
npm-debug.log*
.DS_Store
README.md
构建并测试镜像
# 构建镜像
docker build -t nextjs-app:latest .
# 本地测试
docker run -p 3000:3000 nextjs-app:latest
推送镜像到仓库
将构建好的镜像推送到 Kubernetes 集群可访问的容器仓库
# 以 Docker Hub 为例
docker tag nextjs-app:latest yourusername/nextjs-app:v1.0.0
docker push yourusername/nextjs-app:v1.0.0
如果使用私有仓库如 [[Harbor]],需要在 Kubernetes 中配置 imagePullSecrets。
编写 Kubernetes Manifest
Namespace
为应用创建独立的命名空间,便于资源隔离和管理
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: nextjs-app
ConfigMap
将非敏感的配置信息存放在 ConfigMap 中
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nextjs-config
namespace: nextjs-app
data:
NODE_ENV: "production"
API_BASE_URL: "https://api.example.com"
Secret
敏感信息如数据库连接字符串和 API 密钥存放在 Secret 中。生产环境建议使用 [[HashiCorp Vault]] 或云厂商的密钥管理服务。
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: nextjs-secrets
namespace: nextjs-app
type: Opaque
data:
DATABASE_URL: <base64 编码的连接字符串>
AUTH_SECRET: <base64 编码的密钥>
Deployment
Deployment 定义了应用的部署策略、副本数量和容器配置
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nextjs-app
namespace: nextjs-app
labels:
app: nextjs-app
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: nextjs-app
template:
metadata:
labels:
app: nextjs-app
spec:
containers:
- name: nextjs
image: yourusername/nextjs-app:v1.0.0
ports:
- containerPort: 3000
resources:
requests:
cpu: "200m"
memory: "256Mi"
limits:
cpu: "500m"
memory: "512Mi"
envFrom:
- configMapRef:
name: nextjs-config
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: nextjs-secrets
key: DATABASE_URL
livenessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
配置要点说明
- replicas 设为 2 实现基本的高可用
- RollingUpdate 策略确保更新时零停机
- resources 的 requests 和 limits 避免资源争抢和 OOM
- livenessProbe 检测应用是否存活,失败会触发重启
- readinessProbe 检测应用是否就绪,未就绪的 Pod 不会接收流量
Service
Service 为 Pod 提供稳定的访问入口和负载均衡
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: nextjs-service
namespace: nextjs-app
spec:
selector:
app: nextjs-app
ports:
- port: 80
targetPort: 3000
protocol: TCP
type: ClusterIP
这里使用 ClusterIP 类型,配合 [[Kubernetes Ingress]] 对外暴露服务。如果不使用 Ingress,也可以改为 NodePort 或 LoadBalancer 类型。
Ingress
通过 Ingress 配置域名和 TLS 证书,将外部流量路由到 Service
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nextjs-ingress
namespace: nextjs-app
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: nextjs-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nextjs-service
port:
number: 80
这里通过 [[cert-manager]] 自动申请和续期 [[Let’s Encrypt]] 证书。
部署流程
按照依赖顺序依次执行
# 创建 Namespace
kubectl apply -f namespace.yaml
# 部署配置和密钥
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
# 部署应用
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
验证部署状态
# 查看 Pod 状态
kubectl get pods -n nextjs-app
# 查看 Pod 日志
kubectl logs -f <pod-name> -n nextjs-app
# 查看 Service 和 Ingress
kubectl get svc,ingress -n nextjs-app
配置自动扩缩容
使用 HorizontalPodAutoscaler(HPA)根据 CPU 或内存使用率自动调整副本数
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nextjs-hpa
namespace: nextjs-app
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nextjs-app
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
HPA 需要集群中安装 [[Metrics Server]] 才能正常工作。
使用 Helm 管理部署
当配置文件数量增多时,可以使用 [[Helm]] 将所有 Manifest 打包为 Chart,通过 values 文件管理不同环境的配置差异
# 创建 Helm Chart
helm create nextjs-chart
# 安装到集群
helm install nextjs-app ./nextjs-chart \
--namespace nextjs-app \
--set image.repository=yourusername/nextjs-app \
--set image.tag=v1.0.0
# 升级
helm upgrade nextjs-app ./nextjs-chart \
--set image.tag=v1.1.0
常见问题排查
以下是部署过程中常见的问题和排查思路
- Pod 处于 CrashLoopBackOff 状态:使用
kubectl logs查看错误日志,常见原因是环境变量缺失或数据库连接失败 - 镜像拉取失败(ImagePullBackOff):检查镜像名称和标签是否正确,私有仓库需要配置 imagePullSecrets
- 健康检查失败:确认应用启动完成所需的时间,适当增大 initialDelaySeconds
- 静态资源 404:确认 Dockerfile 中已正确复制
.next/static和public目录 - 内存溢出(OOMKilled):调高 resources.limits.memory,或排查应用是否存在内存泄漏
- 多副本缓存不一致:Next.js 默认使用本地文件系统缓存,多副本部署时需要引入 [[Redis]] 等分布式缓存方案
与其他部署方式的对比
| 方式 | 适合场景 | 特点 |
|---|---|---|
| [[Vercel]] | 个人项目、快速上线 | 零配置,与 Next.js 深度集成 |
| [[Docker Compose]] | 单服务器部署 | 简单易用,适合小规模应用 |
| Kubernetes | 生产级、企业级部署 | 自动扩缩容、滚动更新、高可用 |
| 静态导出 | 纯静态站点 | 不支持 SSR 和 API 路由 |
总结
在 Kubernetes 上部署 Next.js 项目的核心流程可以概括为四步:启用 standalone 输出减小构建产物体积,使用多阶段 Docker 构建生成精简镜像,编写 Deployment、Service、Ingress 等 Manifest 描述部署拓扑,最后通过 HPA 和健康检查保障运行时的稳定性和弹性。虽然比 Vercel 一键部署复杂,但换来了对基础设施的完全控制,适合有定制化需求的生产环境。
相关链接
- Next.js 部署文档 https://nextjs.org/docs/app/getting-started/deploying
- Kubernetes 官方文档 https://kubernetes.io/docs/home/
- Next.js Docker 示例 https://github.com/vercel/next.js/tree/canary/examples/with-docker

