我构建了 Next.js 14 应用程序,我的 API 是使用 Nest.js 和 MongoDB 编写的。对于本地测试,我正在尝试对 API 和前端进行 dockerize。
我的带有 MongoDB 的 Nest.js 已成功 Docker 化,我可以使用以下方式访问 API:
容器正在使用以下名称运行:
nest-server
正在使用以下 URL 访问 mongo-db
:
DATABASE_URI='mongodb://用户
@mongo-db:27017/nesttodos?authSource=admin'
在这里,我正在使用正在运行的 MongoDB 容器访问 MongoDB。
当我使用以下 .env 变量构建 Next.js 应用程序时:
NEXT_PUBLIC_BASE_FRONTEND_URL=http://nextjs-app:4000
NEXT_FRONTEND_URL=http://nextjs-app:4000
NEXT_BASE_BACKEND_URL=http://nest-server:3001
NEXT_PUBLIC_BASE_BACKEND_URL=http://nest-server:3001
NEXT_PUBLIC_ENV_MODE=production
ENV_MODE=production
我在构建时遇到错误,因为它在生成静态页面时进行 API 调用。下面,我提到了我遇到的错误,以及我的 Dockerfile 和 Docker Compose 文件的源代码。
这是我的 docker-compose.yml 文件:
version: "3.9"
services:
nextjs-app:
build:
context: ..
dockerfile: docker/Dockerfile.next.prod
container_name: nextjs-app
env_file:
../nextjs/.env
ports:
"4000:4000"
restart: unless-stopped
networks:
hq-network:
networks:
hq-network:
driver: bridge
我的docker/Dockerfile.next.prod文件如下:
# Stage 1: Build environment (with dependencies)
FROM node:22-alpine as base
# 1. Install dependencies
FROM base as deps
# 2. libc6-compat is needed
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies
COPY ../nextjs/package*.json ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm install; \
else echo "Lockfile not found." && exit 1; \
fi
# 3. Rebuild source code
FROM base as builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY ../nextjs ./
COPY ../nextjs/.env ./
RUN npm run build
# 4. Production image, copy all the files and running Next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
# Now copy files from builder
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
EXPOSE 4000
ENV PORT=4000
CMD ["node", "server.js"]
我的page.tsx文件包含以下代码:
import { RscCreateTodo, RscReadAllTodo } from "@/actions/RscActions";
import { todoData } from "@/types/allTypes";
export default async function Home() {
const AllTodos = await RscReadAllTodo();
// Create todo
const addTodo = async (event: FormData) => {
"use server";
const todoValue = event.get("todo")?.toString();
if (!todoValue) {
return;
}
await RscCreateTodo({
todo: todoValue,
});
event.set("todo", "");
};
return (
<div className="flex min-h-screen items-start justify-items-center gap-16 p-8 pb-20 sm:p-20">
<main className="w-full">
<div className="my-10 w-full">
<h1 className="mb-4 text-center text-3xl font-semibold">
To Do List
</h1>
<div className="mx-auto md:w-1/2">
<div className="rounded-lg bg-white p-6 shadow-md">
<form id="todo-form" action={addTodo}>
<div className="mb-4 flex">
<input
name="todo"
type="text"
className="mr-2 w-full rounded-lg border border-blue-300 px-4 py-2 focus:border-blue-500 focus:outline-none"
id="todo-input"
placeholder="Add new task"
required
/>
<button className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700">
Add
</button>
</div>
</form>
<ul
id="todo-list"
className="scrollbarwork max-h-[calc(100vh-30vh)] overflow-x-hidden"
>
{/* todos list here */}
{AllTodos?.map((todo: todoData, index: number) => {
return (
<li key={todo?._id}>
{index + 1}. {todo.todo}
</li>
);
})}
</ul>
</div>
</div>
</div>
</main>
</div>
);
}
我在 Docker 构建过程中遇到以下错误:
CACHED [nextjs-app builder 2/5] COPY --from=deps /app/node_modules ./node_modules
[nextjs-app builder 3/5] COPY ../nextjs ./
[nextjs-app builder 4/5] COPY ../nextjs/.env ./
ERROR [nextjs-app builder 5/5] RUN npm run build
> [nextjs-app builder 5/5] RUN npm run build:
> 2.544
> 2.544 > [email protected] build
> 2.544 > next build
> 2.544
> 5.089 Attention: Next.js now collects completely anonymous telemetry regarding usage.
> 5.091 This information is used to shape Next.js' roadmap and prioritize features.
> 5.091 You can learn more, including how to opt-out if you'd not like to participate in this anonymous program, by visiting
> 5.091 https://nextjs.org/telemetry
> 5.091
> 5.370 ▲ Next.js 14.2.13
> 5.370 - Environments: .env
> 5.370 - Experiments (use with caution):
> 5.370 · scrollRestoration
> 5.373
> 5.630 Creating an optimized production build ...
> 36.05 ✓ Compiled successfully
> 36.05 Linting and checking validity of types ...
> 43.03 Collecting page data ...
> 45.37 Generating static pages (0/5) ...
> 45.85 Generating static pages (1/5)
> 45.85 Generating static pages (2/5)
> 45.85 Generating static pages (3/5)
> 48.98 TypeError: fetch failed
> 48.98 at node:internal/deps/undici/undici:13185:13
> 48.98 at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
> 48.98 at async o (/app/.next/server/app/page.js:2:63081)
> 48.98 at async o (/app/.next/server/app/page.js:2:64069) {
> 48.98 digest: '3194799139',
> 48.98 [cause]: Error: getaddrinfo ENOTFOUND nest-server
> 48.98 at GetAddrInfoReqWrap.onlookupall [as oncomplete] (node:dns:120:26)
> 48.98 at GetAddrInfoReqWrap.callbackTrampoline (node:internal/async_hooks:130:17) {
> 48.98 errno: -3008,
> 48.98 code: 'ENOTFOUND',
> 48.98 syscall: 'getaddrinfo',
> 48.98 hostname: 'nest-server'
> 48.98 }
> 48.98 }
> 52.24 TypeError: fetch failed
> 52.24 at node:internal/deps/undici/undici:13185:13
> 52.24 at process.processTicksAndRejections (node:internal/process/task_queues:105:5)
> 52.24 at async o (/app/.next/server/app/page.js:2:63081)
> 52.24 at async o (/app/.next/server/app/page.js:2:64069) {
> 52.24 digest: '3194799139',
> 52.24 [cause]: Error: getaddrinfo ENOTFOUND nest-server
> 52.24 at GetAddrInfoReqWrap.on
您遇到的错误与 Docker 设置中的 DNS 问题有关。本质上,当您的 Next.js 应用程序在构建阶段尝试连接到
nest-server
时,它无法找到 nest-server
,因为此时 Docker 网络未处于活动状态。
以下是发生的情况以及解决方法:
nest-server
API 尚未启动,因此应用程序无法访问它。构建过程发生在 Docker 容器完全运行之前,因此此时无法访问网络(包括 nest-server
)。nest-server
API 可用,Next.js 尝试在构建过程中获取数据。但由于服务器尚未运行,因此无法连接。nest-server
名称才会被识别。但是,在构建过程中,Docker 无法解析该名称。不要在构建过程中尝试获取数据,而是等到应用程序运行。您可以在应用程序启动后使用
getServerSideProps
获取数据。这可确保发出数据请求时 API 可用。
如果在构建阶段无法访问 API,您可以提供一些模拟数据以保持构建过程顺利进行。一旦应用程序运行,它将使用真正的API。
您可以在构建阶段设置不同的 API URL(例如,模拟 API 或不同的配置)。这可以让您避免在构建过程中连接到真实的
nest-server
。
确保在构建应用程序时跳过 API 调用。您可以编写一个条件来检查应用程序是否在构建环境中运行,并避免在应用程序上线之前获取数据。
对于本地开发,您可以改用
localhost
而不是像 nest-server
这样的 Docker 服务名称。这有助于避免本地测试时出现 DNS 问题。
最简单的解决方案是通过在 Next.js 中使用
getServerSideProps
将数据获取延迟到运行时。这可确保您的应用程序等到所有服务(包括 nest-server
)都运行后再尝试获取数据。