我有一个 docker 设置,其中一个容器中有一个 nextjs 应用程序,另一个容器中有一个 postgres 数据库,使用 prisma 作为 ORM。容器启动正常,我可以在 localhost:3000 上访问我的 nextjs 应用程序,但没有显示 layout.tsx 或根 page.tsx 中的内容?我也在这些文件中放入了一些日志,但没有看到任何输出。
我相信这是连接到数据库的一些问题,因为我可以在我的开发设置中重现这个问题,只需设置不正确的 postgres .env 变量。在开发过程中,我使用 Neon 作为远程数据库,但我想继续生产,我想要在容器中使用本地 postgres 数据库,这就是问题开始的地方。切换回 Neon 连接变量,站点按预期正常加载。
我可以使用 pgadmin 服务访问数据库,并且可以连接到数据库服务,我可以看到那里的所有表,但是它们是空的,因为该网站不会加载正文内容。
我可以成功地在容器之间执行 ping 操作,因此它们必须以某种方式进行通信。使用 docker dekstop 界面并使用 nextjs-app logobrewer 中的终端:
/app $ ping postgres_db
PING postgres_db (172.31.0.2): 56 data bytes
64 bytes from 172.31.0.2: seq=0 ttl=42 time=0.038 ms
64 bytes from 172.31.0.2: seq=1 ttl=42 time=0.053 ms
.env
DATABASE_URL=postgres://postgres:password@postgres_db:5432/logobrewer?schema=public
PGHOST=postgres_db
PGDATABASE=logobrewer
PGUSER=postgres
PGPASSWORD=password
Docker-compose.yml(Ravis 评论后编辑)
version: '3.8'
services:
postgres_db:
image: postgres:15
container_name: postgres_db
restart: always
environment:
POSTGRES_DB: $PGDATABASE
POSTGRES_USER: $PGUSER
POSTGRES_PASSWORD: $PGPASSWORD
ports:
- "5432:5432"
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app_network
app:
container_name: logobrewer
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
- "5555:5555"
environment:
NODE_ENV: production
DATABASE_URL: postgres://postgres:password@postgres_db:5432/logobrewer?schema=public
volumes:
- F:\React\LogoAi\logo-ai\logs:/logs
command: >
sh -c "
until pg_isready -h postgres_db -p 5432 -U postgres; do
echo 'Waiting for Postgres...';
sleep 2;
done;
npx prisma migrate deploy && node server.js"
depends_on:
postgres_db:
condition: service_healthy
networks:
- app_network
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin4_container
restart: always
ports:
- "8888:80"
environment:
PGADMIN_DEFAULT_EMAIL: [email protected]
PGADMIN_DEFAULT_PASSWORD: 1234
volumes:
- pgadmin_data:/var/lib/pgadmin
depends_on:
- postgres_db
networks:
- app_network
volumes:
postgres_data:
pgadmin_data:
networks:
app_network:
driver: bridge
schema.prisma:
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema
// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "debian-openssl-1.1.x", "debian-openssl-3.0.x", "linux-musl", "linux-musl-openssl-3.0.x", "linux-musl-arm64-openssl-1.1.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Product {
id String @id @default(cuid())
name String
description String?
stripeProductId String
type String
orders Order[] // Establishes a one-to-many relationship with Order
}
Dockerfile:
FROM node:18.18-alpine AS deps
WORKDIR /app
RUN apk add --no-cache \
libc6-compat \
cairo-dev \
pango-dev \
cairo-tools \
giflib-dev \
pixman-dev \
libjpeg-turbo-dev \
build-base \
python3 \
make \
g++ \
bash \
pkgconfig \
wget \
&& ln -sf /usr/bin/python3 /usr/bin/python
COPY package.json package-lock.json* ./
RUN \
if [ -f yarn.lock ]; then yarn --frozen-lockfile; \
elif [ -f package-lock.json ]; then npm ci; \
elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \
else echo "Lockfile not found." && exit 1; \
fi
# Force canvas installation if it has issues
RUN npm install canvas --build-from-source --legacy-peer-deps
# Rebuild the source code only when needed
FROM deps AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npx prisma generate
RUN npm run build
# Production image, copy all the files and run Next.js
FROM builder AS runner
WORKDIR /app
# Install Cairo runtime libraries (needed for canvas)
RUN apk add --no-cache \
cairo \
pango \
fontconfig \
libc6-compat # Sometimes required for compatibility in Alpine
# Set environment variables
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
ENV HOME=/home/nextjs
# Create and configure non-root user
RUN addgroup -S nodejs && adduser -S -G nodejs -u 1001 -D -h /home/nextjs nextjs \
&& mkdir -p /home/nextjs \
&& chown -R nextjs:nodejs /home/nextjs
# Create and set permissions for the /logs directory
RUN mkdir -p /logs && chown nextjs:nodejs /logs
# Copy necessary files from builder stage
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next ./app/.next
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma
# Set ownership for the app directory
RUN chown -R nextjs:nodejs /app
#Use this to specify which folders instead of above which takes all the files
#RUN chown -R nextjs:nodejs /app/.next /app/public /app/prisma
# Use non-root user
USER nextjs
# Expose port and start the server
EXPOSE 3000
CMD ["node", "server.js"]
因此,在开发(本地启动应用程序 npm run dev)中使用 Neon 连接变量,效果很好。但现在切换到在同一 Docker 网络中的容器中并行运行的本地数据库,问题就开始了。
我相信我已经尝试了多种解决方案,例如重写 docker-compose、dockerfile、docker 网络配置。
编辑:经过更多思考,问题可能是在构建 docker 映像时我的 dockerfile 收到如下警告:
Invalid `prisma.generation.findFirst()` invocation:
Can't reach database server at `postgres_db:5432`
Please make sure your database server is running at `postgres_db:5432`. PrismaClientInitializationError:
Invalid `prisma.generation.findFirst()` invocation:
Can't reach database server at `postgres_db:5432`
Please make sure your database server is running at `postgres_db:5432`.
at qn.handleRequestError (F:\React\LogoAi\logo-ai\node_modules\@prisma\client\runtime\library.js:121:7615)
稍后将图像旋转到容器时会导致问题吗?
此外,我尝试使用 Postman 到达代码中的 api 端点,这会进行如下数据库查找:
export async function GET() {
try {
const getUserSession = await prisma.session.findFirst();
if (getUserSession) {
return NextResponse.json(getUserSession);
}
}
我实际上从数据库中获取数据。那么我认为应用程序需要有连接吗?这太奇怪了。
由于某种原因,应用程序仍然不加载layout.tsx子项,但导航栏和页脚已加载。这是布局页面,我在其中进行了一些调试,但它们从未显示任何输出:
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";
import SessionProvider from "./components/SessionProvider";
import CookieConsentBanner from "./components/CookieConsentBanner";
import Register from "./components/Register";
import { Toaster } from "react-hot-toast";
import Footer from "./components/Footer";
import logger from "@/utils/logger";
import Nav from "./components/Nav";
import { log } from "console";
const inter = Inter({ subsets: ["latin"] });
export const metadata: Metadata = {
title: "LogoBrewer",
description: "Generated by create next app",
};
export default async function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
try {
console.log("RootLayout");
logger.info("RootLayout");
return (
<html lang="en" className="bg-base-100 min-h-screen ">
<SessionProvider>
<body className={`${inter.className} min-h-screen flex flex-col`}>
{/* only appears if user hasnt registered */}
<Register />
<Nav />
<div className="flex justify-center">
<Toaster
position="bottom-center"
toastOptions={{
duration: 5000,
style: {
padding: "1em",
},
}}
/>
{children}
<CookieConsentBanner />
</div>
<Footer />
</body>
</SessionProvider>
</html>
);
} catch (error) {
logger.error(error);
}
}
Prisma 依赖
DATABASE_URL
进行连接,如果数据库尚未准备好,迁移和后续查询将失败。
command
中的 docker-compose.yml
:
command: >
sh -c "
until pg_isready -h postgres_db -p 5432 -U postgres; do
echo 'Waiting for Postgres...';
sleep 2;
done;
npx prisma migrate deploy && node server.js"
调整depends_on:虽然depends_on确保启动顺序,但它不会等待准备就绪。使用适当的健康检查,您已经对 postgres_db 进行了检查。
重建容器:确保应用所有更改:
docker-compose down -v
docker-compose up --build