Open jkomyno opened 1 year ago
This follows up on https://github.com/prisma/docs/issues/4303 .
Alpine
subsection (as required by https://github.com/prisma/prisma/pull/17389#issue-1537761994)Working on this draft:
Prisma supports the most of the popular Docker images for Node.js out of the box. However, due to the system dependencies the Prisma engines rely on and the complexities of providing compiled binaries for all operating systems and CPU architectures, some adjustments may be needed.
In this tutorial, we'll walk you through the process of setting up a Docker container for a Node.js application that uses Prisma. Given a simple Prisma schema, an empty Postgres database, and a basic Express.js server running on port 3000
, we'll create a Docker image that initialises Prisma, runs the server and connects to the database.
Create a new prisma-docker-tutorial
directory, open it, and paste the following files in there:
// ./prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgres"
url = env("DATABASE_URL")
}
model Author {
id Int @id
post Post[]
}
model Post {
id Int @id
user Author @relation(fields: [userId], references: [id])
userId Int
}
// ./server.js
const express = require('express')
const app = express()
const port = 3000
const { PrismaClient } = require('@prisma/client')
const client = new PrismaClient()
app.get('/', async (req, res) => {
const data = await client.user.findMany()
res.send(JSON.stringify(data))
})
app.listen(port, () => {
console.log(`App listening at http://localhost:${port}`)
})
// ./package.json
{
"name": "@prisma/docker-tutorial",
"license": "MIT",
"devDependencies": {
"prisma": "4.10.0"
},
"scripts": {
"start": "node server.js"
},
"dependencies": {
"@prisma/client": "4.10.0",
"express": "4.18.2"
}
}
./docker-compose.yml
version: '3.7'
services:
postgres:
image: postgres:15
hostname: postgres_db
restart: always
environment:
- POSTGRES_DB=postgres
- POSTGRES_USER=prisma
- POSTGRES_PASSWORD=prisma
ports:
- '5432:5432'
networks:
- prisma-network
prismacmd:
image: prisma-docker-tutorial
stdin_open: true
environment:
- DATABASE_URL=postgresql://prisma:prisma@postgres_db:5432/postgres
depends_on:
- postgres
networks:
- prisma-network
networks:
prisma-network:
name: prisma-network
This tutorial has been tested on Docker version 20.10.16
.
We define the ./Dockerfile
as follows:
node:alpine
)This is the default Docker image for Node.js. It is based on Alpine Linux, which is a lightweight Linux distribution that uses the musl
C standard library.
Prisma supports Alpine on amd64
out of the box, and supports it on arm64
since prisma@4.10.0
.
Related Docker images:
node:lts-alpine
node:16-alpine
node:14-alpine
# syntax=docker/dockerfile:1
FROM node:lts-alpine3.17
WORKDIR /usr/src/app
COPY --from=app . ./
RUN npm i \
&& npx prisma generate
EXPOSE 3000
ENTRYPOINT ["node", "server.js"]
Note: when running on Linux Alpine, Prisma downloads engines that are compiled for the musl
C standard library. Please don't install glibc
on Alpine (e.g., via the libc6-compat
package), as that would prevent Prisma from running successfully.
node:slim
)This image is based on Linux Debian, and uses the glibc
C standard library.
It is mostly supported out of the box on amd64
and arm64
, but as some older versions of this image come without libssl
installed, it is sometimes necessary to install it manually.
Related Docker images:
node:lts-slim
node:bullseye-slim
node:buster-slim
node:stretch-slim
# syntax=docker/dockerfile:1
FROM node:slim
# Note: uncomment the following block if you're using an image based on Debian Buster, which doesn't come with libssl pre-installed
RUN apt-get update -y \
&& apt-get install -y openssl
WORKDIR /usr/src/app
COPY --from=app . ./
RUN npm install \
&& npx prisma generate
EXPOSE 3000
ENTRYPOINT ["node", "server.js"]
gcr.io/distroless/nodejs18-debian11
)Distroless images are a set of minimal images containing only your application and its runtime dependencies. They do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution, which is great for the security of the system.
Although this image is originally based on Linux Debian Bullseye, some manual care is required before it can be used with Prisma. In particular, commands like npm install
and prisma generate
must be run in a separate build stage, as the distroless image doesn't come with npm
or a shell pre-installed. Moreover, the zlib
system dependency is missing, and must be copied from a compatible Debian image manually.
On Distroless, only the amd64
architecture is supported.
# syntax=docker/dockerfile:1
# Build stage on Debian 11
FROM node:18-bullseye-slim as deps
WORKDIR /usr/src/app
COPY --from=app . ./
# Uncomment the following block if you're using an image based on Debian Buster, which doesn't come with libssl pre-installed
RUN apt-get update -y \
&& apt-get install -y openssl
WORKDIR /usr/src/app
COPY . ./
RUN npm i \
&& npx prisma generate
# Runtime stage on Distroless
FROM gcr.io/distroless/nodejs18-debian11 as runtime
WORKDIR /usr/src/app
COPY --from=deps /usr/src/app/ ./
# Copy zlib from Debian, which is needed by Prisma
COPY --from=deps /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1
# the distroless' image entrypoint is /nodejs/bin/node
CMD ["server.js"]
EXPOSE 3000
Setting up Prisma on Docker environments is non-trivial, as plenty of comments in our Github issues demonstrate. We should prepare a tutorial page that describes step-by-step how to use Prisma in the most popular Docker images, both for deploying serverless services (e.g., via AWS Fargate + AWS ECS) and for local development. We should also take system architecture differences between the users host machine and their runtime machine into account (e.g., macOS users with Silicon CPUs won't be able to use
alpine
-based images locally unless they useDocker Buildx
with--platform="linux/amd64"
, as Prisma doesn't work on Alpine on arm64: https://github.com/prisma/prisma/issues/8478)We should target the following system scenarios, inspired from our ecosystem-tests:
node:lts-alpine3.17
(and variants, likenode:lts-alpine
,node:alpine
) onamd64
andarm64
node:16-slim
(and variants, likenode:lts-buster-slim
) onamd64
and onarm64
gcr.io/distroless/nodejs18-debian11
onamd64
Opinable, these were mainly relevant when
prisma
didn't supportopenssl
3 or for users with weird linuxbrew/snap setups, which are arguably not generally used for serverless and local development on Docker:ubuntu:20.04
onamd64
ubuntu:22.04
onamd64
We should show our users how to:
docker-compose.yaml
, whose URL one can configure when building the Docker image)prisma db push
For constrained, barebone environments (such as
gcr.io/distroless
images and actual serverless services) we should tell users to write multi-stage Dockerfiles and only deploy the Prisma Client, delegating commands likeprisma db push
andprisma generate
to a previous docker stage.On Linux Alpine we should tell users not to install
libc
.Motivating issues
TODOs
libc6-compat
on Alpine foramd64
breaks anything with Prisma