要部署一个 Next.js 项目,你可以选择多种托管平台。
这里列几种方案:
- 部署到 Linux 物理机
- 部署到 docker
- 部署到 Kubernetes
- 部署到 Vercel
1. 部署到 Linux 物理机
这种部署方式非常传统. 在单体项目中比较常见, 简单高效. 有一些几个步骤
-
安装 Node.js
首先,确保你的服务器已安装 Node.js。Next.js 需要 Node.js 运行环境。你可以在官方网站上找到适合你操作系统的安装说明:https://nodejs.org/en/download/
-
上传项目
使用 SCP、SFTP 或者 Git 将 Next.js 项目上传到服务器的一个目录中。确保包含了所有的源代码和依赖文件(
package.json
和yarn.lock
或package-lock.json
)。 -
安装依赖
yarn
-
构建项目
yarn build
-
启动项目
使用以下命令启动项目:
yarn start
默认情况下,Next.js 会在端口 3000 上运行。如果需要修改端口,请在启动命令后面添加
--port
参数,例如:yarn start --port 8080
-
设置反向代理(可选)
如果你已经在服务器上运行了一个 Web 服务器(如 Nginx 或 Apache),你可能需要设置一个反向代理,将用户的请求从 Web 服务器代理到 Next.js 应用上。以下是一个 Nginx 示例配置:
server { listen 80; server_name yourdomain.com; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }
将 yourdomain.com
替换为你的实际域名,并根据需要修改其他配置。
现在,你的 Next.js 项目应该已经在你的服务器上运行了。
你也可以通过 systemd 将 nextjs 程序管理起来. 方便系统重启时自动启动, 或者进程异常终止时自动重启
在 /etc/systemd/system
目录下新建 nextjs.service
文件. 内容模板如下, 根据情况进行调整
[Unit]
Description=nextjs
Documentation=nextjs
After=network-online.target
Wants=network-online.target
[Service]
WorkingDirectory=/workspace/ # 工作目录
Environment="PORT=8080"
EnvironmentFile=-/etc/default/%p
ExecStart=yarn start --port 8080
ExecStop=/bin/kill -HUP $MAINPID
Restart=on-failure
StandardOutput=append:/var/log/app/nextjs.log
StandardError=append:/var/log/app/nextjs-error.log
KillSignal=SIGINT
[Install]
WantedBy=multi-user.target
执行下属命令
# 使 systemd 配置生效
systemctl daemon-reload
# 启动应用
systemctl start nextjs.service
# 开机启动
systemctl enable nextjs.service
2. 部署到 docker
请确保你已经安装 docker. 如果没有, 请先配置 docker.
官方 dockerfile 如下:
一般来说无需调整即可正常使用
FROM node:18-alpine AS base
# Install dependencies only when needed
FROM base AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies based on the preferred package manager
COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then yarn global add pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY /app/node_modules ./node_modules
COPY . .
# Next.js collects completely anonymous telemetry data about general usage.
# Learn more here: https://nextjs.org/telemetry
# Uncomment the following line in case you want to disable telemetry during the build.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN yarn build
# If using npm comment out above and use below instead
# RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
# Uncomment the following line in case you want to disable telemetry during runtime.
# ENV NEXT_TELEMETRY_DISABLED 1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY /app/public ./public
# Set the correct permission for prerender cache
RUN mkdir .next
RUN chown nextjs:nodejs .next
# Automatically leverage output traces to reduce image size
# https://nextjs.org/docs/advanced-features/output-file-tracing
COPY /app/.next/standalone ./
COPY /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT 3000
# set hostname to localhost
ENV HOSTNAME "0.0.0.0"
# server.js is created by next build from the standalone output
# https://nextjs.org/docs/pages/api-reference/next-config-js/output
CMD ["node", "server.js"]
打包. 并推送到远程仓库
# 在代码工作目录(与 package.json 同级)下执行
# 打包
docker build -t jansora/nextjs:v1
# 推送到 docker hub (需要登录)
docker push jansora/nextjs:v1
# 推送到 aliyun hongkong 仓库 (需要登录)
# 先打标签
docker tag jansora/nextjs:v1 registry.cn-hongkong.aliyuncs.com/jansora/nextjs:v1
# 在推送
docker push registry.cn-hongkong.aliyuncs.com/jansora/nextjs:v1
部署
docker run -d -p 3000:3000 registry.cn-hongkong.aliyuncs.com/jansora/nextjs:v1
如果需要配置反向代理, 与部署到linux比较类似, 不再赘述.
3. 部署到 Kubernetes
打包镜像步骤与部署到 docker 比较类似, 不再赘述
apiVersion: apps/v1
kind: Deployment
metadata:
name: nextjs
spec:
selector:
matchLabels:
app: nextjs
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 20
maxUnavailable: 20
replicas: 1 # pod 副本个数
template:
metadata:
labels:
app: nextjs
spec:
containers:
- name: nextjs
image: registry.cn-hongkong.aliyuncs.com/jansora/nextjs:v1
imagePullPolicy: Always
ports:
- containerPort: 3000
env:
- name: xxx
value: 'xxx'
# service 负载均衡
---
apiVersion: v1
kind: Service
metadata:
name: nextjs-service
spec:
type: LoadBalancer # 如果有负载均衡就选负载均衡, 如果没有, 就选 ClusterIP
externalIPs:
- 192.168.36.100
selector:
app: nextjs
ports:
- protocol: TCP
port: 3000
targetPort: 3000
name: http
# 部署 ingress
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: nextjs-service-ingress
spec:
defaultBackend:
service:
name: nextjs-service
port:
number: 3000
ingressClassName: nginx
rules:
- host: nextjs.kubernetes.jansora.com
http:
paths:
- backend:
service:
name: nextjs-service
port:
number: 3000
pathType: Prefix
path: /
4. 部署到 Vercel
在 https://vercel.com/ 使用 Github 登录
新建项目, 绑定 repository 和 branch
框架选择 next 即可
部署完成后, 默认会给个 domain, 也可以选择自定义域名
附录
通过 github action 来管理全流程
在项目根目录下新建 action 文件 .github/workflows/deploy.yaml
name: Build and Publish Docker Image
on:
push:
branches: # 分支匹配时执行 action
- '2.0'
- master
jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
with:
fetch-depth: 2 # fetch head and merge-base
- name: Analysis And Extract Commit Message # 在 commit 中提取 ci 和版本信息, commit message 中包含 ci 时才执行剩余 ci流程
run: |
COMMIT_MESSAGE=$(git log -1 --pretty=%B) # 获取最后一次提交的 commit message
VERSION=$(echo "${COMMIT_MESSAGE}" | grep -o -E "version@[0-9]+\.[0-9]+\.[0-9]+" | sed 's/version@//')
if [[ $(git log -1 --pretty=%B) == *"ci"* ]]; then
DO_CI=true
else
DO_CI=false
fi
echo "BRANCH_NAME=$(basename ${GITHUB_REF})" >> $GITHUB_ENV
echo "VERSION=${VERSION}" >> $GITHUB_ENV
echo "DO_CI=${DO_CI}" >> $GITHUB_ENV
- name: Build Docker image
run: |
if [ "${DO_CI}" = "true" ]; then
docker build -t jansora/nextjs:${VERSION} .
else
echo "skipped."
fi
- name: Push to Aliyun HK Docker Registry # 推送镜像
run: |
if [ "${DO_CI}" = "true" ]; then
echo "${{ secrets.DEPLOYMENT_HOST_PASSWORD }}" | docker login --username=xxx registry.cn-hongkong.aliyuncs.com --password-stdin
docker tag jansora/nextjs:${VERSION} registry.cn-hongkong.aliyuncs.com/jansora/nextjs:${VERSION}
docker push registry.cn-hongkong.aliyuncs.com/jansora/nextjs:${VERSION}
else
echo "skipped."
fi
- name: Deploy to Kubernetes by SSH # 通过代理服务器登录 k8s 集群 controller-pane 所在节点, 自动更新镜像 kubectl set image deployment/nextjs nextjs=registry.cn-hongkong.aliyuncs.com/jansora/nextjs:${VERSION}
run: |
if [ "${DO_CI}" = "true" ]; then
sudo apt install proxytunnel sshpass -y
sshpass -p '${{ secrets.DEPLOYMENT_HOST_PASSWORD }}' ssh -o 'ProxyCommand=proxytunnel -p proxy.jansora.com:3128 -P name:password -d %h:%p' \
root@master.kubernetes.jansora.com -o StrictHostKeyChecking=no \
"export KUBECONFIG=/etc/kubernetes/admin.conf && /usr/bin/kubectl set image deployment/nextjs nextjs=registry.cn-hongkong.aliyuncs.com/jansora/nextjs:${VERSION} "
else
echo "skipped."
fi
其他
推荐 aliyun 香港镜像地址, github 和 内地访问速度都比较友好