作为从事全栈应用程序开发的开发人员,您需要拥有一个尽可能接近生产环境的本地开发环境。这将允许您在将应用程序部署到生产环境之前在本地测试和调试应用程序。
几乎每个全栈应用程序都需要一个数据库和一个文件存储,因此,让我们构建一个基本的全栈应用程序,该应用程序可以保存和检索数据库中的数据,以及上传和下载文件存储中的文件。
您可以在本地运行自己的 PostgreSQL 和 Minio S3 服务器,甚至可以使用 AWS RDS 和 S3 等云服务。但这需要一些时间来设置和配置。使用 docker-compose 可以非常轻松地为您的全栈应用程序设置本地开发环境。在本地对其进行测试后,您需要切换到生产环境所做的全部工作就是更改环境变量。
此外,您可以在尽可能接近生产环境的本地环境中端到端测试您的应用程序(例如,使用 Cypress)。预先配置的 docker-compose 文件将使设置 GitHub Actions 或 GitLab CI 等 CI/CD 管道变得容易。
一旦你拥有一个 docker-compose 文件,你和你团队就可以使用它在任何机器上用一个命令在几分钟内设置相同的本地开发环境。总体而言,它将为你节省大量时间,让你的生活更轻松。
在本文中,我们将研究如何使用 Docker-Compose 为一个完整的 Next.js 应用程序构建一个本地开发环境,该应用程序使用 Prisma ORM 连接到 PostgreSQL 作为数据库,并使用 Minio S3 作为文件存储。
您可以在 GitHub 上找到本教程的完整源代码
当您准备好将应用程序部署到生产环境时,可以使用任何类型的数据库:
文件存储也是如此,您需要一个兼容 S3 协议的文件存储:
要按照本教程进行操作,您需要在计算机上安装 Docker 和 Docker-Compose。您可以在 Docker 官方网站上找到有关如何安装 Docker 和 Docker-Compose 的说明。
当我第一次面临为 Next.js、Prisma 和 PostgreSQL 设置本地开发环境的任务时,我尝试使用 T3 Docker 教程,但它对我不起作用。不过,我将其用作本教程的起点。
1. 创建一个 Next.js 应用程序
让我们从创建一个 Next.js 应用程序开始。在本教程中,我们将使用 T3 堆栈(TypeScript、TailwindCSS 和 Prisma ORM)来跳过安装和配置所有依赖项,这超出了本文的范围。您可以在此处找到有关 T3 堆栈的更多信息。
运行以下命令以创建一个新的 Next.js:
npm create t3-app@latest
运行命令后,系统会询问您几个问题:
___ ___ ___ __ _____ ___ _____ ____ __ ___ ___
/ __| _ \ __| / \_ _| __| |_ _|__ / / \ | _ \ _ \
| (__| / _| / /\ \| | | _| | | |_ \ / /\ \| _/ _/
\___|_|_\___|_/‾‾\_\_| |___| |_| |___/ /_/‾‾\_\_| |_|
◇ What will your project be called?
│ local-nextjs-postgres-s3
│
◇ Will you be using TypeScript or JavaScript?
│ TypeScript
│
◇ Will you be using Tailwind CSS for styling?
│ Yes
│
◇ Would you like to use tRPC?
│ No
│
◇ What authentication provider would you like to use?
│ None
│
◇ What database ORM would you like to use?
│ Prisma
│
◇ EXPERIMENTAL Would you like to use Next.js App Router?
│ No
│
◇ Should we initialize a Git repository and stage the changes?
│ Yes
│
◇ Should we run 'npm install' for you?
│ Yes
│
◇ What import alias would you like to use?
│ ~/
2. 配置 Next.js 以配合 Docker 使用
根据 Next.js 文档
Next.js 可以自动创建一个独立文件夹,其中仅复制生产部署所需的必要文件,包括 node_modules 中的选定文件。
为了减小图像大小,我们需要在 next.config.js 文件中添加 output: "standalone"
。
next.config.js 文件应如下所示:
const config = {
reactStrictMode: true,
output: "standalone",
// ...
};
3. 添加 .dockerignore 文件
我更喜欢为 docker 文件单独创建一个文件夹,因此我在项目的根目录中创建了一个名为 compose
的文件夹。我们需要向此文件夹中添加一个 .dockerignore
文件,以从 Docker 镜像中排除不必要的文件。 .dockerignore
文件应如下所示:
.env
Dockerfile
.dockerignore
.next
.git
.gitignore
node_modules
npm-debug.log
README.md
4. 配置 Prisma 以配合 Docker 使用
在 prisma/schema.prisma
文件中,我们需要
sqlite
更改为 postgresql
:binaryTargets
添加到生成器块。这将允许我们在 Docker 容器内使用 Prisma CLI。您需要将 binaryTargets
指定为特定于您的操作系统和架构。例如,对于 M1 Mac,我使用 "linux-musl-arm64-openssl-3.0.x"
。要支持其他操作系统和架构,您需要将它们添加到 binaryTargets
数组中。有关 Prisma 文档中的 binaryTargets 的更多信息。我想在具有不同操作系统和架构的机器上用于本地开发以及在 GitHub Actions 上用于 CI/CD 管道的 schema.prisma
文件相同。因此,在我的情况下, prisma/schema.prisma
文件如下所示:
generator client {
provider = "prisma-client-js"
binaryTargets = ["native", "linux-musl-arm64-openssl-3.0.x", "linux-musl-openssl-3.0.x", "rhel-openssl-1.0.x"]
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
5. 为 Next.js 应用程序创建一个 Dockerfile
在 compose
文件夹中,创建一个名为 web.Dockerfile
的文件,内容如下:
FROM node:18-alpine
RUN mkdir app
COPY ../prisma ./app
COPY ../package.json ../package-lock.json ./app
WORKDIR /app
RUN npm ci
CMD ["npm", "run", "dev"]
我们将使用 node:18-alpine
镜像作为基础镜像。这是一个轻量级镜像,包含 Node.js 18 和 npm。我们将复制 /prisma
文件夹、 package.json
和 package-lock.json
文件到 /app
文件夹,并运行 npm ci
来安装依赖项。
在 Docker 镜像中使用“clean install”( npm ci
)而不是“install”( npm i
)是一种好习惯。它将确保依赖项是从 package-lock.json
文件中安装的,而不是从 node_modules 缓存中安装的。这比“install”更快,这对于希望尽可能缩短构建时间的 CI/CD 管道尤为重要。
6. 创建 docker compose 文件
Docker compose 文件用于使用单个命令定义和运行多容器 Docker 应用程序 docker-compose up
。
此处我们不会详细介绍 docker-compose 文件。通常,我们的 docker-compose 文件创建 3 个服务:web(使用我们的 Dockerfile 构建的 Next.js 应用程序)、db(PostgreSQL 数据库)和 minio(Minio S3 文件存储)。请记住为数据库和文件存储服务添加卷。否则,停止容器时数据将丢失。
通常不建议在 docker-compose 文件中存储环境变量。但是,在这个特定场景中,出于教育目的,并且我们仅将其用于本地开发和测试,这看起来是可以接受的。
如果您不想将机密存储在 docker-compose 文件中,则应使用 .env 文件并使用 ${VARIABLE_NAME}
语法来引用变量。有关 docker-compose 文件中的环境变量的更多信息,请参阅 Docker compose 文件参考。
在 compose
文件夹中,创建一个名为 docker-compose.yml
的文件,内容如下:
version: "3.9"
name: nextjs-postgres-s3minio
services:
web:
container_name: nextjs
build:
context: ../
dockerfile: compose/web.Dockerfile
args:
NEXT_PUBLIC_CLIENTVAR: "clientvar"
ports:
- 3000:3000
volumes:
- ../:/app
environment:
- DATABASE_URL=postgresql://postgres:postgres@db:5432/myapp-db?schema=public
- S3_ENDPOINT=minio
- S3_PORT=9000
- S3_ACCESS_KEY=minio
- S3_SECRET_KEY=miniosecret
- S3_BUCKET_NAME=s3bucket
depends_on:
- db
- minio
db:
image: postgres:15.3
container_name: postgres
ports:
- 5432:5432
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: myapp-db
volumes:
- postgres-data:/var/lib/postgresql/data
restart: unless-stopped
minio:
container_name: s3minio
image: bitnami/minio:latest
ports:
- "9000:9000"
- "9001:9001"
volumes:
- minio_storage:/data
volumes:
postgres-data:
minio_storage:
根据 docker-compose 文件的位置和要使用的选项,
您需要运行以下命令:
# If you have docker files in the root of the project
docker-compose up
# In our case, we have dockerfile and docker-compose file in the compose folder, so we need to run:
docker-compose -f compose/docker-compose.yml up
# --- Optional ---
# For running the application with secrets ${VARIABLE_NAME} stored in the .env file, we would need to run:
docker-compose -f compose/docker-compose.yml --env-file .env up
# If you want to run the application in the background, you can use the -d flag:
docker-compose -f compose/docker-compose.yml up -d
它将运行构建 Next.js 应用程序并在端口 3000 上运行它。它还将下载 PostgreSQL 和 Minio S3 docker 镜像,并在端口 5432 和 9000 上分别运行它们。
应用程序构建完毕并下载完镜像后,您将看到以下输出:
您可以在 http://localhost:3000 访问应用程序,在 http://localhost:5432 访问 PostgreSQL 数据库(登录名:postgres,密码:postgres),在 http://localhost:9000 访问 Minio S3(登录名:minio,密码:miniosecret)。
数据库和文件存储的凭据存储在 docker-compose 文件中。如果您愿意,可以更改它们。
在本文中,我们研究了如何为一个完整的 Next.js 应用程序构建一个本地开发环境,其中 Prisma ORM 连接到 PostgreSQL 作为数据库,Minio S3 作为文件存储,使用 Docker-Compose。